@sinclair/typebox 0.21.2 → 0.23.1

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.
Files changed (4) hide show
  1. package/package.json +8 -8
  2. package/readme.md +209 -256
  3. package/typebox.d.ts +182 -144
  4. package/typebox.js +149 -114
package/typebox.js CHANGED
@@ -27,16 +27,16 @@ THE SOFTWARE.
27
27
 
28
28
  ---------------------------------------------------------------------------*/
29
29
  Object.defineProperty(exports, "__esModule", { value: true });
30
- exports.Type = exports.TypeBuilder = exports.VoidKind = exports.UndefinedKind = exports.PromiseKind = exports.FunctionKind = exports.ConstructorKind = exports.AnyKind = exports.UnknownKind = exports.NullKind = exports.BooleanKind = exports.IntegerKind = exports.NumberKind = exports.StringKind = exports.LiteralKind = exports.EnumKind = exports.ArrayKind = exports.RecordKind = exports.ObjectKind = exports.TupleKind = exports.UnionKind = exports.IntersectKind = exports.KeyOfKind = exports.BoxKind = exports.ReadonlyModifier = exports.OptionalModifier = exports.ReadonlyOptionalModifier = void 0;
31
- // ------------------------------------------------------------------------
30
+ exports.Type = exports.TypeBuilder = exports.VoidKind = exports.UndefinedKind = exports.PromiseKind = exports.FunctionKind = exports.ConstructorKind = exports.RefKind = exports.AnyKind = exports.UnknownKind = exports.NullKind = exports.BooleanKind = exports.IntegerKind = exports.NumberKind = exports.StringKind = exports.LiteralKind = exports.EnumKind = exports.ArrayKind = exports.RecordKind = exports.ObjectKind = exports.TupleKind = exports.UnionKind = exports.IntersectKind = exports.KeyOfKind = exports.BoxKind = exports.ReadonlyModifier = exports.OptionalModifier = exports.ReadonlyOptionalModifier = void 0;
31
+ // --------------------------------------------------------------------------
32
32
  // Modifiers
33
- // ------------------------------------------------------------------------
33
+ // --------------------------------------------------------------------------
34
34
  exports.ReadonlyOptionalModifier = Symbol('ReadonlyOptionalModifier');
35
35
  exports.OptionalModifier = Symbol('OptionalModifier');
36
36
  exports.ReadonlyModifier = Symbol('ReadonlyModifier');
37
- // ------------------------------------------------------------------------
37
+ // --------------------------------------------------------------------------
38
38
  // Schema Standard
39
- // ------------------------------------------------------------------------
39
+ // --------------------------------------------------------------------------
40
40
  exports.BoxKind = Symbol('BoxKind');
41
41
  exports.KeyOfKind = Symbol('KeyOfKind');
42
42
  exports.IntersectKind = Symbol('IntersectKind');
@@ -54,17 +54,18 @@ exports.BooleanKind = Symbol('BooleanKind');
54
54
  exports.NullKind = Symbol('NullKind');
55
55
  exports.UnknownKind = Symbol('UnknownKind');
56
56
  exports.AnyKind = Symbol('AnyKind');
57
- // ------------------------------------------------------------------------
57
+ exports.RefKind = Symbol('RefKind');
58
+ // --------------------------------------------------------------------------
58
59
  // Extended Schema Types
59
- // ------------------------------------------------------------------------
60
+ // --------------------------------------------------------------------------
60
61
  exports.ConstructorKind = Symbol('ConstructorKind');
61
62
  exports.FunctionKind = Symbol('FunctionKind');
62
63
  exports.PromiseKind = Symbol('PromiseKind');
63
64
  exports.UndefinedKind = Symbol('UndefinedKind');
64
65
  exports.VoidKind = Symbol('VoidKind');
65
- // ------------------------------------------------------------------------
66
+ // --------------------------------------------------------------------------
66
67
  // Utility
67
- // ------------------------------------------------------------------------
68
+ // --------------------------------------------------------------------------
68
69
  function isObject(object) {
69
70
  return typeof object === 'object' && object !== null && !Array.isArray(object);
70
71
  }
@@ -78,32 +79,34 @@ function clone(object) {
78
79
  return object.map((item) => clone(item));
79
80
  return object;
80
81
  }
81
- // ------------------------------------------------------------------------
82
+ // --------------------------------------------------------------------------
82
83
  // TypeBuilder
83
- // ------------------------------------------------------------------------
84
+ // --------------------------------------------------------------------------
84
85
  class TypeBuilder {
85
- /** `standard` Modifies an object property to be both readonly and optional */
86
+ schemas = new Map();
87
+ /** `Standard` Modifies an object property to be both readonly and optional */
86
88
  ReadonlyOptional(item) {
87
89
  return { ...item, modifier: exports.ReadonlyOptionalModifier };
88
90
  }
89
- /** `standard` Modifies an object property to be readonly */
91
+ /** `Standard` Modifies an object property to be readonly */
90
92
  Readonly(item) {
91
93
  return { ...item, modifier: exports.ReadonlyModifier };
92
94
  }
93
- /** `standard` Modifies an object property to be optional */
95
+ /** `Standard` Modifies an object property to be optional */
94
96
  Optional(item) {
95
97
  return { ...item, modifier: exports.OptionalModifier };
96
98
  }
97
- /** `standard` Creates a type type */
99
+ /** `Standard` Creates a type type */
98
100
  Tuple(items, options = {}) {
99
101
  const additionalItems = false;
100
102
  const minItems = items.length;
101
103
  const maxItems = items.length;
102
- return (items.length > 0)
104
+ const schema = ((items.length > 0)
103
105
  ? { ...options, kind: exports.TupleKind, type: 'array', items, additionalItems, minItems, maxItems }
104
- : { ...options, kind: exports.TupleKind, type: 'array', minItems, maxItems };
106
+ : { ...options, kind: exports.TupleKind, type: 'array', minItems, maxItems });
107
+ return this.Store(schema);
105
108
  }
106
- /** `standard` Creates an object type with the given properties */
109
+ /** `Standard` Creates an object type with the given properties */
107
110
  Object(properties, options = {}) {
108
111
  const property_names = Object.keys(properties);
109
112
  const optional = property_names.filter(name => {
@@ -114,88 +117,90 @@ class TypeBuilder {
114
117
  });
115
118
  const required_names = property_names.filter(name => !optional.includes(name));
116
119
  const required = (required_names.length > 0) ? required_names : undefined;
117
- return (required) ?
118
- { ...options, kind: exports.ObjectKind, type: 'object', properties, required } :
119
- { ...options, kind: exports.ObjectKind, type: 'object', properties };
120
+ return this.Store(((required)
121
+ ? { ...options, kind: exports.ObjectKind, type: 'object', properties, required }
122
+ : { ...options, kind: exports.ObjectKind, type: 'object', properties }));
120
123
  }
121
- /** `standard` Creates an intersection type. Note this function requires draft `2019-09` to constrain with `unevaluatedProperties` */
124
+ /** `Standard` Creates an intersect type. */
122
125
  Intersect(items, options = {}) {
123
- return { ...options, kind: exports.IntersectKind, type: 'object', allOf: items };
126
+ return this.Store({ ...options, kind: exports.IntersectKind, type: 'object', allOf: items });
124
127
  }
125
- /** `standard` Creates a union type */
128
+ /** `Standard` Creates a union type */
126
129
  Union(items, options = {}) {
127
- return { ...options, kind: exports.UnionKind, anyOf: items };
130
+ return this.Store({ ...options, kind: exports.UnionKind, anyOf: items });
128
131
  }
129
- /** `standard` Creates an array type */
132
+ /** `Standard` Creates an array type */
130
133
  Array(items, options = {}) {
131
- return { ...options, kind: exports.ArrayKind, type: 'array', items };
134
+ return this.Store({ ...options, kind: exports.ArrayKind, type: 'array', items });
132
135
  }
133
- /** `standard` Creates an enum type from a TypeScript enum */
136
+ /** `Standard` Creates an enum type from a TypeScript enum */
134
137
  Enum(item, options = {}) {
135
138
  const values = Object.keys(item).filter(key => isNaN(key)).map(key => item[key]);
136
139
  const anyOf = values.map(value => typeof value === 'string' ? { type: 'string', const: value } : { type: 'number', const: value });
137
- return { ...options, kind: exports.EnumKind, anyOf };
140
+ return this.Store({ ...options, kind: exports.EnumKind, anyOf });
138
141
  }
139
- /** `standard` Creates a literal type. Supports string, number and boolean values only */
142
+ /** `Standard` Creates a literal type. Supports string, number and boolean values only */
140
143
  Literal(value, options = {}) {
141
- return { ...options, kind: exports.LiteralKind, const: value, type: typeof value };
144
+ return this.Store({ ...options, kind: exports.LiteralKind, const: value, type: typeof value });
142
145
  }
143
- /** `standard` Creates a string type */
146
+ /** `Standard` Creates a string type */
144
147
  String(options = {}) {
145
- return { ...options, kind: exports.StringKind, type: 'string' };
148
+ return this.Store({ ...options, kind: exports.StringKind, type: 'string' });
146
149
  }
147
- /** `standard` Creates a string type from a regular expression */
150
+ /** `Standard` Creates a string type from a regular expression */
148
151
  RegEx(regex, options = {}) {
149
152
  return this.String({ ...options, pattern: regex.source });
150
153
  }
151
- /** `standard` Creates a number type */
154
+ /** `Standard` Creates a number type */
152
155
  Number(options = {}) {
153
- return { ...options, kind: exports.NumberKind, type: 'number' };
156
+ return this.Store({ ...options, kind: exports.NumberKind, type: 'number' });
154
157
  }
155
- /** `standard` Creates an integer type */
158
+ /** `Standard` Creates an integer type */
156
159
  Integer(options = {}) {
157
- return { ...options, kind: exports.IntegerKind, type: 'integer' };
160
+ return this.Store({ ...options, kind: exports.IntegerKind, type: 'integer' });
158
161
  }
159
- /** `standard` Creates a boolean type */
162
+ /** `Standard` Creates a boolean type */
160
163
  Boolean(options = {}) {
161
- return { ...options, kind: exports.BooleanKind, type: 'boolean' };
164
+ return this.Store({ ...options, kind: exports.BooleanKind, type: 'boolean' });
162
165
  }
163
- /** `standard` Creates a null type */
166
+ /** `Standard` Creates a null type */
164
167
  Null(options = {}) {
165
- return { ...options, kind: exports.NullKind, type: 'null' };
168
+ return this.Store({ ...options, kind: exports.NullKind, type: 'null' });
166
169
  }
167
- /** `standard` Creates an unknown type */
170
+ /** `Standard` Creates an unknown type */
168
171
  Unknown(options = {}) {
169
- return { ...options, kind: exports.UnknownKind };
172
+ return this.Store({ ...options, kind: exports.UnknownKind });
170
173
  }
171
- /** `standard` Creates an any type */
174
+ /** `Standard` Creates an any type */
172
175
  Any(options = {}) {
173
- return { ...options, kind: exports.AnyKind };
176
+ return this.Store({ ...options, kind: exports.AnyKind });
174
177
  }
175
- /** `standard` Creates a keyof type from the given object */
176
- KeyOf(schema, options = {}) {
177
- const keys = Object.keys(schema.properties);
178
- return { ...options, kind: exports.KeyOfKind, type: 'string', enum: keys };
179
- }
180
- /** `standard` Creates a record type */
178
+ /** `Standard` Creates a record type */
181
179
  Record(key, value, options = {}) {
182
180
  const pattern = (() => {
183
181
  switch (key.kind) {
184
- case exports.UnionKind: return `^${key.anyOf.map(literal => literal.const).join('|')}$`;
182
+ case exports.UnionKind: return `^${key.anyOf.map((literal) => literal.const).join('|')}$`;
185
183
  case exports.KeyOfKind: return `^${key.enum.join('|')}$`;
186
184
  case exports.NumberKind: return '^(0|[1-9][0-9]*)$';
187
185
  case exports.StringKind: return key.pattern ? key.pattern : '^.*$';
188
186
  default: throw Error('Invalid Record Key');
189
187
  }
190
188
  })();
191
- return { ...options, kind: exports.RecordKind, type: 'object', patternProperties: { [pattern]: value } };
192
- }
193
- /** `standard` Makes all properties in the given object type required */
194
- Required(schema, options = {}) {
195
- const next = { ...clone(schema), ...options };
196
- next.required = Object.keys(next.properties);
197
- for (const key of Object.keys(next.properties)) {
198
- const property = next.properties[key];
189
+ return this.Store({ ...options, kind: exports.RecordKind, type: 'object', patternProperties: { [pattern]: value } });
190
+ }
191
+ /** `Standard` Creates a keyof type from the given object */
192
+ KeyOf(object, options = {}) {
193
+ const source = this.Deref(object);
194
+ const keys = Object.keys(source.properties);
195
+ return this.Store({ ...options, kind: exports.KeyOfKind, type: 'string', enum: keys });
196
+ }
197
+ /** `Standard` Makes all properties in the given object type required */
198
+ Required(object, options = {}) {
199
+ const source = this.Deref(object);
200
+ const schema = { ...clone(source), ...options };
201
+ schema.required = Object.keys(schema.properties);
202
+ for (const key of Object.keys(schema.properties)) {
203
+ const property = schema.properties[key];
199
204
  switch (property.modifier) {
200
205
  case exports.ReadonlyOptionalModifier:
201
206
  property.modifier = exports.ReadonlyModifier;
@@ -211,14 +216,15 @@ class TypeBuilder {
211
216
  break;
212
217
  }
213
218
  }
214
- return next;
215
- }
216
- /** `standard` Makes all properties in the given object type optional */
217
- Partial(schema, options = {}) {
218
- const next = { ...clone(schema), ...options };
219
- delete next.required;
220
- for (const key of Object.keys(next.properties)) {
221
- const property = next.properties[key];
219
+ return this.Store(schema);
220
+ }
221
+ /** `Standard` Makes all properties in the given object type optional */
222
+ Partial(object, options = {}) {
223
+ const source = this.Deref(object);
224
+ const schema = { ...clone(source), ...options };
225
+ delete schema.required;
226
+ for (const key of Object.keys(schema.properties)) {
227
+ const property = schema.properties[key];
222
228
  switch (property.modifier) {
223
229
  case exports.ReadonlyOptionalModifier:
224
230
  property.modifier = exports.ReadonlyOptionalModifier;
@@ -234,71 +240,100 @@ class TypeBuilder {
234
240
  break;
235
241
  }
236
242
  }
237
- return next;
238
- }
239
- /** `standard` Picks property keys from the given object type */
240
- Pick(schema, keys, options = {}) {
241
- const next = { ...clone(schema), ...options };
242
- next.required = next.required ? next.required.filter((key) => keys.includes(key)) : undefined;
243
- for (const key of Object.keys(next.properties)) {
243
+ return this.Store(schema);
244
+ }
245
+ /** `Standard` Picks property keys from the given object type */
246
+ Pick(object, keys, options = {}) {
247
+ const source = this.Deref(object);
248
+ const schema = { ...clone(source), ...options };
249
+ schema.required = schema.required ? schema.required.filter((key) => keys.includes(key)) : undefined;
250
+ for (const key of Object.keys(schema.properties)) {
244
251
  if (!keys.includes(key))
245
- delete next.properties[key];
252
+ delete schema.properties[key];
246
253
  }
247
- return next;
248
- }
249
- /** `standard` Omits property keys from the given object type */
250
- Omit(schema, keys, options = {}) {
251
- const next = { ...clone(schema), ...options };
252
- next.required = next.required ? next.required.filter((key) => !keys.includes(key)) : undefined;
253
- for (const key of Object.keys(next.properties)) {
254
+ return this.Store(schema);
255
+ }
256
+ /** `Standard` Omits property keys from the given object type */
257
+ Omit(object, keys, options = {}) {
258
+ const source = this.Deref(object);
259
+ const schema = { ...clone(source), ...options };
260
+ schema.required = schema.required ? schema.required.filter((key) => !keys.includes(key)) : undefined;
261
+ for (const key of Object.keys(schema.properties)) {
254
262
  if (keys.includes(key))
255
- delete next.properties[key];
263
+ delete schema.properties[key];
256
264
  }
257
- return next;
265
+ return this.Store(schema);
258
266
  }
259
- /** `standard` Omits the `kind` and `modifier` properties from the underlying schema */
267
+ /** `Standard` Omits the `kind` and `modifier` properties from the underlying schema */
260
268
  Strict(schema, options = {}) {
261
269
  return JSON.parse(JSON.stringify({ ...options, ...schema }));
262
270
  }
263
- /** `extended` Creates a constructor type */
271
+ /** `Extended` Creates a constructor type */
264
272
  Constructor(args, returns, options = {}) {
265
- return { ...options, kind: exports.ConstructorKind, type: 'constructor', arguments: args, returns };
273
+ return this.Store({ ...options, kind: exports.ConstructorKind, type: 'constructor', arguments: args, returns });
266
274
  }
267
- /** `extended` Creates a function type */
275
+ /** `Extended` Creates a function type */
268
276
  Function(args, returns, options = {}) {
269
- return { ...options, kind: exports.FunctionKind, type: 'function', arguments: args, returns };
277
+ return this.Store({ ...options, kind: exports.FunctionKind, type: 'function', arguments: args, returns });
270
278
  }
271
- /** `extended` Creates a promise type */
279
+ /** `Extended` Creates a promise type */
272
280
  Promise(item, options = {}) {
273
- return { ...options, type: 'promise', kind: exports.PromiseKind, item };
281
+ return this.Store({ ...options, type: 'promise', kind: exports.PromiseKind, item });
274
282
  }
275
- /** `extended` Creates a undefined type */
283
+ /** `Extended` Creates a undefined type */
276
284
  Undefined(options = {}) {
277
- return { ...options, type: 'undefined', kind: exports.UndefinedKind };
285
+ return this.Store({ ...options, type: 'undefined', kind: exports.UndefinedKind });
278
286
  }
279
- /** `extended` Creates a void type */
287
+ /** `Extended` Creates a void type */
280
288
  Void(options = {}) {
281
- return { ...options, type: 'void', kind: exports.VoidKind };
289
+ return this.Store({ ...options, type: 'void', kind: exports.VoidKind });
282
290
  }
283
- /** `experimental` Creates a recursive type */
284
- Rec(callback, options = {}) {
285
- const $id = options.$id || '';
286
- const self = callback({ $ref: `${$id}#/definitions/self` });
287
- return { ...options, $ref: `${$id}#/definitions/self`, definitions: { self } };
288
- }
289
- /** `experimental` Creates a recursive type. Pending https://github.com/ajv-validator/ajv/issues/1709 */
290
- // public Rec<T extends TProperties>($id: string, callback: (self: TAny) => T, options: ObjectOptions = {}): TObject<T> {
291
- // const properties = callback({ $recursiveRef: `${$id}` } as any)
292
- // return { ...options, kind: ObjectKind, $id, $recursiveAnchor: true, type: 'object', properties }
293
- // }
294
- /** `experimental` Creates a namespace for a set of related types */
295
- Namespace(definitions, options = {}) {
296
- return { ...options, kind: exports.BoxKind, definitions };
291
+ /** `Standard` Creates a namespace for a set of related types */
292
+ Namespace($defs, options = {}) {
293
+ return this.Store({ ...options, kind: exports.BoxKind, $defs });
297
294
  }
298
295
  Ref(...args) {
299
- const $id = args[0]['$id'] || '';
300
- const key = args[1];
301
- return (args.length === 2) ? { $ref: `${$id}#/definitions/${key}` } : { $ref: $id };
296
+ if (args.length === 2) {
297
+ const namespace = args[0];
298
+ const targetKey = args[1];
299
+ if (namespace.$id === undefined)
300
+ throw new Error(`Referenced namespace has no $id`);
301
+ if (!this.schemas.has(namespace.$id))
302
+ throw new Error(`Unable to locate namespace with $id '${namespace.$id}'`);
303
+ return this.Store({ kind: exports.RefKind, $ref: `${namespace.$id}#/$defs/${targetKey}` });
304
+ }
305
+ else if (args.length === 1) {
306
+ const target = args[0];
307
+ if (target.$id === undefined)
308
+ throw new Error(`Referenced schema has no $id`);
309
+ if (!this.schemas.has(target.$id))
310
+ throw new Error(`Unable to locate schema with $id '${target.$id}'`);
311
+ return this.Store({ kind: exports.RefKind, $ref: target.$id });
312
+ }
313
+ else {
314
+ throw new Error('Type.Ref: Invalid arguments');
315
+ }
316
+ }
317
+ /** `Experimental` Creates a recursive type */
318
+ Rec(callback, options = {}) {
319
+ const $id = options.$id || '';
320
+ const self = callback({ $ref: `${$id}#/$defs/self` });
321
+ return this.Store({ ...options, $ref: `${$id}#/$defs/self`, $defs: { self } });
322
+ }
323
+ /** Conditionally stores and schema if it contains an $id and returns. */
324
+ Store(schema) {
325
+ if (!schema.$id)
326
+ return schema;
327
+ this.schemas.set(schema.$id, schema);
328
+ return schema;
329
+ }
330
+ /** Conditionally dereferences a schema if RefKind. Otherwise return argument. */
331
+ Deref(schema) {
332
+ if (schema.kind !== exports.RefKind)
333
+ return schema;
334
+ if (!this.schemas.has(schema.$ref))
335
+ throw Error(`Unable to locate schema with $id '${schema.$ref}'`);
336
+ return this.Deref(this.schemas.get(schema.$ref));
302
337
  }
303
338
  }
304
339
  exports.TypeBuilder = TypeBuilder;