hono 2.2.4 → 2.3.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/dist/cjs/compose.js +10 -1
- package/dist/cjs/middleware/basic-auth/index.js +1 -2
- package/dist/cjs/middleware/bearer-auth/index.js +3 -3
- package/dist/cjs/middleware/compress/index.js +1 -1
- package/dist/cjs/middleware/jwt/index.js +3 -6
- package/dist/cjs/middleware/serve-static/bun.js +1 -0
- package/dist/cjs/middleware/serve-static/serve-static.js +1 -0
- package/dist/cjs/middleware/validator/middleware.js +39 -12
- package/dist/cjs/middleware/validator/validator.js +239 -93
- package/dist/cjs/utils/json.js +67 -13
- package/dist/cjs/utils/object.js +40 -0
- package/dist/compose.js +10 -1
- package/dist/hono.d.ts +1 -1
- package/dist/middleware/basic-auth/index.js +1 -2
- package/dist/middleware/bearer-auth/index.js +3 -3
- package/dist/middleware/compress/index.js +1 -1
- package/dist/middleware/jwt/index.js +3 -6
- package/dist/middleware/serve-static/bun.js +1 -0
- package/dist/middleware/serve-static/serve-static.js +1 -0
- package/dist/middleware/validator/middleware.d.ts +5 -5
- package/dist/middleware/validator/middleware.js +40 -13
- package/dist/middleware/validator/validator.d.ts +48 -17
- package/dist/middleware/validator/validator.js +236 -92
- package/dist/utils/json.d.ts +1 -1
- package/dist/utils/json.js +65 -11
- package/dist/utils/object.d.ts +2 -0
- package/dist/utils/object.js +35 -0
- package/package.json +1 -1
|
@@ -1,26 +1,106 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { JSONPathCopy } from '../../utils/json';
|
|
2
2
|
import { rule } from './rule';
|
|
3
3
|
import { sanitizer } from './sanitizer';
|
|
4
|
+
export class VObjectBase {
|
|
5
|
+
constructor(container, key) {
|
|
6
|
+
this.keys = [];
|
|
7
|
+
this._isOptional = false;
|
|
8
|
+
this.getValidators = () => {
|
|
9
|
+
const validators = [];
|
|
10
|
+
const thisKeys = [];
|
|
11
|
+
Object.assign(thisKeys, this.keys);
|
|
12
|
+
const walk = (container, keys, isOptional) => {
|
|
13
|
+
for (const v of Object.values(container)) {
|
|
14
|
+
if (v instanceof VArray || v instanceof VObject) {
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
16
|
+
// @ts-ignore
|
|
17
|
+
isOptional || (isOptional = v._isOptional);
|
|
18
|
+
keys.push(...v.keys);
|
|
19
|
+
walk(v.container, keys, isOptional);
|
|
20
|
+
const tmp = [];
|
|
21
|
+
Object.assign(tmp, thisKeys);
|
|
22
|
+
keys = tmp;
|
|
23
|
+
}
|
|
24
|
+
else if (v instanceof VBase) {
|
|
25
|
+
if (isOptional)
|
|
26
|
+
v.isOptional();
|
|
27
|
+
v.baseKeys.push(...keys);
|
|
28
|
+
validators.push(v);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
walk(this.container, this.keys, this._isOptional);
|
|
33
|
+
return validators;
|
|
34
|
+
};
|
|
35
|
+
this.container = container;
|
|
36
|
+
if (this instanceof VArray) {
|
|
37
|
+
this.keys.push(key, '[*]');
|
|
38
|
+
}
|
|
39
|
+
else if (this instanceof VObject) {
|
|
40
|
+
this.keys.push(key);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
isOptional() {
|
|
44
|
+
this._isOptional = true;
|
|
45
|
+
return this;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export class VObject extends VObjectBase {
|
|
49
|
+
constructor(container, key) {
|
|
50
|
+
super(container, key);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export class VArray extends VObjectBase {
|
|
54
|
+
constructor(container, key) {
|
|
55
|
+
super(container, key);
|
|
56
|
+
this.type = 'array';
|
|
57
|
+
}
|
|
58
|
+
}
|
|
4
59
|
export class Validator {
|
|
5
60
|
constructor() {
|
|
61
|
+
this.isArray = false;
|
|
6
62
|
this.query = (key) => new VString({ target: 'query', key: key });
|
|
7
63
|
this.header = (key) => new VString({ target: 'header', key: key });
|
|
8
64
|
this.body = (key) => new VString({ target: 'body', key: key });
|
|
9
|
-
this.json = (key) =>
|
|
65
|
+
this.json = (key) => {
|
|
66
|
+
if (this.isArray) {
|
|
67
|
+
return new VStringArray({ target: 'json', key: key });
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
return new VString({ target: 'json', key: key });
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
this.array = (path, validator) => {
|
|
74
|
+
this.isArray = true;
|
|
75
|
+
const res = validator(this);
|
|
76
|
+
const arr = new VArray(res, path);
|
|
77
|
+
return arr;
|
|
78
|
+
};
|
|
79
|
+
this.object = (path, validator) => {
|
|
80
|
+
this.isArray = false;
|
|
81
|
+
const res = validator(this);
|
|
82
|
+
const obj = new VObject(res, path);
|
|
83
|
+
return obj;
|
|
84
|
+
};
|
|
10
85
|
}
|
|
11
86
|
}
|
|
12
87
|
export class VBase {
|
|
13
88
|
constructor(options) {
|
|
14
|
-
this.
|
|
15
|
-
|
|
16
|
-
return this;
|
|
17
|
-
};
|
|
89
|
+
this.baseKeys = [];
|
|
90
|
+
this._nested = () => (this.baseKeys.length ? true : false);
|
|
18
91
|
this.addSanitizer = (sanitizer) => {
|
|
19
92
|
this.sanitizers.push(sanitizer);
|
|
20
93
|
return this;
|
|
21
94
|
};
|
|
95
|
+
this.message = (text) => {
|
|
96
|
+
const len = this.rules.length;
|
|
97
|
+
if (len >= 1) {
|
|
98
|
+
this.rules[len - 1].customMessage = text;
|
|
99
|
+
}
|
|
100
|
+
return this;
|
|
101
|
+
};
|
|
22
102
|
this.isRequired = () => {
|
|
23
|
-
return this.addRule((value) => {
|
|
103
|
+
return this.addRule('isRequired', (value) => {
|
|
24
104
|
if (value !== undefined && value !== null && value !== '')
|
|
25
105
|
return true;
|
|
26
106
|
return false;
|
|
@@ -28,34 +108,28 @@ export class VBase {
|
|
|
28
108
|
};
|
|
29
109
|
this.isOptional = () => {
|
|
30
110
|
this._optional = true;
|
|
31
|
-
return this.addRule(() => true);
|
|
111
|
+
return this.addRule('isOptional', () => true);
|
|
32
112
|
};
|
|
33
113
|
this.isEqual = (comparison) => {
|
|
34
|
-
return this.addRule((value) => {
|
|
114
|
+
return this.addRule('isEqual', (value) => {
|
|
35
115
|
return value === comparison;
|
|
36
116
|
});
|
|
37
117
|
};
|
|
38
118
|
this.asNumber = () => {
|
|
39
|
-
const newVNumber = new VNumber(this);
|
|
119
|
+
const newVNumber = new VNumber({ ...this, type: 'number' });
|
|
120
|
+
if (this.isArray)
|
|
121
|
+
return newVNumber.asArray();
|
|
40
122
|
return newVNumber;
|
|
41
123
|
};
|
|
42
124
|
this.asBoolean = () => {
|
|
43
|
-
const newVBoolean = new VBoolean(this);
|
|
125
|
+
const newVBoolean = new VBoolean({ ...this, type: 'boolean' });
|
|
126
|
+
if (this.isArray)
|
|
127
|
+
return newVBoolean.asArray();
|
|
44
128
|
return newVBoolean;
|
|
45
129
|
};
|
|
46
|
-
this.asObject = () => {
|
|
47
|
-
const newVObject = new VObject(this);
|
|
48
|
-
return newVObject;
|
|
49
|
-
};
|
|
50
130
|
this.validate = async (req) => {
|
|
51
|
-
const result = {
|
|
52
|
-
isValid: true,
|
|
53
|
-
message: undefined,
|
|
54
|
-
target: this.target,
|
|
55
|
-
key: this.key,
|
|
56
|
-
value: undefined,
|
|
57
|
-
};
|
|
58
131
|
let value = undefined;
|
|
132
|
+
let jsonData = undefined;
|
|
59
133
|
if (this.target === 'query') {
|
|
60
134
|
value = req.query(this.key);
|
|
61
135
|
}
|
|
@@ -67,105 +141,178 @@ export class VBase {
|
|
|
67
141
|
value = body[this.key];
|
|
68
142
|
}
|
|
69
143
|
if (this.target === 'json') {
|
|
144
|
+
if (this._nested()) {
|
|
145
|
+
this.key = `${this.baseKeys.join('.')}.${this.key}`;
|
|
146
|
+
}
|
|
147
|
+
let obj = {};
|
|
70
148
|
try {
|
|
71
|
-
|
|
72
|
-
value = JSONPath(obj, this.key);
|
|
149
|
+
obj = (await req.json());
|
|
73
150
|
}
|
|
74
151
|
catch (e) {
|
|
75
152
|
throw new Error('Malformed JSON in request body');
|
|
76
153
|
}
|
|
154
|
+
const dst = {};
|
|
155
|
+
value = JSONPathCopy(obj, dst, this.key);
|
|
156
|
+
if (this._nested())
|
|
157
|
+
jsonData = dst;
|
|
77
158
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (
|
|
82
|
-
|
|
159
|
+
const results = [];
|
|
160
|
+
let typeRule = this.rules.shift();
|
|
161
|
+
for (const rule of this.rules) {
|
|
162
|
+
if (rule.type === 'type') {
|
|
163
|
+
typeRule = rule;
|
|
83
164
|
}
|
|
84
|
-
else {
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
.join(', ')}]`
|
|
89
|
-
: value;
|
|
90
|
-
switch (this.target) {
|
|
91
|
-
case 'query':
|
|
92
|
-
result.message = `Invalid Value: the query parameter "${this.key}" is invalid - ${valToStr}`;
|
|
93
|
-
break;
|
|
94
|
-
case 'header':
|
|
95
|
-
result.message = `Invalid Value: the request header "${this.key}" is invalid - ${valToStr}`;
|
|
96
|
-
break;
|
|
97
|
-
case 'body':
|
|
98
|
-
result.message = `Invalid Value: the request body "${this.key}" is invalid - ${valToStr}`;
|
|
99
|
-
break;
|
|
100
|
-
case 'json':
|
|
101
|
-
result.message = `Invalid Value: the JSON body "${this.key}" is invalid - ${valToStr}`;
|
|
102
|
-
break;
|
|
103
|
-
}
|
|
165
|
+
else if (rule.type === 'value') {
|
|
166
|
+
const result = this.validateRule(rule, value);
|
|
167
|
+
result.jsonData || (result.jsonData = jsonData);
|
|
168
|
+
results.push(result);
|
|
104
169
|
}
|
|
105
170
|
}
|
|
106
|
-
|
|
171
|
+
if (typeRule) {
|
|
172
|
+
const typeResult = this.validateRule(typeRule, value);
|
|
173
|
+
typeResult.jsonData || (typeResult.jsonData = jsonData);
|
|
174
|
+
results.unshift(typeResult);
|
|
175
|
+
}
|
|
176
|
+
return results;
|
|
107
177
|
};
|
|
108
|
-
this.
|
|
109
|
-
// Check type
|
|
178
|
+
this.validateType = (value) => {
|
|
110
179
|
if (this.isArray) {
|
|
111
180
|
if (!Array.isArray(value)) {
|
|
112
181
|
return false;
|
|
113
182
|
}
|
|
114
183
|
for (const val of value) {
|
|
115
|
-
if (typeof val
|
|
116
|
-
|
|
117
|
-
// If not optional, or optional and not undefined, return false
|
|
118
|
-
if (!this._optional || typeof val !== 'undefined')
|
|
119
|
-
return false;
|
|
184
|
+
if (typeof val === 'undefined' && this._nested()) {
|
|
185
|
+
value.pop();
|
|
120
186
|
}
|
|
121
|
-
}
|
|
122
|
-
// Sanitize
|
|
123
|
-
for (const sanitizer of this.sanitizers) {
|
|
124
|
-
value = value.map((innerVal) => sanitizer(innerVal));
|
|
125
|
-
}
|
|
126
|
-
for (const rule of this.rules) {
|
|
127
187
|
for (const val of value) {
|
|
128
|
-
if (
|
|
129
|
-
|
|
188
|
+
if (typeof val !== this.type) {
|
|
189
|
+
// Value is of wrong type here
|
|
190
|
+
// If it is not optional and not undefined, return false
|
|
191
|
+
if (!this._optional || typeof val !== 'undefined')
|
|
192
|
+
return false;
|
|
130
193
|
}
|
|
131
194
|
}
|
|
132
195
|
}
|
|
133
|
-
return true;
|
|
134
196
|
}
|
|
135
197
|
else {
|
|
136
198
|
if (typeof value !== this.type) {
|
|
137
|
-
if (this._optional && typeof value === 'undefined') {
|
|
199
|
+
if (this._optional && (typeof value === 'undefined' || Array.isArray(value))) {
|
|
138
200
|
// Do nothing.
|
|
139
|
-
//
|
|
201
|
+
// If it is optional it's OK to be `undefined` or Array
|
|
140
202
|
}
|
|
141
203
|
else {
|
|
142
204
|
return false;
|
|
143
205
|
}
|
|
144
206
|
}
|
|
207
|
+
}
|
|
208
|
+
return true;
|
|
209
|
+
};
|
|
210
|
+
this.validateValue = (func, value) => {
|
|
211
|
+
if (Array.isArray(value)) {
|
|
145
212
|
// Sanitize
|
|
146
213
|
for (const sanitizer of this.sanitizers) {
|
|
147
|
-
value = sanitizer(
|
|
214
|
+
value = value.map((innerVal) => sanitizer(innerVal));
|
|
148
215
|
}
|
|
149
|
-
for (const
|
|
150
|
-
if (!
|
|
216
|
+
for (const val of value) {
|
|
217
|
+
if (!func(val)) {
|
|
151
218
|
return false;
|
|
152
219
|
}
|
|
153
220
|
}
|
|
154
221
|
return true;
|
|
155
222
|
}
|
|
223
|
+
else {
|
|
224
|
+
// Sanitize
|
|
225
|
+
for (const sanitizer of this.sanitizers) {
|
|
226
|
+
value = sanitizer(value);
|
|
227
|
+
}
|
|
228
|
+
if (!func(value)) {
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
this.getMessage = (opts) => {
|
|
235
|
+
let keyText;
|
|
236
|
+
const valueText = Array.isArray(opts.value)
|
|
237
|
+
? `${opts.value
|
|
238
|
+
.map((val) => val === undefined ? 'undefined' : typeof val === 'string' ? `"${val}"` : val)
|
|
239
|
+
.join(', ')}`
|
|
240
|
+
: opts.value;
|
|
241
|
+
switch (this.target) {
|
|
242
|
+
case 'query':
|
|
243
|
+
keyText = `the query parameter "${this.key}"`;
|
|
244
|
+
break;
|
|
245
|
+
case 'header':
|
|
246
|
+
keyText = `the request header "${this.key}"`;
|
|
247
|
+
break;
|
|
248
|
+
case 'body':
|
|
249
|
+
keyText = `the request body "${this.key}"`;
|
|
250
|
+
break;
|
|
251
|
+
case 'json':
|
|
252
|
+
keyText = `the JSON body "${this.key}"`;
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
return `Invalid Value [${valueText}]: ${keyText} is invalid - ${opts.ruleName}`;
|
|
156
256
|
};
|
|
157
257
|
this.target = options.target;
|
|
158
258
|
this.key = options.key;
|
|
159
259
|
this.type = options.type || 'string';
|
|
160
|
-
this.rules = [
|
|
260
|
+
this.rules = [
|
|
261
|
+
{
|
|
262
|
+
name: this.getTypeRuleName(),
|
|
263
|
+
type: 'type',
|
|
264
|
+
func: this.validateType,
|
|
265
|
+
},
|
|
266
|
+
];
|
|
161
267
|
this.sanitizers = [];
|
|
162
|
-
this.isArray = options.isArray || false;
|
|
163
268
|
this._optional = false;
|
|
269
|
+
this.isArray = options.isArray || false;
|
|
270
|
+
}
|
|
271
|
+
addRule(arg, func) {
|
|
272
|
+
if (typeof arg === 'string' && func) {
|
|
273
|
+
this.rules.push({ name: arg, func, type: 'value' });
|
|
274
|
+
}
|
|
275
|
+
else if (arg instanceof Function) {
|
|
276
|
+
this.rules.push({ name: arg.name, func: arg, type: 'value' });
|
|
277
|
+
}
|
|
278
|
+
return this;
|
|
164
279
|
}
|
|
165
|
-
|
|
166
|
-
|
|
280
|
+
get(value) {
|
|
281
|
+
const len = this.rules.length;
|
|
282
|
+
if (len > 0) {
|
|
283
|
+
this.rules[this.rules.length - 1].customMessage = value;
|
|
284
|
+
}
|
|
167
285
|
return this;
|
|
168
286
|
}
|
|
287
|
+
getTypeRuleName() {
|
|
288
|
+
const prefix = 'should be';
|
|
289
|
+
return this.isArray ? `${prefix} "${this.type}[]"` : `${prefix} "${this.type}"`;
|
|
290
|
+
}
|
|
291
|
+
validateRule(rule, value) {
|
|
292
|
+
let isValid = false;
|
|
293
|
+
if (this._nested() && this.target != 'json') {
|
|
294
|
+
isValid = false;
|
|
295
|
+
}
|
|
296
|
+
else if (rule.type === 'value') {
|
|
297
|
+
isValid = this.validateValue(rule.func, value);
|
|
298
|
+
}
|
|
299
|
+
else if (rule.type === 'type') {
|
|
300
|
+
isValid = this.validateType(value);
|
|
301
|
+
}
|
|
302
|
+
const message = isValid
|
|
303
|
+
? undefined
|
|
304
|
+
: rule.customMessage || this.getMessage({ ruleName: rule.name, value });
|
|
305
|
+
const result = {
|
|
306
|
+
isValid: isValid,
|
|
307
|
+
message: message,
|
|
308
|
+
target: this.target,
|
|
309
|
+
key: this.key,
|
|
310
|
+
value,
|
|
311
|
+
ruleName: rule.name,
|
|
312
|
+
ruleType: rule.type,
|
|
313
|
+
};
|
|
314
|
+
return result;
|
|
315
|
+
}
|
|
169
316
|
}
|
|
170
317
|
export class VString extends VBase {
|
|
171
318
|
constructor(options) {
|
|
@@ -174,28 +321,28 @@ export class VString extends VBase {
|
|
|
174
321
|
return new VStringArray(this);
|
|
175
322
|
};
|
|
176
323
|
this.isEmpty = (options = { ignore_whitespace: false }) => {
|
|
177
|
-
return this.addRule((value) => rule.isEmpty(value, options));
|
|
324
|
+
return this.addRule('isEmpty', (value) => rule.isEmpty(value, options));
|
|
178
325
|
};
|
|
179
326
|
this.isLength = (options, arg2) => {
|
|
180
|
-
return this.addRule((value) => rule.isLength(value, options, arg2));
|
|
327
|
+
return this.addRule('isLength', (value) => rule.isLength(value, options, arg2));
|
|
181
328
|
};
|
|
182
329
|
this.isAlpha = () => {
|
|
183
|
-
return this.addRule((value) => rule.isAlpha(value));
|
|
330
|
+
return this.addRule('isAlpha', (value) => rule.isAlpha(value));
|
|
184
331
|
};
|
|
185
332
|
this.isNumeric = () => {
|
|
186
|
-
return this.addRule((value) => rule.isNumeric(value));
|
|
333
|
+
return this.addRule('isNumeric', (value) => rule.isNumeric(value));
|
|
187
334
|
};
|
|
188
335
|
this.contains = (elem, options = {
|
|
189
336
|
ignoreCase: false,
|
|
190
337
|
minOccurrences: 1,
|
|
191
338
|
}) => {
|
|
192
|
-
return this.addRule((value) => rule.contains(value, elem, options));
|
|
339
|
+
return this.addRule('contains', (value) => rule.contains(value, elem, options));
|
|
193
340
|
};
|
|
194
341
|
this.isIn = (options) => {
|
|
195
|
-
return this.addRule((value) => rule.isIn(value, options));
|
|
342
|
+
return this.addRule('isIn', (value) => rule.isIn(value, options));
|
|
196
343
|
};
|
|
197
344
|
this.match = (regExp) => {
|
|
198
|
-
return this.addRule((value) => rule.match(value, regExp));
|
|
345
|
+
return this.addRule('match', (value) => rule.match(value, regExp));
|
|
199
346
|
};
|
|
200
347
|
this.trim = () => {
|
|
201
348
|
return this.addSanitizer((value) => sanitizer.trim(value));
|
|
@@ -210,10 +357,10 @@ export class VNumber extends VBase {
|
|
|
210
357
|
return new VNumberArray(this);
|
|
211
358
|
};
|
|
212
359
|
this.isGte = (min) => {
|
|
213
|
-
return this.addRule((value) => rule.isGte(value, min));
|
|
360
|
+
return this.addRule('isGte', (value) => rule.isGte(value, min));
|
|
214
361
|
};
|
|
215
362
|
this.isLte = (min) => {
|
|
216
|
-
return this.addRule((value) => rule.isLte(value, min));
|
|
363
|
+
return this.addRule('isLte', (value) => rule.isLte(value, min));
|
|
217
364
|
};
|
|
218
365
|
this.type = 'number';
|
|
219
366
|
}
|
|
@@ -225,35 +372,32 @@ export class VBoolean extends VBase {
|
|
|
225
372
|
return new VBooleanArray(this);
|
|
226
373
|
};
|
|
227
374
|
this.isTrue = () => {
|
|
228
|
-
return this.addRule((value) => rule.isTrue(value));
|
|
375
|
+
return this.addRule('isTrue', (value) => rule.isTrue(value));
|
|
229
376
|
};
|
|
230
377
|
this.isFalse = () => {
|
|
231
|
-
return this.addRule((value) => rule.isFalse(value));
|
|
378
|
+
return this.addRule('isFalse', (value) => rule.isFalse(value));
|
|
232
379
|
};
|
|
233
380
|
this.type = 'boolean';
|
|
234
381
|
}
|
|
235
382
|
}
|
|
236
|
-
export class VObject extends VBase {
|
|
237
|
-
constructor(options) {
|
|
238
|
-
super(options);
|
|
239
|
-
this.type = 'object';
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
383
|
export class VNumberArray extends VNumber {
|
|
243
384
|
constructor(options) {
|
|
244
385
|
super(options);
|
|
245
386
|
this.isArray = true;
|
|
387
|
+
this.rules[0].name = this.getTypeRuleName();
|
|
246
388
|
}
|
|
247
389
|
}
|
|
248
390
|
export class VStringArray extends VString {
|
|
249
391
|
constructor(options) {
|
|
250
392
|
super(options);
|
|
251
393
|
this.isArray = true;
|
|
394
|
+
this.rules[0].name = this.getTypeRuleName();
|
|
252
395
|
}
|
|
253
396
|
}
|
|
254
397
|
export class VBooleanArray extends VBoolean {
|
|
255
398
|
constructor(options) {
|
|
256
399
|
super(options);
|
|
257
400
|
this.isArray = true;
|
|
401
|
+
this.rules[0].name = this.getTypeRuleName();
|
|
258
402
|
}
|
|
259
403
|
}
|
package/dist/utils/json.d.ts
CHANGED
|
@@ -4,4 +4,4 @@ export declare type JSONObject = {
|
|
|
4
4
|
[key: string]: JSONPrimitive | JSONArray | JSONObject;
|
|
5
5
|
};
|
|
6
6
|
export declare type JSONValue = JSONObject | JSONArray | JSONPrimitive;
|
|
7
|
-
export declare const
|
|
7
|
+
export declare const JSONPathCopy: (src: JSONObject, dst: JSONObject, path: string) => JSONPrimitive | JSONObject | JSONArray;
|
package/dist/utils/json.js
CHANGED
|
@@ -1,27 +1,81 @@
|
|
|
1
|
-
const
|
|
1
|
+
const JSONPathCopyInternal = (src, dst, parts, results) => {
|
|
2
|
+
let srcVal = src;
|
|
3
|
+
let dstVal = dst;
|
|
2
4
|
const length = parts.length;
|
|
3
|
-
for (let i = 0; i < length &&
|
|
5
|
+
for (let i = 0; i < length && srcVal !== undefined && dstVal; i++) {
|
|
4
6
|
const p = parts[i];
|
|
5
|
-
if (
|
|
6
|
-
|
|
7
|
+
if (typeof srcVal !== 'object') {
|
|
8
|
+
return srcVal;
|
|
7
9
|
}
|
|
8
|
-
if (
|
|
10
|
+
if (srcVal === null) {
|
|
9
11
|
return undefined;
|
|
10
12
|
}
|
|
11
13
|
if (p === '*') {
|
|
12
14
|
const restParts = parts.slice(i + 1);
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
+
const restLength = srcVal.length;
|
|
16
|
+
if (restLength === undefined) {
|
|
17
|
+
parts = Object.keys(srcVal);
|
|
18
|
+
for (const p of parts) {
|
|
19
|
+
const srcVal2 = srcVal;
|
|
20
|
+
const dst2 = {};
|
|
21
|
+
JSONPathCopyInternal(srcVal2, dst2, [p], results);
|
|
22
|
+
dstVal[p] = dst2[p];
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
const res = [];
|
|
27
|
+
for (let i2 = 0; i2 < restLength; i2++) {
|
|
28
|
+
if (typeof srcVal[i2] !== 'object' || srcVal[i2] === undefined) {
|
|
29
|
+
res.push(srcVal[i2]);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
const srcVal2 = srcVal[i2];
|
|
33
|
+
const dst2 = {};
|
|
34
|
+
const res2 = JSONPathCopyInternal(srcVal2, dst2, restParts, results);
|
|
35
|
+
if (res2 === undefined)
|
|
36
|
+
results.push(undefined);
|
|
37
|
+
dstVal[i2] = dst2;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (res.length) {
|
|
41
|
+
Object.assign(dstVal, srcVal);
|
|
42
|
+
results.push(...res);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return results;
|
|
46
|
+
}
|
|
47
|
+
if (typeof srcVal[p] === 'object') {
|
|
48
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
49
|
+
// @ts-ignore
|
|
50
|
+
dstVal[p] || (dstVal[p] = new srcVal[p].constructor());
|
|
51
|
+
}
|
|
52
|
+
else if (typeof srcVal[p] !== 'undefined') {
|
|
53
|
+
dstVal[p] = srcVal[p];
|
|
15
54
|
}
|
|
16
55
|
else {
|
|
17
|
-
|
|
56
|
+
return undefined;
|
|
18
57
|
}
|
|
58
|
+
srcVal = srcVal[p];
|
|
59
|
+
dstVal = dstVal[p];
|
|
60
|
+
}
|
|
61
|
+
if (typeof srcVal === 'object' && dstVal) {
|
|
62
|
+
Object.assign(dstVal, srcVal);
|
|
19
63
|
}
|
|
20
|
-
|
|
64
|
+
results.push(srcVal);
|
|
65
|
+
return results;
|
|
21
66
|
};
|
|
22
|
-
export const
|
|
67
|
+
export const JSONPathCopy = (src, dst, path) => {
|
|
68
|
+
const results = [];
|
|
69
|
+
const parts = path.replace(/\.?\[(.*?)\]/g, '.$1').split(/\./);
|
|
23
70
|
try {
|
|
24
|
-
|
|
71
|
+
JSONPathCopyInternal(src, dst, parts, results);
|
|
72
|
+
if (results.length === 0) {
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
else if (results.length === 1 && !parts.includes('*')) {
|
|
76
|
+
return results[0];
|
|
77
|
+
}
|
|
78
|
+
return results;
|
|
25
79
|
}
|
|
26
80
|
catch (e) {
|
|
27
81
|
return undefined;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
export const isObject = (val) => val && typeof val === 'object' && !Array.isArray(val);
|
|
3
|
+
export const mergeObjects = (target, source) => {
|
|
4
|
+
const merged = Object.assign({}, target);
|
|
5
|
+
if (isObject(target) && isObject(source)) {
|
|
6
|
+
for (const key of Object.keys(source)) {
|
|
7
|
+
if (isObject(source[key])) {
|
|
8
|
+
if (target[key] === undefined)
|
|
9
|
+
Object.assign(merged, { [key]: source[key] });
|
|
10
|
+
else
|
|
11
|
+
merged[key] = mergeObjects(target[key], source[key]);
|
|
12
|
+
}
|
|
13
|
+
else if (Array.isArray(source[key]) && Array.isArray(target[key])) {
|
|
14
|
+
const srcArr = source[key];
|
|
15
|
+
const tgtArr = target[key];
|
|
16
|
+
const outArr = [];
|
|
17
|
+
for (let i = 0; i < srcArr.length; i += 1) {
|
|
18
|
+
// If corresponding index for both arrays is an object, then merge them
|
|
19
|
+
// Otherwise just copy src arr index into out arr index
|
|
20
|
+
if (isObject(srcArr[i]) && isObject(tgtArr[i])) {
|
|
21
|
+
outArr[i] = mergeObjects(tgtArr[i], srcArr[i]);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
outArr[i] = srcArr[i];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
Object.assign(merged, { [key]: outArr });
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
Object.assign(merged, { [key]: source[key] });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return merged;
|
|
35
|
+
};
|