schematox 0.4.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/README.md +175 -161
- package/package.json +32 -30
- package/src/constants.ts +18 -0
- package/src/index.ts +69 -0
- package/src/parse.ts +437 -0
- package/src/struct.ts +141 -0
- package/src/types/extensions.ts +32 -0
- package/src/types/infer.ts +109 -0
- package/src/types/schema.ts +103 -0
- package/{dist → src}/types/struct.ts +36 -35
- package/src/types/utils.ts +52 -0
- package/src/utils.ts +17 -0
- package/.eslintrc.json +0 -29
- package/dist/constants.d.ts +0 -9
- package/dist/constants.js +0 -32
- package/dist/error.d.ts +0 -14
- package/dist/error.js +0 -7
- package/dist/index.js +0 -22
- package/dist/index.ts +0 -45
- package/dist/parse.d.ts +0 -5
- package/dist/parse.js +0 -136
- package/dist/struct.d.ts +0 -45
- package/dist/struct.js +0 -78
- package/dist/types/compounds.ts +0 -95
- package/dist/types/constructors.ts +0 -94
- package/dist/types/extensions.ts +0 -27
- package/dist/types/primitives.ts +0 -59
- package/dist/utils/fp.d.ts +0 -11
- package/dist/utils/fp.js +0 -7
- package/dist/validate.d.ts +0 -6
- package/dist/validate.js +0 -132
- package/dist/verify-primitive.d.ts +0 -3
- package/dist/verify-primitive.js +0 -65
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.0.0](https://github.com/incerta/schematox/compare/v0.4.0...v1.0.0)
|
|
4
|
+
|
|
5
|
+
The module went through major refactoring so it could be ready for production usage:
|
|
6
|
+
|
|
7
|
+
- [RecordSchema support #34](https://github.com/incerta/schematox/pull/34)
|
|
8
|
+
- [Drop validate/guard feature support #36](https://github.com/incerta/schematox/pull/36)
|
|
9
|
+
- [Pre major release testing architecture and file structure refactoring #38](https://github.com/incerta/schematox/pull/38)
|
|
10
|
+
- [Break down parse logic into smaller functions #39](https://github.com/incerta/schematox/pull/39)
|
|
11
|
+
- [Support unrestricted object schema depth #42](https://github.com/incerta/schematox/pull/42)
|
|
12
|
+
- [Support tuple schema #43](https://github.com/incerta/schematox/pull/43)
|
|
13
|
+
|
|
3
14
|
## [0.4.0](https://github.com/incerta/schematox/compare/v0.3.1...v0.4.0)
|
|
4
15
|
|
|
5
16
|
- [`aa0d95e`](https://github.com/incerta/schematox/commit/aa0d95e30b7784c0ce29317ae808e4ba7950abab) Extend compound structure nesting limit to 12 layers of depth
|
package/README.md
CHANGED
|
@@ -1,46 +1,27 @@
|
|
|
1
|
-
#
|
|
1
|
+
# schematox
|
|
2
2
|
|
|
3
|
-
Schematox is a lightweight typesafe schema defined parser
|
|
3
|
+
Schematox is a lightweight typesafe schema defined parser. All schemas are JSON compatible.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The library is focusing on fixed set of schema types: boolean, literal, number, string, array, object, record, tuple, union. Each schema can have parameters: optional, nullable, description. Each primitive schema has "brand" parameter as mean of making its subject type [nominal](https://github.com/Microsoft/TypeScript/wiki/FAQ#can-i-make-a-type-alias-nominal). The rest parameters is schema specific range limiters.
|
|
6
6
|
|
|
7
7
|
Library supports static schema definition which means your schemas could be completely independent from schematox. One could use such schemas as source for generation other structures like DB models.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
- Statically defined JSON compatible schema
|
|
12
|
-
- Check defined schema correctness using non generic type "Schema"
|
|
13
|
-
- Programmatically defined schema (struct)
|
|
14
|
-
- Schema subject verification methods:
|
|
15
|
-
- parse: constructs new object based on the given schema and subject
|
|
16
|
-
- validate: checks and returns reference to the original schema subject
|
|
17
|
-
- guard: validates and narrows schema subject type in the current scope
|
|
18
|
-
- Ether-style error handling (no unexpected throws)
|
|
19
|
-
- First-class support for branded primitives (primitive nominal types alias)
|
|
20
|
-
|
|
21
|
-
Check out [github issues](https://github.com/incerta/schematox/issues) to know what we are planning to support soon.
|
|
22
|
-
|
|
23
|
-
Currently we on version 0. The public API is mostly defined however few thing left before the first major release:
|
|
24
|
-
|
|
25
|
-
- Record and tuple schema support
|
|
26
|
-
- Allow parser to replace value before it's validated (similar to [coercing](https://docs.superstructjs.org/guides/03-coercing-data) concept)
|
|
27
|
-
- Clearly defined supported versions of typescript/node
|
|
28
|
-
- Support "deno" runtime and publish package on "deno.land"
|
|
29
|
-
- Have a benchmark that compares library performance with other parsers
|
|
30
|
-
|
|
31
|
-
The library is small so exploring README.md is enough for understanding its API, checkout limitations/examples and you good to go:
|
|
9
|
+
The library is small so exploring README.md is enough for understanding its API, checkout [examples](#example-for-all-supported-schema-types) and you good to go:
|
|
32
10
|
|
|
33
11
|
- [Install](#install)
|
|
34
|
-
- [
|
|
12
|
+
- [Minimal requirements](#minimal-requirements)
|
|
13
|
+
- [Features](#features)
|
|
35
14
|
- [Static schema example](#static-schema-example)
|
|
36
15
|
- [Programmatic schema example](#programmatic-schema-example)
|
|
37
16
|
- [Example for all supported schema types](#example-for-all-supported-schema-types)
|
|
38
|
-
- [String](#string)
|
|
39
|
-
- [Number](#number)
|
|
40
17
|
- [Boolean](#boolean)
|
|
41
18
|
- [Literal](#literal)
|
|
42
|
-
- [
|
|
19
|
+
- [Number](#number)
|
|
20
|
+
- [String](#string)
|
|
43
21
|
- [Array](#array)
|
|
22
|
+
- [Object](#object)
|
|
23
|
+
- [Record](#record)
|
|
24
|
+
- [Tuple](#tuple)
|
|
44
25
|
- [Union](#union)
|
|
45
26
|
- [Schema parameters](#schema-parameters)
|
|
46
27
|
- [Error shape](#error-shape)
|
|
@@ -51,47 +32,30 @@ The library is small so exploring README.md is enough for understanding its API,
|
|
|
51
32
|
npm install schematox
|
|
52
33
|
```
|
|
53
34
|
|
|
54
|
-
##
|
|
35
|
+
## Minimal requirements
|
|
55
36
|
|
|
56
|
-
|
|
37
|
+
- ECMAScript version: `2018`
|
|
38
|
+
- TypeScript version: `5.3.2`
|
|
57
39
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
7: object({
|
|
67
|
-
8: object({
|
|
68
|
-
9: object({
|
|
69
|
-
10: object({
|
|
70
|
-
11: object({ 12: object({ x: string() }) }),
|
|
71
|
-
}),
|
|
72
|
-
}),
|
|
73
|
-
}),
|
|
74
|
-
}),
|
|
75
|
-
}),
|
|
76
|
-
}),
|
|
77
|
-
}),
|
|
78
|
-
}),
|
|
79
|
-
}),
|
|
80
|
-
}),
|
|
81
|
-
})
|
|
82
|
-
```
|
|
40
|
+
## Features
|
|
41
|
+
|
|
42
|
+
- Statically defined JSON compatible schema
|
|
43
|
+
- Programmatically defined schema (struct)
|
|
44
|
+
- Check defined schema correctness using non generic type "Schema"
|
|
45
|
+
- Ether-style error handling (no unexpected throws)
|
|
46
|
+
- First-class support for branded primitives (primitive nominal types alias)
|
|
47
|
+
- Construct type requirement for schema itself using exposed type generics
|
|
83
48
|
|
|
84
|
-
Cryptic typescript type error will be raised if the limit is exceeded.
|
|
85
49
|
|
|
86
50
|
## Static schema example
|
|
87
51
|
|
|
88
52
|
Statically defined schema:
|
|
89
53
|
|
|
90
54
|
```typescript
|
|
91
|
-
import { parse
|
|
92
|
-
import type { Schema } from 'schematox'
|
|
55
|
+
import { parse } from 'schematox'
|
|
56
|
+
import type { Schema, Infer } from 'schematox'
|
|
93
57
|
|
|
94
|
-
export const
|
|
58
|
+
export const schema = {
|
|
95
59
|
type: 'object',
|
|
96
60
|
of: {
|
|
97
61
|
id: {
|
|
@@ -102,31 +66,27 @@ export const userSchema = {
|
|
|
102
66
|
},
|
|
103
67
|
} as const satisfies Schema
|
|
104
68
|
|
|
105
|
-
|
|
106
|
-
id: '
|
|
107
|
-
name: 'John',
|
|
108
|
-
} as unknown
|
|
69
|
+
type User = Infer<typeof schema>
|
|
70
|
+
// ^? { id: string & { __idFor: 'User' }, name: string }
|
|
109
71
|
|
|
72
|
+
const subject = { id: '1' name: 'John' }
|
|
110
73
|
const parsed = parse(userSchema, subject)
|
|
74
|
+
// ^? ParseResult<User>
|
|
111
75
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
console.log(parsed.right) // { id: '1', name: 'John' }
|
|
76
|
+
parsed.error
|
|
77
|
+
// ^? InvalidSubject[] | undefined
|
|
117
78
|
|
|
118
|
-
|
|
79
|
+
parsed.data
|
|
80
|
+
// ^? User | undefined
|
|
119
81
|
|
|
120
|
-
if (
|
|
121
|
-
|
|
82
|
+
if (parsed.success === false) {
|
|
83
|
+
parsed.error
|
|
84
|
+
// ^? InvalidSubject[]
|
|
85
|
+
throw Error('Parsing error')
|
|
122
86
|
}
|
|
123
87
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if (guard(userSchema, subject)) {
|
|
127
|
-
// { id: string & { __idFor: 'User' }; name: string }
|
|
128
|
-
subject
|
|
129
|
-
}
|
|
88
|
+
parsed.data
|
|
89
|
+
// ^? User
|
|
130
90
|
```
|
|
131
91
|
|
|
132
92
|
## Programmatic schema example
|
|
@@ -135,48 +95,80 @@ Same schema but defined programmatically:
|
|
|
135
95
|
|
|
136
96
|
```typescript
|
|
137
97
|
import { object, string } from 'schematox'
|
|
138
|
-
import type {
|
|
98
|
+
import type { Infer } from 'schematox'
|
|
139
99
|
|
|
140
100
|
const struct = object({
|
|
141
101
|
id: string().brand('idFor', 'User'),
|
|
142
102
|
name: string(),
|
|
143
103
|
})
|
|
144
104
|
|
|
145
|
-
|
|
105
|
+
type User = Infer<typeof struct>
|
|
106
|
+
// ^? { id: string & { __idFor: 'User' }, name: string }
|
|
146
107
|
|
|
108
|
+
const subject = { id: '1', name: 'John' }
|
|
147
109
|
const parsed = struct.parse(subject)
|
|
110
|
+
// ^? ParseResult<User>
|
|
111
|
+
|
|
112
|
+
parsed.error
|
|
113
|
+
// ^? InvalidSubject[] | undefined
|
|
148
114
|
|
|
149
|
-
|
|
150
|
-
|
|
115
|
+
parsed.data
|
|
116
|
+
// ^? User | undefined
|
|
117
|
+
|
|
118
|
+
if (parsed.success === false) {
|
|
119
|
+
parsed.error
|
|
120
|
+
// ^? InvalidSubject[]
|
|
121
|
+
throw Error('Parsing error')
|
|
151
122
|
}
|
|
152
123
|
|
|
153
|
-
|
|
124
|
+
parsed.data
|
|
125
|
+
// ^? User
|
|
126
|
+
```
|
|
154
127
|
|
|
155
|
-
|
|
128
|
+
All programmatically defined schemas are the same as static, one just needs to access it through `__schema` key.
|
|
129
|
+
A statically defined schema can be transformed to a struct using the `makeStruct` utility function and can have custom props.
|
|
156
130
|
|
|
157
|
-
|
|
158
|
-
throw Error('Not expected')
|
|
159
|
-
}
|
|
131
|
+
## Transform static schema into struct
|
|
160
132
|
|
|
161
|
-
|
|
133
|
+
```typescript
|
|
134
|
+
import { makeStruct } from 'schematox'
|
|
135
|
+
import type { Schema } from 'schematox'
|
|
162
136
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
subject
|
|
166
|
-
}
|
|
137
|
+
const schema = { type: 'string' } as const satisfies Schema
|
|
138
|
+
const string = makeStruct(schema)
|
|
167
139
|
```
|
|
168
140
|
|
|
169
|
-
All programmatically defined schemas are the same as static, one just needs to access it through `__schema` key. We can mix static/programmatic schemas either accessing it through `__schema` or wrap it by `{ __schema: T }` if consumer is programmatic schema.
|
|
170
|
-
|
|
171
141
|
## Example for all supported schema types
|
|
172
142
|
|
|
173
143
|
We distinguish two main categories of schema units:
|
|
174
144
|
|
|
175
|
-
- primitive:
|
|
176
|
-
- compound: object,
|
|
145
|
+
- primitive: boolean, literal, number, boolean
|
|
146
|
+
- compound: array, object, record, union
|
|
177
147
|
|
|
178
148
|
Any schema share optional/nullable/description parameters. Any compound schema could have any other schema type as its member including itself. Any primitive schema can have "brand" parameter.
|
|
179
149
|
|
|
150
|
+
### Boolean
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
const schema = {
|
|
154
|
+
type: 'boolean',
|
|
155
|
+
optional: true,
|
|
156
|
+
nullable: true,
|
|
157
|
+
brand: ['x', 'y'],
|
|
158
|
+
description: 'x',
|
|
159
|
+
} as const satisfies Schema
|
|
160
|
+
|
|
161
|
+
const struct = boolean() //
|
|
162
|
+
.optional()
|
|
163
|
+
.nullable()
|
|
164
|
+
.brand('x', 'y')
|
|
165
|
+
.description('x')
|
|
166
|
+
|
|
167
|
+
// (boolean & { __x: 'y' }) | undefined | null
|
|
168
|
+
type FromSchema = Infer<typeof schema>
|
|
169
|
+
type FromStruct = Infer<typeof struct>
|
|
170
|
+
```
|
|
171
|
+
|
|
180
172
|
### String
|
|
181
173
|
|
|
182
174
|
```typescript
|
|
@@ -199,86 +191,93 @@ const struct = string()
|
|
|
199
191
|
.description('x')
|
|
200
192
|
|
|
201
193
|
// (string & { __x: 'y' }) | undefined | null
|
|
202
|
-
type FromSchema =
|
|
203
|
-
type FromStruct =
|
|
194
|
+
type FromSchema = Infer<typeof schema>
|
|
195
|
+
type FromStruct = Infer<typeof struct>
|
|
204
196
|
```
|
|
205
197
|
|
|
206
|
-
###
|
|
198
|
+
### Literal
|
|
199
|
+
|
|
200
|
+
Could be string/number/boolean literal.
|
|
207
201
|
|
|
208
202
|
```typescript
|
|
209
203
|
const schema = {
|
|
210
|
-
type: '
|
|
204
|
+
type: 'literal',
|
|
205
|
+
of: 'x',
|
|
211
206
|
optional: true,
|
|
212
207
|
nullable: true,
|
|
213
208
|
brand: ['x', 'y'],
|
|
214
|
-
min: 1,
|
|
215
|
-
max: 2,
|
|
216
209
|
description: 'x',
|
|
217
210
|
} as const satisfies Schema
|
|
218
211
|
|
|
219
|
-
const struct =
|
|
212
|
+
const struct = literal('x') //
|
|
220
213
|
.optional()
|
|
221
214
|
.nullable()
|
|
222
215
|
.brand('x', 'y')
|
|
223
|
-
.min(1)
|
|
224
|
-
.max(2)
|
|
225
216
|
.description('x')
|
|
226
217
|
|
|
227
|
-
// (
|
|
228
|
-
type FromSchema =
|
|
229
|
-
type FromStruct =
|
|
230
|
-
//
|
|
218
|
+
// ('x' & { __x: 'y' }) | undefined | null
|
|
219
|
+
type FromSchema = Infer<typeof schema>
|
|
220
|
+
type FromStruct = Infer<typeof struct>
|
|
231
221
|
```
|
|
232
222
|
|
|
233
|
-
###
|
|
223
|
+
### Number
|
|
224
|
+
|
|
225
|
+
We accept only finite numbers as valid number schema subjects.
|
|
234
226
|
|
|
235
227
|
```typescript
|
|
236
228
|
const schema = {
|
|
237
|
-
type: '
|
|
229
|
+
type: 'number',
|
|
238
230
|
optional: true,
|
|
239
231
|
nullable: true,
|
|
240
232
|
brand: ['x', 'y'],
|
|
233
|
+
min: 1,
|
|
234
|
+
max: 2,
|
|
241
235
|
description: 'x',
|
|
242
236
|
} as const satisfies Schema
|
|
243
237
|
|
|
244
|
-
const struct =
|
|
238
|
+
const struct = number()
|
|
245
239
|
.optional()
|
|
246
240
|
.nullable()
|
|
247
241
|
.brand('x', 'y')
|
|
242
|
+
.min(1)
|
|
243
|
+
.max(2)
|
|
248
244
|
.description('x')
|
|
249
245
|
|
|
250
|
-
// (
|
|
251
|
-
type FromSchema =
|
|
252
|
-
type FromStruct =
|
|
246
|
+
// (number & { __x: 'y' }) | undefined | null
|
|
247
|
+
type FromSchema = Infer<typeof schema>
|
|
248
|
+
type FromStruct = Infer<typeof struct>
|
|
249
|
+
//
|
|
253
250
|
```
|
|
254
251
|
|
|
255
|
-
###
|
|
256
|
-
|
|
257
|
-
Could be string/number/boolean literal
|
|
252
|
+
### Array
|
|
258
253
|
|
|
259
254
|
```typescript
|
|
260
255
|
const schema = {
|
|
261
|
-
type: '
|
|
262
|
-
of: '
|
|
256
|
+
type: 'array',
|
|
257
|
+
of: { type: 'string' },
|
|
263
258
|
optional: true,
|
|
264
|
-
|
|
265
|
-
|
|
259
|
+
minLength: 1,
|
|
260
|
+
maxLength: 1000,
|
|
266
261
|
description: 'x',
|
|
267
262
|
} as const satisfies Schema
|
|
268
263
|
|
|
269
|
-
const struct =
|
|
264
|
+
const struct = array(string())
|
|
270
265
|
.optional()
|
|
271
266
|
.nullable()
|
|
272
|
-
.
|
|
267
|
+
.minLength(1)
|
|
268
|
+
.maxLength(1000)
|
|
273
269
|
.description('x')
|
|
274
270
|
|
|
275
|
-
//
|
|
276
|
-
type FromSchema =
|
|
277
|
-
type FromStruct =
|
|
271
|
+
// string[] | undefined | null
|
|
272
|
+
type FromSchema = Infer<typeof schema>
|
|
273
|
+
type FromStruct = Infer<typeof struct>
|
|
278
274
|
```
|
|
279
275
|
|
|
280
276
|
### Object
|
|
281
277
|
|
|
278
|
+
Extra properties in the parsed subject that are not specified in the `object` schema will not cause an error and will be skipped.
|
|
279
|
+
This is a deliberate decision that allows client schemas to remain functional whenever the API is extended.
|
|
280
|
+
|
|
282
281
|
```typescript
|
|
283
282
|
const schema = {
|
|
284
283
|
type: 'object',
|
|
@@ -300,36 +299,60 @@ const struct = object({
|
|
|
300
299
|
.description('x')
|
|
301
300
|
|
|
302
301
|
// { x: string; y: number } | undefined | null
|
|
303
|
-
type FromSchema =
|
|
304
|
-
type FromStruct =
|
|
302
|
+
type FromSchema = Infer<typeof schema>
|
|
303
|
+
type FromStruct = Infer<typeof struct>
|
|
305
304
|
```
|
|
306
305
|
|
|
307
|
-
###
|
|
306
|
+
### Record
|
|
307
|
+
|
|
308
|
+
Undefined record entries are skipped in parsed results. If a key exists, it means a value is also present.
|
|
308
309
|
|
|
309
310
|
```typescript
|
|
310
311
|
const schema = {
|
|
311
|
-
type: '
|
|
312
|
-
|
|
312
|
+
type: 'record',
|
|
313
|
+
// key property is optional
|
|
314
|
+
key: { type: 'string', brand: ['idFor', 'user'] },
|
|
315
|
+
of: { type: 'number' },
|
|
313
316
|
optional: true,
|
|
314
|
-
|
|
315
|
-
maxLength: 1000,
|
|
317
|
+
nullable: true,
|
|
316
318
|
description: 'x',
|
|
317
319
|
} as const satisfies Schema
|
|
318
320
|
|
|
319
|
-
const
|
|
321
|
+
const userId = string().brand('idFor', 'user')
|
|
322
|
+
|
|
323
|
+
// second argument is optional
|
|
324
|
+
const struct = object(number(), userId).optional().nullable().description('x')
|
|
325
|
+
|
|
326
|
+
// Record<string & { __brand: ['idFor', 'user'] }, number | undefined> | null | undefined
|
|
327
|
+
type FromSchema = Infer<typeof schema>
|
|
328
|
+
type FromStruct = Infer<typeof struct>
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Tuple
|
|
332
|
+
|
|
333
|
+
```typescript
|
|
334
|
+
const schema = {
|
|
335
|
+
type: 'tuple',
|
|
336
|
+
of: [{ type: 'string' }, { type: 'number' }],
|
|
337
|
+
optional: true,
|
|
338
|
+
nullable: true,
|
|
339
|
+
description: 'x',
|
|
340
|
+
} as const satisfies Schema
|
|
341
|
+
|
|
342
|
+
const struct = tuple([string(), number()])
|
|
320
343
|
.optional()
|
|
321
344
|
.nullable()
|
|
322
|
-
.minLength(1)
|
|
323
|
-
.maxLength(1000)
|
|
324
345
|
.description('x')
|
|
325
346
|
|
|
326
|
-
// string
|
|
327
|
-
type FromSchema =
|
|
328
|
-
type FromStruct =
|
|
347
|
+
// [string, number] | undefined | null
|
|
348
|
+
type FromSchema = Infer<typeof schema>
|
|
349
|
+
type FromStruct = Infer<typeof struct>
|
|
329
350
|
```
|
|
330
351
|
|
|
331
352
|
### Union
|
|
332
353
|
|
|
354
|
+
Be careful with object unions that do not have a unique discriminant. The parser will check the subject in the order that is specified in the union array and accept the first match.
|
|
355
|
+
|
|
333
356
|
```typescript
|
|
334
357
|
const schema = {
|
|
335
358
|
type: 'union',
|
|
@@ -345,24 +368,13 @@ const struct = union([string(), number()])
|
|
|
345
368
|
.description('x')
|
|
346
369
|
|
|
347
370
|
// string | number | undefined | null
|
|
348
|
-
type FromSchema =
|
|
349
|
-
type FromStruct =
|
|
371
|
+
type FromSchema = Infer<typeof schema>
|
|
372
|
+
type FromStruct = Infer<typeof struct>
|
|
350
373
|
```
|
|
351
374
|
|
|
352
375
|
## Schema parameters
|
|
353
376
|
|
|
354
377
|
- `optional?: boolean` – unionize with `undefined`: `{ type: 'string', optinoal: true }` result in `string | undefined`
|
|
355
|
-
|
|
356
|
-
In the context of the object, optional values will be treated as optional properties:
|
|
357
|
-
|
|
358
|
-
```typescript
|
|
359
|
-
const struct = object({ x: string().optional() })
|
|
360
|
-
|
|
361
|
-
type ExpectedSubjectType = {
|
|
362
|
-
x?: string | undefined
|
|
363
|
-
}
|
|
364
|
-
```
|
|
365
|
-
|
|
366
378
|
- `nullable?: boolean` – unionize with `null`: `{ type: 'string', nullable: true }` result in `string | null`
|
|
367
379
|
- `brand?: [string, string]` – make primitive type nominal "['idFor', 'User'] -> T & { \_\_idFor: 'User' }"
|
|
368
380
|
- `minLength/maxLength/min/max` – schema type dependent limiting characteristics
|
|
@@ -388,22 +400,24 @@ const struct = object({
|
|
|
388
400
|
const result = struct.parse({ x: { y: [{ z: 0 }] } })
|
|
389
401
|
```
|
|
390
402
|
|
|
391
|
-
The `result.
|
|
403
|
+
The `result.error` shape is:
|
|
392
404
|
|
|
393
405
|
```json
|
|
394
406
|
[
|
|
395
407
|
{
|
|
396
408
|
"code": "INVALID_TYPE",
|
|
409
|
+
"path": ["x", "y", 0, "z"]
|
|
397
410
|
"schema": { "type": "string" },
|
|
398
411
|
"subject": 0,
|
|
399
|
-
"path": ["x", "y", 0, "z"]
|
|
400
412
|
}
|
|
401
413
|
]
|
|
402
414
|
```
|
|
403
415
|
|
|
404
|
-
It's always an array
|
|
416
|
+
It's always an array of `InvalidSubject` entries, each has the following properties:
|
|
405
417
|
|
|
406
|
-
- `code`:
|
|
407
|
-
- `
|
|
408
|
-
- `
|
|
409
|
-
- `
|
|
418
|
+
- `code`:
|
|
419
|
+
- `INVALID_TYPE`: schema subject or default value don't meet schema type specifications
|
|
420
|
+
- `INVALID_RANGE`: `min/max` or `minLength/maxLength` schema requirements aren't met
|
|
421
|
+
- `schema`: the specific section of `schema` where the invalid value is found.
|
|
422
|
+
- `subject`: the specific part of the validated subject where the invalid value exists.
|
|
423
|
+
- `path`: traces the route from the root to the error subject, with strings as keys and numbers as array indexes.
|
package/package.json
CHANGED
|
@@ -1,14 +1,38 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "schematox",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"scripts": {
|
|
5
|
+
"prepublishOnly": "bash release-check.sh",
|
|
6
|
+
"test": "jest",
|
|
7
|
+
"test:watch": "jest --watch",
|
|
8
|
+
"test:coverage": "jest --coverage",
|
|
9
|
+
"formatter:fix": "prettier --write .",
|
|
10
|
+
"formatter:check": "prettier --check . || (echo \"ERROR: use `npm run formatter:fix`\" && exit 1)",
|
|
11
|
+
"ts": "tsc --noEmit",
|
|
12
|
+
"check": "npm run formatter:check && npm run ts && npm run test & npm run test:coverage",
|
|
13
|
+
"morph": "jiti src/tests/fold-morph.ts && npm run formatter:fix",
|
|
14
|
+
"version:patch": "npm run check && npm version patch",
|
|
15
|
+
"version:minor": "npm run check && npm version minor",
|
|
16
|
+
"version:major": "npm run check && npm version major"
|
|
17
|
+
},
|
|
18
|
+
"peerDependencies": {
|
|
19
|
+
"typescript": ">=5.3.2"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/jest": "^29.5.1",
|
|
23
|
+
"jest": "^29.5.0",
|
|
24
|
+
"prettier": "^3.1.1",
|
|
25
|
+
"ts-jest": "^29.1.0",
|
|
26
|
+
"ts-morph": "^26.0.0",
|
|
27
|
+
"typescript": "^5.3.2"
|
|
28
|
+
},
|
|
29
|
+
"description": "Define JSON compatible schema statically/programmatically and parse its subject with typesafety",
|
|
5
30
|
"author": "Kanstantsin Mazur",
|
|
6
31
|
"license": "MIT",
|
|
7
32
|
"keywords": [
|
|
8
33
|
"typescript",
|
|
9
34
|
"schema",
|
|
10
35
|
"parser",
|
|
11
|
-
"validator",
|
|
12
36
|
"typeguard",
|
|
13
37
|
"static schema definition",
|
|
14
38
|
"programmatic schema definition",
|
|
@@ -17,32 +41,10 @@
|
|
|
17
41
|
"either style error handling",
|
|
18
42
|
"lightweight"
|
|
19
43
|
],
|
|
20
|
-
"repository":
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"scripts": {
|
|
24
|
-
"prepare": "husky install",
|
|
25
|
-
"test": "jest",
|
|
26
|
-
"test:watch": "jest --watch",
|
|
27
|
-
"prettify": "prettier --write .",
|
|
28
|
-
"prettier-check": "prettier --check . || (echo \"Please use 'npm run prettify' to fix the issue\" && exit 1)",
|
|
29
|
-
"lint": "eslint .",
|
|
30
|
-
"type-check": "tsc --noEmit",
|
|
31
|
-
"build": "tsc && rm -rf dist/types && cp -r src/types dist && rm dist/index.d.ts && cp src/index.ts dist",
|
|
32
|
-
"quality-check": "npm run prettier-check && npm run lint && npm run type-check && npm run test",
|
|
33
|
-
"check-n-build": "npm run quality-check && npm run build",
|
|
34
|
-
"publish-patch": "npm run check-n-build && npm version patch && npm run check-n-build && npm publish",
|
|
35
|
-
"publish-minor": "npm run check-n-build && npm version minor && npm run check-n-build && npm publish"
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "git://github.com/incerta/schematox.git"
|
|
36
47
|
},
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
"@typescript-eslint/eslint-plugin": "^5.59.5",
|
|
40
|
-
"@typescript-eslint/parser": "^5.59.5",
|
|
41
|
-
"eslint": "^8.40.0",
|
|
42
|
-
"husky": "^8.0.0",
|
|
43
|
-
"jest": "^29.5.0",
|
|
44
|
-
"prettier": "^3.1.1",
|
|
45
|
-
"ts-jest": "^29.1.0",
|
|
46
|
-
"typescript": "^5.0.4"
|
|
47
|
-
}
|
|
48
|
+
"main": "src/index.ts",
|
|
49
|
+
"types": "src/index.ts"
|
|
48
50
|
}
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const ERROR_CODE = {
|
|
2
|
+
invalidType: 'INVALID_TYPE',
|
|
3
|
+
invalidRange: 'INVALID_RANGE',
|
|
4
|
+
} as const
|
|
5
|
+
|
|
6
|
+
// prettier-ignore
|
|
7
|
+
export const PARAMS_BY_SCHEMA_TYPE = {
|
|
8
|
+
boolean: new Set(['optional', 'nullable', 'brand', 'description'] as const),
|
|
9
|
+
literal: new Set(['optional', 'nullable', 'brand', 'description'] as const),
|
|
10
|
+
number: new Set(['optional', 'nullable', 'brand', 'description', 'min', 'max'] as const),
|
|
11
|
+
string: new Set(['optional', 'nullable', 'brand', 'description', 'minLength', 'maxLength'] as const),
|
|
12
|
+
//
|
|
13
|
+
array: new Set(['optional', 'nullable', 'description', 'minLength', 'maxLength'] as const),
|
|
14
|
+
object: new Set(['optional', 'nullable', 'description'] as const),
|
|
15
|
+
record: new Set(['optional', 'nullable', 'description'] as const),
|
|
16
|
+
tuple: new Set(['optional', 'nullable', 'description'] as const),
|
|
17
|
+
union: new Set(['optional', 'nullable', 'description'] as const),
|
|
18
|
+
} as const
|