@datasworn-community/build-tools 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -3
- package/schema-source/builders/IdBuilder.ts +849 -0
- package/schema-source/schema/common/Id.ts +3 -1
- package/schema-source/schema/delve/common.ts +3 -1
- package/schema-source/schema/generic/CollectableNode.ts +3 -1
- package/schema-source/schema/generic/CollectionNode.ts +3 -1
- package/schema-source/schema/generic/EmbedOnlyNode.ts +3 -1
- package/schema-source/schema/generic/NonCollectableNode.ts +3 -1
- package/schema-source/schema/generic/PrimaryTypeNode.ts +3 -1
- package/schema-source/schema/rules/TagRule.ts +3 -1
- package/schema-source/schema/utils/EmbeddedNode.ts +3 -1
- package/schema-source/schema/utils/string.ts +2 -2
- package/schema-source/scripts/augmentations.ts +70 -0
- package/schema-source/scripts/codegen/schemaToTsDeclaration.ts +290 -0
- package/schema-source/scripts/codegen/tsDocTags.ts +75 -0
- package/schema-source/scripts/codegen/writeTypescriptTypes.ts +35 -0
- package/schema-source/scripts/const.ts +40 -2
- package/schema-source/scripts/datasworn/sort.ts +80 -0
- package/schema-source/scripts/json-typedef/typedef.ts +1138 -0
- package/schema-source/scripts/json-typedef/utils.ts +349 -0
- package/schema-source/scripts/schema/buildSchema.ts +109 -0
- package/schema-source/scripts/utils/Log.ts +15 -0
- package/schema-source/scripts/utils/readWrite.ts +180 -0
- package/schema-source/scripts/validation/ajv.ts +29 -0
- package/schema-source/scripts/validation/formats.ts +6 -0
- package/schema-source/scripts/validation/keywords.ts +36 -0
|
@@ -0,0 +1,1138 @@
|
|
|
1
|
+
/* eslint-disable object-shorthand */
|
|
2
|
+
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
|
|
3
|
+
/* eslint-disable @typescript-eslint/prefer-reduce-type-parameter */
|
|
4
|
+
/* eslint-disable @typescript-eslint/lines-between-class-members */
|
|
5
|
+
/* eslint-disable @typescript-eslint/no-array-constructor */
|
|
6
|
+
/* eslint-disable @typescript-eslint/dot-notation */
|
|
7
|
+
/* eslint-disable @typescript-eslint/consistent-indexed-object-style */
|
|
8
|
+
/* eslint-disable @typescript-eslint/no-namespace */
|
|
9
|
+
/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
|
|
10
|
+
|
|
11
|
+
// adapted from https://github.com/sinclairzx81/typebox/blob/master/examples/typedef/typedef.ts
|
|
12
|
+
|
|
13
|
+
import * as Types from '@sinclair/typebox'
|
|
14
|
+
import {
|
|
15
|
+
DefaultErrorFunction,
|
|
16
|
+
SetErrorFunction
|
|
17
|
+
} from '@sinclair/typebox/errors'
|
|
18
|
+
import type { SchemaFormEmpty, SchemaFormEnum, SchemaFormType } from 'jtd'
|
|
19
|
+
|
|
20
|
+
// --------------------------------------------------------------------------
|
|
21
|
+
// Utility Types
|
|
22
|
+
// --------------------------------------------------------------------------
|
|
23
|
+
export type Assert<T, U> = T extends U ? T : never
|
|
24
|
+
export type Base = { m: string; t: string }
|
|
25
|
+
export type Base16 = {
|
|
26
|
+
m: 'F'
|
|
27
|
+
t: '01'
|
|
28
|
+
'0': '1'
|
|
29
|
+
'1': '2'
|
|
30
|
+
'2': '3'
|
|
31
|
+
'3': '4'
|
|
32
|
+
'4': '5'
|
|
33
|
+
'5': '6'
|
|
34
|
+
'6': '7'
|
|
35
|
+
'7': '8'
|
|
36
|
+
'8': '9'
|
|
37
|
+
'9': 'A'
|
|
38
|
+
A: 'B'
|
|
39
|
+
B: 'C'
|
|
40
|
+
C: 'D'
|
|
41
|
+
D: 'E'
|
|
42
|
+
E: 'F'
|
|
43
|
+
F: '0'
|
|
44
|
+
}
|
|
45
|
+
export type Base10 = {
|
|
46
|
+
m: '9'
|
|
47
|
+
t: '01'
|
|
48
|
+
'0': '1'
|
|
49
|
+
'1': '2'
|
|
50
|
+
'2': '3'
|
|
51
|
+
'3': '4'
|
|
52
|
+
'4': '5'
|
|
53
|
+
'5': '6'
|
|
54
|
+
'6': '7'
|
|
55
|
+
'7': '8'
|
|
56
|
+
'8': '9'
|
|
57
|
+
'9': '0'
|
|
58
|
+
}
|
|
59
|
+
export type Reverse<T extends string> = T extends `${infer L}${infer R}`
|
|
60
|
+
? `${Reverse<R>}${L}`
|
|
61
|
+
: T
|
|
62
|
+
export type Tick<T extends string, B extends Base> = T extends keyof B
|
|
63
|
+
? B[T]
|
|
64
|
+
: never
|
|
65
|
+
export type Next<T extends string, B extends Base> = T extends Assert<
|
|
66
|
+
B,
|
|
67
|
+
Base
|
|
68
|
+
>['m']
|
|
69
|
+
? Assert<B, Base>['t']
|
|
70
|
+
: T extends `${infer L}${infer R}`
|
|
71
|
+
? L extends Assert<B, Base>['m']
|
|
72
|
+
? `${Assert<Tick<L, B>, string>}${Next<R, B>}`
|
|
73
|
+
: `${Assert<Tick<L, B>, string>}${R}`
|
|
74
|
+
: never
|
|
75
|
+
export type Increment<T extends string, B extends Base = Base10> = Reverse<
|
|
76
|
+
Next<Reverse<T>, B>
|
|
77
|
+
>
|
|
78
|
+
// --------------------------------------------------------------------------
|
|
79
|
+
// SchemaOptions
|
|
80
|
+
// --------------------------------------------------------------------------
|
|
81
|
+
|
|
82
|
+
export type Metadata = {
|
|
83
|
+
/**
|
|
84
|
+
* `description` customizes the documentation comment to put on a type or property in generated code.
|
|
85
|
+
*/
|
|
86
|
+
description?: string
|
|
87
|
+
/** Override for code generation, used as-is. */
|
|
88
|
+
typescriptType?: string
|
|
89
|
+
/** Override for code generation, used as-is. */
|
|
90
|
+
csharpSystemTextType?: string
|
|
91
|
+
/** Override for code generation, used as-is. */
|
|
92
|
+
goType?: string
|
|
93
|
+
/** Override for code generation, used as-is. */
|
|
94
|
+
javaJacksonType?: string
|
|
95
|
+
/** Override for code generation, used as-is. */
|
|
96
|
+
pythonType?: string
|
|
97
|
+
/** Override for code generation, used as-is. */
|
|
98
|
+
rubyType?: string
|
|
99
|
+
/** Override for code generation, used as-is. */
|
|
100
|
+
rustType?: string
|
|
101
|
+
} & Record<string, unknown>
|
|
102
|
+
|
|
103
|
+
export type EnumMetadata = Metadata & {
|
|
104
|
+
/**
|
|
105
|
+
* `enumDescription` is like `description`, but for the members of an `enum`. The keys of `enumDescription` should correspond to the values in the schema’s enum, and the values should be descriptions for those values.
|
|
106
|
+
*/
|
|
107
|
+
enumDescription?: Record<string, string>
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// --------------------------------------------------------------------------
|
|
111
|
+
// TAny
|
|
112
|
+
// --------------------------------------------------------------------------
|
|
113
|
+
|
|
114
|
+
export interface TAny extends Types.TSchema {
|
|
115
|
+
[Types.Kind]: 'TypeDef:Any'
|
|
116
|
+
static: any
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// --------------------------------------------------------------------------
|
|
120
|
+
// TArray
|
|
121
|
+
// --------------------------------------------------------------------------
|
|
122
|
+
export interface TArray<T extends Types.TSchema = Types.TSchema>
|
|
123
|
+
extends Types.TSchema {
|
|
124
|
+
[Types.Kind]: 'TypeDef:Array'
|
|
125
|
+
static: Types.Static<T, this['params']>[]
|
|
126
|
+
elements: T
|
|
127
|
+
}
|
|
128
|
+
// --------------------------------------------------------------------------
|
|
129
|
+
// TBoolean
|
|
130
|
+
// --------------------------------------------------------------------------
|
|
131
|
+
export interface TBoolean extends Types.TSchema {
|
|
132
|
+
[Types.Kind]: 'TypeDef:Boolean'
|
|
133
|
+
static: 'boolean'
|
|
134
|
+
type: 'boolean'
|
|
135
|
+
}
|
|
136
|
+
// --------------------------------------------------------------------------
|
|
137
|
+
// TUnion
|
|
138
|
+
// --------------------------------------------------------------------------
|
|
139
|
+
type InferUnion<
|
|
140
|
+
T extends TStruct[],
|
|
141
|
+
D extends string,
|
|
142
|
+
Index = string
|
|
143
|
+
> = T extends [infer L, ...infer R]
|
|
144
|
+
?
|
|
145
|
+
| Types.Evaluate<{ [_ in D]: Index } & Types.Static<Types.AssertType<L>>>
|
|
146
|
+
| InferUnion<
|
|
147
|
+
Types.AssertRest<R, TStruct[]>,
|
|
148
|
+
D,
|
|
149
|
+
Increment<Types.Assert<Index, string>>
|
|
150
|
+
>
|
|
151
|
+
: never
|
|
152
|
+
|
|
153
|
+
export interface TUnion<
|
|
154
|
+
T extends TStruct[] = TStruct[],
|
|
155
|
+
D extends string = string
|
|
156
|
+
> extends Types.TSchema {
|
|
157
|
+
[Types.Kind]: 'TypeDef:Union'
|
|
158
|
+
static: InferUnion<T, D, '0'>
|
|
159
|
+
discriminator: D
|
|
160
|
+
mapping: T
|
|
161
|
+
}
|
|
162
|
+
// --------------------------------------------------------------------------
|
|
163
|
+
// TEnum
|
|
164
|
+
// --------------------------------------------------------------------------
|
|
165
|
+
export interface TEnum<T extends string[] = string[]>
|
|
166
|
+
extends Types.TSchema,
|
|
167
|
+
SchemaFormEnum {
|
|
168
|
+
[Types.Kind]: 'TypeDef:Enum'
|
|
169
|
+
static: T[number]
|
|
170
|
+
enum: [...T]
|
|
171
|
+
metadata: EnumMetadata
|
|
172
|
+
}
|
|
173
|
+
// --------------------------------------------------------------------------
|
|
174
|
+
// TFloat32
|
|
175
|
+
// --------------------------------------------------------------------------
|
|
176
|
+
export interface TFloat32 extends Types.TSchema, SchemaFormType {
|
|
177
|
+
[Types.Kind]: 'TypeDef:Float32'
|
|
178
|
+
type: 'float32'
|
|
179
|
+
static: number
|
|
180
|
+
}
|
|
181
|
+
// --------------------------------------------------------------------------
|
|
182
|
+
// TFloat64
|
|
183
|
+
// --------------------------------------------------------------------------
|
|
184
|
+
export interface TFloat64 extends Types.TSchema, SchemaFormType {
|
|
185
|
+
[Types.Kind]: 'TypeDef:Float64'
|
|
186
|
+
type: 'float64'
|
|
187
|
+
static: number
|
|
188
|
+
}
|
|
189
|
+
// --------------------------------------------------------------------------
|
|
190
|
+
// TInt8
|
|
191
|
+
// --------------------------------------------------------------------------
|
|
192
|
+
export interface TInt8 extends Types.TSchema, SchemaFormType {
|
|
193
|
+
[Types.Kind]: 'TypeDef:Int8'
|
|
194
|
+
type: 'int8'
|
|
195
|
+
static: number
|
|
196
|
+
}
|
|
197
|
+
// --------------------------------------------------------------------------
|
|
198
|
+
// TInt16
|
|
199
|
+
// --------------------------------------------------------------------------
|
|
200
|
+
export interface TInt16 extends Types.TSchema, SchemaFormType {
|
|
201
|
+
[Types.Kind]: 'TypeDef:Int16'
|
|
202
|
+
type: 'int16'
|
|
203
|
+
static: number
|
|
204
|
+
}
|
|
205
|
+
// --------------------------------------------------------------------------
|
|
206
|
+
// TInt32
|
|
207
|
+
// --------------------------------------------------------------------------
|
|
208
|
+
export interface TInt32 extends Types.TSchema, SchemaFormType {
|
|
209
|
+
[Types.Kind]: 'TypeDef:Int32'
|
|
210
|
+
type: 'int32'
|
|
211
|
+
static: number
|
|
212
|
+
}
|
|
213
|
+
// --------------------------------------------------------------------------
|
|
214
|
+
// TUint8
|
|
215
|
+
// --------------------------------------------------------------------------
|
|
216
|
+
export interface TUint8 extends Types.TSchema, SchemaFormType {
|
|
217
|
+
[Types.Kind]: 'TypeDef:Uint8'
|
|
218
|
+
type: 'uint8'
|
|
219
|
+
static: number
|
|
220
|
+
}
|
|
221
|
+
// --------------------------------------------------------------------------
|
|
222
|
+
// TUint16
|
|
223
|
+
// --------------------------------------------------------------------------
|
|
224
|
+
export interface TUint16 extends Types.TSchema, SchemaFormType {
|
|
225
|
+
[Types.Kind]: 'TypeDef:Uint16'
|
|
226
|
+
type: 'uint16'
|
|
227
|
+
static: number
|
|
228
|
+
}
|
|
229
|
+
// --------------------------------------------------------------------------
|
|
230
|
+
// TUint32
|
|
231
|
+
// --------------------------------------------------------------------------
|
|
232
|
+
export interface TUint32 extends Types.TSchema, SchemaFormType {
|
|
233
|
+
[Types.Kind]: 'TypeDef:Uint32'
|
|
234
|
+
type: 'uint32'
|
|
235
|
+
static: number
|
|
236
|
+
}
|
|
237
|
+
// --------------------------------------------------------------------------
|
|
238
|
+
// TProperties
|
|
239
|
+
// --------------------------------------------------------------------------
|
|
240
|
+
export type TFields = Record<string, Types.TSchema>
|
|
241
|
+
// --------------------------------------------------------------------------
|
|
242
|
+
// TRecord
|
|
243
|
+
// --------------------------------------------------------------------------
|
|
244
|
+
export interface TRecord<T extends Types.TSchema = Types.TSchema>
|
|
245
|
+
extends Types.TSchema {
|
|
246
|
+
[Types.Kind]: 'TypeDef:Record'
|
|
247
|
+
static: Record<string, Types.Static<T, this['params']>>
|
|
248
|
+
values: T
|
|
249
|
+
}
|
|
250
|
+
// --------------------------------------------------------------------------
|
|
251
|
+
// TString
|
|
252
|
+
// --------------------------------------------------------------------------
|
|
253
|
+
export interface TString extends Types.TSchema {
|
|
254
|
+
[Types.Kind]: 'TypeDef:String'
|
|
255
|
+
type: 'string'
|
|
256
|
+
static: string
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// --------------------------------------------------------------------------
|
|
260
|
+
// TStruct
|
|
261
|
+
// --------------------------------------------------------------------------
|
|
262
|
+
// used for structural type inference
|
|
263
|
+
type OptionalKeys<T extends TFields> = {
|
|
264
|
+
[K in keyof T]: T[K] extends Types.TOptional<T[K]> ? T[K] : never
|
|
265
|
+
}
|
|
266
|
+
type RequiredKeys<T extends TFields> = {
|
|
267
|
+
[K in keyof T]: T[K] extends Types.TOptional<T[K]> ? never : T[K]
|
|
268
|
+
}
|
|
269
|
+
// static inference
|
|
270
|
+
type ReadonlyOptionalPropertyKeys<T extends TFields> = {
|
|
271
|
+
[K in keyof T]: T[K] extends Types.TReadonly<Types.TSchema>
|
|
272
|
+
? T[K] extends Types.TOptional<T[K]>
|
|
273
|
+
? K
|
|
274
|
+
: never
|
|
275
|
+
: never
|
|
276
|
+
}[keyof T]
|
|
277
|
+
type ReadonlyPropertyKeys<T extends TFields> = {
|
|
278
|
+
[K in keyof T]: T[K] extends Types.TReadonly<Types.TSchema>
|
|
279
|
+
? T[K] extends Types.TOptional<T[K]>
|
|
280
|
+
? never
|
|
281
|
+
: K
|
|
282
|
+
: never
|
|
283
|
+
}[keyof T]
|
|
284
|
+
type OptionalPropertyKeys<T extends TFields> = {
|
|
285
|
+
[K in keyof T]: T[K] extends Types.TOptional<Types.TSchema>
|
|
286
|
+
? T[K] extends Types.TReadonly<T[K]>
|
|
287
|
+
? never
|
|
288
|
+
: K
|
|
289
|
+
: never
|
|
290
|
+
}[keyof T]
|
|
291
|
+
type RequiredPropertyKeys<T extends TFields> = keyof Omit<
|
|
292
|
+
T,
|
|
293
|
+
| ReadonlyOptionalPropertyKeys<T>
|
|
294
|
+
| ReadonlyPropertyKeys<T>
|
|
295
|
+
| OptionalPropertyKeys<T>
|
|
296
|
+
>
|
|
297
|
+
// prettier-ignore
|
|
298
|
+
type StructStaticProperties<
|
|
299
|
+
T extends TFields,
|
|
300
|
+
R extends Record<keyof any, unknown>
|
|
301
|
+
> = Types.Evaluate<
|
|
302
|
+
Readonly<Partial<Pick<R, ReadonlyOptionalPropertyKeys<T>>>> &
|
|
303
|
+
Readonly<Pick<R, ReadonlyPropertyKeys<T>>> &
|
|
304
|
+
Partial<Pick<R, OptionalPropertyKeys<T>>> &
|
|
305
|
+
Required<Pick<R, RequiredPropertyKeys<T>>>
|
|
306
|
+
>
|
|
307
|
+
// prettier-ignore
|
|
308
|
+
export type StructStatic<
|
|
309
|
+
T extends TFields,
|
|
310
|
+
P extends unknown[]
|
|
311
|
+
> = StructStaticProperties<
|
|
312
|
+
T,
|
|
313
|
+
{
|
|
314
|
+
[K in keyof T]: Static<T[K], P>
|
|
315
|
+
}
|
|
316
|
+
>
|
|
317
|
+
|
|
318
|
+
export interface StructMetadata extends Metadata {
|
|
319
|
+
additionalProperties?: boolean
|
|
320
|
+
}
|
|
321
|
+
export interface TStruct<T extends TFields = TFields>
|
|
322
|
+
extends Types.TSchema,
|
|
323
|
+
StructMetadata {
|
|
324
|
+
[Types.Kind]: 'TypeDef:Struct'
|
|
325
|
+
static: StructStatic<T, this['params']>
|
|
326
|
+
optionalProperties: { [K in Types.Assert<OptionalKeys<T>, keyof T>]: T[K] }
|
|
327
|
+
properties: { [K in Types.Assert<RequiredKeys<T>, keyof T>]: T[K] }
|
|
328
|
+
}
|
|
329
|
+
// --------------------------------------------------------------------------
|
|
330
|
+
// TTimestamp
|
|
331
|
+
// --------------------------------------------------------------------------
|
|
332
|
+
export interface TTimestamp extends Types.TSchema {
|
|
333
|
+
[Types.Kind]: 'TypeDef:Timestamp'
|
|
334
|
+
type: 'timestamp'
|
|
335
|
+
static: string
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export interface TRef<T extends Types.TSchema = Types.TSchema>
|
|
339
|
+
extends Types.TSchema {
|
|
340
|
+
[Types.Kind]: 'TypeDef:Ref'
|
|
341
|
+
ref: string
|
|
342
|
+
static: Static<T>
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export type TNullable<T extends Types.TSchema> = Omit<T, 'static'> & {
|
|
346
|
+
[Types.Kind]: T[typeof Types.Kind]
|
|
347
|
+
params: T['params']
|
|
348
|
+
nullable: true
|
|
349
|
+
static: T['static'] | null
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
export type TNonNullable<T extends Types.TSchema> = (T extends TNullable<
|
|
353
|
+
infer U
|
|
354
|
+
>
|
|
355
|
+
? U
|
|
356
|
+
: T) & { nullable?: false | undefined }
|
|
357
|
+
|
|
358
|
+
// --------------------------------------------------------------------------
|
|
359
|
+
// Static
|
|
360
|
+
// --------------------------------------------------------------------------
|
|
361
|
+
export type Static<
|
|
362
|
+
T extends Types.TSchema,
|
|
363
|
+
P extends unknown[] = []
|
|
364
|
+
> = Types.Static<T, P>
|
|
365
|
+
|
|
366
|
+
// --------------------------------------------------------------------------
|
|
367
|
+
// TimestampFormat
|
|
368
|
+
// --------------------------------------------------------------------------
|
|
369
|
+
// biome-ignore lint/style/noNamespace: groups related timestamp validation logic with private helpers
|
|
370
|
+
export namespace TimestampFormat {
|
|
371
|
+
const DATE_TIME_SEPARATOR = /t|\s/i
|
|
372
|
+
const TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i
|
|
373
|
+
const DATE = /^(\d\d\d\d)-(\d\d)-(\d\d)$/
|
|
374
|
+
const DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
|
375
|
+
function IsLeapYear(year: number): boolean {
|
|
376
|
+
return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0)
|
|
377
|
+
}
|
|
378
|
+
function IsDate(str: string): boolean {
|
|
379
|
+
const matches: string[] | null = DATE.exec(str)
|
|
380
|
+
if (!matches) return false
|
|
381
|
+
const year: number = +matches[1]
|
|
382
|
+
const month: number = +matches[2]
|
|
383
|
+
const day: number = +matches[3]
|
|
384
|
+
return (
|
|
385
|
+
month >= 1 &&
|
|
386
|
+
month <= 12 &&
|
|
387
|
+
day >= 1 &&
|
|
388
|
+
day <= (month === 2 && IsLeapYear(year) ? 29 : DAYS[month])
|
|
389
|
+
)
|
|
390
|
+
}
|
|
391
|
+
function IsTime(str: string, strictTimeZone?: boolean): boolean {
|
|
392
|
+
const matches: string[] | null = TIME.exec(str)
|
|
393
|
+
if (!matches) return false
|
|
394
|
+
const hr: number = +matches[1]
|
|
395
|
+
const min: number = +matches[2]
|
|
396
|
+
const sec: number = +matches[3]
|
|
397
|
+
const tz: string | undefined = matches[4]
|
|
398
|
+
const tzSign: number = matches[5] === '-' ? -1 : 1
|
|
399
|
+
const tzH: number = +(matches[6] || 0)
|
|
400
|
+
const tzM: number = +(matches[7] || 0)
|
|
401
|
+
if (tzH > 23 || tzM > 59 || (strictTimeZone && !tz)) return false
|
|
402
|
+
if (hr <= 23 && min <= 59 && sec < 60) return true
|
|
403
|
+
const utcMin = min - tzM * tzSign
|
|
404
|
+
const utcHr = hr - tzH * tzSign - (utcMin < 0 ? 1 : 0)
|
|
405
|
+
return (
|
|
406
|
+
(utcHr === 23 || utcHr === -1) &&
|
|
407
|
+
(utcMin === 59 || utcMin === -1) &&
|
|
408
|
+
sec < 61
|
|
409
|
+
)
|
|
410
|
+
}
|
|
411
|
+
function IsDateTime(value: string, strictTimeZone?: boolean): boolean {
|
|
412
|
+
const dateTime: string[] = value.split(DATE_TIME_SEPARATOR)
|
|
413
|
+
return (
|
|
414
|
+
dateTime.length === 2 &&
|
|
415
|
+
IsDate(dateTime[0]) &&
|
|
416
|
+
IsTime(dateTime[1], strictTimeZone)
|
|
417
|
+
)
|
|
418
|
+
}
|
|
419
|
+
export function Check(value: string): boolean {
|
|
420
|
+
return IsDateTime(value)
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
// --------------------------------------------------------------------------
|
|
424
|
+
// ValueCheck
|
|
425
|
+
// --------------------------------------------------------------------------
|
|
426
|
+
export class ValueCheckError extends Types.TypeBoxError {
|
|
427
|
+
constructor(public readonly schema: Types.TSchema) {
|
|
428
|
+
super('Unknown type')
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
// biome-ignore lint/style/noNamespace: groups JTD type-checking functions with private helpers
|
|
432
|
+
export namespace ValueCheck {
|
|
433
|
+
// ------------------------------------------------------------------------
|
|
434
|
+
// Guards
|
|
435
|
+
// ------------------------------------------------------------------------
|
|
436
|
+
function IsObject(value: unknown): value is Record<keyof any, any> {
|
|
437
|
+
return (
|
|
438
|
+
typeof value === 'object' &&
|
|
439
|
+
value !== null &&
|
|
440
|
+
!globalThis.Array.isArray(value)
|
|
441
|
+
)
|
|
442
|
+
}
|
|
443
|
+
function IsArray(value: unknown): value is unknown[] {
|
|
444
|
+
return globalThis.Array.isArray(value)
|
|
445
|
+
}
|
|
446
|
+
function IsString(value: unknown): value is string {
|
|
447
|
+
return typeof value === 'string'
|
|
448
|
+
}
|
|
449
|
+
function IsInt(value: unknown, min: number, max: number): value is number {
|
|
450
|
+
return (
|
|
451
|
+
typeof value === 'number' &&
|
|
452
|
+
globalThis.Number.isInteger(value) &&
|
|
453
|
+
value >= min &&
|
|
454
|
+
value < max
|
|
455
|
+
)
|
|
456
|
+
}
|
|
457
|
+
// ------------------------------------------------------------------------
|
|
458
|
+
// Types
|
|
459
|
+
// ------------------------------------------------------------------------
|
|
460
|
+
// biome-ignore lint/suspicious/noShadowRestrictedNames: named after JTD type
|
|
461
|
+
function Array(schema: TArray, value: unknown): boolean {
|
|
462
|
+
return (
|
|
463
|
+
IsArray(value) && value.every((value) => Visit(schema.elements, value))
|
|
464
|
+
)
|
|
465
|
+
}
|
|
466
|
+
// biome-ignore lint/suspicious/noShadowRestrictedNames: named after JTD type
|
|
467
|
+
function Boolean(_schema: TBoolean, value: unknown): boolean {
|
|
468
|
+
return typeof value === 'boolean'
|
|
469
|
+
}
|
|
470
|
+
function Enum(schema: TEnum, value: unknown): boolean {
|
|
471
|
+
return typeof value === 'string' && schema.enum.includes(value)
|
|
472
|
+
}
|
|
473
|
+
function Float32(_schema: TFloat32, value: unknown): boolean {
|
|
474
|
+
return typeof value === 'number'
|
|
475
|
+
}
|
|
476
|
+
function Float64(_schema: TFloat64, value: unknown): boolean {
|
|
477
|
+
return typeof value === 'number'
|
|
478
|
+
}
|
|
479
|
+
function Int8(_schema: TInt8, value: unknown): boolean {
|
|
480
|
+
return IsInt(value, -128, 127)
|
|
481
|
+
}
|
|
482
|
+
function Int16(_schema: TInt16, value: unknown): boolean {
|
|
483
|
+
return IsInt(value, -32_768, 32_767)
|
|
484
|
+
}
|
|
485
|
+
function Int32(_schema: TInt32, value: unknown): boolean {
|
|
486
|
+
return IsInt(value, -2_147_483_648, 2_147_483_647)
|
|
487
|
+
}
|
|
488
|
+
function Uint8(_schema: TUint8, value: unknown): boolean {
|
|
489
|
+
return IsInt(value, 0, 255)
|
|
490
|
+
}
|
|
491
|
+
function Uint16(_schema: TUint16, value: unknown): boolean {
|
|
492
|
+
return IsInt(value, 0, 65535)
|
|
493
|
+
}
|
|
494
|
+
function Uint32(_schema: TUint32, value: unknown): boolean {
|
|
495
|
+
return IsInt(value, 0, 4_294_967_295)
|
|
496
|
+
}
|
|
497
|
+
function Record(schema: TRecord, value: unknown): boolean {
|
|
498
|
+
return (
|
|
499
|
+
IsObject(value) &&
|
|
500
|
+
globalThis.Object.getOwnPropertyNames(value).every((key) =>
|
|
501
|
+
Visit(schema.values, value[key])
|
|
502
|
+
)
|
|
503
|
+
)
|
|
504
|
+
}
|
|
505
|
+
// biome-ignore lint/suspicious/noShadowRestrictedNames: named after JTD type
|
|
506
|
+
function String(_schema: TString, value: unknown): boolean {
|
|
507
|
+
return typeof value === 'string'
|
|
508
|
+
}
|
|
509
|
+
function Struct(
|
|
510
|
+
schema: TStruct,
|
|
511
|
+
value: unknown,
|
|
512
|
+
descriminator?: string
|
|
513
|
+
): boolean {
|
|
514
|
+
if (!IsObject(value)) return false
|
|
515
|
+
const optionalKeys =
|
|
516
|
+
schema.optionalProperties === undefined
|
|
517
|
+
? []
|
|
518
|
+
: globalThis.Object.getOwnPropertyNames(schema.optionalProperties)
|
|
519
|
+
const requiredKeys =
|
|
520
|
+
schema.properties === undefined
|
|
521
|
+
? []
|
|
522
|
+
: globalThis.Object.getOwnPropertyNames(schema.properties)
|
|
523
|
+
const unknownKeys = globalThis.Object.getOwnPropertyNames(value)
|
|
524
|
+
for (const requiredKey of requiredKeys) {
|
|
525
|
+
if (!(requiredKey in value)) return false
|
|
526
|
+
const requiredProperty = value[requiredKey]
|
|
527
|
+
const requiredSchema = (schema as any).properties[requiredKey]
|
|
528
|
+
if (!Visit(requiredSchema, requiredProperty)) return false
|
|
529
|
+
}
|
|
530
|
+
for (const optionalKey of optionalKeys) {
|
|
531
|
+
if (!(optionalKey in value)) continue
|
|
532
|
+
const optionalProperty = value[optionalKey]
|
|
533
|
+
const optionalSchema = (schema as any).properties[optionalKey]
|
|
534
|
+
if (!Visit(optionalSchema, optionalProperty)) return false
|
|
535
|
+
}
|
|
536
|
+
if (schema.additionalProperties === true) return true
|
|
537
|
+
const knownKeys = [...optionalKeys, ...requiredKeys]
|
|
538
|
+
for (const unknownKey of unknownKeys)
|
|
539
|
+
if (
|
|
540
|
+
!knownKeys.includes(unknownKey) &&
|
|
541
|
+
descriminator !== undefined &&
|
|
542
|
+
unknownKey !== descriminator
|
|
543
|
+
)
|
|
544
|
+
return false
|
|
545
|
+
for (const knownKey of knownKeys)
|
|
546
|
+
if (!unknownKeys.includes(knownKey)) return false
|
|
547
|
+
return true
|
|
548
|
+
}
|
|
549
|
+
function Timestamp(_schema: TString, value: unknown): boolean {
|
|
550
|
+
return IsString(value) && TimestampFormat.Check(value)
|
|
551
|
+
}
|
|
552
|
+
function Union(schema: TUnion, value: unknown): boolean {
|
|
553
|
+
if (!IsObject(value)) return false
|
|
554
|
+
if (!(schema.discriminator in value)) return false
|
|
555
|
+
if (!IsString(value[schema.discriminator])) return false
|
|
556
|
+
if (!(value[schema.discriminator] in schema.mapping)) return false
|
|
557
|
+
const struct = schema.mapping[value[schema.discriminator]] as TStruct
|
|
558
|
+
return Struct(struct, value, schema.discriminator)
|
|
559
|
+
}
|
|
560
|
+
function Visit(schema: Types.TSchema, value: unknown): boolean {
|
|
561
|
+
const anySchema = schema as any
|
|
562
|
+
switch (anySchema[Types.Kind]) {
|
|
563
|
+
case 'TypeDef:Array':
|
|
564
|
+
return Array(anySchema, value)
|
|
565
|
+
case 'TypeDef:Boolean':
|
|
566
|
+
return Boolean(anySchema, value)
|
|
567
|
+
case 'TypeDef:Union':
|
|
568
|
+
return Union(anySchema, value)
|
|
569
|
+
case 'TypeDef:Enum':
|
|
570
|
+
return Enum(anySchema, value)
|
|
571
|
+
case 'TypeDef:Float32':
|
|
572
|
+
return Float32(anySchema, value)
|
|
573
|
+
case 'TypeDef:Float64':
|
|
574
|
+
return Float64(anySchema, value)
|
|
575
|
+
case 'TypeDef:Int8':
|
|
576
|
+
return Int8(anySchema, value)
|
|
577
|
+
case 'TypeDef:Int16':
|
|
578
|
+
return Int16(anySchema, value)
|
|
579
|
+
case 'TypeDef:Int32':
|
|
580
|
+
return Int32(anySchema, value)
|
|
581
|
+
case 'TypeDef:Uint8':
|
|
582
|
+
return Uint8(anySchema, value)
|
|
583
|
+
case 'TypeDef:Uint16':
|
|
584
|
+
return Uint16(anySchema, value)
|
|
585
|
+
case 'TypeDef:Uint32':
|
|
586
|
+
return Uint32(anySchema, value)
|
|
587
|
+
case 'TypeDef:Record':
|
|
588
|
+
return Record(anySchema, value)
|
|
589
|
+
case 'TypeDef:String':
|
|
590
|
+
return String(anySchema, value)
|
|
591
|
+
case 'TypeDef:Struct':
|
|
592
|
+
return Struct(anySchema, value)
|
|
593
|
+
case 'TypeDef:Timestamp':
|
|
594
|
+
return Timestamp(anySchema, value)
|
|
595
|
+
default:
|
|
596
|
+
throw new ValueCheckError(anySchema)
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
export function Check<T extends Types.TSchema>(
|
|
600
|
+
schema: T,
|
|
601
|
+
value: unknown
|
|
602
|
+
): value is Types.Static<T> {
|
|
603
|
+
return Visit(schema, value)
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
// --------------------------------------------------------------------------
|
|
607
|
+
// TypeGuard
|
|
608
|
+
// --------------------------------------------------------------------------
|
|
609
|
+
// biome-ignore lint/style/noNamespace: groups JTD type guard functions with private helpers
|
|
610
|
+
export namespace TypeGuard {
|
|
611
|
+
// ------------------------------------------------------------------------
|
|
612
|
+
// Guards
|
|
613
|
+
// ------------------------------------------------------------------------
|
|
614
|
+
function IsObject(value: unknown): value is Record<keyof any, unknown> {
|
|
615
|
+
return typeof value === 'object'
|
|
616
|
+
}
|
|
617
|
+
function IsArray(value: unknown): value is unknown[] {
|
|
618
|
+
return globalThis.Array.isArray(value)
|
|
619
|
+
}
|
|
620
|
+
function IsOptionalBoolean(value: unknown): value is boolean | undefined {
|
|
621
|
+
return IsBoolean(value) || value === undefined
|
|
622
|
+
}
|
|
623
|
+
function IsBoolean(value: unknown): value is boolean {
|
|
624
|
+
return typeof value === 'boolean'
|
|
625
|
+
}
|
|
626
|
+
function IsString(value: unknown): value is string {
|
|
627
|
+
return typeof value === 'string'
|
|
628
|
+
}
|
|
629
|
+
// ------------------------------------------------------------------------
|
|
630
|
+
// Types
|
|
631
|
+
// ------------------------------------------------------------------------
|
|
632
|
+
export function TNullable(
|
|
633
|
+
schema: unknown
|
|
634
|
+
): schema is TNullable<Types.TSchema> {
|
|
635
|
+
return IsObject(schema) && 'nullable' in schema && schema.nullable === true
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
export function TNonNullable(
|
|
639
|
+
schema: unknown
|
|
640
|
+
): schema is TNonNullable<Types.TSchema> {
|
|
641
|
+
return IsObject(schema) && !TNullable(schema)
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
export function TArray(schema: unknown): schema is TArray {
|
|
645
|
+
return (
|
|
646
|
+
IsObject(schema) &&
|
|
647
|
+
schema[Types.Kind] === 'TypeDef:Array' &&
|
|
648
|
+
TSchema(schema.elements)
|
|
649
|
+
)
|
|
650
|
+
}
|
|
651
|
+
export function TBoolean(schema: unknown): schema is TBoolean {
|
|
652
|
+
return (
|
|
653
|
+
IsObject(schema) &&
|
|
654
|
+
schema[Types.Kind] === 'TypeDef:Boolean' &&
|
|
655
|
+
schema.type === 'boolean'
|
|
656
|
+
)
|
|
657
|
+
}
|
|
658
|
+
export function TUnion(schema: unknown): schema is TUnion {
|
|
659
|
+
if (
|
|
660
|
+
!(
|
|
661
|
+
IsObject(schema) &&
|
|
662
|
+
schema[Types.Kind] === 'TypeDef:Union' &&
|
|
663
|
+
IsString(schema.discriminator) &&
|
|
664
|
+
IsObject(schema.mapping)
|
|
665
|
+
)
|
|
666
|
+
)
|
|
667
|
+
return false
|
|
668
|
+
return globalThis.Object.getOwnPropertyNames(schema.mapping).every((key) =>
|
|
669
|
+
TNonNullable(TStruct((schema.mapping as any)[key]))
|
|
670
|
+
)
|
|
671
|
+
}
|
|
672
|
+
export function TEnum(schema: unknown): schema is TEnum {
|
|
673
|
+
return (
|
|
674
|
+
IsObject(schema) &&
|
|
675
|
+
schema[Types.Kind] === 'TypeDef:Enum' &&
|
|
676
|
+
IsArray(schema.enum) &&
|
|
677
|
+
schema.enum.every((item) => IsString(item))
|
|
678
|
+
)
|
|
679
|
+
}
|
|
680
|
+
export function TFloat32(schema: unknown): schema is TFloat32 {
|
|
681
|
+
return (
|
|
682
|
+
IsObject(schema) &&
|
|
683
|
+
schema[Types.Kind] === 'TypeDef:Float32' &&
|
|
684
|
+
schema.type === 'float32'
|
|
685
|
+
)
|
|
686
|
+
}
|
|
687
|
+
export function TFloat64(schema: unknown): schema is TFloat64 {
|
|
688
|
+
return (
|
|
689
|
+
IsObject(schema) &&
|
|
690
|
+
schema[Types.Kind] === 'TypeDef:Float64' &&
|
|
691
|
+
schema.type === 'float64'
|
|
692
|
+
)
|
|
693
|
+
}
|
|
694
|
+
export function TInt8(schema: unknown): schema is TInt8 {
|
|
695
|
+
return (
|
|
696
|
+
IsObject(schema) &&
|
|
697
|
+
schema[Types.Kind] === 'TypeDef:Int8' &&
|
|
698
|
+
schema.type === 'int8'
|
|
699
|
+
)
|
|
700
|
+
}
|
|
701
|
+
export function TInt16(schema: unknown): schema is TInt16 {
|
|
702
|
+
return (
|
|
703
|
+
IsObject(schema) &&
|
|
704
|
+
schema[Types.Kind] === 'TypeDef:Int16' &&
|
|
705
|
+
schema.type === 'int16'
|
|
706
|
+
)
|
|
707
|
+
}
|
|
708
|
+
export function TInt32(schema: unknown): schema is TInt32 {
|
|
709
|
+
return (
|
|
710
|
+
IsObject(schema) &&
|
|
711
|
+
schema[Types.Kind] === 'TypeDef:Int32' &&
|
|
712
|
+
schema.type === 'int32'
|
|
713
|
+
)
|
|
714
|
+
}
|
|
715
|
+
export function TUint8(schema: unknown): schema is TUint8 {
|
|
716
|
+
return (
|
|
717
|
+
IsObject(schema) &&
|
|
718
|
+
schema[Types.Kind] === 'TypeDef:Uint8' &&
|
|
719
|
+
schema.type === 'uint8'
|
|
720
|
+
)
|
|
721
|
+
}
|
|
722
|
+
export function TUint16(schema: unknown): schema is TUint16 {
|
|
723
|
+
return (
|
|
724
|
+
IsObject(schema) &&
|
|
725
|
+
schema[Types.Kind] === 'TypeDef:Uint16' &&
|
|
726
|
+
schema.type === 'uint16'
|
|
727
|
+
)
|
|
728
|
+
}
|
|
729
|
+
export function TUint32(schema: unknown): schema is TUint32 {
|
|
730
|
+
return (
|
|
731
|
+
IsObject(schema) &&
|
|
732
|
+
schema[Types.Kind] === 'TypeDef:Uint32' &&
|
|
733
|
+
schema.type === 'uint32'
|
|
734
|
+
)
|
|
735
|
+
}
|
|
736
|
+
export function TRecord(schema: unknown): schema is TRecord {
|
|
737
|
+
return (
|
|
738
|
+
IsObject(schema) &&
|
|
739
|
+
schema[Types.Kind] === 'TypeDef:Record' &&
|
|
740
|
+
TSchema(schema.values)
|
|
741
|
+
)
|
|
742
|
+
}
|
|
743
|
+
export function TString(schema: unknown): schema is TString {
|
|
744
|
+
return (
|
|
745
|
+
IsObject(schema) &&
|
|
746
|
+
schema[Types.Kind] === 'TypeDef:String' &&
|
|
747
|
+
schema.type === 'string'
|
|
748
|
+
)
|
|
749
|
+
}
|
|
750
|
+
export function TAny(schema: unknown): schema is TAny {
|
|
751
|
+
return !(IsObject(schema) && schema[Types.Kind] === 'TypeDef:Any')
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
export function TStruct(schema: unknown): schema is TStruct {
|
|
755
|
+
if (
|
|
756
|
+
!(
|
|
757
|
+
IsObject(schema) &&
|
|
758
|
+
schema[Types.Kind] === 'TypeDef:Struct' &&
|
|
759
|
+
IsOptionalBoolean(schema.additionalProperties)
|
|
760
|
+
)
|
|
761
|
+
)
|
|
762
|
+
return false
|
|
763
|
+
const optionalProperties = schema.optionalProperties
|
|
764
|
+
const requiredProperties = schema.properties
|
|
765
|
+
const optionalCheck =
|
|
766
|
+
optionalProperties === undefined ||
|
|
767
|
+
(IsObject(optionalProperties) &&
|
|
768
|
+
globalThis.Object.getOwnPropertyNames(optionalProperties).every((key) =>
|
|
769
|
+
TSchema(optionalProperties[key])
|
|
770
|
+
))
|
|
771
|
+
const requiredCheck =
|
|
772
|
+
requiredProperties === undefined ||
|
|
773
|
+
(IsObject(requiredProperties) &&
|
|
774
|
+
globalThis.Object.getOwnPropertyNames(requiredProperties).every((key) =>
|
|
775
|
+
TSchema(requiredProperties[key])
|
|
776
|
+
))
|
|
777
|
+
return optionalCheck && requiredCheck
|
|
778
|
+
}
|
|
779
|
+
export function TTimestamp(schema: unknown): schema is TTimestamp {
|
|
780
|
+
return (
|
|
781
|
+
IsObject(schema) &&
|
|
782
|
+
schema[Types.Kind] === 'TypeDef:Timestamp' &&
|
|
783
|
+
schema.type === 'timestamp'
|
|
784
|
+
)
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
export function TRef(schema: unknown): schema is TRef {
|
|
788
|
+
return (
|
|
789
|
+
IsObject(schema) &&
|
|
790
|
+
schema[Types.Kind] === 'TypeDef:Ref' &&
|
|
791
|
+
typeof schema.ref === 'string'
|
|
792
|
+
)
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
export function TKind(schema: unknown): schema is Types.TKind {
|
|
796
|
+
return (
|
|
797
|
+
IsObject(schema) &&
|
|
798
|
+
Types.Kind in schema &&
|
|
799
|
+
typeof (schema as any)[Types.Kind] === 'string'
|
|
800
|
+
) // TS 4.1.5: any required for symbol indexer
|
|
801
|
+
}
|
|
802
|
+
export function TSchema(schema: unknown): schema is Types.TSchema {
|
|
803
|
+
// prettier-ignore
|
|
804
|
+
return (
|
|
805
|
+
TArray(schema) ||
|
|
806
|
+
TBoolean(schema) ||
|
|
807
|
+
TUnion(schema) ||
|
|
808
|
+
TEnum(schema) ||
|
|
809
|
+
TFloat32(schema) ||
|
|
810
|
+
TFloat64(schema) ||
|
|
811
|
+
TInt8(schema) ||
|
|
812
|
+
TInt16(schema) ||
|
|
813
|
+
TInt32(schema) ||
|
|
814
|
+
TUint8(schema) ||
|
|
815
|
+
TUint16(schema) ||
|
|
816
|
+
TUint32(schema) ||
|
|
817
|
+
TRecord(schema) ||
|
|
818
|
+
TString(schema) ||
|
|
819
|
+
TStruct(schema) ||
|
|
820
|
+
TTimestamp(schema) ||
|
|
821
|
+
TRef(schema) ||
|
|
822
|
+
(TKind(schema) && Types.TypeRegistry.Has(schema[Types.Kind]))
|
|
823
|
+
)
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
// --------------------------------------------------------------------------
|
|
827
|
+
// TypeRegistry
|
|
828
|
+
// --------------------------------------------------------------------------
|
|
829
|
+
Types.TypeRegistry.Set<TArray>('TypeDef:Array', (schema, value) =>
|
|
830
|
+
ValueCheck.Check(schema, value)
|
|
831
|
+
)
|
|
832
|
+
Types.TypeRegistry.Set<TBoolean>('TypeDef:Boolean', (schema, value) =>
|
|
833
|
+
ValueCheck.Check(schema, value)
|
|
834
|
+
)
|
|
835
|
+
Types.TypeRegistry.Set<TUnion>('TypeDef:Union', (schema, value) =>
|
|
836
|
+
ValueCheck.Check(schema, value)
|
|
837
|
+
)
|
|
838
|
+
Types.TypeRegistry.Set<TInt8>('TypeDef:Int8', (schema, value) =>
|
|
839
|
+
ValueCheck.Check(schema, value)
|
|
840
|
+
)
|
|
841
|
+
Types.TypeRegistry.Set<TInt16>('TypeDef:Int16', (schema, value) =>
|
|
842
|
+
ValueCheck.Check(schema, value)
|
|
843
|
+
)
|
|
844
|
+
Types.TypeRegistry.Set<TInt32>('TypeDef:Int32', (schema, value) =>
|
|
845
|
+
ValueCheck.Check(schema, value)
|
|
846
|
+
)
|
|
847
|
+
Types.TypeRegistry.Set<TUint8>('TypeDef:Uint8', (schema, value) =>
|
|
848
|
+
ValueCheck.Check(schema, value)
|
|
849
|
+
)
|
|
850
|
+
Types.TypeRegistry.Set<TUint16>('TypeDef:Uint16', (schema, value) =>
|
|
851
|
+
ValueCheck.Check(schema, value)
|
|
852
|
+
)
|
|
853
|
+
Types.TypeRegistry.Set<TUint32>('TypeDef:Uint32', (schema, value) =>
|
|
854
|
+
ValueCheck.Check(schema, value)
|
|
855
|
+
)
|
|
856
|
+
Types.TypeRegistry.Set<TRecord>('TypeDef:Record', (schema, value) =>
|
|
857
|
+
ValueCheck.Check(schema, value)
|
|
858
|
+
)
|
|
859
|
+
Types.TypeRegistry.Set<TString>('TypeDef:String', (schema, value) =>
|
|
860
|
+
ValueCheck.Check(schema, value)
|
|
861
|
+
)
|
|
862
|
+
Types.TypeRegistry.Set<TStruct>('TypeDef:Struct', (schema, value) =>
|
|
863
|
+
ValueCheck.Check(schema, value)
|
|
864
|
+
)
|
|
865
|
+
Types.TypeRegistry.Set<TTimestamp>('TypeDef:Timestamp', (schema, value) =>
|
|
866
|
+
ValueCheck.Check(schema, value)
|
|
867
|
+
)
|
|
868
|
+
Types.TypeRegistry.Set<TRef>('TypeDef:Ref', (schema, value) =>
|
|
869
|
+
ValueCheck.Check(schema, value)
|
|
870
|
+
)
|
|
871
|
+
Types.TypeRegistry.Set<TAny>('TypeDef:Any', (schema, value) =>
|
|
872
|
+
ValueCheck.Check(schema, value)
|
|
873
|
+
)
|
|
874
|
+
|
|
875
|
+
// --------------------------------------------------------------------------
|
|
876
|
+
// TypeSystemErrorFunction
|
|
877
|
+
// --------------------------------------------------------------------------
|
|
878
|
+
SetErrorFunction((error) => {
|
|
879
|
+
switch (error.schema[Types.Kind]) {
|
|
880
|
+
case 'TypeDef:Array':
|
|
881
|
+
return 'Expected Array'
|
|
882
|
+
case 'TypeDef:Boolean':
|
|
883
|
+
return 'Expected Boolean'
|
|
884
|
+
case 'TypeDef:Union':
|
|
885
|
+
return 'Expected Union'
|
|
886
|
+
case 'TypeDef:Int8':
|
|
887
|
+
return 'Expected Int8'
|
|
888
|
+
case 'TypeDef:Int16':
|
|
889
|
+
return 'Expected Int16'
|
|
890
|
+
case 'TypeDef:Int32':
|
|
891
|
+
return 'Expected Int32'
|
|
892
|
+
case 'TypeDef:Uint8':
|
|
893
|
+
return 'Expected Uint8'
|
|
894
|
+
case 'TypeDef:Uint16':
|
|
895
|
+
return 'Expected Uint16'
|
|
896
|
+
case 'TypeDef:Uint32':
|
|
897
|
+
return 'Expected Uint32'
|
|
898
|
+
case 'TypeDef:Record':
|
|
899
|
+
return 'Expected Record'
|
|
900
|
+
case 'TypeDef:String':
|
|
901
|
+
return 'Expected String'
|
|
902
|
+
case 'TypeDef:Struct':
|
|
903
|
+
return 'Expected Struct'
|
|
904
|
+
case 'TypeDef:Timestamp':
|
|
905
|
+
return 'Expected Timestamp'
|
|
906
|
+
case 'TypeDef:Ref':
|
|
907
|
+
return 'Expected Ref'
|
|
908
|
+
case 'TypeDef:Any':
|
|
909
|
+
return 'Expected Any'
|
|
910
|
+
}
|
|
911
|
+
return DefaultErrorFunction(error)
|
|
912
|
+
})
|
|
913
|
+
// --------------------------------------------------------------------------
|
|
914
|
+
// TypeDefBuilder
|
|
915
|
+
// --------------------------------------------------------------------------
|
|
916
|
+
export class TypeDefBuilder {
|
|
917
|
+
// ------------------------------------------------------------------------
|
|
918
|
+
// Core
|
|
919
|
+
// ------------------------------------------------------------------------
|
|
920
|
+
protected Create(schema: Record<PropertyKey, any>, metadata: Metadata): any {
|
|
921
|
+
const keys = globalThis.Object.getOwnPropertyNames(metadata)
|
|
922
|
+
return keys.length > 0
|
|
923
|
+
? { ...schema, metadata: { ...metadata } }
|
|
924
|
+
: { ...schema }
|
|
925
|
+
}
|
|
926
|
+
/** [Standard] Removes compositing symbols from this schema */
|
|
927
|
+
public Strict<T extends Types.TSchema>(schema: T): T {
|
|
928
|
+
return JSON.parse(JSON.stringify(schema)) as T
|
|
929
|
+
}
|
|
930
|
+
// ------------------------------------------------------------------------
|
|
931
|
+
// Modifiers
|
|
932
|
+
// ------------------------------------------------------------------------
|
|
933
|
+
/** `[Standard]` Creates an Optional property */
|
|
934
|
+
public Optional<T extends Types.TSchema>(schema: T): Types.TOptional<T> {
|
|
935
|
+
// return this.Optional(schema)
|
|
936
|
+
return JtdType.Optional(schema)
|
|
937
|
+
}
|
|
938
|
+
/** `[Standard]` Creates a Readonly property */
|
|
939
|
+
public Readonly<T extends Types.TSchema>(schema: T): Types.TReadonly<T> {
|
|
940
|
+
// return this.Readonly(schema)
|
|
941
|
+
return JtdType.Readonly(schema)
|
|
942
|
+
}
|
|
943
|
+
/** `[Standard]` Creates a Nullable schema */
|
|
944
|
+
public Nullable<T extends Types.TSchema = Types.TSchema>(schema: T) {
|
|
945
|
+
const newType = { ...Types.CloneType(schema), nullable: true }
|
|
946
|
+
return newType as TNullable<T>
|
|
947
|
+
}
|
|
948
|
+
// ------------------------------------------------------------------------
|
|
949
|
+
// Types
|
|
950
|
+
// ------------------------------------------------------------------------
|
|
951
|
+
public Any(metadata: Metadata = {}): SchemaFormEmpty {
|
|
952
|
+
return this.Create({ [Types.Kind]: 'TypeDef:Any' }, metadata)
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
/** [Standard] Creates a Array type */
|
|
956
|
+
public Array<T extends Types.TSchema>(
|
|
957
|
+
elements: T,
|
|
958
|
+
metadata: Metadata = {}
|
|
959
|
+
): TArray<T> {
|
|
960
|
+
return this.Create({ [Types.Kind]: 'TypeDef:Array', elements }, metadata)
|
|
961
|
+
}
|
|
962
|
+
/** [Standard] Creates a Boolean type */
|
|
963
|
+
public Boolean(metadata: Metadata = {}): TBoolean {
|
|
964
|
+
return this.Create(
|
|
965
|
+
{ [Types.Kind]: 'TypeDef:Boolean', type: 'boolean' },
|
|
966
|
+
metadata
|
|
967
|
+
)
|
|
968
|
+
}
|
|
969
|
+
/** [Standard] Creates a Enum type */
|
|
970
|
+
public Enum<T extends string[]>(
|
|
971
|
+
values: [...T],
|
|
972
|
+
metadata: EnumMetadata = {}
|
|
973
|
+
): TEnum<T> {
|
|
974
|
+
return this.Create(
|
|
975
|
+
{ [Types.Kind]: 'TypeDef:Enum', enum: Array.from(new Set(values)) },
|
|
976
|
+
metadata
|
|
977
|
+
)
|
|
978
|
+
}
|
|
979
|
+
/** [Standard] Creates a Float32 type */
|
|
980
|
+
public Float32(metadata: Metadata = {}): TFloat32 {
|
|
981
|
+
return this.Create(
|
|
982
|
+
{ [Types.Kind]: 'TypeDef:Float32', type: 'float32' },
|
|
983
|
+
metadata
|
|
984
|
+
)
|
|
985
|
+
}
|
|
986
|
+
/** [Standard] Creates a Float64 type */
|
|
987
|
+
public Float64(metadata: Metadata = {}): TFloat64 {
|
|
988
|
+
return this.Create(
|
|
989
|
+
{ [Types.Kind]: 'TypeDef:Float64', type: 'float64' },
|
|
990
|
+
metadata
|
|
991
|
+
)
|
|
992
|
+
}
|
|
993
|
+
/** [Standard] Creates a Int8 type */
|
|
994
|
+
public Int8(metadata: Metadata = {}): TInt8 {
|
|
995
|
+
return this.Create({ [Types.Kind]: 'TypeDef:Int8', type: 'int8' }, metadata)
|
|
996
|
+
}
|
|
997
|
+
/** [Standard] Creates a Int16 type */
|
|
998
|
+
public Int16(metadata: Metadata = {}): TInt16 {
|
|
999
|
+
return this.Create(
|
|
1000
|
+
{ [Types.Kind]: 'TypeDef:Int16', type: 'int16' },
|
|
1001
|
+
metadata
|
|
1002
|
+
)
|
|
1003
|
+
}
|
|
1004
|
+
/** [Standard] Creates a Int32 type */
|
|
1005
|
+
public Int32(metadata: Metadata = {}): TInt32 {
|
|
1006
|
+
return this.Create(
|
|
1007
|
+
{ [Types.Kind]: 'TypeDef:Int32', type: 'int32' },
|
|
1008
|
+
metadata
|
|
1009
|
+
)
|
|
1010
|
+
}
|
|
1011
|
+
/** [Standard] Creates a Uint8 type */
|
|
1012
|
+
public Uint8(metadata: Metadata = {}): TUint8 {
|
|
1013
|
+
return this.Create(
|
|
1014
|
+
{ [Types.Kind]: 'TypeDef:Uint8', type: 'uint8' },
|
|
1015
|
+
metadata
|
|
1016
|
+
)
|
|
1017
|
+
}
|
|
1018
|
+
/** [Standard] Creates a Uint16 type */
|
|
1019
|
+
public Uint16(metadata: Metadata = {}): TUint16 {
|
|
1020
|
+
return this.Create(
|
|
1021
|
+
{ [Types.Kind]: 'TypeDef:Uint16', type: 'uint16' },
|
|
1022
|
+
metadata
|
|
1023
|
+
)
|
|
1024
|
+
}
|
|
1025
|
+
/** [Standard] Creates a Uint32 type */
|
|
1026
|
+
public Uint32(metadata: Metadata = {}): TUint32 {
|
|
1027
|
+
return this.Create(
|
|
1028
|
+
{ [Types.Kind]: 'TypeDef:Uint32', type: 'uint32' },
|
|
1029
|
+
metadata
|
|
1030
|
+
)
|
|
1031
|
+
}
|
|
1032
|
+
/** [Standard] Creates a Record type */
|
|
1033
|
+
public Record<T extends Types.TSchema>(
|
|
1034
|
+
values: T,
|
|
1035
|
+
metadata: Metadata = {}
|
|
1036
|
+
): TRecord<T> {
|
|
1037
|
+
return this.Create({ [Types.Kind]: 'TypeDef:Record', values }, metadata)
|
|
1038
|
+
}
|
|
1039
|
+
/** [Standard] Creates a String type */
|
|
1040
|
+
public String(metadata: Metadata = {}): TString {
|
|
1041
|
+
return this.Create(
|
|
1042
|
+
{ [Types.Kind]: 'TypeDef:String', type: 'string' },
|
|
1043
|
+
metadata
|
|
1044
|
+
)
|
|
1045
|
+
}
|
|
1046
|
+
/** [Standard] Creates a Struct type */
|
|
1047
|
+
public Struct<T extends TFields>(
|
|
1048
|
+
fields: T,
|
|
1049
|
+
metadata: StructMetadata = {}
|
|
1050
|
+
): TStruct<T> {
|
|
1051
|
+
const optionalProperties: TFields = {} as TFields
|
|
1052
|
+
const properties: TFields = {} as TFields
|
|
1053
|
+
for (const key of globalThis.Object.getOwnPropertyNames(fields)) {
|
|
1054
|
+
if (Types.TypeGuard.IsOptional(fields[key])) {
|
|
1055
|
+
optionalProperties[key] = fields[key]
|
|
1056
|
+
} else {
|
|
1057
|
+
properties[key] = fields[key]
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
const optionalObject =
|
|
1061
|
+
globalThis.Object.getOwnPropertyNames(optionalProperties).length > 0
|
|
1062
|
+
? { optionalProperties: optionalProperties }
|
|
1063
|
+
: {}
|
|
1064
|
+
const requiredObject =
|
|
1065
|
+
globalThis.Object.getOwnPropertyNames(properties).length === 0
|
|
1066
|
+
? {}
|
|
1067
|
+
: { properties: properties }
|
|
1068
|
+
return this.Create(
|
|
1069
|
+
{
|
|
1070
|
+
[Types.Kind]: 'TypeDef:Struct',
|
|
1071
|
+
...requiredObject,
|
|
1072
|
+
...optionalObject
|
|
1073
|
+
},
|
|
1074
|
+
metadata
|
|
1075
|
+
)
|
|
1076
|
+
}
|
|
1077
|
+
/** [Standard] Creates a Union type */
|
|
1078
|
+
// public Union<T extends TStruct<TFields>[], D extends string = 'type'>(
|
|
1079
|
+
// structs: [...T],
|
|
1080
|
+
// discriminator?: D
|
|
1081
|
+
// ): TUnion<T, D> {
|
|
1082
|
+
// discriminator = (discriminator || 'type') as D
|
|
1083
|
+
// if (structs.length === 0)
|
|
1084
|
+
// throw new Error(
|
|
1085
|
+
// 'TypeDefBuilder: Union types must contain at least one struct'
|
|
1086
|
+
// )
|
|
1087
|
+
// const mapping = structs.reduce(
|
|
1088
|
+
// (acc, current, index) => ({ ...acc, [index.toString()]: current }),
|
|
1089
|
+
// {}
|
|
1090
|
+
// )
|
|
1091
|
+
// return this.Create(
|
|
1092
|
+
// { [Types.Kind]: 'TypeDef:Union', discriminator, mapping },
|
|
1093
|
+
// {}
|
|
1094
|
+
// )
|
|
1095
|
+
// }
|
|
1096
|
+
public Union<T extends Record<string, TStruct<TFields>>, D extends string>(
|
|
1097
|
+
mapping: T,
|
|
1098
|
+
discriminator: D
|
|
1099
|
+
): TUnion<T[keyof T][], D> {
|
|
1100
|
+
if (Object.values(mapping).length === 0)
|
|
1101
|
+
throw new Error(
|
|
1102
|
+
'TypeDefBuilder: Union types must contain at least one struct'
|
|
1103
|
+
)
|
|
1104
|
+
|
|
1105
|
+
const result = this.Create(
|
|
1106
|
+
{ [Types.Kind]: 'TypeDef:Union', discriminator, mapping },
|
|
1107
|
+
{}
|
|
1108
|
+
)
|
|
1109
|
+
|
|
1110
|
+
// console.log(JSON.stringify(result, undefined, '\t'))
|
|
1111
|
+
|
|
1112
|
+
return result
|
|
1113
|
+
}
|
|
1114
|
+
/** [Standard] Creates a Timestamp type */
|
|
1115
|
+
public Timestamp(metadata: Metadata = {}): TTimestamp {
|
|
1116
|
+
return this.Create(
|
|
1117
|
+
{ [Types.Kind]: 'TypeDef:Timestamp', type: 'timestamp' },
|
|
1118
|
+
metadata
|
|
1119
|
+
)
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
public Ref<T extends Types.TSchema = Types.TSchema>(
|
|
1123
|
+
ref: string,
|
|
1124
|
+
metadata: Metadata = {}
|
|
1125
|
+
): TRef<T> {
|
|
1126
|
+
return this.Create(
|
|
1127
|
+
{
|
|
1128
|
+
[Types.Kind]: 'TypeDef:Ref',
|
|
1129
|
+
ref
|
|
1130
|
+
},
|
|
1131
|
+
metadata
|
|
1132
|
+
)
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
/** JSON Type Definition Type Builder */
|
|
1137
|
+
const JtdType = new TypeDefBuilder()
|
|
1138
|
+
export default JtdType
|