Socket
Socket
Sign inDemoInstall

aws-lambda-middleware

Package Overview
Dependencies
Maintainers
1
Versions
27
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

aws-lambda-middleware - npm Package Compare versions

Comparing version 0.9.1 to 1.0.0

src/Message.js

146

index.js
const Middleware = require('./src/Middleware')
const PropTypes = require('./src/PropTypes')
const Validate = require('./src/Validate')
const Message = require('./src/Message')
const common = require('./src/common')
/** ========== addRules ========== */
/** ========== Default global option ========== */
Middleware.globalOption({
//single, simple, full
pathPropNameType: 'simple'
})
/** ========== PropTypes addRules ========== */
PropTypes.addRules({

@@ -92,26 +102,118 @@ //String

})
}
})
/** ========== Validate addRules ========== */
Validate.addRules({
/**
* @param {String | Array | Object} value
* @param {Object} option { min, max }
*/
length: {
valid: (value, option, sibling, event) => {
const length = common.isObject(value) ? Object.keys(value).length : (value?.length || 0)
if (common.isObject(option)) {
let isValid = true
if (!common.isEmpty(option.min)) {
isValid = length >= option.min
}
if (isValid && !common.isEmpty(option.max)) {
isValid = length <= option.max
}
return isValid
} else {
return length == option
}
},
message: `length of '{{propName}}'{{#unless max}} can be from {{#unless min}}{{min}} {{/unless}}~ {{max}}{{/unless}}{{#less max}} must be {{option}}{{/less}}`
},
//Array
get array () {
return PropTypes.makeRule({
validType: (value, isDefaultValue) => {
return Array.isArray(value)
},
validRequired: (value) => {
return value.length > 0
/**
* @param {Number | Int} value
* @param {Int} option
*/
min: {
valid: (value, option, sibling, event) => {
const val = common.isNumber(value) ? value : 0
return val >= option
},
message: `'{{propName}}' can be from {{min}}`
},
/**
* @param {Number | Int} value
* @param {Int} option
*/
max: {
valid: (value, option, sibling, event) => {
const val = common.isNumber(value) ? value : 0
return val <= option
},
message: `'{{propName}}' can be up to {{max}}`
},
/**
* @param {Number | Int | String | Boolean} value
* @param {Array} option [1, 2]
*/
or: {
valid: (value, option, sibling, event) => {
const opt = Array.isArray(option) ? option : []
return opt.includes(value)
},
message: `'{{propName}}' can only have values {{option}}`
},
/**
* @param {String} value
*/
digit: {
valid: (value, option, sibling, event) => {
return /^[0-9]+$/.test(value)
},
message: `'{{propName}}' can only be the string 0-9`
},
/**
* @param {String} value
* @param {String} option upper, lower
*/
alphabet: {
valid: (value, option, sibling, event) => {
let reg = /^[a-z]+$/i
if (option === 'upper') {
reg = /^[A-Z]+$/
} else if (option === 'lower') {
reg = /^[a-z]+$/
}
})
return reg.test(value)
},
message: `'{{propName}}' can only contain {{#unless option}}{{option}} {{/unless}}alphabets`
},
//Object
get object () {
return PropTypes.makeRule({
validType: (value, isDefaultValue) => {
return common.isObject(value)
},
validRequired: (value) => {
return !common.isEmpty(value)
/**
* alphabets + 0-9
* @param {String} value
* @param {String} option upper, lower
*/
alphaDigit: {
valid: (value, option, sibling, event) => {
let reg = /^[a-z0-9]+$/i
if (option === 'upper') {
reg = /^[A-Z0-9]+$/
} else if (option === 'lower') {
reg = /^[a-z0-9]+$/
}
})
return reg.test(value)
},
message: `'{{propName}}' can only contain {{#unless option}}{{option}} {{/unless}}alphabets and 0-9`
}

@@ -121,4 +223,8 @@ })

exports.common = common
exports.Message = Message
exports.Middleware = Middleware
exports.PropTypes = PropTypes
exports.common = common
exports.Validate = Validate
//short constant
exports.Prop = PropTypes

13

package.json
{
"name": "aws-lambda-middleware",
"version": "0.9.1",
"version": "1.0.0",
"engines": {
"node": ">=8.3.0"
"node": ">=12.0.0"
},

@@ -12,6 +12,8 @@ "description": "AWS Lambda Middleware",

"eslint-plugin-node": "^11.1.0",
"eslint-plugin-import": "^2.27.5"
"eslint-plugin-import": "^2.29.1"
},
"scripts": {
"test": "node ./test/run.js"
"validate": "node ./test/validate.js",
"testCase": "node ./test/testCase.js",
"message": "node ./test/message.js"
},

@@ -26,3 +28,6 @@ "repository": {

"middleware",
"validate",
"validation",
"lambda middleware",
"lambda validation",
"lambda payload 2.0"

@@ -29,0 +34,0 @@ ],

@@ -10,4 +10,8 @@ # aws-lambda-middleware

It is implemented as lightly as possible to reduce the burden when running Lambda.
> Lambda Payload 2.0 supported.
> **🚀 v1.0 added features**
> A Validate function that is easy to expand and use has been added, and deep data of arrays and objects can now be processed.
>
> It is compatible even in environments other than lambda. (node express etc.)
&nbsp;

@@ -24,3 +28,3 @@

```js
const { Middleware, PropTypes } = require('aws-lambda-middleware')
const { Middleware, Prop } = require('aws-lambda-middleware')

@@ -30,5 +34,10 @@

queryStringParameters: {
username: PropTypes.string.isRequired,
age: PropTypes.integer,
photos: PropTypes.array.default([])
username: Prop.string.required(),
age: Prop.integer,
friends: [
{
name: Prop.string.length({ max: 20 }),
gender: Prop.string.or(['male', 'female'])
}
]
}

@@ -52,204 +61,80 @@ }).add(async (event, context, prevData) => {

## Options
You can set global options and cluster options.
Setting priority is `globalOption < clusterOption < callbackResult`
You can set global options and cluster options.
> You can set options such as `trim`.
### callbackData: *{Object}*
> Common data applied during callback
[📖 Options detail docs](docs/OPTIONS.md)
```js
const { Middleware, PropTypes } = require('aws-lambda-middleware')
## Middleware
You can simply apply Middleware in Lambda. 
Middleware.globalOption({
callbackData: {
headers: { 'Access-Control-Allow-Origin': '*' }
}
})
```
[📖 Middleware detail docs](docs/MIDDLEWEAR.md)
### bodyParser: *{Function}*
> Common event.body parser
## PropTypes
Checks and corrects the data types of request parameters.
> `PropTypes` and `Prop` are the same object.
Currently, event.body parser supports `Content-Type` : `application/json`, `application/x-www-form-urlencoded`.
The query string parser supports the following formats (application/x-www-form-urlencoded):
```
'foo=1&foo=&foo=3&name=jim&profile[age]=20'
'foo[]=1&foo[]=&foo[]=3&name=jim&profile[age]=20'
'foo[2]=3&foo[1]=&foo[0]=1&name=jim&profile[age]=20'
'foo[2]=3&foo[1]=&foo[0]=1&name=jim&profile[age]=20'
[📖 PropTypes detail docs](docs/PROP_TYPES.md)
//only parse up to 2 depth
//return { foo: [ '1', '', '3' ], name: 'jim', profile: { age: '20' } }
```
If you want to use another type of body parser, you can apply it at this point.
## Validate
It only verifies the validity of the request parameter value.
> You can use it by adding custom rules.
```js
const { Middleware, PropTypes, common } = require('aws-lambda-middleware')
const qs = require('qs')
[📖 Validate detail docs](docs/VALIDATE.md)
Middleware.globalOption({
bodyParser: (event = {}) => {
const contentType = common.getHeader(event, 'Content-Type')
if (/application\/x-www-form-urlencoded/i.test(contentType)) {
event.body = qs.parse(event.body)
}
}
})
```
&nbsp;
Cluster option can be applied to each middleware.
```js
const { Middleware, PropTypes } = require('aws-lambda-middleware')
The rules added to PropTypes and Validate are written in one line and used.
exports.handler = new Middleware({
callbackData: {
headers: { 'Access-Control-Allow-Origin': '*' }
},
bodyParser: (event = {}) => {
//code
},
trim: true
})
```
<img src="https://github.com/blaxk/aws-lambda-middleware/assets/16889775/519de528-3cf3-4c70-9695-c9c1f72e81ee" alt="code" style="max-width: 300px;">
### trim: *{Boolean}*
> When the Trim option is set, whitespaces are removed from both ends of the parameter string.
&nbsp;
The trim option can be applied in three forms.
```js
const { Middleware, PropTypes } = require('aws-lambda-middleware')
## ⚠️ Upgrading from v0.9 to v1.0
//Apply trim to all parameters for which PropType is set
Middleware.globalOption({
trim: true
})
### 1. `object` and `array` expressions
`object` and `array` are designated as reserved prop name, so the rule cannot be overwritten.
//Trim option is applied per handler function
exports.handler = new Middleware({
trim: true
}).add({
queryStringParameters: {
username: PropTypes.string.isRequired,
//Apply trim to each parameter (highest priority)
age: {
propType: PropTypes.integer,
trim: false
}
}
})
[⚠️ Reserved prop names](docs/RESERVED_PROPS.md)
### 2. Option settings for each PropTypes
trim settings for each PropTypes use `.option()`.
```js
{
param: Prop.string.option({ trim: false })
}
```
&nbsp;
### 3. `PropTypes` and `Prop`
The abbreviated `Prop` can be used instead of the `PropTypes`.
`PropTypes` can still be used as well.
## Middleware
### 4. `.isRequired` has been replaced by `.required()`.
`.isRequired` is also compatible, but not recommended.
### Middleware
> constructor
### 5. Parameter type of PropTypes.*.default() function
When dynamically setting the default value of PropTypes, the parameter type has been changed to `named parameters`.
| Param | Type | Description |
| --- | --- | --- |
| clusterOption | *Object* | middleware options |
### add(handler) : *{Middleware}*
> Add Flow handler & ProType rules
| Param | Type | Description |
| --- | --- | --- |
| handler | *Function* | @param *{Object}* `event` Lambda event (converted data type)<br>@param *{Object}* `context` Lambda context<br>@param *{Object}* `prevData` Previous handler return data|
| handler | *Object* | PropTypes rules |
<br/>
**v0.9**
```js
exports.handler = new Middleware().add(async (event, context, prevData) => {
if (event.source === 'serverless-plugin-warmup') {
//If Promise.reject() is returned, execute Lambda handler callback(null, rejectValue) without executing next handler
return Promise.reject('Lambda is warm!')
}
}).add({
//PropTypes do not need to be added as a first flow
body: {
username: PropTypes.string.isRequired
}
}).add(async (event, context, prevData) => {
//code
return {
statusCode: 200,
body: JSON.stringify({
message: 'success'
})
}
})
Prop.*.default((event) => {})
```
#### Example
**v1.0**
```js
exports.handler = new Middleware().add({
queryStringParameters: {
age: PropTypes.integer.isRequired
},
pathParameters: {
groupId: PropTypes.integer.isRequired
}
}).add(async (event, context, prevData) => {
const query = event.queryStringParameters
if (query.age > 20) {
return {
myName: 'jone'
}
} else {
return Promise.reject({
statusCode: 404,
body: JSON.stringify({
message: 'not found'
})
})
}
}).add(async (event, context, prevData) => {
const pathParam = event.pathParameters
console.log(prevData.myName) // 'jone'
return {
statusCode: 200,
body: JSON.stringify({
message: 'success'
})
}
})
Prop.*.default(({ event }) => {})
```
&nbsp;
[📖 PropTypes > Support methods](docs/PROP_TYPES.md?tab=readme-ov-file#support-methods)
## PropTypes
Parameter PropTypes validater
### 6. Interpreting `object` and `array` expressions
The interpretation of Object and Array expressions has been changed from `validate only when value exists` to `required validation`.
When setting the body as shown below, the returned status depends, so check the `item` document in `PropTypes > Support methods`.
### Support Types
[📖 PropTypes > Support methods](docs/PROP_TYPES.md?tab=readme-ov-file#support-methods)
| Type | Description |
| --- | --- |
| string | String |
| number | Number or Numberic string |
| integer | Integer or Integeric string |
| bool | Boolean or Boolean string |
| array | Array, isRequired = array.length > 0 |
| object | Object, isRequired = Object.length > 0 |
<br/>
```js
exports.handler = new Middleware().add({
//Validate child property of Lambda event (queryStringParameters, body, pathParameters ...)
queryStringParameters: {
//Type + Required
username: PropTypes.string.isRequired,
//Only Type (Do not check when there is empty value)
age: PropTypes.integer,
//Type + Set the value that is replaced when the request value is empty
photos: PropTypes.array.default([]),
//The value returned by the function can be set as the default value.
startTime: PropTypes.number.default(event => Date.now())
body: {
myId: Prop.string
}

@@ -259,94 +144,16 @@ })

### addRules(rules)
> In addition to the basic rules, new rules can be added.
> Adding with the same type name overrides the existing rule.
> This setting is applied globally.
**v0.9**
Even if the body of the request parameter is an empty Object or has no value, `status = 200` is returned.
| Param | Type | Description |
| --- | --- | --- |
| rules | *Object* | - |
<br/>
**v1.0**
If the body of the request parameter is an empty Object or has no value, `status = 400` is returned.
In order to `validate only when value exists` for the body, you must also set PropTypes on the body.
```js
const { Middleware, PropTypes } = require('aws-lambda-middleware')
PropTypes.addRules({
//It overrides the existing string rule.
get number () {
return PropTypes.makeRule({
/**
* Valid function to check data type
* @param {*} value
* @param {Boolean} isDefaultValue Returns true when validating the value type set as the default.
* */
validType: (value, isDefaultValue) => {
if (!isDefaultValue && typeof value === 'string') {
return /^-*[0-9]*[\.]*[0-9]+$/.test(value) && !/^0[0-9]+/.test(value) && !/^-0[0-9]+/.test(value) && !(value.length === 1 && value === '-')
} else {
return typeof value === 'number'
}
},
//Valid function to check if it is required
validRequired: (value) => {
return !isNaN(value)
},
//A function that converts the value of Paramers when it is incorrectly converted to a string. (Set only when necessary)
convert: (value) => {
if (typeof value === 'string') {
return Number(value)
} else {
return value
}
}
})
},
//Multiple settings are possible at once
get string () {
return PropTypes.makeRule({
validType: (value, isDefaultValue) => {
return ...
},
validRequired: (value) => {
return ...
},
convert: (value) => {
return ...
}
})
}
exports.handler = new Middleware().add({
body: Prop.object.item({
myId: Prop.string
})
})
```
&nbsp;
## Node Version Compatibility
Node.js ^8.3.0
&nbsp;
## Changelog
#### 0.9.1
- Added trim option
- Fixed a bug where "convert" was not executed when applying PropTypes.*.default
#### 0.8.4
- PropTypes.*.default, Added ability to set the value returned from a method as a default value.
- Validate value type set as default
- Fixed a bug PropTypes.addRules
- body parser improvements
#### 0.7.1
- Added PropTypes.*.default method
- Added PropTypes.object
#### 0.6.1
- Lambda Payload 2.0 support
- Added Lambda error log
#### 0.5.3
- Fixed a bug PropTypes.boo.isRequired
#### 0.5.2
- Added and modify body parser options

@@ -5,6 +5,15 @@ const { URLSearchParams } = require('url')

const Common = {
RESERVED_PROPS: Object.freeze([
'addRules', 'makeRule', 'isEmpty', 'default', 'isRequired', 'option', 'array', 'object',
'required', 'valid', 'item', 'items'
]),
isObject: (value) => {
return Object.prototype.toString.call(value) === '[object Object]'
return Object.prototype.toString.call(value) === '[object Object]' && !value?._isRule
},
isNumber: (value) => {
return typeof value === 'number'
},
isEmpty: (value, isTypeData) => {

@@ -14,16 +23,15 @@ let result = true

if (Array.isArray(value)) {
result = isTypeData ? false : value.length === 0
result = isTypeData ? false : !value.length
} else if (Common.isObject(value)) {
if (isTypeData) {
result = false
result = !value
} else {
for (const key in value) {
result = false
break
}
result = !(value && Object.keys(value).length)
}
} else if (typeof value === 'number') {
result = isNaN(value)
} else if (typeof value === 'boolean' || value) {
} else if (typeof value === 'boolean') {
result = false
} else {
result = !value
}

@@ -38,2 +46,26 @@

//Object, Array, String, Number, NaN, Null, Function, Date, Boolean, RegExp, Error, Undefined
type: (value, lowerCase) => {
let result = Object.prototype.toString.call(value)
result = result.match(/^\[\W*object\W+([a-zA-Z]+)\W*\]$/)
if (result && result.length > 1) {
result = result[1]
}
if (result === 'Number') {
if (isNaN(value)) {
result = 'NaN'
}
} else if (result === 'Object') {
if (undefined === value) {
result = 'Undefined'
} else if (null === value) {
result = 'Null'
}
}
return lowerCase ? result.toLowerCase() : result
},
/**

@@ -55,3 +87,3 @@ * get header

clone: (value) => {
let result;
let result

@@ -71,2 +103,10 @@ if (Array.isArray(value) || Common.isObject(value)) {

info: (...arg) => {
console.info('[aws-lambda-middleware]', ...arg)
},
warn: (...arg) => {
console.warn('[aws-lambda-middleware]', ...arg)
},
error: (...arg) => {

@@ -95,5 +135,7 @@ console.error('[aws-lambda-middleware]', ...arg)

for (const [propName, value] of searchParams) {
if (/([^\[\]]+)(\[.*\])/i.test(propName)) {
const name = RegExp.$1
const depthAry = RegExp.$2.match(/\[[^\[\]]*\]/g)
const match = Common.matchProp(propName)
if (match) {
const name = match.name
const depthAry = match.value.match(/\[[^\[\]]*\]/g)
const depthLength = depthAry.length

@@ -108,3 +150,3 @@

//object
if (!params.hasOwnProperty(name)) {
if (!Object.hasOwn(params, name)) {
params[name] = {}

@@ -116,3 +158,3 @@ }

//array
if (!params.hasOwnProperty(name)) {
if (!Object.hasOwn(params, name)) {
params[name] = []

@@ -127,3 +169,3 @@ }

//array
if (params.hasOwnProperty(propName)) {
if (Object.hasOwn(params, propName)) {
if (Array.isArray(params[propName])) {

@@ -158,2 +200,19 @@ params[propName].push(value)

}
},
/**
* @param {String} propName
* @returns {Object} { name, value } || null
*/
matchProp (propName) {
const matchAry = propName.match(/([^\[\]]+)(\[.*\])/i)
if (matchAry?.length) {
return {
name: matchAry[1],
value: matchAry[2],
}
} else {
return null
}
}

@@ -163,2 +222,2 @@ }

module.exports = Common
module.exports = Object.freeze(Common)

@@ -0,4 +1,5 @@

const PropTypes = require('./PropTypes')
const common = require('./common')
let globalOptions = {}
const _globalOptions = {}

@@ -14,3 +15,5 @@

if (common.isObject(options)) {
globalOptions = options
for (const key in options) {
_globalOptions[key] = options[key]
}
} else {

@@ -35,2 +38,3 @@ common.error('The globalOptions type is available only for objects.')

this._handler.add = this.add.bind(this)
this._handler.valid = this.valid.bind(this)
}

@@ -60,95 +64,125 @@

/** ========== Private Methods ========== */
//lambda handler
async _handler (event = {}, context = {}, callback) {
const evt = this._parseEvent(event)
const flowLength = this._flows.length
let prevData = {}
/**
* add orgin lambda handler
* @param {Function} func
*/
handler (func) {
if (typeof func === 'function') {
this._flows.push({
type: 'lambda-handler',
handler: func
})
}
for (const i in this._flows) {
const flow = this._flows[i]
return this._handler
}
try {
if (flow.type === 'handler') {
prevData = await flow.handler(evt, context, prevData)
} else {
prevData = await this._validPropTypes(evt, flow.props)
}
/**
* Validate data with "PropTypeRule + ValidateRule" set in Middleware
* @param {Object} data Data to be validated
* @returns {Object} { status, message }
* status = none, valid, invalid, error
*/
valid (data) {
//none, valid, invalid, error
let status = 'none'
let error = ''
//last flow callback
if (flowLength - 1 == i) {
callback(null, common.isObject(prevData) ? {
...globalOptions.callbackData,
...this._options.callbackData,
...prevData
} : prevData)
if (common.isObject(data)) {
for (const i in this._flows) {
const flow = this._flows[i]
break
if (flow.type === 'props') {
try {
error = this._validError(data, flow.props)
if (error) {
status = 'invalid'
}
} catch (err) {
error = err?.stack || err?.message || 'valid error!'
status = 'error'
}
}
} catch (error) {
common.error(error)
if (common.isError(error)) {
callback(error)
} else {
callback(null, common.isObject(error) ? {
...globalOptions.callbackData,
...this._options.callbackData,
...error
} : error)
}
break
if (error) break
}
if (!error) {
status = 'valid'
}
}
return {
status,
message: error
}
}
async _validPropTypes (event, propGroup) {
let errorMsg = event.middlewareBodyParseError || ''
/** ========== Private Methods ========== */
//lambda handler
async _handler (event = {}, context = {}, callback) {
const evt = this._parseEvent(event)
const flowLength = this._flows.length
let prevData = {}
if (!errorMsg && common.isObject(propGroup)) {
for (const groupKey in propGroup) {
const propTypeRules = propGroup[groupKey]
if (evt.middlewareBodyParseError) {
common.error(evt.middlewareBodyParseError)
callback(null, {
..._globalOptions.callbackData,
...this._options.callbackData,
statusCode: 400,
body: JSON.stringify({
message: evt.middlewareBodyParseError
})
})
} else {
for (const i in this._flows) {
const flow = this._flows[i]
const isLambdaHandler = flow.type === 'lambda-handler'
for (const propName in propTypeRules) {
const propTypeRule = this._getPropTypeRule(propTypeRules[propName])
const rule = propTypeRule.rule
let val = common.isObject(event[groupKey]) ? event[groupKey][propName] : undefined
try {
if (flow.type === 'props') {
prevData = await this._validation(evt, flow.props)
} else if (flow.type === 'handler') {
prevData = await flow.handler(evt, context, prevData)
} else if (isLambdaHandler) {
prevData = await flow.handler(evt, context, callback)
}
//trim option
if (val && typeof val === 'string') {
event[groupKey][propName] = val = this._trim(val, propTypeRule.options)
//last flow callback
if (flowLength - 1 == i) {
if (!isLambdaHandler) callback(null, common.isObject(prevData) ? {
..._globalOptions.callbackData,
...this._options.callbackData,
...prevData
} : prevData)
break
}
if (typeof rule._invalid === 'function') {
errorMsg = rule._invalid(propName, val)
} catch (error) {
if (common.isError(error)) {
common.error(error)
if (!isLambdaHandler) callback(error)
} else {
// errorMsg = 'You have set propTypes that are not supported.'
common.info(error)
if (!isLambdaHandler) callback(null, common.isObject(error) ? {
..._globalOptions.callbackData,
...this._options.callbackData,
...error
} : error)
}
if (errorMsg) {
break
} else {
//set value & convert
if (common.isObject(event[groupKey])) {
const hasProp = event[groupKey].hasOwnProperty(propName)
if (common.isEmpty(val) && rule._default) {
try {
const defaultVal = await rule._default(propName, event)
event[groupKey][propName] = defaultVal
} catch (error) {
errorMsg = error
break
}
} else if (hasProp && !common.isEmpty(val) && typeof rule._convert === 'function') {
event[groupKey][propName] = rule._convert(val)
}
}
}
break
}
}
}
}
//propType and validate
async _validation (event, props) {
const errorMsg = this._validError(event, props)
if (errorMsg) {

@@ -165,3 +199,174 @@ return Promise.reject({

}
/**
* All request data is validated using PropTypeRule and ValidateRule and an error message is returned.
* @param {Object} event
* @param {Object} ruleGroup
* @returns {String}
*/
_validError (event, ruleGroup) {
let error = ''
if (common.isObject(event) && common.isObject(ruleGroup)) {
//propType check
for (const propName in ruleGroup) {
error = this._validProps(propName, ruleGroup, event, event, [propName])
if (error) break
}
if (!error) {
//validate check
for (const propName in ruleGroup) {
error = this._validValidates(propName, ruleGroup, event, event, [propName])
if (error) break
}
}
}
return error
}
/**
* recursive call function
* @param {String} propName
* @param {Object} rules
* @param {*} sibling
* @param {Object} event
* @param {Array} pathPropNames
* @returns {String}
*/
_validProps (propName, rules, sibling, event, pathPropNames = []) {
let error = ''
const propTypeRule = this._getRule(rules[propName])
if (propTypeRule) {
error = propTypeRule._validPropRules(propName, sibling, event, {
...this._getEtcOption(),
pathPropNames
})
//When a rule item exists inside propTypeRule
if (!error && propTypeRule._props.item) {
const value = propTypeRule._toValue(propName, sibling)
const item = propTypeRule._getItem(propName, sibling, event)
if (item && !common.isEmpty(value)) {
if (common.isObject(item)) {
for (const key in item) {
error = this._validProps(key, item, value, event, [...pathPropNames, key])
if (error) break
}
} else if (Array.isArray(item)) {
const arryItems = this._makeArrayItems(value, item)
//array all value
for (const i in value) {
error = this._validProps(i, arryItems, value, event, [...pathPropNames, i])
if (error) break
}
}
}
}
}
return error
}
/**
* recursive call function
*/
_validValidates (propName, rules, sibling, event, pathPropNames = []) {
let error = ''
const propTypeRule = this._getRule(rules[propName])
if (propTypeRule) {
error = propTypeRule._validValidateRules(propName, sibling, event, {
...this._getEtcOption(),
pathPropNames
})
//When a rule item exists inside validateRule
if (!error && propTypeRule._props.item) {
const value = propTypeRule._toValue(propName, sibling)
const item = propTypeRule._getItem(propName, sibling, event)
if (item && !common.isEmpty(value)) {
if (common.isObject(item)) {
for (const key in item) {
error = this._validValidates(key, item, value, event, [...pathPropNames, key])
if (error) break
}
} else if (Array.isArray(item)) {
const arryItems = this._makeArrayItems(value, item)
//array all value
for (const i in value) {
error = this._validValidates(i, arryItems, value, event, [...pathPropNames, i])
if (error) break
}
}
}
}
}
return error
}
_getRule (propTypeRule) {
let result = null
if (propTypeRule) {
if (propTypeRule._isRule) {
result = propTypeRule
} else if (common.isObject(propTypeRule) || Array.isArray(propTypeRule)) {
//Object and Array create default PropTypes
//"required" must be present to maintain compatibility with versions prior to v1.0
const rule = Array.isArray(propTypeRule) ? PropTypes.array.required() : PropTypes.object.required()
if (!common.isEmpty(propTypeRule)) {
rule.item(propTypeRule)
}
result = rule
}
}
return result
}
_makeArrayItems (values, items) {
const result = []
const itemLength = items.length
for (const i in values) {
result.push(items[i % itemLength])
}
return result
}
_getEtcOption () {
let isTrim = !!_globalOptions.trim
let pathPropNameType = _globalOptions.pathPropNameType
let ignoreFirstPathPropNames = _globalOptions.ignoreFirstPathPropNames || []
if (!common.isEmpty(this._options.trim)) {
isTrim = !!this._options.trim
}
if (!common.isEmpty(this._options.pathPropNameType)) {
pathPropNameType = this._options.pathPropNameType
}
if (Array.isArray(this._options.ignoreFirstPathPropNames)) {
ignoreFirstPathPropNames = this._options.ignoreFirstPathPropNames
}
return {
isTrim,
pathPropNameType,
ignoreFirstPathPropNames
}
}
_parseEvent (event = {}) {

@@ -176,4 +381,4 @@ /**

this._options.bodyParser(event)
} else if (typeof globalOptions.bodyParser === 'function') {
globalOptions.bodyParser(event)
} else if (typeof _globalOptions.bodyParser === 'function') {
_globalOptions.bodyParser(event)
} else {

@@ -198,40 +403,2 @@ common.bodyParser(event)

}
// @returns {Object} { rule, options }
_getPropTypeRule (propTypeRuleData) {
const result = {
rule: {},
options: {}
}
if (propTypeRuleData && common.isObject(propTypeRuleData)) {
if (propTypeRuleData._isRule) {
result.rule = propTypeRuleData
} else {
for (const key in propTypeRuleData) {
if (key === 'propType') {
if (propTypeRuleData.propType._isRule) {
result.rule = propTypeRuleData.propType
}
} else {
result.options[key] = propTypeRuleData[key]
}
}
}
}
return result
}
_trim (val, propTypeRuleOptions = {}) {
let isTrim = !!globalOptions.trim
if (!common.isEmpty(propTypeRuleOptions.trim)) {
isTrim = propTypeRuleOptions.trim
} else if (!common.isEmpty(this._options.trim)) {
isTrim = this._options.trim
}
return isTrim ? val.trim() : val
}
}

@@ -238,0 +405,0 @@

@@ -0,3 +1,5 @@

const ValidateRule = require('./ValidateRule')
const common = require('./common')
/**

@@ -8,3 +10,3 @@ * PropTypes

* PropTypes.string.default('200')
* PropTypes.string.isRequired
* PropTypes.string.required()
*/

@@ -14,9 +16,42 @@ const PropTypes = {

/** ========== Public Methods ========== */
//Array
get array () {
return PropTypes.makeRule({
propType: 'array',
validType: (value, isDefaultValue) => {
return Array.isArray(value)
},
validRequired: (value) => {
return !common.isEmpty(value)
}
})
},
//Object
get object () {
return PropTypes.makeRule({
propType: 'object',
validType: (value, isDefaultValue) => {
return common.isObject(value)
},
validRequired: (value) => {
return !common.isEmpty(value)
}
})
},
/**
* add propType rules
* @param {Object} obj
*/
addRules (obj) {
// Object.setPrototypeOf(PropTypes, obj)
for (const key in obj) {
if (!['addRules', 'makeRule'].includes(key)) {
PropTypes[key] = obj[key]
PropTypes[key]._type = key
if (/^_/.test(key) || common.RESERVED_PROPS.includes(key)) {
common.error(`'${key}' is a reserved word and cannot be added to the rule.`)
} else if (Object.hasOwn(ValidateRule.prototype, key)) {
common.error(`'${key}' rule cannot be added because it overlaps with the validate rule.`)
} else {
const prop = Object.getOwnPropertyDescriptor(obj, key)
Object.defineProperty(PropTypes, key, prop)
}

@@ -26,86 +61,20 @@ }

makeRule ({ validType, validRequired, convert } = {}) {
const invalid = (propName, value, isDefaultValue) => {
let isEmpty = false
/**
* Make propType rule
* @param {Object} rule
* - {Function} validType
* - {Function} validRequired
* - {Function} convert
* @returns {Object}
*/
makeRule ({ propType, validType, validRequired, convert } = {}) {
return new ValidateRule({ propType, validType, validRequired, convert })
},
if (isDefaultValue) {
isEmpty = !(['boolean', 'number', 'string', 'undefined'].includes(typeof value) || value)
} else {
isEmpty = common.isEmpty(value)
}
if (typeof validType === 'function' && !isEmpty && !validType(value, isDefaultValue)) {
return `invalid parameter type '${propName}'`
}
}
const Rule = {
_invalid: invalid,
_convert: convert,
get isRequired () {
return {
_invalid: (propName, value) => {
if (typeof validType === 'function' && typeof validRequired === 'function') {
if (common.isEmpty(value)) {
return `required parameter '${propName}'`
} else if (!validType(value)) {
return `invalid parameter type '${propName}'`
} else if (!validRequired(value)) {
return `required parameter '${propName}'`
}
}
},
_convert: convert,
_required: true,
_type: this._type,
_isRule: true
}
},
/**
* Set the value that is replaced when the request value is empty
* @param {*} val
*/
default: (val) => {
const defaultVal = val
return {
_invalid: invalid,
_convert: convert,
_default: async (propName, event) => {
let value
//get default value
if (typeof defaultVal === 'function') {
try {
value = defaultVal(event)
} catch (error) {
const errMsg = `'${propName}' default function execution error`
common.error(`${errMsg}:`, error)
return Promise.reject(errMsg)
}
} else {
value = defaultVal
}
//valid type
if (invalid(propName, value, true)) {
const invalidMsg = `'${propName}' default value type error`
common.error(`${invalidMsg}, -value:`, value, ' -type:', typeof value)
return Promise.reject(invalidMsg)
} else {
return common.clone(value)
}
},
_type: Rule._type,
_isRule: true
}
},
_type: this._type,
_isRule: true
}
return Rule
/**
* @param {*} val
* @returns {Boolean}
*/
isEmpty (val) {
return common.isEmpty(val)
}

@@ -112,0 +81,0 @@ }

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc