kayvee 3.17.0 → 3.18.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/.eslintrc.js +124 -0
- package/.github/workflows/notify-ci-status.yml +20 -0
- package/.nvmrc +1 -1
- package/Makefile +0 -1
- package/build/lib/kayvee.js +13 -17
- package/build/lib/logger/logger.js +84 -76
- package/build/lib/middleware.js +56 -84
- package/build/lib/router/index.js +61 -63
- package/build/package.json +10 -6
- package/build/test/context_logger.js +36 -44
- package/build/test/kayvee.js +16 -16
- package/build/test/logger_test.js +112 -101
- package/build/test/middleware.js +81 -82
- package/build/test/router.js +232 -92
- package/lib/logger/logger.ts +17 -1
- package/lib/middleware.ts +1 -1
- package/package.json +10 -6
- package/test/middleware.ts +1 -1
- package/tsconfig.json +1 -1
- package/.eslintrc.yml +0 -44
- package/tslint.json +0 -134
package/.eslintrc.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
env: {
|
|
3
|
+
browser: true,
|
|
4
|
+
jasmine: true,
|
|
5
|
+
jest: true,
|
|
6
|
+
mocha: true,
|
|
7
|
+
node: true,
|
|
8
|
+
},
|
|
9
|
+
extends: [
|
|
10
|
+
// https://github.com/eslint/eslint
|
|
11
|
+
"eslint:recommended",
|
|
12
|
+
// https://github.com/yannickcr/eslint-plugin-react
|
|
13
|
+
"plugin:react/recommended",
|
|
14
|
+
// https://github.com/facebook/react/tree/master/packages/eslint-plugin-react-hooks
|
|
15
|
+
"plugin:react-hooks/recommended",
|
|
16
|
+
// https: //github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin
|
|
17
|
+
"plugin:@typescript-eslint/eslint-recommended",
|
|
18
|
+
"plugin:@typescript-eslint/recommended",
|
|
19
|
+
// https://github.com/prettier/eslint-config-prettier
|
|
20
|
+
"prettier",
|
|
21
|
+
"prettier/@typescript-eslint",
|
|
22
|
+
],
|
|
23
|
+
parser: "@typescript-eslint/parser",
|
|
24
|
+
parserOptions: {
|
|
25
|
+
ecmaFeatures: {
|
|
26
|
+
jsx: true,
|
|
27
|
+
},
|
|
28
|
+
ecmaVersion: 13,
|
|
29
|
+
sourceType: "module",
|
|
30
|
+
project: "./tsconfig.json",
|
|
31
|
+
},
|
|
32
|
+
plugins: ["react", "react-hooks", "@typescript-eslint"],
|
|
33
|
+
rules: {
|
|
34
|
+
camelcase: "off",
|
|
35
|
+
"comma-dangle": ["error", "always-multiline"],
|
|
36
|
+
eqeqeq: ["error", "smart"],
|
|
37
|
+
"global-require": "off",
|
|
38
|
+
"import/no-unresolved": "off",
|
|
39
|
+
indent: "off",
|
|
40
|
+
"key-spacing": ["error", { mode: "minimum" }],
|
|
41
|
+
"max-len": [
|
|
42
|
+
"error",
|
|
43
|
+
{
|
|
44
|
+
code: 115,
|
|
45
|
+
ignorePattern: "^import",
|
|
46
|
+
ignoreRegExpLiterals: true,
|
|
47
|
+
ignoreStrings: true,
|
|
48
|
+
ignoreTemplateLiterals: true,
|
|
49
|
+
ignoreUrls: true,
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
"new-cap": [
|
|
53
|
+
"error",
|
|
54
|
+
{
|
|
55
|
+
capIsNewExceptions: ["immutable.OrderedMap", "OrderedMap"],
|
|
56
|
+
newIsCapExceptions: ["kayvee.logger"],
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
"newline-per-chained-call": "off",
|
|
60
|
+
"no-console": "off",
|
|
61
|
+
"no-multi-spaces": [
|
|
62
|
+
"error",
|
|
63
|
+
{
|
|
64
|
+
exceptions: {
|
|
65
|
+
ImportDeclaration: true,
|
|
66
|
+
VariableDeclarator: true,
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
"no-param-reassign": [
|
|
71
|
+
"error",
|
|
72
|
+
{
|
|
73
|
+
props: false,
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
"no-restricted-syntax": [
|
|
77
|
+
"error",
|
|
78
|
+
{
|
|
79
|
+
message: "Prefer useTypedDispatch to get a typed version of dispatch.",
|
|
80
|
+
selector: "CallExpression[callee.name='useDispatch']",
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
"no-underscore-dangle": "off",
|
|
84
|
+
"no-unused-vars": "off",
|
|
85
|
+
"no-var": "off",
|
|
86
|
+
quotes: ["error", "double", "avoid-escape"],
|
|
87
|
+
"react/display-name": "off",
|
|
88
|
+
"react/jsx-indent": "off",
|
|
89
|
+
"react/no-did-update-set-state": "off",
|
|
90
|
+
"react/prop-types": "off",
|
|
91
|
+
"react/sort-comp": "off",
|
|
92
|
+
"vars-on-top": "off",
|
|
93
|
+
"@typescript-eslint/ban-ts-comment": [
|
|
94
|
+
"error",
|
|
95
|
+
{
|
|
96
|
+
"ts-ignore": false,
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
"@typescript-eslint/ban-types": "off",
|
|
100
|
+
"@typescript-eslint/explicit-function-return-type": "off",
|
|
101
|
+
"@typescript-eslint/no-empty-function": ["error", { allow: ["arrowFunctions"] }],
|
|
102
|
+
"@typescript-eslint/no-empty-interface": "off",
|
|
103
|
+
"@typescript-eslint/no-explicit-any": "off",
|
|
104
|
+
"@typescript-eslint/no-floating-promises": "off",
|
|
105
|
+
"@typescript-eslint/no-non-null-assertion": "off",
|
|
106
|
+
"@typescript-eslint/no-unused-vars": ["error", { args: "none" }],
|
|
107
|
+
"@typescript-eslint/no-use-before-define": "off",
|
|
108
|
+
"@typescript-eslint/no-var-requires": "off",
|
|
109
|
+
},
|
|
110
|
+
overrides: [
|
|
111
|
+
{
|
|
112
|
+
files: ["*.ts", "*.tsx"],
|
|
113
|
+
rules: {
|
|
114
|
+
"no-undef": "off",
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
settings: {
|
|
119
|
+
"import/resolver": "webpack",
|
|
120
|
+
react: {
|
|
121
|
+
version: "detect",
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
name: Notify CI status
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
check_suite:
|
|
5
|
+
types: [completed]
|
|
6
|
+
status:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
call-workflow:
|
|
10
|
+
if: >-
|
|
11
|
+
(github.event.branches[0].name == github.event.repository.default_branch &&
|
|
12
|
+
(github.event.state == 'error' || github.event.state == 'failure')) ||
|
|
13
|
+
(github.event.check_suite.head_branch == github.event.repository.default_branch &&
|
|
14
|
+
github.event.check_suite.conclusion != 'success')
|
|
15
|
+
uses: Clever/ci-scripts/.github/workflows/reusable-notify-ci-status.yml@master
|
|
16
|
+
secrets:
|
|
17
|
+
CIRCLE_CI_INTEGRATIONS_URL: ${{ secrets.CIRCLE_CI_INTEGRATIONS_URL }}
|
|
18
|
+
CIRCLE_CI_INTEGRATIONS_USERNAME: ${{ secrets.CIRCLE_CI_INTEGRATIONS_USERNAME }}
|
|
19
|
+
CIRCLE_CI_INTEGRATIONS_PASSWORD: ${{ secrets.CIRCLE_CI_INTEGRATIONS_PASSWORD }}
|
|
20
|
+
SLACK_BOT_TOKEN: ${{ secrets.DAPPLE_BOT_TOKEN }}
|
package/.nvmrc
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
14
|
package/Makefile
CHANGED
|
@@ -49,7 +49,6 @@ format-check:
|
|
|
49
49
|
(echo -e "❌ \033[0;31m Prettier found discrepancies in the above files. Run 'make format' to fix.\033[0m" && false)
|
|
50
50
|
|
|
51
51
|
lint: format-check
|
|
52
|
-
./node_modules/.bin/tslint $(TS_FILES)
|
|
53
52
|
./node_modules/.bin/eslint $(TS_FILES)
|
|
54
53
|
|
|
55
54
|
test/tests.json:
|
package/build/lib/kayvee.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
var _ = require("underscore");
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
const deploy_env = process.env._DEPLOY_ENV;
|
|
3
|
+
const workflow_id = process.env._EXECUTION_NAME;
|
|
4
|
+
const pod_id = process.env._POD_ID;
|
|
5
|
+
const pod_shortname = process.env._POD_SHORTNAME;
|
|
6
|
+
const pod_region = process.env._POD_REGION;
|
|
7
|
+
const pod_account = process.env._POD_ACCOUNT;
|
|
8
8
|
// Encode errors to strings instead of toJSON()
|
|
9
9
|
function replaceErrors(key, value) {
|
|
10
10
|
if (value instanceof Error) {
|
|
@@ -15,7 +15,7 @@ function replaceErrors(key, value) {
|
|
|
15
15
|
// Converts a map to a string space-delimited key=val pairs
|
|
16
16
|
function format(data) {
|
|
17
17
|
if (deploy_env || workflow_id || pod_id || pod_shortname || pod_account || pod_region) {
|
|
18
|
-
|
|
18
|
+
const extras = {};
|
|
19
19
|
if (deploy_env) {
|
|
20
20
|
extras.deploy_env = deploy_env;
|
|
21
21
|
}
|
|
@@ -39,24 +39,20 @@ function format(data) {
|
|
|
39
39
|
return JSON.stringify(data, replaceErrors);
|
|
40
40
|
}
|
|
41
41
|
// Similar to format, but takes additional reserved params to promote logging best-practices
|
|
42
|
-
function formatLog(source, level, title, data) {
|
|
43
|
-
|
|
44
|
-
if (level === void 0) { level = ""; }
|
|
45
|
-
if (title === void 0) { title = ""; }
|
|
46
|
-
if (data === void 0) { data = {}; }
|
|
47
|
-
var info = data;
|
|
42
|
+
function formatLog(source = "", level = "", title = "", data = {}) {
|
|
43
|
+
let info = data;
|
|
48
44
|
if (!_.isObject(data)) {
|
|
49
45
|
info = {};
|
|
50
46
|
}
|
|
51
|
-
|
|
47
|
+
const reserved = { source, level, title };
|
|
52
48
|
// reserved keys overwrite other keys in data
|
|
53
49
|
return format(_.extend({}, info, reserved));
|
|
54
50
|
}
|
|
55
51
|
module.exports = {
|
|
56
|
-
format
|
|
57
|
-
formatLog
|
|
52
|
+
format,
|
|
53
|
+
formatLog,
|
|
58
54
|
};
|
|
59
|
-
|
|
55
|
+
const LOG_LEVELS = {
|
|
60
56
|
UNKNOWN: "unknown",
|
|
61
57
|
CRITICAL: "critical",
|
|
62
58
|
ERROR: "error",
|
|
@@ -17,8 +17,8 @@ var LOG_LEVEL_ENUM = {
|
|
|
17
17
|
error: 4,
|
|
18
18
|
critical: 5,
|
|
19
19
|
};
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
const assign = Object.assign || _.assign; // Use the faster Object.assign if possible
|
|
21
|
+
let globalRouter;
|
|
22
22
|
function setGlobalRouting(filename) {
|
|
23
23
|
globalRouter = new router.Router();
|
|
24
24
|
globalRouter.loadConfig(filename);
|
|
@@ -27,21 +27,20 @@ function getGlobalRouter() {
|
|
|
27
27
|
return globalRouter;
|
|
28
28
|
}
|
|
29
29
|
// This is a port from kayvee-go/logger/logger.go
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if (logLvl === void 0) { logLvl = process.env.KAYVEE_LOG_LEVEL; }
|
|
33
|
-
if (formatter === void 0) { formatter = kv.format; }
|
|
34
|
-
if (output === void 0) { output = console.error; }
|
|
30
|
+
class Logger {
|
|
31
|
+
constructor(source, logLvl = process.env.KAYVEE_LOG_LEVEL, formatter = kv.format, output = console.error) {
|
|
35
32
|
this.formatter = null;
|
|
36
33
|
this.logLvl = null;
|
|
37
34
|
this.globals = null;
|
|
38
35
|
this.logWriter = null;
|
|
39
36
|
this.logRouter = null;
|
|
37
|
+
this.asyncLocalStorage = null;
|
|
40
38
|
this.formatter = formatter;
|
|
41
39
|
this.logLvl = this._validateLogLvl(logLvl);
|
|
42
40
|
this.globals = {};
|
|
43
41
|
this.globals.source = source;
|
|
44
42
|
this.logWriter = output;
|
|
43
|
+
this.asyncLocalStorage = null;
|
|
45
44
|
if (process.env._TEAM_OWNER) {
|
|
46
45
|
this.globals.team = process.env._TEAM_OWNER;
|
|
47
46
|
}
|
|
@@ -64,17 +63,20 @@ var Logger = /** @class */ (function () {
|
|
|
64
63
|
this.globals["pod-account"] = process.env._POD_ACCOUNT;
|
|
65
64
|
}
|
|
66
65
|
}
|
|
67
|
-
|
|
66
|
+
setAsyncLocalStorage(asyncLocalStorage) {
|
|
67
|
+
this.asyncLocalStorage = asyncLocalStorage;
|
|
68
|
+
}
|
|
69
|
+
setRouter(r) {
|
|
68
70
|
this.logRouter = r;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
+
}
|
|
72
|
+
setConfig(source, logLvl, formatter, output) {
|
|
71
73
|
this.globals.source = source;
|
|
72
74
|
this.logLvl = this._validateLogLvl(logLvl);
|
|
73
75
|
this.formatter = formatter;
|
|
74
76
|
this.logWriter = output;
|
|
75
77
|
return this.logWriter;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
+
}
|
|
79
|
+
_validateLogLvl(logLvl) {
|
|
78
80
|
if (logLvl == null) {
|
|
79
81
|
return LEVELS.Debug;
|
|
80
82
|
}
|
|
@@ -87,92 +89,99 @@ var Logger = /** @class */ (function () {
|
|
|
87
89
|
}
|
|
88
90
|
}
|
|
89
91
|
return LEVELS.Debug;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
+
}
|
|
93
|
+
setLogLevel(logLvl) {
|
|
92
94
|
this.logLvl = this._validateLogLvl(logLvl);
|
|
93
95
|
return this.logLvl;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
+
}
|
|
97
|
+
setFormatter(formatter) {
|
|
96
98
|
this.formatter = formatter;
|
|
97
99
|
return this.formatter;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
+
}
|
|
101
|
+
setOutput(output) {
|
|
100
102
|
this.logWriter = output;
|
|
101
103
|
return this.logWriter;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
+
}
|
|
105
|
+
trace(title) {
|
|
104
106
|
this.traceD(title, {});
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
+
}
|
|
108
|
+
debug(title) {
|
|
107
109
|
this.debugD(title, {});
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
+
}
|
|
111
|
+
info(title) {
|
|
110
112
|
this.infoD(title, {});
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
+
}
|
|
114
|
+
warn(title) {
|
|
113
115
|
this.warnD(title, {});
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
+
}
|
|
117
|
+
error(title) {
|
|
116
118
|
this.errorD(title, {});
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
+
}
|
|
120
|
+
critical(title) {
|
|
119
121
|
this.criticalD(title, {});
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
+
}
|
|
123
|
+
counter(title) {
|
|
122
124
|
this.counterD(title, 1, {});
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
+
}
|
|
126
|
+
gauge(title, value) {
|
|
125
127
|
this.gaugeD(title, value, {});
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
+
}
|
|
129
|
+
traceD(title, data) {
|
|
128
130
|
this._logWithLevel(LEVELS.Trace, {
|
|
129
|
-
title
|
|
131
|
+
title,
|
|
130
132
|
}, data);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
+
}
|
|
134
|
+
debugD(title, data) {
|
|
133
135
|
this._logWithLevel(LEVELS.Debug, {
|
|
134
|
-
title
|
|
136
|
+
title,
|
|
135
137
|
}, data);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
+
}
|
|
139
|
+
infoD(title, data) {
|
|
138
140
|
this._logWithLevel(LEVELS.Info, {
|
|
139
|
-
title
|
|
141
|
+
title,
|
|
140
142
|
}, data);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
+
}
|
|
144
|
+
warnD(title, data) {
|
|
143
145
|
this._logWithLevel(LEVELS.Warning, {
|
|
144
|
-
title
|
|
146
|
+
title,
|
|
145
147
|
}, data);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
+
}
|
|
149
|
+
errorD(title, data) {
|
|
148
150
|
this._logWithLevel(LEVELS.Error, {
|
|
149
|
-
title
|
|
151
|
+
title,
|
|
150
152
|
}, data);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
+
}
|
|
154
|
+
criticalD(title, data) {
|
|
153
155
|
this._logWithLevel(LEVELS.Critical, {
|
|
154
|
-
title
|
|
156
|
+
title,
|
|
155
157
|
}, data);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
+
}
|
|
159
|
+
counterD(title, value, data) {
|
|
158
160
|
this._logWithLevel(LEVELS.Info, {
|
|
159
|
-
title
|
|
160
|
-
value
|
|
161
|
+
title,
|
|
162
|
+
value,
|
|
161
163
|
type: "counter",
|
|
162
164
|
}, data);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
+
}
|
|
166
|
+
gaugeD(title, value, data) {
|
|
165
167
|
this._logWithLevel(LEVELS.Info, {
|
|
166
|
-
title
|
|
167
|
-
value
|
|
168
|
+
title,
|
|
169
|
+
value,
|
|
168
170
|
type: "gauge",
|
|
169
171
|
}, data);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
+
}
|
|
173
|
+
_logWithLevel(logLvl, metadata, userdata) {
|
|
172
174
|
if (LOG_LEVEL_ENUM[logLvl] < LOG_LEVEL_ENUM[this.logLvl]) {
|
|
173
175
|
return;
|
|
174
176
|
}
|
|
175
|
-
|
|
177
|
+
// I'm not clever enough to want to do these in one line without extra vars.
|
|
178
|
+
// We're on a REALLY old version of TS compiling to ES5. So I don't get a lot of the fancy tools
|
|
179
|
+
// like ?. and ??.
|
|
180
|
+
const store = this.asyncLocalStorage && this.asyncLocalStorage.getStore();
|
|
181
|
+
const storeData = store || { get: () => ({}) };
|
|
182
|
+
const contextData = storeData.get("context") ? storeData.get("context") : {};
|
|
183
|
+
const plainContextData = contextData instanceof Map ? Object.fromEntries(contextData) : contextData;
|
|
184
|
+
var data = assign({ level: logLvl }, this.globals, metadata, plainContextData, userdata);
|
|
176
185
|
if (this.logRouter) {
|
|
177
186
|
data._kvmeta = this.logRouter.route(data);
|
|
178
187
|
}
|
|
@@ -180,27 +189,26 @@ var Logger = /** @class */ (function () {
|
|
|
180
189
|
data._kvmeta = globalRouter.route(data);
|
|
181
190
|
}
|
|
182
191
|
this.logWriter(this.formatter(data));
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
}());
|
|
192
|
+
}
|
|
193
|
+
}
|
|
186
194
|
module.exports = Logger;
|
|
187
195
|
module.exports.setGlobalRouting = setGlobalRouting;
|
|
188
196
|
module.exports.getGlobalRouter = getGlobalRouter;
|
|
189
|
-
module.exports.mockRouting =
|
|
190
|
-
|
|
197
|
+
module.exports.mockRouting = (cb) => {
|
|
198
|
+
const _logWithLevel = Logger.prototype._logWithLevel;
|
|
191
199
|
if (_logWithLevel.isMocked) {
|
|
192
200
|
throw Error("Nested kv.mockRouting calls are not supported");
|
|
193
201
|
}
|
|
194
|
-
|
|
202
|
+
const ruleMatches = {};
|
|
195
203
|
Logger.prototype._logWithLevel = function (logLvl, metadata, userdata) {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
this.formatter =
|
|
199
|
-
this.logWriter =
|
|
204
|
+
const formatter = this.formatter;
|
|
205
|
+
const logWriter = this.logWriter;
|
|
206
|
+
this.formatter = (msg) => msg;
|
|
207
|
+
this.logWriter = (msg) => {
|
|
200
208
|
if (!msg._kvmeta) {
|
|
201
209
|
return;
|
|
202
210
|
}
|
|
203
|
-
msg._kvmeta.routes.forEach(
|
|
211
|
+
msg._kvmeta.routes.forEach((route) => {
|
|
204
212
|
ruleMatches[route.rule] = (ruleMatches[route.rule] || []).concat(route);
|
|
205
213
|
});
|
|
206
214
|
};
|
|
@@ -208,9 +216,9 @@ module.exports.mockRouting = function (cb) {
|
|
|
208
216
|
this.formatter = formatter;
|
|
209
217
|
this.logWriter = logWriter;
|
|
210
218
|
};
|
|
211
|
-
|
|
219
|
+
const stfuTypeScript = Logger.prototype._logWithLevel;
|
|
212
220
|
stfuTypeScript.isMocked = true;
|
|
213
|
-
|
|
221
|
+
const done = () => {
|
|
214
222
|
Logger.prototype._logWithLevel = _logWithLevel;
|
|
215
223
|
return ruleMatches;
|
|
216
224
|
};
|