-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathjsonscript.js
More file actions
126 lines (99 loc) · 3.64 KB
/
jsonscript.js
File metadata and controls
126 lines (99 loc) · 3.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
'use strict';
var util = require('./util');
var Ajv = require('ajv');
var Evaluation = require('./evaluation');
var generateSchema = require('./generate_schema');
var instructions = require('jsonscript/instructions');
var evaluationKeywords = require('./evaluation_keywords');
var instructionKeywords = require('./instruction_keywords');
module.exports = JSONScript;
function JSONScript(opts) {
if (!(this instanceof JSONScript)) return new JSONScript(opts);
this._opts = util.copy(opts);
this._instructions = [];
this._evalKeywords = {};
this._executors = util.copy(this._opts.executors);
this._util = util;
this.ajv = Ajv({ v5: true, jsonPointers: true, passContext: true });
this.Script = Script;
addAjvKeywords.call(this);
addCoreInstructions.call(this);
}
JSONScript.prototype.validate = validateScript;
JSONScript.prototype.evaluate = evaluateScript;
JSONScript.prototype.addInstruction = addInstruction;
JSONScript.prototype.addExecutor = addExecutor;
function validateScript(script) {
var valid = this._validate(script);
validateScript.errors = this._validate.errors;
return valid;
}
function evaluateScript(script, data) {
var wrapped = { script: script };
var valid;
try {
valid = this._evaluate.call(new Evaluation(this, script, data, '/script'), wrapped);
} catch(e) {
return Promise.reject(e);
}
if (!valid) return Promise.reject(new Ajv.ValidationError(this._evaluate.errors));
script = wrapped.script;
if (script && typeof script.then == 'function') return script;
return Promise.resolve(script);
}
function addInstruction(definition, keywordFunc, regenerateSchemas) {
var valid = this._validateInstruction(definition);
if (!valid) throw new Ajv.ValidationError(this._validateInstruction.errors);
// TODO check instruction is unique
this._instructions.push(definition);
var keyword = definition.evaluate.validatorKeyword;
this._evalKeywords[keyword] = keywordFunc;
addAjvKeyword.call(this, keyword, 'object', true);
if (regenerateSchemas !== false) generateSchemas();
}
function addExecutor(name, executor) {
// TODO check duplicates, show warnings
// ? TODO whitelist methods?
this._executors[name] = executor;
}
function addAjvKeywords() {
var self = this;
addAjvKeyword.call(this, 'validateAsync');
addAjvKeyword.call(this, 'itemsSerial', 'array');
this._evalKeywords.objectToAsync = util.objectToPromise;
addAjvKeyword.call(this, 'objectToAsync', 'object', true);
this.ajv.addKeyword('resolvePendingRefs', {
validate: evaluationKeywords.resolvePendingRefs,
schema: false
});
}
function addAjvKeyword(keyword, types, inlineInstruction) {
var inlineFunc = evaluationKeywords[inlineInstruction ? 'inlineInstruction' : keyword];
this.ajv.addKeyword(keyword, {
type: types,
inline: inlineFunc,
statements: true,
errors: 'full'
});
}
function addCoreInstructions() {
this._validateInstruction = this.ajv.compile(require('jsonscript/schema/instruction.json'));
instructions.forEach(function (inst) {
this.addInstruction(inst, instructionKeywords[inst.evaluate.validatorKeyword], false);
}, this);
generateSchemas.call(this);
}
function generateSchemas() {
this.ajv.addMetaSchema(_generate.call(this, 'evaluate_metaschema'));
this._validate = this.ajv.compile(_generate.call(this, 'schema'));
this._evaluate = this.ajv.compile(_generate.call(this, 'evaluate'));
// console.log(this._validate.toString().length, this._evaluate.toString().length);
}
function _generate(schemaName) {
var schema = generateSchema(schemaName, this._instructions);
this.ajv.removeSchema(schema.id);
return schema;
}
function Script(script) {
this.script = script;
}