@aws-cdk/cloud-assembly-schema 40.3.0 → 40.5.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.
@@ -0,0 +1,390 @@
1
+ 'use strict';
2
+
3
+ var uri = require('url');
4
+
5
+ var ValidationError = exports.ValidationError = function ValidationError (message, instance, schema, path, name, argument) {
6
+ if(Array.isArray(path)){
7
+ this.path = path;
8
+ this.property = path.reduce(function(sum, item){
9
+ return sum + makeSuffix(item);
10
+ }, 'instance');
11
+ }else if(path !== undefined){
12
+ this.property = path;
13
+ }
14
+ if (message) {
15
+ this.message = message;
16
+ }
17
+ if (schema) {
18
+ var id = schema.$id || schema.id;
19
+ this.schema = id || schema;
20
+ }
21
+ if (instance !== undefined) {
22
+ this.instance = instance;
23
+ }
24
+ this.name = name;
25
+ this.argument = argument;
26
+ this.stack = this.toString();
27
+ };
28
+
29
+ ValidationError.prototype.toString = function toString() {
30
+ return this.property + ' ' + this.message;
31
+ };
32
+
33
+ var ValidatorResult = exports.ValidatorResult = function ValidatorResult(instance, schema, options, ctx) {
34
+ this.instance = instance;
35
+ this.schema = schema;
36
+ this.options = options;
37
+ this.path = ctx.path;
38
+ this.propertyPath = ctx.propertyPath;
39
+ this.errors = [];
40
+ this.throwError = options && options.throwError;
41
+ this.throwFirst = options && options.throwFirst;
42
+ this.throwAll = options && options.throwAll;
43
+ this.disableFormat = options && options.disableFormat === true;
44
+ };
45
+
46
+ ValidatorResult.prototype.addError = function addError(detail) {
47
+ var err;
48
+ if (typeof detail == 'string') {
49
+ err = new ValidationError(detail, this.instance, this.schema, this.path);
50
+ } else {
51
+ if (!detail) throw new Error('Missing error detail');
52
+ if (!detail.message) throw new Error('Missing error message');
53
+ if (!detail.name) throw new Error('Missing validator type');
54
+ err = new ValidationError(detail.message, this.instance, this.schema, this.path, detail.name, detail.argument);
55
+ }
56
+
57
+ this.errors.push(err);
58
+ if (this.throwFirst) {
59
+ throw new ValidatorResultError(this);
60
+ }else if(this.throwError){
61
+ throw err;
62
+ }
63
+ return err;
64
+ };
65
+
66
+ ValidatorResult.prototype.importErrors = function importErrors(res) {
67
+ if (typeof res == 'string' || (res && res.validatorType)) {
68
+ this.addError(res);
69
+ } else if (res && res.errors) {
70
+ this.errors = this.errors.concat(res.errors);
71
+ }
72
+ };
73
+
74
+ function stringizer (v,i){
75
+ return i+': '+v.toString()+'\n';
76
+ }
77
+ ValidatorResult.prototype.toString = function toString(res) {
78
+ return this.errors.map(stringizer).join('');
79
+ };
80
+
81
+ Object.defineProperty(ValidatorResult.prototype, "valid", { get: function() {
82
+ return !this.errors.length;
83
+ } });
84
+
85
+ module.exports.ValidatorResultError = ValidatorResultError;
86
+ function ValidatorResultError(result) {
87
+ if(Error.captureStackTrace){
88
+ Error.captureStackTrace(this, ValidatorResultError);
89
+ }
90
+ this.instance = result.instance;
91
+ this.schema = result.schema;
92
+ this.options = result.options;
93
+ this.errors = result.errors;
94
+ }
95
+ ValidatorResultError.prototype = new Error();
96
+ ValidatorResultError.prototype.constructor = ValidatorResultError;
97
+ ValidatorResultError.prototype.name = "Validation Error";
98
+
99
+ /**
100
+ * Describes a problem with a Schema which prevents validation of an instance
101
+ * @name SchemaError
102
+ * @constructor
103
+ */
104
+ var SchemaError = exports.SchemaError = function SchemaError (msg, schema) {
105
+ this.message = msg;
106
+ this.schema = schema;
107
+ Error.call(this, msg);
108
+ Error.captureStackTrace(this, SchemaError);
109
+ };
110
+ SchemaError.prototype = Object.create(Error.prototype,
111
+ {
112
+ constructor: {value: SchemaError, enumerable: false},
113
+ name: {value: 'SchemaError', enumerable: false},
114
+ });
115
+
116
+ var SchemaContext = exports.SchemaContext = function SchemaContext (schema, options, path, base, schemas) {
117
+ this.schema = schema;
118
+ this.options = options;
119
+ if(Array.isArray(path)){
120
+ this.path = path;
121
+ this.propertyPath = path.reduce(function(sum, item){
122
+ return sum + makeSuffix(item);
123
+ }, 'instance');
124
+ }else{
125
+ this.propertyPath = path;
126
+ }
127
+ this.base = base;
128
+ this.schemas = schemas;
129
+ };
130
+
131
+ SchemaContext.prototype.resolve = function resolve (target) {
132
+ return uri.resolve(this.base, target);
133
+ };
134
+
135
+ SchemaContext.prototype.makeChild = function makeChild(schema, propertyName){
136
+ var path = (propertyName===undefined) ? this.path : this.path.concat([propertyName]);
137
+ var id = schema.$id || schema.id;
138
+ var base = uri.resolve(this.base, id||'');
139
+ var ctx = new SchemaContext(schema, this.options, path, base, Object.create(this.schemas));
140
+ if(id && !ctx.schemas[base]){
141
+ ctx.schemas[base] = schema;
142
+ }
143
+ return ctx;
144
+ };
145
+
146
+ var FORMAT_REGEXPS = exports.FORMAT_REGEXPS = {
147
+ // 7.3.1. Dates, Times, and Duration
148
+ 'date-time': /^\d{4}-(?:0[0-9]{1}|1[0-2]{1})-(3[01]|0[1-9]|[12][0-9])[tT ](2[0-4]|[01][0-9]):([0-5][0-9]):(60|[0-5][0-9])(\.\d+)?([zZ]|[+-]([0-5][0-9]):(60|[0-5][0-9]))$/,
149
+ 'date': /^\d{4}-(?:0[0-9]{1}|1[0-2]{1})-(3[01]|0[1-9]|[12][0-9])$/,
150
+ 'time': /^(2[0-4]|[01][0-9]):([0-5][0-9]):(60|[0-5][0-9])$/,
151
+ 'duration': /P(T\d+(H(\d+M(\d+S)?)?|M(\d+S)?|S)|\d+(D|M(\d+D)?|Y(\d+M(\d+D)?)?)(T\d+(H(\d+M(\d+S)?)?|M(\d+S)?|S))?|\d+W)/i,
152
+
153
+ // 7.3.2. Email Addresses
154
+ // TODO: fix the email production
155
+ 'email': /^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!\.)){0,61}[a-zA-Z0-9]?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!$)){0,61}[a-zA-Z0-9]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/,
156
+ 'idn-email': /^("(?:[!#-\[\]-\u{10FFFF}]|\\[\t -\u{10FFFF}])*"|[!#-'*+\-/-9=?A-Z\^-\u{10FFFF}](?:\.?[!#-'*+\-/-9=?A-Z\^-\u{10FFFF}])*)@([!#-'*+\-/-9=?A-Z\^-\u{10FFFF}](?:\.?[!#-'*+\-/-9=?A-Z\^-\u{10FFFF}])*|\[[!-Z\^-\u{10FFFF}]*\])$/u,
157
+
158
+ // 7.3.3. Hostnames
159
+
160
+ // 7.3.4. IP Addresses
161
+ 'ip-address': /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/,
162
+ // FIXME whitespace is invalid
163
+ 'ipv6': /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/,
164
+
165
+ // 7.3.5. Resource Identifiers
166
+ // TODO: A more accurate regular expression for "uri" goes:
167
+ // [A-Za-z][+\-.0-9A-Za-z]*:((/(/((%[0-9A-Fa-f]{2}|[!$&-.0-9;=A-Z_a-z~])+|(\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~]+)?|[.0-:A-Fa-f]+)\])?)(:\d*)?)?)?#(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~])*|(/(/((%[0-9A-Fa-f]{2}|[!$&-.0-9;=A-Z_a-z~])+|(\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~]+)?|[.0-:A-Fa-f]+)\])?)(:\d*)?[/?]|[!$&-.0-;=?-Z_a-z~])|/?%[0-9A-Fa-f]{2}|[!$&-.0-;=?-Z_a-z~])(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~])*(#(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~])*)?|/(/((%[0-9A-Fa-f]{2}|[!$&-.0-9;=A-Z_a-z~])+(:\d*)?|(\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~]+)?|[.0-:A-Fa-f]+)\])?:\d*|\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~]+)?|[.0-:A-Fa-f]+)\])?)?)?
168
+ 'uri': /^[a-zA-Z][a-zA-Z0-9+.-]*:[^\s]*$/,
169
+ 'uri-reference': /^(((([A-Za-z][+\-.0-9A-Za-z]*(:%[0-9A-Fa-f]{2}|:[!$&-.0-;=?-Z_a-z~]|[/?])|\?)(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~])*|([A-Za-z][+\-.0-9A-Za-z]*:?)?)|([A-Za-z][+\-.0-9A-Za-z]*:)?\/((%[0-9A-Fa-f]{2}|\/((%[0-9A-Fa-f]{2}|[!$&-.0-9;=A-Z_a-z~])+|(\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~]+)?|[.0-:A-Fa-f]+)\])?)(:\d*)?[/?]|[!$&-.0-;=?-Z_a-z~])(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~])*|(\/((%[0-9A-Fa-f]{2}|[!$&-.0-9;=A-Z_a-z~])+|(\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~]+)?|[.0-:A-Fa-f]+)\])?)(:\d*)?)?))#(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~])*|(([A-Za-z][+\-.0-9A-Za-z]*)?%[0-9A-Fa-f]{2}|[!$&-.0-9;=@_~]|[A-Za-z][+\-.0-9A-Za-z]*[!$&-*,;=@_~])(%[0-9A-Fa-f]{2}|[!$&-.0-9;=@-Z_a-z~])*((([/?](%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~])*)?#|[/?])(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~])*)?|([A-Za-z][+\-.0-9A-Za-z]*(:%[0-9A-Fa-f]{2}|:[!$&-.0-;=?-Z_a-z~]|[/?])|\?)(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~])*|([A-Za-z][+\-.0-9A-Za-z]*:)?\/((%[0-9A-Fa-f]{2}|\/((%[0-9A-Fa-f]{2}|[!$&-.0-9;=A-Z_a-z~])+|(\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~]+)?|[.0-:A-Fa-f]+)\])?)(:\d*)?[/?]|[!$&-.0-;=?-Z_a-z~])(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~])*|\/((%[0-9A-Fa-f]{2}|[!$&-.0-9;=A-Z_a-z~])+(:\d*)?|(\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~]+)?|[.0-:A-Fa-f]+)\])?:\d*|\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~]+)?|[.0-:A-Fa-f]+)\])?)?|[A-Za-z][+\-.0-9A-Za-z]*:?)?$/,
170
+ 'iri': /^[a-zA-Z][a-zA-Z0-9+.-]*:[^\s]*$/,
171
+ 'iri-reference': /^(((([A-Za-z][+\-.0-9A-Za-z]*(:%[0-9A-Fa-f]{2}|:[!$&-.0-;=?-Z_a-z~-\u{10FFFF}]|[/?])|\?)(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~-\u{10FFFF}])*|([A-Za-z][+\-.0-9A-Za-z]*:?)?)|([A-Za-z][+\-.0-9A-Za-z]*:)?\/((%[0-9A-Fa-f]{2}|\/((%[0-9A-Fa-f]{2}|[!$&-.0-9;=A-Z_a-z~-\u{10FFFF}])+|(\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~-\u{10FFFF}]+)?|[.0-:A-Fa-f]+)\])?)(:\d*)?[/?]|[!$&-.0-;=?-Z_a-z~-\u{10FFFF}])(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~-\u{10FFFF}])*|(\/((%[0-9A-Fa-f]{2}|[!$&-.0-9;=A-Z_a-z~-\u{10FFFF}])+|(\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~-\u{10FFFF}]+)?|[.0-:A-Fa-f]+)\])?)(:\d*)?)?))#(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~-\u{10FFFF}])*|(([A-Za-z][+\-.0-9A-Za-z]*)?%[0-9A-Fa-f]{2}|[!$&-.0-9;=@_~-\u{10FFFF}]|[A-Za-z][+\-.0-9A-Za-z]*[!$&-*,;=@_~-\u{10FFFF}])(%[0-9A-Fa-f]{2}|[!$&-.0-9;=@-Z_a-z~-\u{10FFFF}])*((([/?](%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~-\u{10FFFF}])*)?#|[/?])(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~-\u{10FFFF}])*)?|([A-Za-z][+\-.0-9A-Za-z]*(:%[0-9A-Fa-f]{2}|:[!$&-.0-;=?-Z_a-z~-\u{10FFFF}]|[/?])|\?)(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~-\u{10FFFF}])*|([A-Za-z][+\-.0-9A-Za-z]*:)?\/((%[0-9A-Fa-f]{2}|\/((%[0-9A-Fa-f]{2}|[!$&-.0-9;=A-Z_a-z~-\u{10FFFF}])+|(\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~-\u{10FFFF}]+)?|[.0-:A-Fa-f]+)\])?)(:\d*)?[/?]|[!$&-.0-;=?-Z_a-z~-\u{10FFFF}])(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~-\u{10FFFF}])*|\/((%[0-9A-Fa-f]{2}|[!$&-.0-9;=A-Z_a-z~-\u{10FFFF}])+(:\d*)?|(\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~-\u{10FFFF}]+)?|[.0-:A-Fa-f]+)\])?:\d*|\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~-\u{10FFFF}]+)?|[.0-:A-Fa-f]+)\])?)?|[A-Za-z][+\-.0-9A-Za-z]*:?)?$/u,
172
+ 'uuid': /^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i,
173
+
174
+ // 7.3.6. uri-template
175
+ 'uri-template': /(%[0-9a-f]{2}|[!#$&(-;=?@\[\]_a-z~]|\{[!#&+,./;=?@|]?(%[0-9a-f]{2}|[0-9_a-z])(\.?(%[0-9a-f]{2}|[0-9_a-z]))*(:[1-9]\d{0,3}|\*)?(,(%[0-9a-f]{2}|[0-9_a-z])(\.?(%[0-9a-f]{2}|[0-9_a-z]))*(:[1-9]\d{0,3}|\*)?)*\})*/iu,
176
+
177
+ // 7.3.7. JSON Pointers
178
+ 'json-pointer': /^(\/([\x00-\x2e0-@\[-}\x7f]|~[01])*)*$/iu,
179
+ 'relative-json-pointer': /^\d+(#|(\/([\x00-\x2e0-@\[-}\x7f]|~[01])*)*)$/iu,
180
+
181
+ // hostname regex from: http://stackoverflow.com/a/1420225/5628
182
+ 'hostname': /^(?=.{1,255}$)[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?)*\.?$/,
183
+ 'host-name': /^(?=.{1,255}$)[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?)*\.?$/,
184
+
185
+ 'utc-millisec': function (input) {
186
+ return (typeof input === 'string') && parseFloat(input) === parseInt(input, 10) && !isNaN(input);
187
+ },
188
+
189
+ // 7.3.8. regex
190
+ 'regex': function (input) {
191
+ var result = true;
192
+ try {
193
+ new RegExp(input);
194
+ } catch (e) {
195
+ result = false;
196
+ }
197
+ return result;
198
+ },
199
+
200
+ // Other definitions
201
+ // "style" was removed from JSON Schema in draft-4 and is deprecated
202
+ 'style': /[\r\n\t ]*[^\r\n\t ][^:]*:[\r\n\t ]*[^\r\n\t ;]*[\r\n\t ]*;?/,
203
+ // "color" was removed from JSON Schema in draft-4 and is deprecated
204
+ 'color': /^(#?([0-9A-Fa-f]{3}){1,2}\b|aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow|(rgb\(\s*\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\b\s*,\s*\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\b\s*,\s*\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\b\s*\))|(rgb\(\s*(\d?\d%|100%)+\s*,\s*(\d?\d%|100%)+\s*,\s*(\d?\d%|100%)+\s*\)))$/,
205
+ 'phone': /^\+(?:[0-9] ?){6,14}[0-9]$/,
206
+ 'alpha': /^[a-zA-Z]+$/,
207
+ 'alphanumeric': /^[a-zA-Z0-9]+$/,
208
+ };
209
+
210
+ FORMAT_REGEXPS.regexp = FORMAT_REGEXPS.regex;
211
+ FORMAT_REGEXPS.pattern = FORMAT_REGEXPS.regex;
212
+ FORMAT_REGEXPS.ipv4 = FORMAT_REGEXPS['ip-address'];
213
+
214
+ exports.isFormat = function isFormat (input, format, validator) {
215
+ if (typeof input === 'string' && FORMAT_REGEXPS[format] !== undefined) {
216
+ if (FORMAT_REGEXPS[format] instanceof RegExp) {
217
+ return FORMAT_REGEXPS[format].test(input);
218
+ }
219
+ if (typeof FORMAT_REGEXPS[format] === 'function') {
220
+ return FORMAT_REGEXPS[format](input);
221
+ }
222
+ } else if (validator && validator.customFormats &&
223
+ typeof validator.customFormats[format] === 'function') {
224
+ return validator.customFormats[format](input);
225
+ }
226
+ return true;
227
+ };
228
+
229
+ var makeSuffix = exports.makeSuffix = function makeSuffix (key) {
230
+ key = key.toString();
231
+ // This function could be capable of outputting valid a ECMAScript string, but the
232
+ // resulting code for testing which form to use would be tens of thousands of characters long
233
+ // That means this will use the name form for some illegal forms
234
+ if (!key.match(/[.\s\[\]]/) && !key.match(/^[\d]/)) {
235
+ return '.' + key;
236
+ }
237
+ if (key.match(/^\d+$/)) {
238
+ return '[' + key + ']';
239
+ }
240
+ return '[' + JSON.stringify(key) + ']';
241
+ };
242
+
243
+ exports.deepCompareStrict = function deepCompareStrict (a, b) {
244
+ if (typeof a !== typeof b) {
245
+ return false;
246
+ }
247
+ if (Array.isArray(a)) {
248
+ if (!Array.isArray(b)) {
249
+ return false;
250
+ }
251
+ if (a.length !== b.length) {
252
+ return false;
253
+ }
254
+ return a.every(function (v, i) {
255
+ return deepCompareStrict(a[i], b[i]);
256
+ });
257
+ }
258
+ if (typeof a === 'object') {
259
+ if (!a || !b) {
260
+ return a === b;
261
+ }
262
+ var aKeys = Object.keys(a);
263
+ var bKeys = Object.keys(b);
264
+ if (aKeys.length !== bKeys.length) {
265
+ return false;
266
+ }
267
+ return aKeys.every(function (v) {
268
+ return deepCompareStrict(a[v], b[v]);
269
+ });
270
+ }
271
+ return a === b;
272
+ };
273
+
274
+ function deepMerger (target, dst, e, i) {
275
+ if (typeof e === 'object') {
276
+ dst[i] = deepMerge(target[i], e);
277
+ } else {
278
+ if (target.indexOf(e) === -1) {
279
+ dst.push(e);
280
+ }
281
+ }
282
+ }
283
+
284
+ function copyist (src, dst, key) {
285
+ dst[key] = src[key];
286
+ }
287
+
288
+ function copyistWithDeepMerge (target, src, dst, key) {
289
+ if (typeof src[key] !== 'object' || !src[key]) {
290
+ dst[key] = src[key];
291
+ }
292
+ else {
293
+ if (!target[key]) {
294
+ dst[key] = src[key];
295
+ } else {
296
+ dst[key] = deepMerge(target[key], src[key]);
297
+ }
298
+ }
299
+ }
300
+
301
+ function deepMerge (target, src) {
302
+ var array = Array.isArray(src);
303
+ var dst = array && [] || {};
304
+
305
+ if (array) {
306
+ target = target || [];
307
+ dst = dst.concat(target);
308
+ src.forEach(deepMerger.bind(null, target, dst));
309
+ } else {
310
+ if (target && typeof target === 'object') {
311
+ Object.keys(target).forEach(copyist.bind(null, target, dst));
312
+ }
313
+ Object.keys(src).forEach(copyistWithDeepMerge.bind(null, target, src, dst));
314
+ }
315
+
316
+ return dst;
317
+ }
318
+
319
+ module.exports.deepMerge = deepMerge;
320
+
321
+ /**
322
+ * Validates instance against the provided schema
323
+ * Implements URI+JSON Pointer encoding, e.g. "%7e"="~0"=>"~", "~1"="%2f"=>"/"
324
+ * @param o
325
+ * @param s The path to walk o along
326
+ * @return any
327
+ */
328
+ exports.objectGetPath = function objectGetPath(o, s) {
329
+ var parts = s.split('/').slice(1);
330
+ var k;
331
+ while (typeof (k=parts.shift()) == 'string') {
332
+ var n = decodeURIComponent(k.replace(/~0/,'~').replace(/~1/g,'/'));
333
+ if (!(n in o)) return;
334
+ o = o[n];
335
+ }
336
+ return o;
337
+ };
338
+
339
+ function pathEncoder (v) {
340
+ return '/'+encodeURIComponent(v).replace(/~/g,'%7E');
341
+ }
342
+ /**
343
+ * Accept an Array of property names and return a JSON Pointer URI fragment
344
+ * @param Array a
345
+ * @return {String}
346
+ */
347
+ exports.encodePath = function encodePointer(a){
348
+ // ~ must be encoded explicitly because hacks
349
+ // the slash is encoded by encodeURIComponent
350
+ return a.map(pathEncoder).join('');
351
+ };
352
+
353
+
354
+ /**
355
+ * Calculate the number of decimal places a number uses
356
+ * We need this to get correct results out of multipleOf and divisibleBy
357
+ * when either figure is has decimal places, due to IEEE-754 float issues.
358
+ * @param number
359
+ * @returns {number}
360
+ */
361
+ exports.getDecimalPlaces = function getDecimalPlaces(number) {
362
+
363
+ var decimalPlaces = 0;
364
+ if (isNaN(number)) return decimalPlaces;
365
+
366
+ if (typeof number !== 'number') {
367
+ number = Number(number);
368
+ }
369
+
370
+ var parts = number.toString().split('e');
371
+ if (parts.length === 2) {
372
+ if (parts[1][0] !== '-') {
373
+ return decimalPlaces;
374
+ } else {
375
+ decimalPlaces = Number(parts[1].slice(1));
376
+ }
377
+ }
378
+
379
+ var decimalParts = parts[0].split('.');
380
+ if (decimalParts.length === 2) {
381
+ decimalPlaces += decimalParts[1].length;
382
+ }
383
+
384
+ return decimalPlaces;
385
+ };
386
+
387
+ exports.isSchema = function isSchema(val){
388
+ return (typeof val === 'object' && val) || (typeof val === 'boolean');
389
+ };
390
+
@@ -0,0 +1,142 @@
1
+ /*
2
+ This is type definition for typescript.
3
+ This is for library users. Thus, properties and methods for internal use is omitted.
4
+ */
5
+ export declare class Validator {
6
+ constructor();
7
+ customFormats: {[formatName: string]: CustomFormat};
8
+ schemas: {[id: string]: Schema};
9
+ unresolvedRefs: string[];
10
+
11
+ attributes: {[property: string]: CustomProperty};
12
+
13
+ addSchema(schema?: Schema, uri?: string): Schema|void;
14
+ validate(instance: any, schema: Schema, options?: Options, ctx?: SchemaContext): ValidatorResult;
15
+ }
16
+
17
+ export declare class ValidatorResult {
18
+ constructor(instance: any, schema: Schema, options: Options, ctx: SchemaContext)
19
+ instance: any;
20
+ schema: Schema;
21
+ propertyPath: string;
22
+ errors: ValidationError[];
23
+ throwError: boolean;
24
+ disableFormat: boolean;
25
+ valid: boolean;
26
+ addError(detail: string|ErrorDetail): ValidationError;
27
+ toString(): string;
28
+ }
29
+
30
+ export declare class ValidationError {
31
+ constructor(message?: string, instance?: any, schema?: Schema, propertyPath?: any, name?: string, argument?: any);
32
+ path: (string|number)[];
33
+ property: string;
34
+ message: string;
35
+ schema: string|Schema;
36
+ instance: any;
37
+ name: string;
38
+ argument: any;
39
+ toString(): string;
40
+ stack: string;
41
+ }
42
+
43
+ export declare class SchemaError extends Error{
44
+ constructor(msg: string, schema: Schema);
45
+ schema: Schema;
46
+ message: string;
47
+ }
48
+
49
+ export declare function validate(instance: any, schema: any, options?: Options): ValidatorResult
50
+
51
+ export interface Schema {
52
+ $id?: string
53
+ id?: string
54
+ $schema?: string
55
+ $ref?: string
56
+ title?: string
57
+ description?: string
58
+ multipleOf?: number
59
+ maximum?: number
60
+ exclusiveMaximum?: number | boolean
61
+ minimum?: number
62
+ exclusiveMinimum?: number | boolean
63
+ maxLength?: number
64
+ minLength?: number
65
+ pattern?: string | RegExp
66
+ additionalItems?: boolean | Schema
67
+ items?: Schema | Schema[]
68
+ maxItems?: number
69
+ minItems?: number
70
+ uniqueItems?: boolean
71
+ maxProperties?: number
72
+ minProperties?: number
73
+ required?: string[] | boolean
74
+ additionalProperties?: boolean | Schema
75
+ definitions?: {
76
+ [name: string]: Schema
77
+ }
78
+ properties?: {
79
+ [name: string]: Schema
80
+ }
81
+ patternProperties?: {
82
+ [name: string]: Schema
83
+ }
84
+ dependencies?: {
85
+ [name: string]: Schema | string[]
86
+ }
87
+ const?: any
88
+ 'enum'?: any[]
89
+ type?: string | string[]
90
+ format?: string
91
+ allOf?: Schema[]
92
+ anyOf?: Schema[]
93
+ oneOf?: Schema[]
94
+ not?: Schema
95
+ if?: Schema
96
+ then?: Schema
97
+ else?: Schema
98
+ }
99
+
100
+ export interface Options {
101
+ skipAttributes?: string[];
102
+ allowUnknownAttributes?: boolean;
103
+ preValidateProperty?: PreValidatePropertyFunction;
104
+ rewrite?: RewriteFunction;
105
+ base?: string;
106
+ throwError?: boolean;
107
+ required?: boolean;
108
+ throwFirst?: boolean;
109
+ throwAll?: boolean;
110
+ nestedErrors?: boolean;
111
+ }
112
+
113
+ export interface RewriteFunction {
114
+ (instance: any, schema: Schema, options: Options, ctx: SchemaContext): any;
115
+ }
116
+
117
+ export interface PreValidatePropertyFunction {
118
+ (instance: any, key: string, schema: Schema, options: Options, ctx: SchemaContext): any;
119
+ }
120
+
121
+ export interface SchemaContext {
122
+ schema: Schema;
123
+ options: Options;
124
+ propertyPath: string;
125
+ base: string;
126
+ schemas: {[base: string]: Schema};
127
+ makeChild: (schema: Schema, key: string) => SchemaContext;
128
+ }
129
+
130
+ export interface CustomFormat {
131
+ (input: any): boolean;
132
+ }
133
+
134
+ export interface CustomProperty {
135
+ (instance: any, schema: Schema, options: Options, ctx: SchemaContext): string|ValidatorResult;
136
+ }
137
+
138
+ export interface ErrorDetail {
139
+ message: string;
140
+ name: string;
141
+ argument: string;
142
+ }
@@ -0,0 +1,15 @@
1
+ 'use strict';
2
+
3
+ var Validator = module.exports.Validator = require('./validator');
4
+
5
+ module.exports.ValidatorResult = require('./helpers').ValidatorResult;
6
+ module.exports.ValidatorResultError = require('./helpers').ValidatorResultError;
7
+ module.exports.ValidationError = require('./helpers').ValidationError;
8
+ module.exports.SchemaError = require('./helpers').SchemaError;
9
+ module.exports.SchemaScanResult = require('./scan').SchemaScanResult;
10
+ module.exports.scan = require('./scan').scan;
11
+
12
+ module.exports.validate = function (instance, schema, options) {
13
+ var v = new Validator();
14
+ return v.validate(instance, schema, options);
15
+ };
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+
3
+ var urilib = require('url');
4
+ var helpers = require('./helpers');
5
+
6
+ module.exports.SchemaScanResult = SchemaScanResult;
7
+ function SchemaScanResult(found, ref){
8
+ this.id = found;
9
+ this.ref = ref;
10
+ }
11
+
12
+ /**
13
+ * Adds a schema with a certain urn to the Validator instance.
14
+ * @param string uri
15
+ * @param object schema
16
+ * @return {Object}
17
+ */
18
+ module.exports.scan = function scan(base, schema){
19
+ function scanSchema(baseuri, schema){
20
+ if(!schema || typeof schema!='object') return;
21
+ // Mark all referenced schemas so we can tell later which schemas are referred to, but never defined
22
+ if(schema.$ref){
23
+ var resolvedUri = urilib.resolve(baseuri, schema.$ref);
24
+ ref[resolvedUri] = ref[resolvedUri] ? ref[resolvedUri]+1 : 0;
25
+ return;
26
+ }
27
+ var id = schema.$id || schema.id;
28
+ var ourBase = id ? urilib.resolve(baseuri, id) : baseuri;
29
+ if (ourBase) {
30
+ // If there's no fragment, append an empty one
31
+ if(ourBase.indexOf('#')<0) ourBase += '#';
32
+ if(found[ourBase]){
33
+ if(!helpers.deepCompareStrict(found[ourBase], schema)){
34
+ throw new Error('Schema <'+ourBase+'> already exists with different definition');
35
+ }
36
+ return found[ourBase];
37
+ }
38
+ found[ourBase] = schema;
39
+ // strip trailing fragment
40
+ if(ourBase[ourBase.length-1]=='#'){
41
+ found[ourBase.substring(0, ourBase.length-1)] = schema;
42
+ }
43
+ }
44
+ scanArray(ourBase+'/items', (Array.isArray(schema.items)?schema.items:[schema.items]));
45
+ scanArray(ourBase+'/extends', (Array.isArray(schema.extends)?schema.extends:[schema.extends]));
46
+ scanSchema(ourBase+'/additionalItems', schema.additionalItems);
47
+ scanObject(ourBase+'/properties', schema.properties);
48
+ scanSchema(ourBase+'/additionalProperties', schema.additionalProperties);
49
+ scanObject(ourBase+'/definitions', schema.definitions);
50
+ scanObject(ourBase+'/patternProperties', schema.patternProperties);
51
+ scanObject(ourBase+'/dependencies', schema.dependencies);
52
+ scanArray(ourBase+'/disallow', schema.disallow);
53
+ scanArray(ourBase+'/allOf', schema.allOf);
54
+ scanArray(ourBase+'/anyOf', schema.anyOf);
55
+ scanArray(ourBase+'/oneOf', schema.oneOf);
56
+ scanSchema(ourBase+'/not', schema.not);
57
+ }
58
+ function scanArray(baseuri, schemas){
59
+ if(!Array.isArray(schemas)) return;
60
+ for(var i=0; i<schemas.length; i++){
61
+ scanSchema(baseuri+'/'+i, schemas[i]);
62
+ }
63
+ }
64
+ function scanObject(baseuri, schemas){
65
+ if(!schemas || typeof schemas!='object') return;
66
+ for(var p in schemas){
67
+ scanSchema(baseuri+'/'+p, schemas[p]);
68
+ }
69
+ }
70
+
71
+ var found = {};
72
+ var ref = {};
73
+ scanSchema(base, schema);
74
+ return new SchemaScanResult(found, ref);
75
+ };