rpc-express-toolkit 4.1.0 → 4.2.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.
- package/README.md +72 -0
- package/examples/introspection/client.js +68 -0
- package/examples/introspection/server.js +89 -0
- package/examples/safe-preset-example.js +3 -3
- package/package.json +1 -1
- package/src/batch.js +6 -2
- package/src/index.js +1035 -842
- package/src/safe.d.ts +33 -0
- package/src/safe.js +78 -20
- package/src/validation.js +222 -221
package/src/safe.d.ts
CHANGED
|
@@ -2,6 +2,38 @@ import type { Router } from 'express';
|
|
|
2
2
|
import type { RpcEndpoint, RpcClient, RpcEndpointOptions, RpcClientOptions } from './index';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
+
* RPC Client with safe serialization enabled by default.
|
|
6
|
+
* Extends RpcClient with safeEnabled: true preset.
|
|
7
|
+
*/
|
|
8
|
+
export class RpcSafeClient extends RpcClient {
|
|
9
|
+
constructor(
|
|
10
|
+
endpoint: string,
|
|
11
|
+
defaultHeaders?: Record<string, string>,
|
|
12
|
+
options?: RpcClientOptions
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* RPC Endpoint with safe serialization enabled by default.
|
|
18
|
+
* Extends RpcEndpoint with safeEnabled: true and strictMode: true presets.
|
|
19
|
+
*/
|
|
20
|
+
export class RpcSafeEndpoint<C = any> extends RpcEndpoint<C> {
|
|
21
|
+
constructor(
|
|
22
|
+
router: Router,
|
|
23
|
+
context: C,
|
|
24
|
+
endpoint?: string,
|
|
25
|
+
options?: RpcEndpointOptions
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
constructor(
|
|
29
|
+
router: Router,
|
|
30
|
+
context: C,
|
|
31
|
+
options?: RpcEndpointOptions
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @deprecated Use `new RpcSafeEndpoint(...)` instead.
|
|
5
37
|
* Factory: create a RpcEndpoint with safe defaults (safeEnabled=true, strictMode=true).
|
|
6
38
|
*/
|
|
7
39
|
export function createSafeEndpoint<C = any>(
|
|
@@ -18,6 +50,7 @@ export function createSafeEndpoint<C = any>(
|
|
|
18
50
|
): RpcEndpoint<C>;
|
|
19
51
|
|
|
20
52
|
/**
|
|
53
|
+
* @deprecated Use `new RpcSafeClient(...)` instead.
|
|
21
54
|
* Factory: create a RpcClient with safe defaults (safeEnabled=true).
|
|
22
55
|
*/
|
|
23
56
|
export function createSafeClient(
|
package/src/safe.js
CHANGED
|
@@ -1,14 +1,72 @@
|
|
|
1
|
+
/* eslint-disable max-classes-per-file */
|
|
1
2
|
const Main = require('./index');
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
/**
|
|
5
|
+
* RPC Client with safe serialization enabled by default.
|
|
6
|
+
* Extends RpcClient with safeEnabled: true preset.
|
|
7
|
+
*
|
|
8
|
+
* This class automatically enables safe prefixes for strings (S:) and dates (D:)
|
|
9
|
+
* to avoid ambiguity with BigInt and ISO date strings.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const client = new RpcSafeClient('http://localhost:3000/api');
|
|
13
|
+
* const result = await client.call('add', { a: 10n, b: 20n });
|
|
14
|
+
*/
|
|
15
|
+
class RpcSafeClient extends Main.RpcClient {
|
|
16
|
+
/**
|
|
17
|
+
* @param {string} endpoint The JSON-RPC endpoint URL.
|
|
18
|
+
* @param {Object} [defaultHeaders={}] Optional default headers to include in requests.
|
|
19
|
+
* @param {Object} [options={}] Optional configuration options (safeEnabled will be set to true).
|
|
20
|
+
*/
|
|
21
|
+
constructor(endpoint, defaultHeaders = {}, options = {}) {
|
|
22
|
+
super(endpoint, defaultHeaders, { safeEnabled: true, ...options });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* RPC Endpoint with safe serialization enabled by default.
|
|
28
|
+
* Extends RpcEndpoint with safeEnabled: true and strictMode: true presets.
|
|
29
|
+
*
|
|
30
|
+
* This class automatically enables safe prefixes for strings (S:) and dates (D:)
|
|
31
|
+
* and enforces strict compatibility checking with clients.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* const app = express();
|
|
35
|
+
* const rpc = new RpcSafeEndpoint(app, {});
|
|
36
|
+
* rpc.addMethod('add', {
|
|
37
|
+
* handler: (req, ctx, params) => params.a + params.b,
|
|
38
|
+
* });
|
|
39
|
+
*
|
|
40
|
+
* @template C
|
|
41
|
+
*/
|
|
42
|
+
class RpcSafeEndpoint extends Main.RpcEndpoint {
|
|
43
|
+
/**
|
|
44
|
+
* @param {import('express').Router} router Express router or app
|
|
45
|
+
* @param {C} context Context object passed to handlers
|
|
46
|
+
* @param {string|object} [endpointOrOptions='/api'] Endpoint path or options object
|
|
47
|
+
* @param {object} [options] Options object (when endpointOrOptions is a string)
|
|
48
|
+
*/
|
|
49
|
+
constructor(router, context, endpointOrOptions = '/api', options = {}) {
|
|
50
|
+
const defaultSafeOptions = {
|
|
51
|
+
safeEnabled: true,
|
|
52
|
+
strictMode: true,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
if (typeof endpointOrOptions === 'string') {
|
|
56
|
+
// String endpoint with options object
|
|
57
|
+
const finalOptions = { ...defaultSafeOptions, ...options };
|
|
58
|
+
super(router, context, endpointOrOptions, finalOptions);
|
|
59
|
+
} else {
|
|
60
|
+
// Options object form
|
|
61
|
+
const finalOptions = { ...defaultSafeOptions, ...endpointOrOptions };
|
|
62
|
+
super(router, context, finalOptions);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
8
66
|
|
|
9
67
|
/**
|
|
68
|
+
* @deprecated Use `new RpcSafeEndpoint(...)` instead.
|
|
10
69
|
* Create a RpcEndpoint with safe defaults (safeEnabled=true, strictMode=true).
|
|
11
|
-
* Preserves both constructor signatures supported by RpcEndpoint.
|
|
12
70
|
*
|
|
13
71
|
* @param {import('express').Router} router
|
|
14
72
|
* @param {any} context
|
|
@@ -21,34 +79,34 @@ function createSafeEndpoint(
|
|
|
21
79
|
endpointOrOptions = '/api',
|
|
22
80
|
options = {}
|
|
23
81
|
) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
context,
|
|
29
|
-
endpointOrOptions,
|
|
30
|
-
finalOptions
|
|
31
|
-
);
|
|
32
|
-
}
|
|
33
|
-
// options object form
|
|
34
|
-
const final = { ...defaultSafeOptions, ...endpointOrOptions };
|
|
35
|
-
return new Main.RpcEndpoint(router, context, final);
|
|
82
|
+
console.warn(
|
|
83
|
+
'createSafeEndpoint() is deprecated. Use `new RpcSafeEndpoint(...)` instead.'
|
|
84
|
+
);
|
|
85
|
+
return new RpcSafeEndpoint(router, context, endpointOrOptions, options);
|
|
36
86
|
}
|
|
37
87
|
|
|
38
88
|
/**
|
|
89
|
+
* @deprecated Use `new RpcSafeClient(...)` instead.
|
|
39
90
|
* Create a RpcClient with safe defaults (safeEnabled=true).
|
|
91
|
+
*
|
|
40
92
|
* @param {string} endpoint
|
|
41
93
|
* @param {Record<string,string>} [defaultHeaders]
|
|
42
94
|
* @param {object} [options]
|
|
43
95
|
*/
|
|
44
96
|
function createSafeClient(endpoint, defaultHeaders = {}, options = {}) {
|
|
45
|
-
|
|
46
|
-
|
|
97
|
+
console.warn(
|
|
98
|
+
'createSafeClient() is deprecated. Use `new RpcSafeClient(...)` instead.'
|
|
99
|
+
);
|
|
100
|
+
return new RpcSafeClient(endpoint, defaultHeaders, options);
|
|
47
101
|
}
|
|
48
102
|
|
|
49
103
|
module.exports = {
|
|
50
104
|
// Re-export everything for convenience
|
|
51
105
|
...Main,
|
|
106
|
+
// New class-based API
|
|
107
|
+
RpcSafeClient,
|
|
108
|
+
RpcSafeEndpoint,
|
|
109
|
+
// Deprecated factory functions (kept for backward compatibility)
|
|
52
110
|
createSafeEndpoint,
|
|
53
111
|
createSafeClient,
|
|
54
112
|
};
|
package/src/validation.js
CHANGED
|
@@ -1,221 +1,222 @@
|
|
|
1
|
-
/* eslint max-classes-per-file: off */
|
|
2
|
-
const Ajv = require('ajv');
|
|
3
|
-
const addFormats = require('ajv-formats');
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* JSON-RPC 2.0 Schema Validation Error
|
|
7
|
-
*/
|
|
8
|
-
class SchemaValidator {
|
|
9
|
-
constructor(options = {}) {
|
|
10
|
-
this.ajv = new Ajv({
|
|
11
|
-
allErrors: true,
|
|
12
|
-
removeAdditional: options.removeAdditional || false,
|
|
13
|
-
useDefaults: options.useDefaults !== false,
|
|
14
|
-
coerceTypes: options.coerceTypes || false,
|
|
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
|
-
* @param {
|
|
45
|
-
* @
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
*
|
|
61
|
-
* @
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
error
|
|
71
|
-
error.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
*
|
|
90
|
-
* @param {
|
|
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
|
-
{ type: 'string', format: '
|
|
117
|
-
{ type: '
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
*
|
|
164
|
-
* @param {
|
|
165
|
-
* @param {
|
|
166
|
-
* @
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
*
|
|
179
|
-
* @
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
*
|
|
191
|
-
* @
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
*
|
|
201
|
-
* @
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
*
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
1
|
+
/* eslint max-classes-per-file: off */
|
|
2
|
+
const Ajv = require('ajv');
|
|
3
|
+
const addFormats = require('ajv-formats');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* JSON-RPC 2.0 Schema Validation Error
|
|
7
|
+
*/
|
|
8
|
+
class SchemaValidator {
|
|
9
|
+
constructor(options = {}) {
|
|
10
|
+
this.ajv = new Ajv({
|
|
11
|
+
allErrors: true,
|
|
12
|
+
removeAdditional: options.removeAdditional || false,
|
|
13
|
+
useDefaults: options.useDefaults !== false,
|
|
14
|
+
coerceTypes: options.coerceTypes || false,
|
|
15
|
+
strict: false, // Allow unknown keywords like exposeSchema in method options
|
|
16
|
+
...options.ajvOptions,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
addFormats(this.ajv);
|
|
20
|
+
|
|
21
|
+
// Add custom formats
|
|
22
|
+
this.addCustomFormats();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Add custom format validators
|
|
27
|
+
*/
|
|
28
|
+
addCustomFormats() {
|
|
29
|
+
// BigInt format
|
|
30
|
+
this.ajv.addFormat('bigint', {
|
|
31
|
+
type: 'string',
|
|
32
|
+
validate: (data) => /^-?\d+n?$/.test(data),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// ObjectId format (MongoDB style)
|
|
36
|
+
this.ajv.addFormat('objectid', {
|
|
37
|
+
type: 'string',
|
|
38
|
+
validate: (data) => /^[0-9a-fA-F]{24}$/.test(data),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Validate parameters against a schema
|
|
44
|
+
* @param {any} params
|
|
45
|
+
* @param {Object} schema
|
|
46
|
+
* @returns {Object} validation result
|
|
47
|
+
*/
|
|
48
|
+
validate(params, schema) {
|
|
49
|
+
const validator = this.ajv.compile(schema);
|
|
50
|
+
const valid = validator(params);
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
valid,
|
|
54
|
+
errors: valid ? null : validator.errors,
|
|
55
|
+
data: params,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Create a validation middleware
|
|
61
|
+
* @param {Object} schema
|
|
62
|
+
* @returns {Function}
|
|
63
|
+
*/
|
|
64
|
+
middleware(schema) {
|
|
65
|
+
return async (context) => {
|
|
66
|
+
const { params } = context;
|
|
67
|
+
const result = this.validate(params, schema);
|
|
68
|
+
|
|
69
|
+
if (!result.valid) {
|
|
70
|
+
const error = new Error('Validation failed');
|
|
71
|
+
error.code = -32602; // Invalid params
|
|
72
|
+
error.data = {
|
|
73
|
+
validationErrors: result.errors.map((err) => ({
|
|
74
|
+
field: err.instancePath || err.schemaPath,
|
|
75
|
+
message: err.message,
|
|
76
|
+
value: err.data,
|
|
77
|
+
})),
|
|
78
|
+
};
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Update params with validated/coerced data
|
|
83
|
+
context.params = result.data;
|
|
84
|
+
return context;
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Add a custom keyword
|
|
90
|
+
* @param {string} name
|
|
91
|
+
* @param {Object} definition
|
|
92
|
+
*/
|
|
93
|
+
addKeyword(name, definition) {
|
|
94
|
+
this.ajv.addKeyword(name, definition);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Common schema definitions
|
|
100
|
+
*/
|
|
101
|
+
const commonSchemas = {
|
|
102
|
+
// Pagination parameters
|
|
103
|
+
pagination: {
|
|
104
|
+
type: 'object',
|
|
105
|
+
properties: {
|
|
106
|
+
page: { type: 'integer', minimum: 1, default: 1 },
|
|
107
|
+
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
|
108
|
+
sort: { type: 'string' },
|
|
109
|
+
order: { type: 'string', enum: ['asc', 'desc'], default: 'asc' },
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
// User identification
|
|
114
|
+
userId: {
|
|
115
|
+
oneOf: [
|
|
116
|
+
{ type: 'string', format: 'objectid' },
|
|
117
|
+
{ type: 'string', format: 'uuid' },
|
|
118
|
+
{ type: 'integer', minimum: 1 },
|
|
119
|
+
],
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
// Email validation
|
|
123
|
+
email: {
|
|
124
|
+
type: 'string',
|
|
125
|
+
format: 'email',
|
|
126
|
+
maxLength: 255,
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
// Password requirements
|
|
130
|
+
password: {
|
|
131
|
+
type: 'string',
|
|
132
|
+
minLength: 8,
|
|
133
|
+
maxLength: 128,
|
|
134
|
+
pattern: '^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)',
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
// BigInt validation
|
|
138
|
+
bigintString: {
|
|
139
|
+
type: 'string',
|
|
140
|
+
format: 'bigint',
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
// Date validation
|
|
144
|
+
dateString: {
|
|
145
|
+
type: 'string',
|
|
146
|
+
format: 'date-time',
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Schema builder helper
|
|
152
|
+
*/
|
|
153
|
+
class SchemaBuilder {
|
|
154
|
+
constructor() {
|
|
155
|
+
this.schema = {
|
|
156
|
+
type: 'object',
|
|
157
|
+
properties: {},
|
|
158
|
+
required: [],
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Add a property to the schema
|
|
164
|
+
* @param {string} name
|
|
165
|
+
* @param {Object} definition
|
|
166
|
+
* @param {boolean} required
|
|
167
|
+
* @returns {SchemaBuilder}
|
|
168
|
+
*/
|
|
169
|
+
property(name, definition, required = false) {
|
|
170
|
+
this.schema.properties[name] = definition;
|
|
171
|
+
if (required) {
|
|
172
|
+
this.schema.required.push(name);
|
|
173
|
+
}
|
|
174
|
+
return this;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Add multiple properties
|
|
179
|
+
* @param {Object} properties
|
|
180
|
+
* @returns {SchemaBuilder}
|
|
181
|
+
*/
|
|
182
|
+
properties(properties) {
|
|
183
|
+
Object.entries(properties).forEach(([name, definition]) => {
|
|
184
|
+
this.property(name, definition);
|
|
185
|
+
});
|
|
186
|
+
return this;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Set required fields
|
|
191
|
+
* @param {string[]} fields
|
|
192
|
+
* @returns {SchemaBuilder}
|
|
193
|
+
*/
|
|
194
|
+
required(fields) {
|
|
195
|
+
this.schema.required = [...new Set([...this.schema.required, ...fields])];
|
|
196
|
+
return this;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Add additional properties setting
|
|
201
|
+
* @param {boolean} allowed
|
|
202
|
+
* @returns {SchemaBuilder}
|
|
203
|
+
*/
|
|
204
|
+
additionalProperties(allowed) {
|
|
205
|
+
this.schema.additionalProperties = allowed;
|
|
206
|
+
return this;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Build and return the schema
|
|
211
|
+
* @returns {Object}
|
|
212
|
+
*/
|
|
213
|
+
build() {
|
|
214
|
+
return this.schema;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
module.exports = {
|
|
219
|
+
SchemaValidator,
|
|
220
|
+
commonSchemas,
|
|
221
|
+
SchemaBuilder,
|
|
222
|
+
};
|