rpc-express-toolkit 2.3.3 → 3.0.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.
package/src/validation.js CHANGED
@@ -1,221 +1,220 @@
1
- "use strict";
2
-
3
- const Ajv = require('ajv');
4
- const addFormats = require('ajv-formats');
5
-
6
- /**
7
- * Schema validator for JSON-RPC methods
8
- */
9
- class SchemaValidator {
10
- constructor(options = {}) {
11
- this.ajv = new Ajv({
12
- allErrors: true,
13
- removeAdditional: options.removeAdditional || false,
14
- useDefaults: options.useDefaults !== false,
15
- ...options.ajvOptions
16
- });
17
-
18
- addFormats(this.ajv);
19
-
20
- // Add custom formats
21
- this.addCustomFormats();
22
- }
23
-
24
- /**
25
- * Add custom format validators
26
- */
27
- addCustomFormats() {
28
- // BigInt format
29
- this.ajv.addFormat('bigint', {
30
- type: 'string',
31
- validate: (data) => /^-?\d+n?$/.test(data)
32
- });
33
-
34
- // ObjectId format (MongoDB style)
35
- this.ajv.addFormat('objectid', {
36
- type: 'string',
37
- validate: (data) => /^[0-9a-fA-F]{24}$/.test(data)
38
- });
39
- }
40
-
41
- /**
42
- * Validate parameters against a schema
43
- * @param {any} params
44
- * @param {Object} schema
45
- * @returns {Object} validation result
46
- */
47
- validate(params, schema) {
48
- const validator = this.ajv.compile(schema);
49
- const valid = validator(params);
50
-
51
- return {
52
- valid,
53
- errors: valid ? null : validator.errors,
54
- data: params
55
- };
56
- }
57
-
58
- /**
59
- * Create a validation middleware
60
- * @param {Object} schema
61
- * @returns {Function}
62
- */
63
- middleware(schema) {
64
- return async (context) => {
65
- const { params } = context;
66
- const result = this.validate(params, schema);
67
-
68
- if (!result.valid) {
69
- const error = new Error('Validation failed');
70
- error.code = -32602; // Invalid params
71
- error.data = {
72
- validationErrors: result.errors.map(err => ({
73
- field: err.instancePath || err.schemaPath,
74
- message: err.message,
75
- value: err.data
76
- }))
77
- };
78
- throw error;
79
- }
80
-
81
- // Update params with validated/coerced data
82
- context.params = result.data;
83
- return context;
84
- };
85
- }
86
-
87
- /**
88
- * Add a custom keyword
89
- * @param {string} name
90
- * @param {Object} definition
91
- */
92
- addKeyword(name, definition) {
93
- this.ajv.addKeyword(name, definition);
94
- }
95
- }
96
-
97
- /**
98
- * Common schema definitions
99
- */
100
- const commonSchemas = {
101
- // Pagination parameters
102
- pagination: {
103
- type: 'object',
104
- properties: {
105
- page: { type: 'integer', minimum: 1, default: 1 },
106
- limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
107
- sort: { type: 'string' },
108
- order: { type: 'string', enum: ['asc', 'desc'], default: 'asc' }
109
- }
110
- },
111
-
112
- // User identification
113
- userId: {
114
- oneOf: [
115
- { type: 'string', format: 'objectid' },
116
- { type: 'string', format: 'uuid' },
117
- { type: 'integer', minimum: 1 }
118
- ]
119
- },
120
-
121
- // Email validation
122
- email: {
123
- type: 'string',
124
- format: 'email',
125
- maxLength: 255
126
- },
127
-
128
- // Password requirements
129
- password: {
130
- type: 'string',
131
- minLength: 8,
132
- maxLength: 128,
133
- pattern: '^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)'
134
- },
135
-
136
- // BigInt validation
137
- bigintString: {
138
- type: 'string',
139
- format: 'bigint'
140
- },
141
-
142
- // Date validation
143
- dateString: {
144
- type: 'string',
145
- format: 'date-time'
146
- }
147
- };
148
-
149
- /**
150
- * Schema builder helper
151
- */
152
- class SchemaBuilder {
153
- constructor() {
154
- this.schema = {
155
- type: 'object',
156
- properties: {},
157
- required: []
158
- };
159
- }
160
-
161
- /**
162
- * Add a property to the schema
163
- * @param {string} name
164
- * @param {Object} definition
165
- * @param {boolean} required
166
- * @returns {SchemaBuilder}
167
- */
168
- property(name, definition, required = false) {
169
- this.schema.properties[name] = definition;
170
- if (required) {
171
- this.schema.required.push(name);
172
- }
173
- return this;
174
- }
175
-
176
- /**
177
- * Add multiple properties
178
- * @param {Object} properties
179
- * @returns {SchemaBuilder}
180
- */
181
- properties(properties) {
182
- Object.entries(properties).forEach(([name, definition]) => {
183
- this.property(name, definition);
184
- });
185
- return this;
186
- }
187
-
188
- /**
189
- * Set required fields
190
- * @param {string[]} fields
191
- * @returns {SchemaBuilder}
192
- */
193
- required(fields) {
194
- this.schema.required = [...new Set([...this.schema.required, ...fields])];
195
- return this;
196
- }
197
-
198
- /**
199
- * Add additional properties setting
200
- * @param {boolean} allowed
201
- * @returns {SchemaBuilder}
202
- */
203
- additionalProperties(allowed) {
204
- this.schema.additionalProperties = allowed;
205
- return this;
206
- }
207
-
208
- /**
209
- * Build and return the schema
210
- * @returns {Object}
211
- */
212
- build() {
213
- return this.schema;
214
- }
215
- }
216
-
217
- module.exports = {
218
- SchemaValidator,
219
- commonSchemas,
220
- SchemaBuilder
221
- };
1
+ const Ajv = require('ajv');
2
+ const addFormats = require('ajv-formats');
3
+
4
+ /**
5
+ * JSON-RPC 2.0 Schema Validation Error
6
+ */
7
+ class SchemaValidator {
8
+ constructor(options = {}) {
9
+ this.ajv = new Ajv({
10
+ allErrors: true,
11
+ removeAdditional: options.removeAdditional || false,
12
+ useDefaults: options.useDefaults !== false,
13
+ coerceTypes: options.coerceTypes || false,
14
+ ...options.ajvOptions,
15
+ });
16
+
17
+ addFormats(this.ajv);
18
+
19
+ // Add custom formats
20
+ this.addCustomFormats();
21
+ }
22
+
23
+ /**
24
+ * Add custom format validators
25
+ */
26
+ addCustomFormats() {
27
+ // BigInt format
28
+ this.ajv.addFormat('bigint', {
29
+ type: 'string',
30
+ validate: (data) => /^-?\d+n?$/.test(data),
31
+ });
32
+
33
+ // ObjectId format (MongoDB style)
34
+ this.ajv.addFormat('objectid', {
35
+ type: 'string',
36
+ validate: (data) => /^[0-9a-fA-F]{24}$/.test(data),
37
+ });
38
+ }
39
+
40
+ /**
41
+ * Validate parameters against a schema
42
+ * @param {any} params
43
+ * @param {Object} schema
44
+ * @returns {Object} validation result
45
+ */
46
+ validate(params, schema) {
47
+ const validator = this.ajv.compile(schema);
48
+ const valid = validator(params);
49
+
50
+ return {
51
+ valid,
52
+ errors: valid ? null : validator.errors,
53
+ data: params,
54
+ };
55
+ }
56
+
57
+ /**
58
+ * Create a validation middleware
59
+ * @param {Object} schema
60
+ * @returns {Function}
61
+ */
62
+ middleware(schema) {
63
+ return async (context) => {
64
+ const { params } = context;
65
+ const result = this.validate(params, schema);
66
+
67
+ if (!result.valid) {
68
+ const error = new Error('Validation failed');
69
+ error.code = -32602; // Invalid params
70
+ error.data = {
71
+ validationErrors: result.errors.map((err) => ({
72
+ field: err.instancePath || err.schemaPath,
73
+ message: err.message,
74
+ value: err.data,
75
+ })),
76
+ };
77
+ throw error;
78
+ }
79
+
80
+ // Update params with validated/coerced data
81
+ context.params = result.data;
82
+ return context;
83
+ };
84
+ }
85
+
86
+ /**
87
+ * Add a custom keyword
88
+ * @param {string} name
89
+ * @param {Object} definition
90
+ */
91
+ addKeyword(name, definition) {
92
+ this.ajv.addKeyword(name, definition);
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Common schema definitions
98
+ */
99
+ const commonSchemas = {
100
+ // Pagination parameters
101
+ pagination: {
102
+ type: 'object',
103
+ properties: {
104
+ page: { type: 'integer', minimum: 1, default: 1 },
105
+ limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
106
+ sort: { type: 'string' },
107
+ order: { type: 'string', enum: ['asc', 'desc'], default: 'asc' },
108
+ },
109
+ },
110
+
111
+ // User identification
112
+ userId: {
113
+ oneOf: [
114
+ { type: 'string', format: 'objectid' },
115
+ { type: 'string', format: 'uuid' },
116
+ { type: 'integer', minimum: 1 },
117
+ ],
118
+ },
119
+
120
+ // Email validation
121
+ email: {
122
+ type: 'string',
123
+ format: 'email',
124
+ maxLength: 255,
125
+ },
126
+
127
+ // Password requirements
128
+ password: {
129
+ type: 'string',
130
+ minLength: 8,
131
+ maxLength: 128,
132
+ pattern: '^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)',
133
+ },
134
+
135
+ // BigInt validation
136
+ bigintString: {
137
+ type: 'string',
138
+ format: 'bigint',
139
+ },
140
+
141
+ // Date validation
142
+ dateString: {
143
+ type: 'string',
144
+ format: 'date-time',
145
+ },
146
+ };
147
+
148
+ /**
149
+ * Schema builder helper
150
+ */
151
+ class SchemaBuilder {
152
+ constructor() {
153
+ this.schema = {
154
+ type: 'object',
155
+ properties: {},
156
+ required: [],
157
+ };
158
+ }
159
+
160
+ /**
161
+ * Add a property to the schema
162
+ * @param {string} name
163
+ * @param {Object} definition
164
+ * @param {boolean} required
165
+ * @returns {SchemaBuilder}
166
+ */
167
+ property(name, definition, required = false) {
168
+ this.schema.properties[name] = definition;
169
+ if (required) {
170
+ this.schema.required.push(name);
171
+ }
172
+ return this;
173
+ }
174
+
175
+ /**
176
+ * Add multiple properties
177
+ * @param {Object} properties
178
+ * @returns {SchemaBuilder}
179
+ */
180
+ properties(properties) {
181
+ Object.entries(properties).forEach(([name, definition]) => {
182
+ this.property(name, definition);
183
+ });
184
+ return this;
185
+ }
186
+
187
+ /**
188
+ * Set required fields
189
+ * @param {string[]} fields
190
+ * @returns {SchemaBuilder}
191
+ */
192
+ required(fields) {
193
+ this.schema.required = [...new Set([...this.schema.required, ...fields])];
194
+ return this;
195
+ }
196
+
197
+ /**
198
+ * Add additional properties setting
199
+ * @param {boolean} allowed
200
+ * @returns {SchemaBuilder}
201
+ */
202
+ additionalProperties(allowed) {
203
+ this.schema.additionalProperties = allowed;
204
+ return this;
205
+ }
206
+
207
+ /**
208
+ * Build and return the schema
209
+ * @returns {Object}
210
+ */
211
+ build() {
212
+ return this.schema;
213
+ }
214
+ }
215
+
216
+ module.exports = {
217
+ SchemaValidator,
218
+ commonSchemas,
219
+ SchemaBuilder,
220
+ };