1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const error_1 = require("./error");
|
4 | class InvalidSchema extends error_1.JsonSchemaErrorBase {
|
5 | }
|
6 | exports.InvalidSchema = InvalidSchema;
|
7 | class InvalidValueError extends error_1.JsonSchemaErrorBase {
|
8 | }
|
9 | exports.InvalidValueError = InvalidValueError;
|
10 | class MissingImplementationError extends error_1.JsonSchemaErrorBase {
|
11 | }
|
12 | exports.MissingImplementationError = MissingImplementationError;
|
13 | class SettingReadOnlyPropertyError extends error_1.JsonSchemaErrorBase {
|
14 | }
|
15 | exports.SettingReadOnlyPropertyError = SettingReadOnlyPropertyError;
|
16 | class InvalidUpdateValue extends error_1.JsonSchemaErrorBase {
|
17 | }
|
18 | exports.InvalidUpdateValue = InvalidUpdateValue;
|
19 |
|
20 |
|
21 |
|
22 | class SchemaTreeNode {
|
23 | constructor(nodeMetaData) {
|
24 | this._defined = false;
|
25 | this._dirty = false;
|
26 | this._schema = nodeMetaData.schema;
|
27 | this._name = nodeMetaData.name;
|
28 | this._value = nodeMetaData.value;
|
29 | this._forward = nodeMetaData.forward;
|
30 | this._parent = nodeMetaData.parent;
|
31 | }
|
32 | dispose() {
|
33 | this._parent = null;
|
34 | this._schema = null;
|
35 | this._value = null;
|
36 | if (this._forward) {
|
37 | this._forward.dispose();
|
38 | }
|
39 | this._forward = null;
|
40 | }
|
41 | get defined() { return this._defined; }
|
42 | get dirty() { return this._dirty; }
|
43 | set dirty(v) {
|
44 | if (v) {
|
45 | this._defined = true;
|
46 | this._dirty = true;
|
47 | if (this._parent) {
|
48 | this._parent.dirty = true;
|
49 | }
|
50 | }
|
51 | }
|
52 | get value() { return this.get(); }
|
53 | get name() { return this._name; }
|
54 | get readOnly() { return this._schema['readOnly']; }
|
55 | get frozen() { return true; }
|
56 | get description() {
|
57 | return 'description' in this._schema ? this._schema['description'] : null;
|
58 | }
|
59 | get required() {
|
60 | if (!this._parent) {
|
61 | return false;
|
62 | }
|
63 | return this._parent.isChildRequired(this.name);
|
64 | }
|
65 | isChildRequired(_name) { return false; }
|
66 | get parent() { return this._parent; }
|
67 | get children() { return null; }
|
68 | get items() { return null; }
|
69 | get itemPrototype() { return null; }
|
70 | set(_v, _init = false, _force = false) {
|
71 | if (!this.readOnly) {
|
72 | throw new MissingImplementationError();
|
73 | }
|
74 | throw new SettingReadOnlyPropertyError();
|
75 | }
|
76 | isCompatible(_v) { return false; }
|
77 | static _defineProperty(proto, treeNode) {
|
78 | if (treeNode.readOnly) {
|
79 | Object.defineProperty(proto, treeNode.name, {
|
80 | enumerable: true,
|
81 | get: () => treeNode.get()
|
82 | });
|
83 | }
|
84 | else {
|
85 | Object.defineProperty(proto, treeNode.name, {
|
86 | enumerable: true,
|
87 | get: () => treeNode.get(),
|
88 | set: (v) => treeNode.set(v)
|
89 | });
|
90 | }
|
91 | }
|
92 | }
|
93 | exports.SchemaTreeNode = SchemaTreeNode;
|
94 | /** Base Class used for Non-Leaves TreeNode. Meaning they can have children. */
|
95 | class NonLeafSchemaTreeNode extends SchemaTreeNode {
|
96 | dispose() {
|
97 | for (const key of Object.keys(this.children || {})) {
|
98 | this.children[key].dispose();
|
99 | }
|
100 | for (let item of this.items || []) {
|
101 | item.dispose();
|
102 | }
|
103 | super.dispose();
|
104 | }
|
105 | get() {
|
106 | if (this.defined) {
|
107 | return this._value;
|
108 | }
|
109 | else {
|
110 | return undefined;
|
111 | }
|
112 | }
|
113 | destroy() {
|
114 | this._defined = false;
|
115 | this._value = null;
|
116 | }
|
117 |
|
118 | _createChildProperty(name, value, forward, schema, define = true) {
|
119 | const type = ('oneOf' in schema) ? 'oneOf' :
|
120 | ('enum' in schema) ? 'enum' : schema['type'];
|
121 | let Klass = null;
|
122 | switch (type) {
|
123 | case 'object':
|
124 | Klass = ObjectSchemaTreeNode;
|
125 | break;
|
126 | case 'array':
|
127 | Klass = ArraySchemaTreeNode;
|
128 | break;
|
129 | case 'string':
|
130 | Klass = StringSchemaTreeNode;
|
131 | break;
|
132 | case 'boolean':
|
133 | Klass = BooleanSchemaTreeNode;
|
134 | break;
|
135 | case 'number':
|
136 | Klass = NumberSchemaTreeNode;
|
137 | break;
|
138 | case 'integer':
|
139 | Klass = IntegerSchemaTreeNode;
|
140 | break;
|
141 | case 'enum':
|
142 | Klass = EnumSchemaTreeNode;
|
143 | break;
|
144 | case 'oneOf':
|
145 | Klass = OneOfSchemaTreeNode;
|
146 | break;
|
147 | default:
|
148 | throw new InvalidSchema('Type ' + type + ' not understood by SchemaClassFactory.');
|
149 | }
|
150 | const metaData = new Klass({ parent: this, forward, value, schema, name });
|
151 | if (define) {
|
152 | SchemaTreeNode._defineProperty(this._value, metaData);
|
153 | }
|
154 | return metaData;
|
155 | }
|
156 | }
|
157 | exports.NonLeafSchemaTreeNode = NonLeafSchemaTreeNode;
|
158 | class OneOfSchemaTreeNode extends NonLeafSchemaTreeNode {
|
159 | constructor(metaData) {
|
160 | super(metaData);
|
161 | let { value, forward, schema } = metaData;
|
162 | this._typesPrototype = schema['oneOf'].map((schema) => {
|
163 | return this._createChildProperty('', '', forward, schema, false);
|
164 | });
|
165 | this._currentTypeHolder = null;
|
166 | this._set(value, true, false);
|
167 | }
|
168 | _set(v, init, force) {
|
169 | if (!init && this.readOnly && !force) {
|
170 | throw new SettingReadOnlyPropertyError();
|
171 | }
|
172 |
|
173 | let proto = null;
|
174 | for (let i = 0; i < this._typesPrototype.length; i++) {
|
175 | const p = this._typesPrototype[i];
|
176 | if (p.isCompatible(v)) {
|
177 | proto = p;
|
178 | break;
|
179 | }
|
180 | }
|
181 | if (proto == null) {
|
182 | return;
|
183 | }
|
184 | if (!init) {
|
185 | this.dirty = true;
|
186 | }
|
187 | this._currentTypeHolder = proto;
|
188 | this._currentTypeHolder.set(v, false, true);
|
189 | }
|
190 | set(v, _init = false, force = false) {
|
191 | return this._set(v, false, force);
|
192 | }
|
193 | get() {
|
194 | return this._currentTypeHolder ? this._currentTypeHolder.get() : null;
|
195 | }
|
196 | get defaultValue() {
|
197 | return null;
|
198 | }
|
199 | get defined() { return this._currentTypeHolder ? this._currentTypeHolder.defined : false; }
|
200 | get items() { return this._typesPrototype; }
|
201 | get type() { return 'oneOf'; }
|
202 | get tsType() { return null; }
|
203 | serialize(serializer) { serializer.outputOneOf(this); }
|
204 | }
|
205 | exports.OneOfSchemaTreeNode = OneOfSchemaTreeNode;
|
206 |
|
207 | class ObjectSchemaTreeNode extends NonLeafSchemaTreeNode {
|
208 | constructor(metaData) {
|
209 | super(metaData);
|
210 | this._frozen = false;
|
211 | this._set(metaData.value, true, false);
|
212 | }
|
213 | _set(value, init, force) {
|
214 | if (!init && this.readOnly && !force) {
|
215 | throw new SettingReadOnlyPropertyError();
|
216 | }
|
217 | const schema = this._schema;
|
218 | const forward = this._forward;
|
219 | this._defined = !!value;
|
220 | this._children = Object.create(null);
|
221 | this._value = Object.create(null);
|
222 | this._dirty = this._dirty || !init;
|
223 | if (schema['properties']) {
|
224 | for (const name of Object.keys(schema['properties'])) {
|
225 | const propertySchema = schema['properties'][name];
|
226 | this._children[name] = this._createChildProperty(name, value ? value[name] : undefined, forward ? forward.children[name] : null, propertySchema);
|
227 | }
|
228 | }
|
229 | else if (!schema['additionalProperties']) {
|
230 | throw new InvalidSchema('Schema does not have a properties, but doesnt allow for '
|
231 | + 'additional properties.');
|
232 | }
|
233 | if (!schema['additionalProperties']) {
|
234 | this._frozen = true;
|
235 | Object.freeze(this._value);
|
236 | Object.freeze(this._children);
|
237 | }
|
238 | else if (value) {
|
239 |
|
240 | for (const key of Object.keys(value)) {
|
241 | if (!this._children[key]) {
|
242 | this._value[key] = value[key];
|
243 | }
|
244 | }
|
245 | }
|
246 | }
|
247 | set(v, force = false) {
|
248 | return this._set(v, false, force);
|
249 | }
|
250 | get frozen() { return this._frozen; }
|
251 | get children() { return this._children; }
|
252 | get type() { return 'object'; }
|
253 | get tsType() { return Object; }
|
254 | get defaultValue() { return null; }
|
255 | isCompatible(v) { return typeof v == 'object' && v !== null; }
|
256 | isChildRequired(name) {
|
257 | if (this._schema['required']) {
|
258 | return this._schema['required'].indexOf(name) != -1;
|
259 | }
|
260 | return false;
|
261 | }
|
262 | serialize(serializer) { serializer.object(this); }
|
263 | }
|
264 | exports.ObjectSchemaTreeNode = ObjectSchemaTreeNode;
|
265 |
|
266 | class ArraySchemaTreeNode extends NonLeafSchemaTreeNode {
|
267 | constructor(metaData) {
|
268 | super(metaData);
|
269 | this._set(metaData.value, true, false);
|
270 |
|
271 | this._itemPrototype = this._createChildProperty('', undefined, null, metaData.schema['items'], false);
|
272 | }
|
273 | _set(value, init, _force) {
|
274 | const schema = this._schema;
|
275 | const forward = this._forward;
|
276 | this._value = Object.create(null);
|
277 | this._dirty = this._dirty || !init;
|
278 | if (value) {
|
279 | this._defined = true;
|
280 | }
|
281 | else {
|
282 | this._defined = false;
|
283 | value = [];
|
284 | }
|
285 | this._items = [];
|
286 | this._value = [];
|
287 | for (let index = 0; index < value.length; index++) {
|
288 | this._items[index] = this._createChildProperty('' + index, value && value[index], forward && forward.items[index], schema['items']);
|
289 | }
|
290 | }
|
291 | set(v, init = false, force = false) {
|
292 | return this._set(v, init, force);
|
293 | }
|
294 | isCompatible(v) { return Array.isArray(v); }
|
295 | get type() { return 'array'; }
|
296 | get tsType() { return Array; }
|
297 | get items() { return this._items; }
|
298 | get itemPrototype() { return this._itemPrototype; }
|
299 | get defaultValue() { return null; }
|
300 | serialize(serializer) { serializer.array(this); }
|
301 | }
|
302 | exports.ArraySchemaTreeNode = ArraySchemaTreeNode;
|
303 |
|
304 |
|
305 |
|
306 |
|
307 | class RootSchemaTreeNode extends ObjectSchemaTreeNode {
|
308 | constructor(proto, metaData) {
|
309 | super(metaData);
|
310 | for (const key of Object.keys(this._children)) {
|
311 | if (this._children[key]) {
|
312 | SchemaTreeNode._defineProperty(proto, this._children[key]);
|
313 | }
|
314 | }
|
315 | }
|
316 | }
|
317 | exports.RootSchemaTreeNode = RootSchemaTreeNode;
|
318 |
|
319 | class LeafSchemaTreeNode extends SchemaTreeNode {
|
320 | constructor(metaData) {
|
321 | super(metaData);
|
322 | this._defined = metaData.value !== undefined;
|
323 | if ('default' in metaData.schema) {
|
324 | this._default = this.convert(metaData.schema['default']);
|
325 | }
|
326 | }
|
327 | get() {
|
328 | if (!this.defined && this._forward) {
|
329 | return this._forward.get();
|
330 | }
|
331 | if (!this.defined) {
|
332 | return 'default' in this._schema ? this._default : undefined;
|
333 | }
|
334 | return this._value === undefined
|
335 | ? undefined
|
336 | : (this._value === null ? null : this.convert(this._value));
|
337 | }
|
338 | set(v, init = false, force = false) {
|
339 | if (this.readOnly && !force) {
|
340 | throw new SettingReadOnlyPropertyError();
|
341 | }
|
342 | let convertedValue = this.convert(v);
|
343 | if (convertedValue === null || convertedValue === undefined) {
|
344 | if (this.required) {
|
345 | throw new InvalidValueError(`Invalid value "${v}" on a required field.`);
|
346 | }
|
347 | }
|
348 | this.dirty = !init;
|
349 | this._value = convertedValue;
|
350 | }
|
351 | destroy() {
|
352 | this._defined = false;
|
353 | this._value = null;
|
354 | }
|
355 | get defaultValue() {
|
356 | return this.hasDefault ? this._default : null;
|
357 | }
|
358 | get hasDefault() {
|
359 | return 'default' in this._schema;
|
360 | }
|
361 | serialize(serializer) {
|
362 | serializer.outputValue(this);
|
363 | }
|
364 | }
|
365 | exports.LeafSchemaTreeNode = LeafSchemaTreeNode;
|
366 |
|
367 | class StringSchemaTreeNode extends LeafSchemaTreeNode {
|
368 | serialize(serializer) { serializer.outputString(this); }
|
369 | isCompatible(v) { return typeof v == 'string' || v instanceof String; }
|
370 | convert(v) { return v === undefined ? undefined : '' + v; }
|
371 | get type() { return 'string'; }
|
372 | get tsType() { return String; }
|
373 | }
|
374 | class EnumSchemaTreeNode extends LeafSchemaTreeNode {
|
375 | constructor(metaData) {
|
376 | super(metaData);
|
377 | if (!Array.isArray(metaData.schema['enum'])) {
|
378 | throw new InvalidSchema();
|
379 | }
|
380 | if (this.hasDefault && !this._isInEnum(this._default)) {
|
381 | throw new InvalidSchema();
|
382 | }
|
383 | this.set(metaData.value, true, true);
|
384 | }
|
385 | _isInEnum(value) {
|
386 | return this._schema['enum'].some((v) => v === value);
|
387 | }
|
388 | get items() { return this._schema['enum']; }
|
389 | set(value, init = false, force = false) {
|
390 | if (!(value === undefined || this._isInEnum(value))) {
|
391 | throw new InvalidUpdateValue('Invalid value can only be one of these: ' + this.items);
|
392 | }
|
393 | super.set(value, init, force);
|
394 | }
|
395 | isCompatible(v) {
|
396 | return this._isInEnum(v);
|
397 | }
|
398 | convert(v) {
|
399 | if (v === undefined) {
|
400 | return undefined;
|
401 | }
|
402 | if (!this._isInEnum(v)) {
|
403 | return undefined;
|
404 | }
|
405 | return v;
|
406 | }
|
407 | get type() {
|
408 | return this._schema['type'] || 'any';
|
409 | }
|
410 | get tsType() { return null; }
|
411 | serialize(serializer) { serializer.outputEnum(this); }
|
412 | }
|
413 | class BooleanSchemaTreeNode extends LeafSchemaTreeNode {
|
414 | serialize(serializer) { serializer.outputBoolean(this); }
|
415 | isCompatible(v) { return typeof v == 'boolean' || v instanceof Boolean; }
|
416 | convert(v) { return v === undefined ? undefined : !!v; }
|
417 | get type() { return 'boolean'; }
|
418 | get tsType() { return Boolean; }
|
419 | }
|
420 | class NumberSchemaTreeNode extends LeafSchemaTreeNode {
|
421 | serialize(serializer) { serializer.outputNumber(this); }
|
422 | isCompatible(v) { return typeof v == 'number' || v instanceof Number; }
|
423 | convert(v) { return v === undefined ? undefined : +v; }
|
424 | get type() { return 'number'; }
|
425 | get tsType() { return Number; }
|
426 | }
|
427 | class IntegerSchemaTreeNode extends NumberSchemaTreeNode {
|
428 | convert(v) { return v === undefined ? undefined : Math.floor(+v); }
|
429 | }
|
430 |
|
\ | No newline at end of file |