diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 00000000..339bc5af --- /dev/null +++ b/.eslintrc @@ -0,0 +1,30 @@ +{ + "parser": "@typescript-eslint/parser", + "plugins": [ + "@typescript-eslint" + ], + + "rules": { + "@typescript-eslint/return-await": "off", + "@typescript-eslint/no-this-alias": "off", + "@typescript-eslint/no-var-requires": "off", + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/restrict-template-expressions": "off", + "@typescript-eslint/no-misused-promises": "off", + "@typescript-eslint/strict-boolean-expressions": "off", + "@typescript-eslint/consistent-type-assertions": "off", + "@typescript-eslint/no-extraneous-class": "off", + "@typescript-eslint/no-dynamic-delete": "off", + "no-this-assignment": "off", + "no-async-promise-executor": "off", + "valid-typeof": "off" + }, + + "extends": [ + "standard-with-typescript" + ], + + "parserOptions": { + "project": "./tsconfig.json" + } +} diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 9d42242d..00000000 --- a/.eslintrc.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - extends: 'standard-with-typescript', - parserOptions: { - project: './tsconfig.json' - } -} diff --git a/.gitignore b/.gitignore index 83b95961..100c36b0 100644 --- a/.gitignore +++ b/.gitignore @@ -82,3 +82,6 @@ data/session/certs/client/client.crt data/session/certs/client/client.key data/session/certs/server/server.crt data/session/certs/server/server.key +/jsfix.test_http-client.txt +/jsfix.test_http-server.txt +/app.log.txt diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 1beadde0..184a8cc3 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -1,6 +1,12 @@ \ No newline at end of file diff --git a/data/examples/FIX.4.4/quickfix/heartbeat/fix.txt b/data/examples/FIX.4.4/quickfix/heartbeat/fix.txt new file mode 100644 index 00000000..33704de9 --- /dev/null +++ b/data/examples/FIX.4.4/quickfix/heartbeat/fix.txt @@ -0,0 +1 @@ +8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=2|57=fix|52=20230101-14:14:20.930|112=Sun, 01 Jan 2023 14:14:20 GMT|10=029| \ No newline at end of file diff --git a/data/examples/FIX.4.4/quickfix/heartbeat/object.json b/data/examples/FIX.4.4/quickfix/heartbeat/object.json new file mode 100644 index 00000000..9606edf0 --- /dev/null +++ b/data/examples/FIX.4.4/quickfix/heartbeat/object.json @@ -0,0 +1,16 @@ +{ + "StandardHeader": { + "BeginString": "FIX.4.4", + "BodyLength": 104, + "MsgType": "0", + "SenderCompID": "init-comp", + "TargetCompID": "accept-comp", + "MsgSeqNum": 2, + "TargetSubID": "fix", + "SendingTime": "2023-01-01T14:14:20.930Z" + }, + "TestReqID": "Sun, 01 Jan 2023 14:14:20 GMT", + "StandardTrailer": { + "CheckSum": "029" + } +} \ No newline at end of file diff --git a/data/examples/FIX.4.4/quickfix/heartbeat/tokens.txt b/data/examples/FIX.4.4/quickfix/heartbeat/tokens.txt new file mode 100644 index 00000000..314d1208 --- /dev/null +++ b/data/examples/FIX.4.4/quickfix/heartbeat/tokens.txt @@ -0,0 +1,5 @@ +[0] 8 (BeginString) = FIX.4.4, [1] 9 (BodyLength) = 0000104 +[2] 35 (MsgType) = 0[Heartbeat], [3] 49 (SenderCompID) = init-comp +[4] 56 (TargetCompID) = accept-comp, [5] 34 (MsgSeqNum) = 2 +[6] 57 (TargetSubID) = fix, [7] 52 (SendingTime) = 20230101-14:14:20.930 +[8] 112 (TestReqID) = Sun, 01 Jan 2023 14:14:20 GMT, [9] 10 (CheckSum) = 029 \ No newline at end of file diff --git a/data/session/test-http-initiator.json b/data/session/test-http-initiator.json index bb9e492f..a92ff290 100644 --- a/data/session/test-http-initiator.json +++ b/data/session/test-http-initiator.json @@ -8,7 +8,7 @@ "name": "UserReq", "value": { "method": "POST", - "uri": "http://localhost:2434/authorise", + "url": "http://localhost:2434/authorise", "json":true, "resolveWithFullResponse": true, "headers": { @@ -21,7 +21,7 @@ "name": "default", "value": { "method": "GET", - "uri": "http://localhost:2434/query", + "url": "http://localhost:2434/query", "json": true, "resolveWithFullResponse": true, "headers": { diff --git a/package-lock.json b/package-lock.json index d7d8baa7..6dd618f6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,24 +1,23 @@ { "name": "jspurefix", - "version": "2.2.1", + "version": "3.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "jspurefix", - "version": "2.2.1", + "version": "3.0.0", "license": "MIT", "dependencies": { "align-text": "^1.0.2", + "axios": "^1.2.2", "express": "^4.18.2", "lodash": "^4.17.21", - "mathjs": "^11.3.0", + "mathjs": "^11.5.0", "minimist": "^1.2.7", "moment": "^2.29.4", "node-fs-extra": "^0.8.2", "reflect-metadata": "^0.1.13", - "request": "^2.88.2", - "request-promise-native": "^1.0.9", "sax": "^1.2.4", "tsyringe": "^4.7.0", "uuid": "^9.0.0", @@ -27,24 +26,28 @@ "yauzl": "^2.10.0" }, "devDependencies": { - "@types/express": "^4.17.14", - "@types/express-serve-static-core": "^4.17.31", - "@types/jest": "^29.1.2", - "@types/lodash": "^4.14.186", + "@types/express": "^4.17.15", + "@types/express-serve-static-core": "^4.17.32", + "@types/jest": "^29.2.5", + "@types/lodash": "^4.14.191", "@types/mathjs": "^9.4.1", "@types/minimist": "^1.2.2", - "@types/node": "^18.11.0", + "@types/node": "^18.11.18", "@types/request-promise-native": "^1.0.18", "@types/sax": "^1.2.4", - "@types/uuid": "^8.3.4", + "@types/uuid": "^9.0.0", "@types/winston": "^2.4.4", - "jest": "^29.2.0", + "@typescript-eslint/eslint-plugin-tslint": "^5.47.1", + "eslint": "^8.31.0", + "eslint-config-standard-with-typescript": "^24.0.0", + "eslint-plugin-jquery": "^1.5.1", + "eslint-plugin-jsdoc": "^39.6.4", + "eslint-plugin-node": "^11.1.0", + "jest": "^29.3.1", "madge": "^5.0.1", "standard": "^17.0.0", "ts-jest": "^29.0.3", - "tslint": "^6.1.3", - "tslint-config-standard": "^9.0.0", - "typescript": "^4.8.4" + "typescript": "^4.9.4" } }, "node_modules/@ampproject/remapping": { @@ -503,12 +506,12 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz", - "integrity": "sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", + "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.19.0" }, "engines": { "node": ">=6.9.0" @@ -518,11 +521,11 @@ } }, "node_modules/@babel/runtime": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.4.tgz", - "integrity": "sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.7.tgz", + "integrity": "sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==", "dependencies": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.13.11" }, "engines": { "node": ">=6.9.0" @@ -601,16 +604,30 @@ "kuler": "^2.0.0" } }, + "node_modules/@es-joy/jsdoccomment": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.36.1.tgz", + "integrity": "sha512-922xqFsTpHs6D0BUiG4toiyPOMc8/jafnWKxz1KWgS4XzKPy2qXf1Pe6UFuNSCQqt6tOuhAWXBNuuyUhJmw9Vg==", + "dev": true, + "dependencies": { + "comment-parser": "1.3.1", + "esquery": "^1.4.0", + "jsdoc-type-pratt-parser": "~3.1.0" + }, + "engines": { + "node": "^14 || ^16 || ^17 || ^18 || ^19" + } + }, "node_modules/@eslint/eslintrc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", - "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", + "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.3.2", - "globals": "^13.15.0", + "espree": "^9.4.0", + "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -619,6 +636,9 @@ }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/@eslint/eslintrc/node_modules/argparse": { @@ -628,9 +648,9 @@ "dev": true }, "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", - "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", + "version": "13.19.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", + "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -667,19 +687,32 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", - "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", - "minimatch": "^3.0.4" + "minimatch": "^3.0.5" }, "engines": { "node": ">=10.10.0" } }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@humanwhocodes/object-schema": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", @@ -712,16 +745,16 @@ } }, "node_modules/@jest/console": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.2.0.tgz", - "integrity": "sha512-Xz1Wu+ZZxcB3RS8U3HdkFxlRJ7kLXI/by9X7d2/gvseIWPwYu/c1EsYy77cB5iyyHGOy3whS2HycjcuzIF4Jow==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.3.1.tgz", + "integrity": "sha512-IRE6GD47KwcqA09RIWrabKdHPiKDGgtAL31xDxbi/RjQMsr+lY+ppxmHwY0dUEV3qvvxZzoe5Hl0RXZJOjQNUg==", "dev": true, "dependencies": { - "@jest/types": "^29.2.0", + "@jest/types": "^29.3.1", "@types/node": "*", "chalk": "^4.0.0", - "jest-message-util": "^29.2.0", - "jest-util": "^29.2.0", + "jest-message-util": "^29.3.1", + "jest-util": "^29.3.1", "slash": "^3.0.0" }, "engines": { @@ -745,16 +778,16 @@ } }, "node_modules/@jest/core": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.2.0.tgz", - "integrity": "sha512-+gyJ3bX+kGEW/eqt/0kI7fLjqiFr3AN8O+rlEl1fYRf7D8h4Sj4tBGo9YOSirvWgvemoH2EPRya35bgvcPFzHQ==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.3.1.tgz", + "integrity": "sha512-0ohVjjRex985w5MmO5L3u5GR1O30DexhBSpuwx2P+9ftyqHdJXnk7IUWiP80oHMvt7ubHCJHxV0a0vlKVuZirw==", "dev": true, "dependencies": { - "@jest/console": "^29.2.0", - "@jest/reporters": "^29.2.0", - "@jest/test-result": "^29.2.0", - "@jest/transform": "^29.2.0", - "@jest/types": "^29.2.0", + "@jest/console": "^29.3.1", + "@jest/reporters": "^29.3.1", + "@jest/test-result": "^29.3.1", + "@jest/transform": "^29.3.1", + "@jest/types": "^29.3.1", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", @@ -762,20 +795,20 @@ "exit": "^0.1.2", "graceful-fs": "^4.2.9", "jest-changed-files": "^29.2.0", - "jest-config": "^29.2.0", - "jest-haste-map": "^29.2.0", - "jest-message-util": "^29.2.0", + "jest-config": "^29.3.1", + "jest-haste-map": "^29.3.1", + "jest-message-util": "^29.3.1", "jest-regex-util": "^29.2.0", - "jest-resolve": "^29.2.0", - "jest-resolve-dependencies": "^29.2.0", - "jest-runner": "^29.2.0", - "jest-runtime": "^29.2.0", - "jest-snapshot": "^29.2.0", - "jest-util": "^29.2.0", - "jest-validate": "^29.2.0", - "jest-watcher": "^29.2.0", + "jest-resolve": "^29.3.1", + "jest-resolve-dependencies": "^29.3.1", + "jest-runner": "^29.3.1", + "jest-runtime": "^29.3.1", + "jest-snapshot": "^29.3.1", + "jest-util": "^29.3.1", + "jest-validate": "^29.3.1", + "jest-watcher": "^29.3.1", "micromatch": "^4.0.4", - "pretty-format": "^29.2.0", + "pretty-format": "^29.3.1", "slash": "^3.0.0", "strip-ansi": "^6.0.0" }, @@ -808,37 +841,37 @@ } }, "node_modules/@jest/environment": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.2.0.tgz", - "integrity": "sha512-foaVv1QVPB31Mno3LlL58PxEQQOLZd9zQfCpyQQCQIpUAtdFP1INBjkphxrCfKT13VxpA0z5jFGIkmZk0DAg2Q==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.3.1.tgz", + "integrity": "sha512-pMmvfOPmoa1c1QpfFW0nXYtNLpofqo4BrCIk6f2kW4JFeNlHV2t3vd+3iDLf31e2ot2Mec0uqZfmI+U0K2CFag==", "dev": true, "dependencies": { - "@jest/fake-timers": "^29.2.0", - "@jest/types": "^29.2.0", + "@jest/fake-timers": "^29.3.1", + "@jest/types": "^29.3.1", "@types/node": "*", - "jest-mock": "^29.2.0" + "jest-mock": "^29.3.1" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.2.0.tgz", - "integrity": "sha512-+3lxcYL9e0xPJGOR33utxxejn+Mulz40kY0oy0FVsmIESW87NZDJ7B1ovaIqeX0xIgPX4laS5SGlqD2uSoBMcw==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.3.1.tgz", + "integrity": "sha512-QivM7GlSHSsIAWzgfyP8dgeExPRZ9BIe2LsdPyEhCGkZkoyA+kGsoIzbKAfZCvvRzfZioKwPtCZIt5SaoxYCvg==", "dev": true, "dependencies": { - "expect": "^29.2.0", - "jest-snapshot": "^29.2.0" + "expect": "^29.3.1", + "jest-snapshot": "^29.3.1" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect-utils": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.2.0.tgz", - "integrity": "sha512-nz2IDF7nb1qmj9hx8Ja3MFab2q9Ml8QbOaaeJNyX5JQJHU8QUvEDiMctmhGEkk3Kzr8w8vAqz4hPk/ogJSrUhg==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.3.1.tgz", + "integrity": "sha512-wlrznINZI5sMjwvUoLVk617ll/UYfGIZNxmbU+Pa7wmkL4vYzhV9R2pwVqUh4NWWuLQWkI8+8mOkxs//prKQ3g==", "dev": true, "dependencies": { "jest-get-type": "^29.2.0" @@ -848,48 +881,48 @@ } }, "node_modules/@jest/fake-timers": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.2.0.tgz", - "integrity": "sha512-mX0V0uQsgeSLTt0yTqanAhhpeUKMGd2uq+PSLAfO40h72bvfNNQ7pIEl9vIwNMFxRih1ENveEjSBsLjxGGDPSw==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.3.1.tgz", + "integrity": "sha512-iHTL/XpnDlFki9Tq0Q1GGuVeQ8BHZGIYsvCO5eN/O/oJaRzofG9Xndd9HuSDBI/0ZS79pg0iwn07OMTQ7ngF2A==", "dev": true, "dependencies": { - "@jest/types": "^29.2.0", + "@jest/types": "^29.3.1", "@sinonjs/fake-timers": "^9.1.2", "@types/node": "*", - "jest-message-util": "^29.2.0", - "jest-mock": "^29.2.0", - "jest-util": "^29.2.0" + "jest-message-util": "^29.3.1", + "jest-mock": "^29.3.1", + "jest-util": "^29.3.1" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/globals": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.2.0.tgz", - "integrity": "sha512-JQxtEVNWiai1p3PIzAJZSyEqQdAJGvNKvinZDPfu0mhiYEVx6E+PiBuDWj1sVUW8hzu+R3DVqaWC9K2xcLRIAA==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.3.1.tgz", + "integrity": "sha512-cTicd134vOcwO59OPaB6AmdHQMCtWOe+/DitpTZVxWgMJ+YvXL1HNAmPyiGbSHmF/mXVBkvlm8YYtQhyHPnV6Q==", "dev": true, "dependencies": { - "@jest/environment": "^29.2.0", - "@jest/expect": "^29.2.0", - "@jest/types": "^29.2.0", - "jest-mock": "^29.2.0" + "@jest/environment": "^29.3.1", + "@jest/expect": "^29.3.1", + "@jest/types": "^29.3.1", + "jest-mock": "^29.3.1" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/reporters": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.2.0.tgz", - "integrity": "sha512-BXoAJatxTZ18U0cwD7C8qBo8V6vef8AXYRBZdhqE5DF9CmpqmhMfw9c7OUvYqMTnBBK9A0NgXGO4Lc9EJzdHvw==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.3.1.tgz", + "integrity": "sha512-GhBu3YFuDrcAYW/UESz1JphEAbvUjaY2vShRZRoRY1mxpCMB3yGSJ4j9n0GxVlEOdCf7qjvUfBCrTUUqhVfbRA==", "dev": true, "dependencies": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.2.0", - "@jest/test-result": "^29.2.0", - "@jest/transform": "^29.2.0", - "@jest/types": "^29.2.0", + "@jest/console": "^29.3.1", + "@jest/test-result": "^29.3.1", + "@jest/transform": "^29.3.1", + "@jest/types": "^29.3.1", "@jridgewell/trace-mapping": "^0.3.15", "@types/node": "*", "chalk": "^4.0.0", @@ -902,9 +935,9 @@ "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.2.0", - "jest-util": "^29.2.0", - "jest-worker": "^29.2.0", + "jest-message-util": "^29.3.1", + "jest-util": "^29.3.1", + "jest-worker": "^29.3.1", "slash": "^3.0.0", "string-length": "^4.0.1", "strip-ansi": "^6.0.0", @@ -965,13 +998,13 @@ } }, "node_modules/@jest/test-result": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.2.0.tgz", - "integrity": "sha512-l76EPJ6QqtzsCLS4aimJqWO53pxZ82o3aE+Brcmo1HJ/phb9+MR7gPhyDdN6VSGaLJCRVJBZgWEhAEz+qON0Fw==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.3.1.tgz", + "integrity": "sha512-qeLa6qc0ddB0kuOZyZIhfN5q0e2htngokyTWsGriedsDhItisW7SDYZ7ceOe57Ii03sL988/03wAcBh3TChMGw==", "dev": true, "dependencies": { - "@jest/console": "^29.2.0", - "@jest/types": "^29.2.0", + "@jest/console": "^29.3.1", + "@jest/types": "^29.3.1", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" }, @@ -980,14 +1013,14 @@ } }, "node_modules/@jest/test-sequencer": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.2.0.tgz", - "integrity": "sha512-NCnjZcGnVdva6IDqF7TCuFsXs2F1tohiNF9sasSJNzD7VfN5ic9XgcS/oPDalGiPLxCmGKj4kewqqrKAqBACcQ==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.3.1.tgz", + "integrity": "sha512-IqYvLbieTv20ArgKoAMyhLHNrVHJfzO6ARZAbQRlY4UGWfdDnLlZEF0BvKOMd77uIiIjSZRwq3Jb3Fa3I8+2UA==", "dev": true, "dependencies": { - "@jest/test-result": "^29.2.0", + "@jest/test-result": "^29.3.1", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.2.0", + "jest-haste-map": "^29.3.1", "slash": "^3.0.0" }, "engines": { @@ -995,22 +1028,22 @@ } }, "node_modules/@jest/transform": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.2.0.tgz", - "integrity": "sha512-NXMujGHy+B4DAj4dGnVPD0SIXlR2Z/N8Gp9h3mF66kcIRult1WWqY3/CEIrJcKviNWaFPYhZjCG2L3fteWzcUw==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.3.1.tgz", + "integrity": "sha512-8wmCFBTVGYqFNLWfcOWoVuMuKYPUBTnTMDkdvFtAYELwDOl9RGwOsvQWGPFxDJ8AWY9xM/8xCXdqmPK3+Q5Lug==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", - "@jest/types": "^29.2.0", + "@jest/types": "^29.3.1", "@jridgewell/trace-mapping": "^0.3.15", "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", + "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.2.0", + "jest-haste-map": "^29.3.1", "jest-regex-util": "^29.2.0", - "jest-util": "^29.2.0", + "jest-util": "^29.3.1", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", @@ -1036,10 +1069,16 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/@jest/transform/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, "node_modules/@jest/types": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.2.0.tgz", - "integrity": "sha512-mfgpQz4Z2xGo37m6KD8xEpKelaVzvYVRijmLPePn9pxgaPEtX+SqIyPNzzoeCPXKYbB4L/wYSgXDL8o3Gop78Q==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.3.1.tgz", + "integrity": "sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA==", "dev": true, "dependencies": { "@jest/schemas": "^29.0.0", @@ -1158,9 +1197,9 @@ "dev": true }, "node_modules/@sinonjs/commons": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", "dev": true, "dependencies": { "type-detect": "4.0.8" @@ -1248,21 +1287,21 @@ } }, "node_modules/@types/express": { - "version": "4.17.14", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz", - "integrity": "sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==", + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.15.tgz", + "integrity": "sha512-Yv0k4bXGOH+8a+7bELd2PqHQsuiANB+A8a4gnQrkRWzrkKlb6KHaVvyXhqs04sVW/OWlbPyYxRgYlIXLfrufMQ==", "dev": true, "dependencies": { "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.18", + "@types/express-serve-static-core": "^4.17.31", "@types/qs": "*", "@types/serve-static": "*" } }, "node_modules/@types/express-serve-static-core": { - "version": "4.17.31", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz", - "integrity": "sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==", + "version": "4.17.32", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.32.tgz", + "integrity": "sha512-aI5h/VOkxOF2Z1saPy0Zsxs5avets/iaiAJYznQFm5By/pamU31xWKL//epiF4OfUA2qTOc9PV6tCUjhO8wlZA==", "dev": true, "dependencies": { "@types/node": "*", @@ -1304,15 +1343,21 @@ } }, "node_modules/@types/jest": { - "version": "29.1.2", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.1.2.tgz", - "integrity": "sha512-y+nlX0h87U0R+wsGn6EBuoRWYyv3KFtwRNP3QWp9+k2tJ2/bqcGS3UxD7jgT+tiwJWWq3UsyV4Y+T6rsMT4XMg==", + "version": "29.2.5", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.2.5.tgz", + "integrity": "sha512-H2cSxkKgVmqNHXP7TC2L/WUorrZu8ZigyRywfVzv6EyBlxj39n4C00hjXYQWsbwqgElaj/CiAeSRmk5GoaKTgw==", "dev": true, "dependencies": { "expect": "^29.0.0", "pretty-format": "^29.0.0" } }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -1320,9 +1365,9 @@ "dev": true }, "node_modules/@types/lodash": { - "version": "4.14.186", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.186.tgz", - "integrity": "sha512-eHcVlLXP0c2FlMPm56ITode2AgLMSa6aJ05JTTbYbI+7EMkCEE5qk2E41d5g2lCVTqRe0GnnRFurmlCsDODrPw==", + "version": "4.14.191", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz", + "integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==", "dev": true }, "node_modules/@types/mathjs": { @@ -1348,15 +1393,15 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.11.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.0.tgz", - "integrity": "sha512-IOXCvVRToe7e0ny7HpT/X9Rb2RYtElG1a+VshjwT00HxrM2dWBApHQoqsI6WiY7Q03vdf2bCrIGzVrkF/5t10w==", + "version": "18.11.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", + "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", "dev": true }, "node_modules/@types/prettier": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.1.tgz", - "integrity": "sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==", + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", + "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", "dev": true }, "node_modules/@types/qs": { @@ -1415,6 +1460,12 @@ "@types/node": "*" } }, + "node_modules/@types/semver": { + "version": "7.3.13", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", + "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "dev": true + }, "node_modules/@types/serve-static": { "version": "1.13.8", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.8.tgz", @@ -1438,9 +1489,9 @@ "dev": true }, "node_modules/@types/uuid": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", - "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-kr90f+ERiQtKWMz5rP32ltJ/BtULDI5RVO0uavn1HQUOwjx0R1h0rnDYNL0CepF1zL5bSY6FISAfd9tOdDhU5Q==", "dev": true }, "node_modules/@types/winston": { @@ -1467,6 +1518,399 @@ "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", "dev": true }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.47.1.tgz", + "integrity": "sha512-r4RZ2Jl9kcQN7K/dcOT+J7NAimbiis4sSM9spvWimsBvDegMhKLA5vri2jG19PmIPbDjPeWzfUPQ2hjEzA4Nmg==", + "dev": true, + "peer": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.47.1", + "@typescript-eslint/type-utils": "5.47.1", + "@typescript-eslint/utils": "5.47.1", + "debug": "^4.3.4", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin-tslint": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin-tslint/-/eslint-plugin-tslint-5.47.1.tgz", + "integrity": "sha512-cbuJ1pOHJPnBT85VPJBgLUOCPzgmBttr/k7JHmSeRoIzmOJAiN0rrRWytZQwIH5JNqjCgz4MBmrU/tf4NGIf+Q==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "5.47.1", + "lodash": "^4.17.21" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0", + "tslint": "^5.0.0 || ^6.0.0", + "typescript": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "peer": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "peer": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.47.1.tgz", + "integrity": "sha512-9Vb+KIv29r6GPu4EboWOnQM7T+UjpjXvjCPhNORlgm40a9Ia9bvaPJswvtae1gip2QEeVeGh6YquqAzEgoRAlw==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.47.1", + "@typescript-eslint/types": "5.47.1", + "@typescript-eslint/typescript-estree": "5.47.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.47.1.tgz", + "integrity": "sha512-CmALY9YWXEpwuu6377ybJBZdtSAnzXLSQcxLSqSQSbC7VfpMu/HLVdrnVJj7ycI138EHqocW02LPJErE35cE9A==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.47.1.tgz", + "integrity": "sha512-4+ZhFSuISAvRi2xUszEj0xXbNTHceV9GbH9S8oAD2a/F9SW57aJNQVOCxG8GPfSWH/X4eOPdMEU2jYVuWKEpWA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.47.1", + "@typescript-eslint/visitor-keys": "5.47.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.47.1.tgz", + "integrity": "sha512-rF3pmut2JCCjh6BLRhNKdYjULMb1brvoaiWDlHfLNVgmnZ0sBVJrs3SyaKE1XoDDnJuAx/hDQryHYmPUuNq0ig==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.47.1", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.47.1.tgz", + "integrity": "sha512-9hsFDsgUwrdOoW1D97Ewog7DYSHaq4WKuNs0LHF9RiCmqB0Z+XRR4Pf7u7u9z/8CciHuJ6yxNws1XznI3ddjEw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.47.1", + "@typescript-eslint/visitor-keys": "5.47.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/scope-manager/node_modules/@typescript-eslint/types": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.47.1.tgz", + "integrity": "sha512-CmALY9YWXEpwuu6377ybJBZdtSAnzXLSQcxLSqSQSbC7VfpMu/HLVdrnVJj7ycI138EHqocW02LPJErE35cE9A==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/scope-manager/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.47.1.tgz", + "integrity": "sha512-rF3pmut2JCCjh6BLRhNKdYjULMb1brvoaiWDlHfLNVgmnZ0sBVJrs3SyaKE1XoDDnJuAx/hDQryHYmPUuNq0ig==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.47.1", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/scope-manager/node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.47.1.tgz", + "integrity": "sha512-/UKOeo8ee80A7/GJA427oIrBi/Gd4osk/3auBUg4Rn9EahFpevVV1mUK8hjyQD5lHPqX397x6CwOk5WGh1E/1w==", + "dev": true, + "peer": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "5.47.1", + "@typescript-eslint/utils": "5.47.1", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.47.1.tgz", + "integrity": "sha512-CmALY9YWXEpwuu6377ybJBZdtSAnzXLSQcxLSqSQSbC7VfpMu/HLVdrnVJj7ycI138EHqocW02LPJErE35cE9A==", + "dev": true, + "peer": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.47.1.tgz", + "integrity": "sha512-4+ZhFSuISAvRi2xUszEj0xXbNTHceV9GbH9S8oAD2a/F9SW57aJNQVOCxG8GPfSWH/X4eOPdMEU2jYVuWKEpWA==", + "dev": true, + "peer": true, + "dependencies": { + "@typescript-eslint/types": "5.47.1", + "@typescript-eslint/visitor-keys": "5.47.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.47.1.tgz", + "integrity": "sha512-rF3pmut2JCCjh6BLRhNKdYjULMb1brvoaiWDlHfLNVgmnZ0sBVJrs3SyaKE1XoDDnJuAx/hDQryHYmPUuNq0ig==", + "dev": true, + "peer": true, + "dependencies": { + "@typescript-eslint/types": "5.47.1", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "peer": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "peer": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "peer": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, "node_modules/@typescript-eslint/types": { "version": "4.33.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz", @@ -1486,16 +1930,112 @@ "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/visitor-keys": "4.33.0", - "debug": "^4.3.1", - "globby": "^11.0.3", - "is-glob": "^4.0.1", - "semver": "^7.3.5", + "@typescript-eslint/types": "4.33.0", + "@typescript-eslint/visitor-keys": "4.33.0", + "debug": "^4.3.1", + "globby": "^11.0.3", + "is-glob": "^4.0.1", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.47.1.tgz", + "integrity": "sha512-l90SdwqfmkuIVaREZ2ykEfCezepCLxzWMo5gVfcJsJCaT4jHT+QjgSkYhs5BMQmWqE9k3AtIfk4g211z/sTMVw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.47.1", + "@typescript-eslint/types": "5.47.1", + "@typescript-eslint/typescript-estree": "5.47.1", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.47.1.tgz", + "integrity": "sha512-CmALY9YWXEpwuu6377ybJBZdtSAnzXLSQcxLSqSQSbC7VfpMu/HLVdrnVJj7ycI138EHqocW02LPJErE35cE9A==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.47.1.tgz", + "integrity": "sha512-4+ZhFSuISAvRi2xUszEj0xXbNTHceV9GbH9S8oAD2a/F9SW57aJNQVOCxG8GPfSWH/X4eOPdMEU2jYVuWKEpWA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.47.1", + "@typescript-eslint/visitor-keys": "5.47.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", "tsutils": "^3.21.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", @@ -1507,10 +2047,58 @@ } } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.47.1.tgz", + "integrity": "sha512-rF3pmut2JCCjh6BLRhNKdYjULMb1brvoaiWDlHfLNVgmnZ0sBVJrs3SyaKE1XoDDnJuAx/hDQryHYmPUuNq0ig==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.47.1", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -1522,7 +2110,7 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/tsutils": { + "node_modules/@typescript-eslint/utils/node_modules/tsutils": { "version": "3.21.0", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", @@ -1567,9 +2155,9 @@ } }, "node_modules/acorn": { - "version": "8.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", - "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -1591,6 +2179,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -1673,9 +2262,9 @@ } }, "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "dependencies": { "normalize-path": "^3.0.0", @@ -1769,22 +2358,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "engines": { - "node": ">=0.8" - } - }, "node_modules/ast-module-types": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/ast-module-types/-/ast-module-types-2.7.1.tgz", @@ -1801,26 +2374,23 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "engines": { - "node": "*" + "node_modules/axios": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.2.tgz", + "integrity": "sha512-bz/J4gS2S3I7mpN/YZfGFTqhXTYzRho8Ay38w2otuuDR322KzFIWm/4W2K6gIwvWaws5n+mnb7D1lN9uD+QH6Q==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" } }, - "node_modules/aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" - }, "node_modules/babel-jest": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.2.0.tgz", - "integrity": "sha512-c8FkrW1chgcbyBqOo7jFGpQYfVnb43JqjQGV+C2r94k2rZJOukYOZ6+csAqKE4ms+PHc+yevnONxs27jQIxylw==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.3.1.tgz", + "integrity": "sha512-aard+xnMoxgjwV70t0L6wkW/3HQQtV+O0PEimxKgzNqCJnbYmroPojdP2tqKSOAt8QAKV/uSZU8851M7B5+fcA==", "dev": true, "dependencies": { - "@jest/transform": "^29.2.0", + "@jest/transform": "^29.3.1", "@types/babel__core": "^7.1.14", "babel-plugin-istanbul": "^6.1.1", "babel-preset-jest": "^29.2.0", @@ -1947,14 +2517,6 @@ } ] }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -2125,6 +2687,7 @@ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", "dev": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -2207,11 +2770,6 @@ } ] }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" - }, "node_modules/chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", @@ -2391,6 +2949,15 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, + "node_modules/comment-parser": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.3.1.tgz", + "integrity": "sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==", + "dev": true, + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -2475,11 +3042,6 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -2494,17 +3056,6 @@ "node": ">= 8" } }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -2529,9 +3080,9 @@ "dev": true }, "node_modules/decimal.js": { - "version": "10.4.2", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.2.tgz", - "integrity": "sha512-ic1yEvwT6GuvaYwBLLY6/aFFgjZdySKTE8en/fkU3QICTmRtgtSlFn0u0BXN06InZwtfCelR7j8LRiDI/02iGA==" + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" }, "node_modules/decomment": { "version": "0.9.5", @@ -2806,14 +3357,15 @@ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true, + "peer": true, "engines": { "node": ">=0.3.1" } }, "node_modules/diff-sequences": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.2.0.tgz", - "integrity": "sha512-413SY5JpYeSBZxmenGEmCVQ8mCgtFJF0w9PROdaS6z987XC2Pd2GOKqOITLtMftmyFZqgtCOb/QA7/Z3ZXfzIw==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.3.1.tgz", + "integrity": "sha512-hlM3QR272NXCi4pq+N4Kok4kOp6EsgOM3ZSpJI7Da3UAs+Ttsi8MRmB6trM/lhyzUxGfOgnpkHtgqm5Q/CTcfQ==", "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -2852,15 +3404,6 @@ "node": ">=6.0.0" } }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -2873,9 +3416,9 @@ "dev": true }, "node_modules/emittery": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", - "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", "dev": true, "engines": { "node": ">=12" @@ -3039,13 +3582,15 @@ } }, "node_modules/eslint": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.16.0.tgz", - "integrity": "sha512-MBndsoXY/PeVTDJeWsYj7kLZ5hQpJOfMYLsF6LicLHQWbRDG19lK5jOix4DPl8yY4SUFcE3txy86OzFLWT+yoA==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.31.0.tgz", + "integrity": "sha512-0tQQEVdmPZ1UtUKXjX7EMm9BlgJ08G90IhWh0PKDCb3ZLsgAOHI8fYSIzYVZej92zsgq+ft0FGsxhJ3xo2tbuA==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.3.0", - "@humanwhocodes/config-array": "^0.9.2", + "@eslint/eslintrc": "^1.4.1", + "@humanwhocodes/config-array": "^0.11.8", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -3055,18 +3600,21 @@ "eslint-scope": "^7.1.1", "eslint-utils": "^3.0.0", "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.2", + "espree": "^9.4.0", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", - "globals": "^13.15.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", @@ -3077,8 +3625,7 @@ "regexpp": "^3.2.0", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" + "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" @@ -3140,6 +3687,24 @@ "eslint-plugin-react": "^7.28.0" } }, + "node_modules/eslint-config-standard-with-typescript": { + "version": "24.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard-with-typescript/-/eslint-config-standard-with-typescript-24.0.0.tgz", + "integrity": "sha512-vEnGXZ5aiR1enl9652iIP4nTpY3GPcNEwuhrsPbKO3Ce3D6T3yCqZdkUPk8nJetfdL/yO0DLsHg2d/l9iECIdg==", + "dev": true, + "dependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint-config-standard": "17.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^5.0.0", + "eslint": "^8.0.1", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-n": "^15.0.0", + "eslint-plugin-promise": "^6.0.0", + "typescript": "*" + } + }, "node_modules/eslint-import-resolver-node": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", @@ -3368,6 +3933,63 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/eslint-plugin-jquery": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jquery/-/eslint-plugin-jquery-1.5.1.tgz", + "integrity": "sha512-L7v1eaK5t80C0lvUXPFP9MKnBOqPSKhCOYyzy4LZ0+iK+TJwN8S9gAkzzP1AOhypRIwA88HF6phQ9C7jnOpW8w==", + "dev": true, + "peerDependencies": { + "eslint": ">=5.4.0" + } + }, + "node_modules/eslint-plugin-jsdoc": { + "version": "39.6.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-39.6.4.tgz", + "integrity": "sha512-fskvdLCfwmPjHb6e+xNGDtGgbF8X7cDwMtVLAP2WwSf9Htrx68OAx31BESBM1FAwsN2HTQyYQq7m4aW4Q4Nlag==", + "dev": true, + "dependencies": { + "@es-joy/jsdoccomment": "~0.36.1", + "comment-parser": "1.3.1", + "debug": "^4.3.4", + "escape-string-regexp": "^4.0.0", + "esquery": "^1.4.0", + "semver": "^7.3.8", + "spdx-expression-parse": "^3.0.1" + }, + "engines": { + "node": "^14 || ^16 || ^17 || ^18 || ^19" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/eslint-plugin-n": { "version": "15.2.0", "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-15.2.0.tgz", @@ -3402,6 +4024,78 @@ "semver": "bin/semver.js" } }, + "node_modules/eslint-plugin-node": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", + "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "dev": true, + "dependencies": { + "eslint-plugin-es": "^3.0.0", + "eslint-utils": "^2.0.0", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "peerDependencies": { + "eslint": ">=5.16.0" + } + }, + "node_modules/eslint-plugin-node/node_modules/eslint-plugin-es": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", + "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "dev": true, + "dependencies": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=4.19.1" + } + }, + "node_modules/eslint-plugin-node/node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-plugin-node/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-node/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/eslint-plugin-promise": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.0.0.tgz", @@ -3559,6 +4253,22 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint/node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -3572,9 +4282,9 @@ } }, "node_modules/eslint/node_modules/globals": { - "version": "13.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", - "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", + "version": "13.19.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", + "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -3611,6 +4321,21 @@ "node": ">= 0.8.0" } }, + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint/node_modules/optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -3628,6 +4353,36 @@ "node": ">= 0.8.0" } }, + "node_modules/eslint/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint/node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -3662,17 +4417,20 @@ } }, "node_modules/espree": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", - "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", + "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", "dev": true, "dependencies": { - "acorn": "^8.7.1", + "acorn": "^8.8.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.3.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/espree/node_modules/eslint-visitor-keys": { @@ -3780,16 +4538,16 @@ } }, "node_modules/expect": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.2.0.tgz", - "integrity": "sha512-03ClF3GWwUqd9Grgkr9ZSdaCJGMRA69PQ8jT7o+Bx100VlGiAFf9/8oIm9Qve7ZVJhuJxFftqFhviZJRxxNfvg==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.3.1.tgz", + "integrity": "sha512-gGb1yTgU30Q0O/tQq+z30KBWv24ApkMgFUpvKBkyLUBL68Wv8dHdJxTBZFl/iT8K/bqDHvUYRH6IIN3rToopPA==", "dev": true, "dependencies": { - "@jest/expect-utils": "^29.2.0", + "@jest/expect-utils": "^29.3.1", "jest-get-type": "^29.2.0", - "jest-matcher-utils": "^29.2.0", - "jest-message-util": "^29.2.0", - "jest-util": "^29.2.0" + "jest-matcher-utils": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-util": "^29.3.1" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -3877,28 +4635,16 @@ } ] }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "engines": [ - "node >=0.6.0" - ] - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true }, "node_modules/fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -3908,13 +4654,14 @@ "micromatch": "^4.0.4" }, "engines": { - "node": ">=8" + "node": ">=8.6.0" } }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true }, "node_modules/fast-levenshtein": { "version": "2.0.6", @@ -4086,12 +4833,36 @@ "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], "engines": { - "node": "*" + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" } }, "node_modules/forwarded": { @@ -4165,12 +4936,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, "node_modules/functions-have-names": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", @@ -4279,14 +5044,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dependencies": { - "assert-plus": "^1.0.0" - } - }, "node_modules/glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -4326,16 +5083,16 @@ } }, "node_modules/globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" }, "engines": { @@ -4366,6 +5123,12 @@ "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", "dev": true }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, "node_modules/graphviz": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/graphviz/-/graphviz-0.0.9.tgz", @@ -4378,27 +5141,6 @@ "node": ">=0.6.8" } }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "deprecated": "this library is no longer supported", - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -4487,20 +5229,6 @@ "node": ">= 0.8" } }, - "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -4818,6 +5546,15 @@ "node": ">=0.10.0" } }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -4899,11 +5636,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -4952,11 +5684,6 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, "node_modules/istanbul-lib-coverage": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", @@ -5038,15 +5765,15 @@ "integrity": "sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k=" }, "node_modules/jest": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.2.0.tgz", - "integrity": "sha512-6krPemKUXCEu5Fh3j6ZVoLMjpTQVm0OCU+7f3K/9gllX8wNIE6NSCQ6s0q2RDoiKLRaQlVRHyscjSPRPqCI0Fg==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.3.1.tgz", + "integrity": "sha512-6iWfL5DTT0Np6UYs/y5Niu7WIfNv/wRTtN5RSXt2DIEft3dx3zPuw/3WJQBCJfmEzvDiEKwoqMbGD9n49+qLSA==", "dev": true, "dependencies": { - "@jest/core": "^29.2.0", - "@jest/types": "^29.2.0", + "@jest/core": "^29.3.1", + "@jest/types": "^29.3.1", "import-local": "^3.0.2", - "jest-cli": "^29.2.0" + "jest-cli": "^29.3.1" }, "bin": { "jest": "bin/jest.js" @@ -5092,28 +5819,28 @@ } }, "node_modules/jest-circus": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.2.0.tgz", - "integrity": "sha512-bpJRMe+VtvYlF3q8JNx+/cAo4FYvNCiR5s7Z0Scf8aC+KJ2ineSjZKtw1cIZbythlplkiro0My8nc65pfCqJ3A==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.3.1.tgz", + "integrity": "sha512-wpr26sEvwb3qQQbdlmei+gzp6yoSSoSL6GsLPxnuayZSMrSd5Ka7IjAvatpIernBvT2+Ic6RLTg+jSebScmasg==", "dev": true, "dependencies": { - "@jest/environment": "^29.2.0", - "@jest/expect": "^29.2.0", - "@jest/test-result": "^29.2.0", - "@jest/types": "^29.2.0", + "@jest/environment": "^29.3.1", + "@jest/expect": "^29.3.1", + "@jest/test-result": "^29.3.1", + "@jest/types": "^29.3.1", "@types/node": "*", "chalk": "^4.0.0", "co": "^4.6.0", "dedent": "^0.7.0", "is-generator-fn": "^2.0.0", - "jest-each": "^29.2.0", - "jest-matcher-utils": "^29.2.0", - "jest-message-util": "^29.2.0", - "jest-runtime": "^29.2.0", - "jest-snapshot": "^29.2.0", - "jest-util": "^29.2.0", + "jest-each": "^29.3.1", + "jest-matcher-utils": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-runtime": "^29.3.1", + "jest-snapshot": "^29.3.1", + "jest-util": "^29.3.1", "p-limit": "^3.1.0", - "pretty-format": "^29.2.0", + "pretty-format": "^29.3.1", "slash": "^3.0.0", "stack-utils": "^2.0.3" }, @@ -5153,21 +5880,21 @@ } }, "node_modules/jest-cli": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.2.0.tgz", - "integrity": "sha512-/581TzbXeO+5kbtSlhXEthGiVJCC8AP0jgT0iZINAAMW+tTFj2uWU7z+HNUH5yIYdHV7AvRr0fWLrmHJGIruHg==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.3.1.tgz", + "integrity": "sha512-TO/ewvwyvPOiBBuWZ0gm04z3WWP8TIK8acgPzE4IxgsLKQgb377NYGrQLc3Wl/7ndWzIH2CDNNsUjGxwLL43VQ==", "dev": true, "dependencies": { - "@jest/core": "^29.2.0", - "@jest/test-result": "^29.2.0", - "@jest/types": "^29.2.0", + "@jest/core": "^29.3.1", + "@jest/test-result": "^29.3.1", + "@jest/types": "^29.3.1", "chalk": "^4.0.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", "import-local": "^3.0.2", - "jest-config": "^29.2.0", - "jest-util": "^29.2.0", - "jest-validate": "^29.2.0", + "jest-config": "^29.3.1", + "jest-util": "^29.3.1", + "jest-validate": "^29.3.1", "prompts": "^2.0.1", "yargs": "^17.3.1" }, @@ -5203,31 +5930,31 @@ } }, "node_modules/jest-config": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.2.0.tgz", - "integrity": "sha512-IkdCsrHIoxDPZAyFcdtQrCQ3uftLqns6Joj0tlbxiAQW4k/zTXmIygqWBmPNxO9FbFkDrhtYZiLHXjaJh9rS+Q==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.3.1.tgz", + "integrity": "sha512-y0tFHdj2WnTEhxmGUK1T7fgLen7YK4RtfvpLFBXfQkh2eMJAQq24Vx9472lvn5wg0MAO6B+iPfJfzdR9hJYalg==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.2.0", - "@jest/types": "^29.2.0", - "babel-jest": "^29.2.0", + "@jest/test-sequencer": "^29.3.1", + "@jest/types": "^29.3.1", + "babel-jest": "^29.3.1", "chalk": "^4.0.0", "ci-info": "^3.2.0", "deepmerge": "^4.2.2", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-circus": "^29.2.0", - "jest-environment-node": "^29.2.0", + "jest-circus": "^29.3.1", + "jest-environment-node": "^29.3.1", "jest-get-type": "^29.2.0", "jest-regex-util": "^29.2.0", - "jest-resolve": "^29.2.0", - "jest-runner": "^29.2.0", - "jest-util": "^29.2.0", - "jest-validate": "^29.2.0", + "jest-resolve": "^29.3.1", + "jest-runner": "^29.3.1", + "jest-util": "^29.3.1", + "jest-validate": "^29.3.1", "micromatch": "^4.0.4", "parse-json": "^5.2.0", - "pretty-format": "^29.2.0", + "pretty-format": "^29.3.1", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" }, @@ -5264,15 +5991,15 @@ } }, "node_modules/jest-diff": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.2.0.tgz", - "integrity": "sha512-GsH07qQL+/D/GxlnU+sSg9GL3fBOcuTlmtr3qr2pnkiODCwubNN2/7slW4m3CvxDsEus/VEOfQKRFLyXsUlnZw==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.3.1.tgz", + "integrity": "sha512-vU8vyiO7568tmin2lA3r2DP8oRvzhvRcD4DjpXc6uGveQodyk7CKLhQlCSiwgx3g0pFaE88/KLZ0yaTWMc4Uiw==", "dev": true, "dependencies": { "chalk": "^4.0.0", - "diff-sequences": "^29.2.0", + "diff-sequences": "^29.3.1", "jest-get-type": "^29.2.0", - "pretty-format": "^29.2.0" + "pretty-format": "^29.3.1" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -5307,16 +6034,16 @@ } }, "node_modules/jest-each": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.2.0.tgz", - "integrity": "sha512-h4LeC3L/R7jIMfTdYowevPIssvcPYQ7Qzs+pCSYsJgPztIizXwKmnfhZXBA4WVqdmvMcpmseYEXb67JT7IJ2eg==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.3.1.tgz", + "integrity": "sha512-qrZH7PmFB9rEzCSl00BWjZYuS1BSOH8lLuC0azQE9lQrAx3PWGKHTDudQiOSwIy5dGAJh7KA0ScYlCP7JxvFYA==", "dev": true, "dependencies": { - "@jest/types": "^29.2.0", + "@jest/types": "^29.3.1", "chalk": "^4.0.0", "jest-get-type": "^29.2.0", - "jest-util": "^29.2.0", - "pretty-format": "^29.2.0" + "jest-util": "^29.3.1", + "pretty-format": "^29.3.1" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -5339,17 +6066,17 @@ } }, "node_modules/jest-environment-node": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.2.0.tgz", - "integrity": "sha512-b4qQGVStPMvtZG97Ac0rvnmSIjCZturFU7MQRMp4JDFl7zoaDLTtXmFjFP1tNmi9te6kR8d+Htbv3nYeoaIz6g==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.3.1.tgz", + "integrity": "sha512-xm2THL18Xf5sIHoU7OThBPtuH6Lerd+Y1NLYiZJlkE3hbE+7N7r8uvHIl/FkZ5ymKXJe/11SQuf3fv4v6rUMag==", "dev": true, "dependencies": { - "@jest/environment": "^29.2.0", - "@jest/fake-timers": "^29.2.0", - "@jest/types": "^29.2.0", + "@jest/environment": "^29.3.1", + "@jest/fake-timers": "^29.3.1", + "@jest/types": "^29.3.1", "@types/node": "*", - "jest-mock": "^29.2.0", - "jest-util": "^29.2.0" + "jest-mock": "^29.3.1", + "jest-util": "^29.3.1" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -5365,20 +6092,20 @@ } }, "node_modules/jest-haste-map": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.2.0.tgz", - "integrity": "sha512-qu9lGFi7qJ8v37egS1phZZUJYiMyWnKwu83NlNT1qs50TbedIX2hFl+9ztsJ7U/ENaHwk1/Bs8fqOIQsScIRwg==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.3.1.tgz", + "integrity": "sha512-/FFtvoG1xjbbPXQLFef+WSU4yrc0fc0Dds6aRPBojUid7qlPqZvxdUBA03HW0fnVHXVCnCdkuoghYItKNzc/0A==", "dev": true, "dependencies": { - "@jest/types": "^29.2.0", + "@jest/types": "^29.3.1", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", "jest-regex-util": "^29.2.0", - "jest-util": "^29.2.0", - "jest-worker": "^29.2.0", + "jest-util": "^29.3.1", + "jest-worker": "^29.3.1", "micromatch": "^4.0.4", "walker": "^1.0.8" }, @@ -5390,28 +6117,28 @@ } }, "node_modules/jest-leak-detector": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.2.0.tgz", - "integrity": "sha512-FXT9sCFdct42+oOqGIr/9kmUw3RbhvpkwidCBT5ySHHoWNGd3c9n7HXpFKjEz9UnUITRCGdn0q2s6Sxrq36kwg==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.3.1.tgz", + "integrity": "sha512-3DA/VVXj4zFOPagGkuqHnSQf1GZBmmlagpguxEERO6Pla2g84Q1MaVIB3YMxgUaFIaYag8ZnTyQgiZ35YEqAQA==", "dev": true, "dependencies": { "jest-get-type": "^29.2.0", - "pretty-format": "^29.2.0" + "pretty-format": "^29.3.1" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-matcher-utils": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.2.0.tgz", - "integrity": "sha512-FcEfKZ4vm28yCdBsvC69EkrEhcfex+IYlRctNJXsRG9+WC3WxgBNORnECIgqUtj7o/h1d8o7xB/dFUiLi4bqtw==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.3.1.tgz", + "integrity": "sha512-fkRMZUAScup3txIKfMe3AIZZmPEjWEdsPJFK3AIy5qRohWqQFg1qrmKfYXR9qEkNc7OdAu2N4KPHibEmy4HPeQ==", "dev": true, "dependencies": { "chalk": "^4.0.0", - "jest-diff": "^29.2.0", + "jest-diff": "^29.3.1", "jest-get-type": "^29.2.0", - "pretty-format": "^29.2.0" + "pretty-format": "^29.3.1" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -5434,18 +6161,18 @@ } }, "node_modules/jest-message-util": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.2.0.tgz", - "integrity": "sha512-arBfk5yMFMTnMB22GyG601xGSGthA02vWSewPaxoFo0F9wBqDOyxccPbCcYu8uibw3kduSHXdCOd1PsLSgdomg==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.3.1.tgz", + "integrity": "sha512-lMJTbgNcDm5z+6KDxWtqOFWlGQxD6XaYwBqHR8kmpkP+WWWG90I35kdtQHY67Ay5CSuydkTBbJG+tH9JShFCyA==", "dev": true, "dependencies": { "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.2.0", + "@jest/types": "^29.3.1", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", - "pretty-format": "^29.2.0", + "pretty-format": "^29.3.1", "slash": "^3.0.0", "stack-utils": "^2.0.3" }, @@ -5470,23 +6197,23 @@ } }, "node_modules/jest-mock": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.2.0.tgz", - "integrity": "sha512-aiWGR0P8ivssIO17xkehLGFtCcef2ZwQFNPwEer1jQLHxPctDlIg3Hs6QMq1KpPz5dkCcgM7mwGif4a9IPznlg==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.3.1.tgz", + "integrity": "sha512-H8/qFDtDVMFvFP4X8NuOT3XRDzOUTz+FeACjufHzsOIBAxivLqkB1PoLCaJx9iPPQ8dZThHPp/G3WRWyMgA3JA==", "dev": true, "dependencies": { - "@jest/types": "^29.2.0", + "@jest/types": "^29.3.1", "@types/node": "*", - "jest-util": "^29.2.0" + "jest-util": "^29.3.1" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-pnp-resolver": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", - "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", "dev": true, "engines": { "node": ">=6" @@ -5510,17 +6237,17 @@ } }, "node_modules/jest-resolve": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.2.0.tgz", - "integrity": "sha512-f5c0ljNg2guDBCC7wi92vAhNuA0BtAG5vkY7Fob0c7sUMU1g87mTXqRmjrVFe2XvdwP5m5T/e5KJsCKu9hRvBA==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.3.1.tgz", + "integrity": "sha512-amXJgH/Ng712w3Uz5gqzFBBjxV8WFLSmNjoreBGMqxgCz5cH7swmBZzgBaCIOsvb0NbpJ0vgaSFdJqMdT+rADw==", "dev": true, "dependencies": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.2.0", + "jest-haste-map": "^29.3.1", "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.2.0", - "jest-validate": "^29.2.0", + "jest-util": "^29.3.1", + "jest-validate": "^29.3.1", "resolve": "^1.20.0", "resolve.exports": "^1.1.0", "slash": "^3.0.0" @@ -5530,13 +6257,13 @@ } }, "node_modules/jest-resolve-dependencies": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.2.0.tgz", - "integrity": "sha512-Cd0Z39sDntEnfR9PoUdFHUAGDvtKI0/7Wt73l3lt03A3yQ+A6Qi3XmBuqGjdFl2QbXaPa937oLhilG612P8HGQ==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.3.1.tgz", + "integrity": "sha512-Vk0cYq0byRw2WluNmNWGqPeRnZ3p3hHmjJMp2dyyZeYIfiBskwq4rpiuGFR6QGAdbj58WC7HN4hQHjf2mpvrLA==", "dev": true, "dependencies": { "jest-regex-util": "^29.2.0", - "jest-snapshot": "^29.2.0" + "jest-snapshot": "^29.3.1" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -5559,30 +6286,30 @@ } }, "node_modules/jest-runner": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.2.0.tgz", - "integrity": "sha512-VPBrCwl9fM2mc5yk6yZhNrgXzRJMD5jfLmntkMLlrVq4hQPWbRK998iJlR+DOGCO04TC9PPYLntOJ001Vnf28g==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.3.1.tgz", + "integrity": "sha512-oFvcwRNrKMtE6u9+AQPMATxFcTySyKfLhvso7Sdk/rNpbhg4g2GAGCopiInk1OP4q6gz3n6MajW4+fnHWlU3bA==", "dev": true, "dependencies": { - "@jest/console": "^29.2.0", - "@jest/environment": "^29.2.0", - "@jest/test-result": "^29.2.0", - "@jest/transform": "^29.2.0", - "@jest/types": "^29.2.0", + "@jest/console": "^29.3.1", + "@jest/environment": "^29.3.1", + "@jest/test-result": "^29.3.1", + "@jest/transform": "^29.3.1", + "@jest/types": "^29.3.1", "@types/node": "*", "chalk": "^4.0.0", - "emittery": "^0.10.2", + "emittery": "^0.13.1", "graceful-fs": "^4.2.9", "jest-docblock": "^29.2.0", - "jest-environment-node": "^29.2.0", - "jest-haste-map": "^29.2.0", - "jest-leak-detector": "^29.2.0", - "jest-message-util": "^29.2.0", - "jest-resolve": "^29.2.0", - "jest-runtime": "^29.2.0", - "jest-util": "^29.2.0", - "jest-watcher": "^29.2.0", - "jest-worker": "^29.2.0", + "jest-environment-node": "^29.3.1", + "jest-haste-map": "^29.3.1", + "jest-leak-detector": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-resolve": "^29.3.1", + "jest-runtime": "^29.3.1", + "jest-util": "^29.3.1", + "jest-watcher": "^29.3.1", + "jest-worker": "^29.3.1", "p-limit": "^3.1.0", "source-map-support": "0.5.13" }, @@ -5622,31 +6349,31 @@ } }, "node_modules/jest-runtime": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.2.0.tgz", - "integrity": "sha512-+GDmzCrswQF+mvI0upTYMe/OPYnlRRNLLDHM9AFLp2y7zxWoDoYgb8DL3WwJ8d9m743AzrnvBV9JQHi/0ed7dg==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.3.1.tgz", + "integrity": "sha512-jLzkIxIqXwBEOZx7wx9OO9sxoZmgT2NhmQKzHQm1xwR1kNW/dn0OjxR424VwHHf1SPN6Qwlb5pp1oGCeFTQ62A==", "dev": true, "dependencies": { - "@jest/environment": "^29.2.0", - "@jest/fake-timers": "^29.2.0", - "@jest/globals": "^29.2.0", + "@jest/environment": "^29.3.1", + "@jest/fake-timers": "^29.3.1", + "@jest/globals": "^29.3.1", "@jest/source-map": "^29.2.0", - "@jest/test-result": "^29.2.0", - "@jest/transform": "^29.2.0", - "@jest/types": "^29.2.0", + "@jest/test-result": "^29.3.1", + "@jest/transform": "^29.3.1", + "@jest/types": "^29.3.1", "@types/node": "*", "chalk": "^4.0.0", "cjs-module-lexer": "^1.0.0", "collect-v8-coverage": "^1.0.0", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.2.0", - "jest-message-util": "^29.2.0", - "jest-mock": "^29.2.0", + "jest-haste-map": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-mock": "^29.3.1", "jest-regex-util": "^29.2.0", - "jest-resolve": "^29.2.0", - "jest-snapshot": "^29.2.0", - "jest-util": "^29.2.0", + "jest-resolve": "^29.3.1", + "jest-snapshot": "^29.3.1", + "jest-util": "^29.3.1", "slash": "^3.0.0", "strip-bom": "^4.0.0" }, @@ -5671,9 +6398,9 @@ } }, "node_modules/jest-snapshot": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.2.0.tgz", - "integrity": "sha512-YCKrOR0PLRXROmww73fHO9oeY4tL+LPQXWR3yml1+hKbQDR8j1VUrVzB65hKSJJgxBOr1vWx+hmz2by8JjAU5w==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.3.1.tgz", + "integrity": "sha512-+3JOc+s28upYLI2OJM4PWRGK9AgpsMs/ekNryUV0yMBClT9B1DF2u2qay8YxcQd338PPYSFNb0lsar1B49sLDA==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", @@ -5682,23 +6409,23 @@ "@babel/plugin-syntax-typescript": "^7.7.2", "@babel/traverse": "^7.7.2", "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.2.0", - "@jest/transform": "^29.2.0", - "@jest/types": "^29.2.0", + "@jest/expect-utils": "^29.3.1", + "@jest/transform": "^29.3.1", + "@jest/types": "^29.3.1", "@types/babel__traverse": "^7.0.6", "@types/prettier": "^2.1.5", "babel-preset-current-node-syntax": "^1.0.0", "chalk": "^4.0.0", - "expect": "^29.2.0", + "expect": "^29.3.1", "graceful-fs": "^4.2.9", - "jest-diff": "^29.2.0", + "jest-diff": "^29.3.1", "jest-get-type": "^29.2.0", - "jest-haste-map": "^29.2.0", - "jest-matcher-utils": "^29.2.0", - "jest-message-util": "^29.2.0", - "jest-util": "^29.2.0", + "jest-haste-map": "^29.3.1", + "jest-matcher-utils": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-util": "^29.3.1", "natural-compare": "^1.4.0", - "pretty-format": "^29.2.0", + "pretty-format": "^29.3.1", "semver": "^7.3.5" }, "engines": { @@ -5737,12 +6464,12 @@ } }, "node_modules/jest-util": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.2.0.tgz", - "integrity": "sha512-8M1dx12ujkBbnhwytrezWY0Ut79hbflwodE+qZKjxSRz5qt4xDp6dQQJaOCFvCmE0QJqp9KyEK33lpPNjnhevw==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.3.1.tgz", + "integrity": "sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ==", "dev": true, "dependencies": { - "@jest/types": "^29.2.0", + "@jest/types": "^29.3.1", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", @@ -5770,17 +6497,17 @@ } }, "node_modules/jest-validate": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.2.0.tgz", - "integrity": "sha512-4Vl51bPNeFeDok9aJiOnrC6tqJbOp4iMCYlewoC2ZzYJZ5+6pfr3KObAdx5wP8auHcg2MRaguiqj5OdScZa72g==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.3.1.tgz", + "integrity": "sha512-N9Lr3oYR2Mpzuelp1F8negJR3YE+L1ebk1rYA5qYo9TTY3f9OWdptLoNSPP9itOCBIRBqjt/S5XHlzYglLN67g==", "dev": true, "dependencies": { - "@jest/types": "^29.2.0", + "@jest/types": "^29.3.1", "camelcase": "^6.2.0", "chalk": "^4.0.0", "jest-get-type": "^29.2.0", "leven": "^3.1.0", - "pretty-format": "^29.2.0" + "pretty-format": "^29.3.1" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -5815,18 +6542,18 @@ } }, "node_modules/jest-watcher": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.2.0.tgz", - "integrity": "sha512-bRh0JdUeN+cl9XfK7tMnXLm4Mv70hG2SZlqbkFe5CTs7oeCkbwlGBk/mEfEJ63mrxZ8LPbnfaMpfSmkhEQBEGA==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.3.1.tgz", + "integrity": "sha512-RspXG2BQFDsZSRKGCT/NiNa8RkQ1iKAjrO0//soTMWx/QUt+OcxMqMSBxz23PYGqUuWm2+m2mNNsmj0eIoOaFg==", "dev": true, "dependencies": { - "@jest/test-result": "^29.2.0", - "@jest/types": "^29.2.0", + "@jest/test-result": "^29.3.1", + "@jest/types": "^29.3.1", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", - "emittery": "^0.10.2", - "jest-util": "^29.2.0", + "emittery": "^0.13.1", + "jest-util": "^29.3.1", "string-length": "^4.0.1" }, "engines": { @@ -5850,13 +6577,13 @@ } }, "node_modules/jest-worker": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.2.0.tgz", - "integrity": "sha512-mluOlMbRX1H59vGVzPcVg2ALfCausbBpxC8a2KWOzInhYHZibbHH8CB0C1JkmkpfurrkOYgF7FPmypuom1OM9A==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.3.1.tgz", + "integrity": "sha512-lY4AnnmsEWeiXirAIA0c9SDPbuCBq8IYuDVL8PMm0MZ2PEs2yPvRA/J64QBXuZp7CYKrDM/rmNrc9/i3KJQncw==", "dev": true, "dependencies": { "@types/node": "*", - "jest-util": "^29.2.0", + "jest-util": "^29.3.1", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, @@ -5888,6 +6615,16 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/js-sdsl": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", + "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -5907,10 +6644,14 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + "node_modules/jsdoc-type-pratt-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-3.1.0.tgz", + "integrity": "sha512-MgtD0ZiCDk9B+eI73BextfRrVQl0oyzRG8B2BjORts6jbunj4ScKPcyXGTbB6eXL4y9TzxCm6hyeLq/2ASzNdw==", + "dev": true, + "engines": { + "node": ">=12.0.0" + } }, "node_modules/jsesc": { "version": "2.5.2", @@ -5936,15 +6677,11 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" - }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -5952,11 +6689,6 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, "node_modules/json5": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", @@ -5974,20 +6706,6 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-1.1.1.tgz", "integrity": "sha1-2k/WrXfxolUgPqY8e8Mtwx72RDM=" }, - "node_modules/jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, "node_modules/jsx-ast-utils": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.0.tgz", @@ -6319,13 +7037,13 @@ } }, "node_modules/mathjs": { - "version": "11.3.0", - "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-11.3.0.tgz", - "integrity": "sha512-HAUFLxyH8WMSDisNljQFIKjp//d1iL0C36nhGEJFwzdMb46gACMG1E8Ze06sES4hJ5jHDv5ieq7r3mDDcU/d4g==", + "version": "11.5.0", + "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-11.5.0.tgz", + "integrity": "sha512-vJ/+SqWtxjW6/aeDRt8xL3TlOVKqwN15BIyTGVqGbIWuiqgY4SxZ0yLuna82YH9CB757iFP7uJ4m3KvVBX7Qcg==", "dependencies": { - "@babel/runtime": "^7.19.4", + "@babel/runtime": "^7.20.6", "complex.js": "^2.1.1", - "decimal.js": "^10.4.1", + "decimal.js": "^10.4.3", "escape-latex": "^1.2.0", "fraction.js": "^4.2.0", "javascript-natural-sort": "^0.7.1", @@ -6453,6 +7171,7 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "dev": true, + "peer": true, "dependencies": { "minimist": "^1.2.5" }, @@ -6526,6 +7245,13 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true, + "peer": true + }, "node_modules/ncp": { "version": "0.4.2", "resolved": "http://registry.npmjs.org/ncp/-/ncp-0.4.2.tgz", @@ -6611,14 +7337,6 @@ "node": ">=8" } }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "engines": { - "node": "*" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -6949,11 +7667,6 @@ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -7171,9 +7884,9 @@ } }, "node_modules/pretty-format": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.2.0.tgz", - "integrity": "sha512-QCSUFdwOi924g24czhOH5eTkXxUCqlLGZBRCySlwDYHIXRJkdGyjJc9nZaqhlFBZws8dq5Dvk0lCilsmlfsPxw==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.3.1.tgz", + "integrity": "sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg==", "dev": true, "dependencies": { "@jest/schemas": "^29.0.0", @@ -7253,27 +7966,20 @@ "node": ">= 0.10" } }, - "node_modules/psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, "node_modules/punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, "engines": { "node": ">=6" } }, - "node_modules/qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "engines": { - "node": ">=0.6" - } - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -7371,9 +8077,9 @@ "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" }, "node_modules/regenerator-runtime": { - "version": "0.13.10", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz", - "integrity": "sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw==" + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, "node_modules/regexp.prototype.flags": { "version": "1.4.3", @@ -7412,90 +8118,6 @@ "node": ">=0.10" } }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/request-promise-core": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", - "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", - "dependencies": { - "lodash": "^4.17.19" - }, - "engines": { - "node": ">=0.10.0" - }, - "peerDependencies": { - "request": "^2.34" - } - }, - "node_modules/request-promise-native": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", - "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", - "deprecated": "request-promise-native has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142", - "dependencies": { - "request-promise-core": "1.1.4", - "stealthy-require": "^1.1.1", - "tough-cookie": "^2.3.3" - }, - "engines": { - "node": ">=0.12.0" - }, - "peerDependencies": { - "request": "^2.34" - } - }, - "node_modules/request/node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/request/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "bin": { - "uuid": "bin/uuid" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -7647,7 +8269,8 @@ "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true }, "node_modules/safe-stable-stringify": { "version": "2.3.1", @@ -7692,6 +8315,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", "dev": true, + "peer": true, "bin": { "semver": "bin/semver" } @@ -7852,36 +8476,34 @@ "source-map": "^0.6.0" } }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", + "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", + "dev": true + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, - "node_modules/sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", @@ -7984,14 +8606,6 @@ "node": ">= 0.8" } }, - "node_modules/stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -8267,18 +8881,6 @@ "node": ">=0.6" } }, - "node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, "node_modules/triple-beam": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", @@ -8382,6 +8984,7 @@ "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", "dev": true, + "peer": true, "dependencies": { "@babel/code-frame": "^7.0.0", "builtin-modules": "^1.1.1", @@ -8404,77 +9007,12 @@ "node": ">=4.8.0" } }, - "node_modules/tslint-config-standard": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/tslint-config-standard/-/tslint-config-standard-9.0.0.tgz", - "integrity": "sha512-CAw9J743RnPMemQV/XQ4YyNreC+A1NItACfkm+cBedrOkz6CQfwlnbKn8anUXBfoa4Zo4tjAhblRbsMNcSLfSw==", - "dev": true, - "dependencies": { - "tslint-eslint-rules": "^5.3.1" - } - }, - "node_modules/tslint-eslint-rules": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/tslint-eslint-rules/-/tslint-eslint-rules-5.4.0.tgz", - "integrity": "sha512-WlSXE+J2vY/VPgIcqQuijMQiel+UtmXS+4nvK4ZzlDiqBfXse8FAvkNnTcYhnQyOTW5KFM+uRRGXxYhFpuBc6w==", - "dev": true, - "dependencies": { - "doctrine": "0.7.2", - "tslib": "1.9.0", - "tsutils": "^3.0.0" - } - }, - "node_modules/tslint-eslint-rules/node_modules/doctrine": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-0.7.2.tgz", - "integrity": "sha1-fLhgNZujvpDgQLJrcpzkv6ZUxSM=", - "dev": true, - "dependencies": { - "esutils": "^1.1.6", - "isarray": "0.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/tslint-eslint-rules/node_modules/esutils": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.1.6.tgz", - "integrity": "sha1-wBzKqa5LiXxtDD4hCuUvPHqEQ3U=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/tslint-eslint-rules/node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "node_modules/tslint-eslint-rules/node_modules/tslib": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.0.tgz", - "integrity": "sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ==", - "dev": true - }, - "node_modules/tslint-eslint-rules/node_modules/tsutils": { - "version": "3.17.1", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", - "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/tsutils": { "version": "2.29.0", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", "dev": true, + "peer": true, "dependencies": { "tslib": "^1.8.1" } @@ -8490,22 +9028,6 @@ "node": ">= 6.0.0" } }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" - }, "node_modules/type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", @@ -8560,9 +9082,9 @@ } }, "node_modules/typescript": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", - "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -8631,6 +9153,7 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, "dependencies": { "punycode": "^2.1.0" } @@ -8656,12 +9179,6 @@ "uuid": "dist/bin/uuid" } }, - "node_modules/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, "node_modules/v8-to-istanbul": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", @@ -8684,19 +9201,6 @@ "node": ">= 0.8" } }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, "node_modules/walkdir": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.4.1.tgz", @@ -8858,9 +9362,9 @@ "dev": true }, "node_modules/yargs": { - "version": "17.6.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.0.tgz", - "integrity": "sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==", + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", + "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", "dev": true, "dependencies": { "cliui": "^8.0.1", @@ -8869,16 +9373,16 @@ "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" + "yargs-parser": "^21.1.1" }, "engines": { "node": ">=12" } }, "node_modules/yargs-parser": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", - "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, "engines": { "node": ">=12" @@ -9245,20 +9749,20 @@ } }, "@babel/plugin-syntax-typescript": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz", - "integrity": "sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", + "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.19.0" } }, "@babel/runtime": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.4.tgz", - "integrity": "sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.7.tgz", + "integrity": "sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==", "requires": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.13.11" } }, "@babel/template": { @@ -9322,16 +9826,27 @@ "kuler": "^2.0.0" } }, + "@es-joy/jsdoccomment": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.36.1.tgz", + "integrity": "sha512-922xqFsTpHs6D0BUiG4toiyPOMc8/jafnWKxz1KWgS4XzKPy2qXf1Pe6UFuNSCQqt6tOuhAWXBNuuyUhJmw9Vg==", + "dev": true, + "requires": { + "comment-parser": "1.3.1", + "esquery": "^1.4.0", + "jsdoc-type-pratt-parser": "~3.1.0" + } + }, "@eslint/eslintrc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", - "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", + "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.3.2", - "globals": "^13.15.0", + "espree": "^9.4.0", + "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -9346,9 +9861,9 @@ "dev": true }, "globals": { - "version": "13.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", - "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", + "version": "13.19.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", + "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -9372,16 +9887,22 @@ } }, "@humanwhocodes/config-array": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", - "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", "dev": true, "requires": { "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", - "minimatch": "^3.0.4" + "minimatch": "^3.0.5" } }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, "@humanwhocodes/object-schema": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", @@ -9408,16 +9929,16 @@ "dev": true }, "@jest/console": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.2.0.tgz", - "integrity": "sha512-Xz1Wu+ZZxcB3RS8U3HdkFxlRJ7kLXI/by9X7d2/gvseIWPwYu/c1EsYy77cB5iyyHGOy3whS2HycjcuzIF4Jow==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.3.1.tgz", + "integrity": "sha512-IRE6GD47KwcqA09RIWrabKdHPiKDGgtAL31xDxbi/RjQMsr+lY+ppxmHwY0dUEV3qvvxZzoe5Hl0RXZJOjQNUg==", "dev": true, "requires": { - "@jest/types": "^29.2.0", + "@jest/types": "^29.3.1", "@types/node": "*", "chalk": "^4.0.0", - "jest-message-util": "^29.2.0", - "jest-util": "^29.2.0", + "jest-message-util": "^29.3.1", + "jest-util": "^29.3.1", "slash": "^3.0.0" }, "dependencies": { @@ -9434,16 +9955,16 @@ } }, "@jest/core": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.2.0.tgz", - "integrity": "sha512-+gyJ3bX+kGEW/eqt/0kI7fLjqiFr3AN8O+rlEl1fYRf7D8h4Sj4tBGo9YOSirvWgvemoH2EPRya35bgvcPFzHQ==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.3.1.tgz", + "integrity": "sha512-0ohVjjRex985w5MmO5L3u5GR1O30DexhBSpuwx2P+9ftyqHdJXnk7IUWiP80oHMvt7ubHCJHxV0a0vlKVuZirw==", "dev": true, "requires": { - "@jest/console": "^29.2.0", - "@jest/reporters": "^29.2.0", - "@jest/test-result": "^29.2.0", - "@jest/transform": "^29.2.0", - "@jest/types": "^29.2.0", + "@jest/console": "^29.3.1", + "@jest/reporters": "^29.3.1", + "@jest/test-result": "^29.3.1", + "@jest/transform": "^29.3.1", + "@jest/types": "^29.3.1", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", @@ -9451,20 +9972,20 @@ "exit": "^0.1.2", "graceful-fs": "^4.2.9", "jest-changed-files": "^29.2.0", - "jest-config": "^29.2.0", - "jest-haste-map": "^29.2.0", - "jest-message-util": "^29.2.0", + "jest-config": "^29.3.1", + "jest-haste-map": "^29.3.1", + "jest-message-util": "^29.3.1", "jest-regex-util": "^29.2.0", - "jest-resolve": "^29.2.0", - "jest-resolve-dependencies": "^29.2.0", - "jest-runner": "^29.2.0", - "jest-runtime": "^29.2.0", - "jest-snapshot": "^29.2.0", - "jest-util": "^29.2.0", - "jest-validate": "^29.2.0", - "jest-watcher": "^29.2.0", + "jest-resolve": "^29.3.1", + "jest-resolve-dependencies": "^29.3.1", + "jest-runner": "^29.3.1", + "jest-runtime": "^29.3.1", + "jest-snapshot": "^29.3.1", + "jest-util": "^29.3.1", + "jest-validate": "^29.3.1", + "jest-watcher": "^29.3.1", "micromatch": "^4.0.4", - "pretty-format": "^29.2.0", + "pretty-format": "^29.3.1", "slash": "^3.0.0", "strip-ansi": "^6.0.0" }, @@ -9482,73 +10003,73 @@ } }, "@jest/environment": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.2.0.tgz", - "integrity": "sha512-foaVv1QVPB31Mno3LlL58PxEQQOLZd9zQfCpyQQCQIpUAtdFP1INBjkphxrCfKT13VxpA0z5jFGIkmZk0DAg2Q==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.3.1.tgz", + "integrity": "sha512-pMmvfOPmoa1c1QpfFW0nXYtNLpofqo4BrCIk6f2kW4JFeNlHV2t3vd+3iDLf31e2ot2Mec0uqZfmI+U0K2CFag==", "dev": true, "requires": { - "@jest/fake-timers": "^29.2.0", - "@jest/types": "^29.2.0", + "@jest/fake-timers": "^29.3.1", + "@jest/types": "^29.3.1", "@types/node": "*", - "jest-mock": "^29.2.0" + "jest-mock": "^29.3.1" } }, "@jest/expect": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.2.0.tgz", - "integrity": "sha512-+3lxcYL9e0xPJGOR33utxxejn+Mulz40kY0oy0FVsmIESW87NZDJ7B1ovaIqeX0xIgPX4laS5SGlqD2uSoBMcw==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.3.1.tgz", + "integrity": "sha512-QivM7GlSHSsIAWzgfyP8dgeExPRZ9BIe2LsdPyEhCGkZkoyA+kGsoIzbKAfZCvvRzfZioKwPtCZIt5SaoxYCvg==", "dev": true, "requires": { - "expect": "^29.2.0", - "jest-snapshot": "^29.2.0" + "expect": "^29.3.1", + "jest-snapshot": "^29.3.1" } }, "@jest/expect-utils": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.2.0.tgz", - "integrity": "sha512-nz2IDF7nb1qmj9hx8Ja3MFab2q9Ml8QbOaaeJNyX5JQJHU8QUvEDiMctmhGEkk3Kzr8w8vAqz4hPk/ogJSrUhg==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.3.1.tgz", + "integrity": "sha512-wlrznINZI5sMjwvUoLVk617ll/UYfGIZNxmbU+Pa7wmkL4vYzhV9R2pwVqUh4NWWuLQWkI8+8mOkxs//prKQ3g==", "dev": true, "requires": { "jest-get-type": "^29.2.0" } }, "@jest/fake-timers": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.2.0.tgz", - "integrity": "sha512-mX0V0uQsgeSLTt0yTqanAhhpeUKMGd2uq+PSLAfO40h72bvfNNQ7pIEl9vIwNMFxRih1ENveEjSBsLjxGGDPSw==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.3.1.tgz", + "integrity": "sha512-iHTL/XpnDlFki9Tq0Q1GGuVeQ8BHZGIYsvCO5eN/O/oJaRzofG9Xndd9HuSDBI/0ZS79pg0iwn07OMTQ7ngF2A==", "dev": true, "requires": { - "@jest/types": "^29.2.0", + "@jest/types": "^29.3.1", "@sinonjs/fake-timers": "^9.1.2", "@types/node": "*", - "jest-message-util": "^29.2.0", - "jest-mock": "^29.2.0", - "jest-util": "^29.2.0" + "jest-message-util": "^29.3.1", + "jest-mock": "^29.3.1", + "jest-util": "^29.3.1" } }, "@jest/globals": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.2.0.tgz", - "integrity": "sha512-JQxtEVNWiai1p3PIzAJZSyEqQdAJGvNKvinZDPfu0mhiYEVx6E+PiBuDWj1sVUW8hzu+R3DVqaWC9K2xcLRIAA==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.3.1.tgz", + "integrity": "sha512-cTicd134vOcwO59OPaB6AmdHQMCtWOe+/DitpTZVxWgMJ+YvXL1HNAmPyiGbSHmF/mXVBkvlm8YYtQhyHPnV6Q==", "dev": true, "requires": { - "@jest/environment": "^29.2.0", - "@jest/expect": "^29.2.0", - "@jest/types": "^29.2.0", - "jest-mock": "^29.2.0" + "@jest/environment": "^29.3.1", + "@jest/expect": "^29.3.1", + "@jest/types": "^29.3.1", + "jest-mock": "^29.3.1" } }, "@jest/reporters": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.2.0.tgz", - "integrity": "sha512-BXoAJatxTZ18U0cwD7C8qBo8V6vef8AXYRBZdhqE5DF9CmpqmhMfw9c7OUvYqMTnBBK9A0NgXGO4Lc9EJzdHvw==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.3.1.tgz", + "integrity": "sha512-GhBu3YFuDrcAYW/UESz1JphEAbvUjaY2vShRZRoRY1mxpCMB3yGSJ4j9n0GxVlEOdCf7qjvUfBCrTUUqhVfbRA==", "dev": true, "requires": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.2.0", - "@jest/test-result": "^29.2.0", - "@jest/transform": "^29.2.0", - "@jest/types": "^29.2.0", + "@jest/console": "^29.3.1", + "@jest/test-result": "^29.3.1", + "@jest/transform": "^29.3.1", + "@jest/types": "^29.3.1", "@jridgewell/trace-mapping": "^0.3.15", "@types/node": "*", "chalk": "^4.0.0", @@ -9561,9 +10082,9 @@ "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.2.0", - "jest-util": "^29.2.0", - "jest-worker": "^29.2.0", + "jest-message-util": "^29.3.1", + "jest-util": "^29.3.1", + "jest-worker": "^29.3.1", "slash": "^3.0.0", "string-length": "^4.0.1", "strip-ansi": "^6.0.0", @@ -9603,46 +10124,46 @@ } }, "@jest/test-result": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.2.0.tgz", - "integrity": "sha512-l76EPJ6QqtzsCLS4aimJqWO53pxZ82o3aE+Brcmo1HJ/phb9+MR7gPhyDdN6VSGaLJCRVJBZgWEhAEz+qON0Fw==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.3.1.tgz", + "integrity": "sha512-qeLa6qc0ddB0kuOZyZIhfN5q0e2htngokyTWsGriedsDhItisW7SDYZ7ceOe57Ii03sL988/03wAcBh3TChMGw==", "dev": true, "requires": { - "@jest/console": "^29.2.0", - "@jest/types": "^29.2.0", + "@jest/console": "^29.3.1", + "@jest/types": "^29.3.1", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" } }, "@jest/test-sequencer": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.2.0.tgz", - "integrity": "sha512-NCnjZcGnVdva6IDqF7TCuFsXs2F1tohiNF9sasSJNzD7VfN5ic9XgcS/oPDalGiPLxCmGKj4kewqqrKAqBACcQ==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.3.1.tgz", + "integrity": "sha512-IqYvLbieTv20ArgKoAMyhLHNrVHJfzO6ARZAbQRlY4UGWfdDnLlZEF0BvKOMd77uIiIjSZRwq3Jb3Fa3I8+2UA==", "dev": true, "requires": { - "@jest/test-result": "^29.2.0", + "@jest/test-result": "^29.3.1", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.2.0", + "jest-haste-map": "^29.3.1", "slash": "^3.0.0" } }, "@jest/transform": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.2.0.tgz", - "integrity": "sha512-NXMujGHy+B4DAj4dGnVPD0SIXlR2Z/N8Gp9h3mF66kcIRult1WWqY3/CEIrJcKviNWaFPYhZjCG2L3fteWzcUw==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.3.1.tgz", + "integrity": "sha512-8wmCFBTVGYqFNLWfcOWoVuMuKYPUBTnTMDkdvFtAYELwDOl9RGwOsvQWGPFxDJ8AWY9xM/8xCXdqmPK3+Q5Lug==", "dev": true, "requires": { "@babel/core": "^7.11.6", - "@jest/types": "^29.2.0", + "@jest/types": "^29.3.1", "@jridgewell/trace-mapping": "^0.3.15", "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", + "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.2.0", + "jest-haste-map": "^29.3.1", "jest-regex-util": "^29.2.0", - "jest-util": "^29.2.0", + "jest-util": "^29.3.1", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", @@ -9658,13 +10179,19 @@ "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } + }, + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true } } }, "@jest/types": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.2.0.tgz", - "integrity": "sha512-mfgpQz4Z2xGo37m6KD8xEpKelaVzvYVRijmLPePn9pxgaPEtX+SqIyPNzzoeCPXKYbB4L/wYSgXDL8o3Gop78Q==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.3.1.tgz", + "integrity": "sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA==", "dev": true, "requires": { "@jest/schemas": "^29.0.0", @@ -9758,9 +10285,9 @@ "dev": true }, "@sinonjs/commons": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", "dev": true, "requires": { "type-detect": "4.0.8" @@ -9848,21 +10375,21 @@ } }, "@types/express": { - "version": "4.17.14", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz", - "integrity": "sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==", + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.15.tgz", + "integrity": "sha512-Yv0k4bXGOH+8a+7bELd2PqHQsuiANB+A8a4gnQrkRWzrkKlb6KHaVvyXhqs04sVW/OWlbPyYxRgYlIXLfrufMQ==", "dev": true, "requires": { "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.18", + "@types/express-serve-static-core": "^4.17.31", "@types/qs": "*", "@types/serve-static": "*" } }, "@types/express-serve-static-core": { - "version": "4.17.31", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz", - "integrity": "sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==", + "version": "4.17.32", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.32.tgz", + "integrity": "sha512-aI5h/VOkxOF2Z1saPy0Zsxs5avets/iaiAJYznQFm5By/pamU31xWKL//epiF4OfUA2qTOc9PV6tCUjhO8wlZA==", "dev": true, "requires": { "@types/node": "*", @@ -9904,15 +10431,21 @@ } }, "@types/jest": { - "version": "29.1.2", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.1.2.tgz", - "integrity": "sha512-y+nlX0h87U0R+wsGn6EBuoRWYyv3KFtwRNP3QWp9+k2tJ2/bqcGS3UxD7jgT+tiwJWWq3UsyV4Y+T6rsMT4XMg==", + "version": "29.2.5", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.2.5.tgz", + "integrity": "sha512-H2cSxkKgVmqNHXP7TC2L/WUorrZu8ZigyRywfVzv6EyBlxj39n4C00hjXYQWsbwqgElaj/CiAeSRmk5GoaKTgw==", "dev": true, "requires": { "expect": "^29.0.0", "pretty-format": "^29.0.0" } }, + "@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -9920,9 +10453,9 @@ "dev": true }, "@types/lodash": { - "version": "4.14.186", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.186.tgz", - "integrity": "sha512-eHcVlLXP0c2FlMPm56ITode2AgLMSa6aJ05JTTbYbI+7EMkCEE5qk2E41d5g2lCVTqRe0GnnRFurmlCsDODrPw==", + "version": "4.14.191", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz", + "integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==", "dev": true }, "@types/mathjs": { @@ -9947,15 +10480,15 @@ "dev": true }, "@types/node": { - "version": "18.11.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.0.tgz", - "integrity": "sha512-IOXCvVRToe7e0ny7HpT/X9Rb2RYtElG1a+VshjwT00HxrM2dWBApHQoqsI6WiY7Q03vdf2bCrIGzVrkF/5t10w==", + "version": "18.11.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", + "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", "dev": true }, "@types/prettier": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.1.tgz", - "integrity": "sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==", + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", + "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", "dev": true }, "@types/qs": { @@ -10013,6 +10546,12 @@ "@types/node": "*" } }, + "@types/semver": { + "version": "7.3.13", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", + "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "dev": true + }, "@types/serve-static": { "version": "1.13.8", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.8.tgz", @@ -10036,9 +10575,9 @@ "dev": true }, "@types/uuid": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", - "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-kr90f+ERiQtKWMz5rP32ltJ/BtULDI5RVO0uavn1HQUOwjx0R1h0rnDYNL0CepF1zL5bSY6FISAfd9tOdDhU5Q==", "dev": true }, "@types/winston": { @@ -10065,6 +10604,235 @@ "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", "dev": true }, + "@typescript-eslint/eslint-plugin": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.47.1.tgz", + "integrity": "sha512-r4RZ2Jl9kcQN7K/dcOT+J7NAimbiis4sSM9spvWimsBvDegMhKLA5vri2jG19PmIPbDjPeWzfUPQ2hjEzA4Nmg==", + "dev": true, + "peer": true, + "requires": { + "@typescript-eslint/scope-manager": "5.47.1", + "@typescript-eslint/type-utils": "5.47.1", + "@typescript-eslint/utils": "5.47.1", + "debug": "^4.3.4", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "dependencies": { + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "peer": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "peer": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, + "@typescript-eslint/eslint-plugin-tslint": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin-tslint/-/eslint-plugin-tslint-5.47.1.tgz", + "integrity": "sha512-cbuJ1pOHJPnBT85VPJBgLUOCPzgmBttr/k7JHmSeRoIzmOJAiN0rrRWytZQwIH5JNqjCgz4MBmrU/tf4NGIf+Q==", + "dev": true, + "requires": { + "@typescript-eslint/utils": "5.47.1", + "lodash": "^4.17.21" + } + }, + "@typescript-eslint/parser": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.47.1.tgz", + "integrity": "sha512-9Vb+KIv29r6GPu4EboWOnQM7T+UjpjXvjCPhNORlgm40a9Ia9bvaPJswvtae1gip2QEeVeGh6YquqAzEgoRAlw==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.47.1", + "@typescript-eslint/types": "5.47.1", + "@typescript-eslint/typescript-estree": "5.47.1", + "debug": "^4.3.4" + }, + "dependencies": { + "@typescript-eslint/types": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.47.1.tgz", + "integrity": "sha512-CmALY9YWXEpwuu6377ybJBZdtSAnzXLSQcxLSqSQSbC7VfpMu/HLVdrnVJj7ycI138EHqocW02LPJErE35cE9A==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.47.1.tgz", + "integrity": "sha512-4+ZhFSuISAvRi2xUszEj0xXbNTHceV9GbH9S8oAD2a/F9SW57aJNQVOCxG8GPfSWH/X4eOPdMEU2jYVuWKEpWA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.47.1", + "@typescript-eslint/visitor-keys": "5.47.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.47.1.tgz", + "integrity": "sha512-rF3pmut2JCCjh6BLRhNKdYjULMb1brvoaiWDlHfLNVgmnZ0sBVJrs3SyaKE1XoDDnJuAx/hDQryHYmPUuNq0ig==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.47.1", + "eslint-visitor-keys": "^3.3.0" + } + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.47.1.tgz", + "integrity": "sha512-9hsFDsgUwrdOoW1D97Ewog7DYSHaq4WKuNs0LHF9RiCmqB0Z+XRR4Pf7u7u9z/8CciHuJ6yxNws1XznI3ddjEw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.47.1", + "@typescript-eslint/visitor-keys": "5.47.1" + }, + "dependencies": { + "@typescript-eslint/types": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.47.1.tgz", + "integrity": "sha512-CmALY9YWXEpwuu6377ybJBZdtSAnzXLSQcxLSqSQSbC7VfpMu/HLVdrnVJj7ycI138EHqocW02LPJErE35cE9A==", + "dev": true + }, + "@typescript-eslint/visitor-keys": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.47.1.tgz", + "integrity": "sha512-rF3pmut2JCCjh6BLRhNKdYjULMb1brvoaiWDlHfLNVgmnZ0sBVJrs3SyaKE1XoDDnJuAx/hDQryHYmPUuNq0ig==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.47.1", + "eslint-visitor-keys": "^3.3.0" + } + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true + } + } + }, + "@typescript-eslint/type-utils": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.47.1.tgz", + "integrity": "sha512-/UKOeo8ee80A7/GJA427oIrBi/Gd4osk/3auBUg4Rn9EahFpevVV1mUK8hjyQD5lHPqX397x6CwOk5WGh1E/1w==", + "dev": true, + "peer": true, + "requires": { + "@typescript-eslint/typescript-estree": "5.47.1", + "@typescript-eslint/utils": "5.47.1", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "dependencies": { + "@typescript-eslint/types": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.47.1.tgz", + "integrity": "sha512-CmALY9YWXEpwuu6377ybJBZdtSAnzXLSQcxLSqSQSbC7VfpMu/HLVdrnVJj7ycI138EHqocW02LPJErE35cE9A==", + "dev": true, + "peer": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.47.1.tgz", + "integrity": "sha512-4+ZhFSuISAvRi2xUszEj0xXbNTHceV9GbH9S8oAD2a/F9SW57aJNQVOCxG8GPfSWH/X4eOPdMEU2jYVuWKEpWA==", + "dev": true, + "peer": true, + "requires": { + "@typescript-eslint/types": "5.47.1", + "@typescript-eslint/visitor-keys": "5.47.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.47.1.tgz", + "integrity": "sha512-rF3pmut2JCCjh6BLRhNKdYjULMb1brvoaiWDlHfLNVgmnZ0sBVJrs3SyaKE1XoDDnJuAx/hDQryHYmPUuNq0ig==", + "dev": true, + "peer": true, + "requires": { + "@typescript-eslint/types": "5.47.1", + "eslint-visitor-keys": "^3.3.0" + } + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "peer": true + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "peer": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "peer": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, "@typescript-eslint/types": { "version": "4.33.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz", @@ -10106,6 +10874,95 @@ } } }, + "@typescript-eslint/utils": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.47.1.tgz", + "integrity": "sha512-l90SdwqfmkuIVaREZ2ykEfCezepCLxzWMo5gVfcJsJCaT4jHT+QjgSkYhs5BMQmWqE9k3AtIfk4g211z/sTMVw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.47.1", + "@typescript-eslint/types": "5.47.1", + "@typescript-eslint/typescript-estree": "5.47.1", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0", + "semver": "^7.3.7" + }, + "dependencies": { + "@typescript-eslint/types": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.47.1.tgz", + "integrity": "sha512-CmALY9YWXEpwuu6377ybJBZdtSAnzXLSQcxLSqSQSbC7VfpMu/HLVdrnVJj7ycI138EHqocW02LPJErE35cE9A==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.47.1.tgz", + "integrity": "sha512-4+ZhFSuISAvRi2xUszEj0xXbNTHceV9GbH9S8oAD2a/F9SW57aJNQVOCxG8GPfSWH/X4eOPdMEU2jYVuWKEpWA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.47.1", + "@typescript-eslint/visitor-keys": "5.47.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.47.1.tgz", + "integrity": "sha512-rF3pmut2JCCjh6BLRhNKdYjULMb1brvoaiWDlHfLNVgmnZ0sBVJrs3SyaKE1XoDDnJuAx/hDQryHYmPUuNq0ig==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.47.1", + "eslint-visitor-keys": "^3.3.0" + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, "@typescript-eslint/visitor-keys": { "version": "4.33.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz", @@ -10126,9 +10983,9 @@ } }, "acorn": { - "version": "8.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", - "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", "dev": true }, "acorn-jsx": { @@ -10142,6 +10999,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -10203,9 +11061,9 @@ } }, "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "requires": { "normalize-path": "^3.0.0", @@ -10275,19 +11133,6 @@ "es-shim-unscopables": "^1.0.0" } }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - }, "ast-module-types": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/ast-module-types/-/ast-module-types-2.7.1.tgz", @@ -10304,23 +11149,23 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" - }, - "aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" + "axios": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.2.tgz", + "integrity": "sha512-bz/J4gS2S3I7mpN/YZfGFTqhXTYzRho8Ay38w2otuuDR322KzFIWm/4W2K6gIwvWaws5n+mnb7D1lN9uD+QH6Q==", + "requires": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } }, "babel-jest": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.2.0.tgz", - "integrity": "sha512-c8FkrW1chgcbyBqOo7jFGpQYfVnb43JqjQGV+C2r94k2rZJOukYOZ6+csAqKE4ms+PHc+yevnONxs27jQIxylw==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.3.1.tgz", + "integrity": "sha512-aard+xnMoxgjwV70t0L6wkW/3HQQtV+O0PEimxKgzNqCJnbYmroPojdP2tqKSOAt8QAKV/uSZU8851M7B5+fcA==", "dev": true, "requires": { - "@jest/transform": "^29.2.0", + "@jest/transform": "^29.3.1", "@types/babel__core": "^7.1.14", "babel-plugin-istanbul": "^6.1.1", "babel-preset-jest": "^29.2.0", @@ -10408,14 +11253,6 @@ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "requires": { - "tweetnacl": "^0.14.3" - } - }, "bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -10538,7 +11375,8 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true + "dev": true, + "peer": true }, "builtins": { "version": "4.1.0", @@ -10592,11 +11430,6 @@ "integrity": "sha512-bWTlaXUy/rq0BBtYShc/jArYfBPjEV95euvZ8JVtO43oQExEN/WquoqpufFjNu4kSpi5cy5kMbNvzztWDfv1Jg==", "dev": true }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" - }, "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", @@ -10744,6 +11577,12 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, + "comment-parser": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.3.1.tgz", + "integrity": "sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==", + "dev": true + }, "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -10800,11 +11639,6 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -10816,14 +11650,6 @@ "which": "^2.0.1" } }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { - "assert-plus": "^1.0.0" - } - }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -10842,9 +11668,9 @@ } }, "decimal.js": { - "version": "10.4.2", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.2.tgz", - "integrity": "sha512-ic1yEvwT6GuvaYwBLLY6/aFFgjZdySKTE8en/fkU3QICTmRtgtSlFn0u0BXN06InZwtfCelR7j8LRiDI/02iGA==" + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" }, "decomment": { "version": "0.9.5", @@ -11046,12 +11872,13 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true + "dev": true, + "peer": true }, "diff-sequences": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.2.0.tgz", - "integrity": "sha512-413SY5JpYeSBZxmenGEmCVQ8mCgtFJF0w9PROdaS6z987XC2Pd2GOKqOITLtMftmyFZqgtCOb/QA7/Z3ZXfzIw==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.3.1.tgz", + "integrity": "sha512-hlM3QR272NXCi4pq+N4Kok4kOp6EsgOM3ZSpJI7Da3UAs+Ttsi8MRmB6trM/lhyzUxGfOgnpkHtgqm5Q/CTcfQ==", "dev": true }, "dir-glob": { @@ -11080,15 +11907,6 @@ "esutils": "^2.0.2" } }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -11101,9 +11919,9 @@ "dev": true }, "emittery": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", - "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", "dev": true }, "emoji-regex": { @@ -11228,13 +12046,15 @@ } }, "eslint": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.16.0.tgz", - "integrity": "sha512-MBndsoXY/PeVTDJeWsYj7kLZ5hQpJOfMYLsF6LicLHQWbRDG19lK5jOix4DPl8yY4SUFcE3txy86OzFLWT+yoA==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.31.0.tgz", + "integrity": "sha512-0tQQEVdmPZ1UtUKXjX7EMm9BlgJ08G90IhWh0PKDCb3ZLsgAOHI8fYSIzYVZej92zsgq+ft0FGsxhJ3xo2tbuA==", "dev": true, "requires": { - "@eslint/eslintrc": "^1.3.0", - "@humanwhocodes/config-array": "^0.9.2", + "@eslint/eslintrc": "^1.4.1", + "@humanwhocodes/config-array": "^0.11.8", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -11244,18 +12064,21 @@ "eslint-scope": "^7.1.1", "eslint-utils": "^3.0.0", "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.2", + "espree": "^9.4.0", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", - "globals": "^13.15.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", @@ -11266,8 +12089,7 @@ "regexpp": "^3.2.0", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" + "text-table": "^0.2.0" }, "dependencies": { "argparse": { @@ -11298,6 +12120,16 @@ "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", "dev": true }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, "glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -11308,9 +12140,9 @@ } }, "globals": { - "version": "13.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", - "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", + "version": "13.19.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", + "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -11331,8 +12163,17 @@ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" } }, "optionator": { @@ -11349,6 +12190,24 @@ "word-wrap": "^1.2.3" } }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -11386,6 +12245,16 @@ "dev": true, "requires": {} }, + "eslint-config-standard-with-typescript": { + "version": "24.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard-with-typescript/-/eslint-config-standard-with-typescript-24.0.0.tgz", + "integrity": "sha512-vEnGXZ5aiR1enl9652iIP4nTpY3GPcNEwuhrsPbKO3Ce3D6T3yCqZdkUPk8nJetfdL/yO0DLsHg2d/l9iECIdg==", + "dev": true, + "requires": { + "@typescript-eslint/parser": "^5.0.0", + "eslint-config-standard": "17.0.0" + } + }, "eslint-import-resolver-node": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", @@ -11568,6 +12437,45 @@ } } }, + "eslint-plugin-jquery": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jquery/-/eslint-plugin-jquery-1.5.1.tgz", + "integrity": "sha512-L7v1eaK5t80C0lvUXPFP9MKnBOqPSKhCOYyzy4LZ0+iK+TJwN8S9gAkzzP1AOhypRIwA88HF6phQ9C7jnOpW8w==", + "dev": true, + "requires": {} + }, + "eslint-plugin-jsdoc": { + "version": "39.6.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-39.6.4.tgz", + "integrity": "sha512-fskvdLCfwmPjHb6e+xNGDtGgbF8X7cDwMtVLAP2WwSf9Htrx68OAx31BESBM1FAwsN2HTQyYQq7m4aW4Q4Nlag==", + "dev": true, + "requires": { + "@es-joy/jsdoccomment": "~0.36.1", + "comment-parser": "1.3.1", + "debug": "^4.3.4", + "escape-string-regexp": "^4.0.0", + "esquery": "^1.4.0", + "semver": "^7.3.8", + "spdx-expression-parse": "^3.0.1" + }, + "dependencies": { + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, "eslint-plugin-n": { "version": "15.2.0", "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-15.2.0.tgz", @@ -11592,6 +12500,53 @@ } } }, + "eslint-plugin-node": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", + "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "dev": true, + "requires": { + "eslint-plugin-es": "^3.0.0", + "eslint-utils": "^2.0.0", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "dependencies": { + "eslint-plugin-es": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", + "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "dev": true, + "requires": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, "eslint-plugin-promise": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.0.0.tgz", @@ -11674,12 +12629,12 @@ "dev": true }, "espree": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", - "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", + "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", "dev": true, "requires": { - "acorn": "^8.7.1", + "acorn": "^8.8.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.3.0" }, @@ -11757,16 +12712,16 @@ "dev": true }, "expect": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.2.0.tgz", - "integrity": "sha512-03ClF3GWwUqd9Grgkr9ZSdaCJGMRA69PQ8jT7o+Bx100VlGiAFf9/8oIm9Qve7ZVJhuJxFftqFhviZJRxxNfvg==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.3.1.tgz", + "integrity": "sha512-gGb1yTgU30Q0O/tQq+z30KBWv24ApkMgFUpvKBkyLUBL68Wv8dHdJxTBZFl/iT8K/bqDHvUYRH6IIN3rToopPA==", "dev": true, "requires": { - "@jest/expect-utils": "^29.2.0", + "@jest/expect-utils": "^29.3.1", "jest-get-type": "^29.2.0", - "jest-matcher-utils": "^29.2.0", - "jest-message-util": "^29.2.0", - "jest-util": "^29.2.0" + "jest-matcher-utils": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-util": "^29.3.1" } }, "express": { @@ -11830,25 +12785,16 @@ } } }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true }, "fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -11861,7 +12807,8 @@ "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true }, "fast-levenshtein": { "version": "2.0.6", @@ -12008,10 +12955,20 @@ "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + "follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } }, "forwarded": { "version": "0.2.0", @@ -12058,12 +13015,6 @@ "functions-have-names": "^1.2.2" } }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, "functions-have-names": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", @@ -12136,14 +13087,6 @@ "get-intrinsic": "^1.1.1" } }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "requires": { - "assert-plus": "^1.0.0" - } - }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -12174,16 +13117,16 @@ "dev": true }, "globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "requires": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" } }, @@ -12202,6 +13145,12 @@ "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", "dev": true }, + "grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, "graphviz": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/graphviz/-/graphviz-0.0.9.tgz", @@ -12211,20 +13160,6 @@ "temp": "~0.4.0" } }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" - }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - } - }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -12286,16 +13221,6 @@ "toidentifier": "1.0.1" } }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, "human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -12508,6 +13433,12 @@ "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", "dev": true }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, "is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -12562,11 +13493,6 @@ "has-symbols": "^1.0.2" } }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, "is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -12600,11 +13526,6 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, "istanbul-lib-coverage": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", @@ -12670,15 +13591,15 @@ "integrity": "sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k=" }, "jest": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.2.0.tgz", - "integrity": "sha512-6krPemKUXCEu5Fh3j6ZVoLMjpTQVm0OCU+7f3K/9gllX8wNIE6NSCQ6s0q2RDoiKLRaQlVRHyscjSPRPqCI0Fg==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.3.1.tgz", + "integrity": "sha512-6iWfL5DTT0Np6UYs/y5Niu7WIfNv/wRTtN5RSXt2DIEft3dx3zPuw/3WJQBCJfmEzvDiEKwoqMbGD9n49+qLSA==", "dev": true, "requires": { - "@jest/core": "^29.2.0", - "@jest/types": "^29.2.0", + "@jest/core": "^29.3.1", + "@jest/types": "^29.3.1", "import-local": "^3.0.2", - "jest-cli": "^29.2.0" + "jest-cli": "^29.3.1" } }, "jest-changed-files": { @@ -12703,28 +13624,28 @@ } }, "jest-circus": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.2.0.tgz", - "integrity": "sha512-bpJRMe+VtvYlF3q8JNx+/cAo4FYvNCiR5s7Z0Scf8aC+KJ2ineSjZKtw1cIZbythlplkiro0My8nc65pfCqJ3A==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.3.1.tgz", + "integrity": "sha512-wpr26sEvwb3qQQbdlmei+gzp6yoSSoSL6GsLPxnuayZSMrSd5Ka7IjAvatpIernBvT2+Ic6RLTg+jSebScmasg==", "dev": true, "requires": { - "@jest/environment": "^29.2.0", - "@jest/expect": "^29.2.0", - "@jest/test-result": "^29.2.0", - "@jest/types": "^29.2.0", + "@jest/environment": "^29.3.1", + "@jest/expect": "^29.3.1", + "@jest/test-result": "^29.3.1", + "@jest/types": "^29.3.1", "@types/node": "*", "chalk": "^4.0.0", "co": "^4.6.0", "dedent": "^0.7.0", "is-generator-fn": "^2.0.0", - "jest-each": "^29.2.0", - "jest-matcher-utils": "^29.2.0", - "jest-message-util": "^29.2.0", - "jest-runtime": "^29.2.0", - "jest-snapshot": "^29.2.0", - "jest-util": "^29.2.0", + "jest-each": "^29.3.1", + "jest-matcher-utils": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-runtime": "^29.3.1", + "jest-snapshot": "^29.3.1", + "jest-util": "^29.3.1", "p-limit": "^3.1.0", - "pretty-format": "^29.2.0", + "pretty-format": "^29.3.1", "slash": "^3.0.0", "stack-utils": "^2.0.3" }, @@ -12751,21 +13672,21 @@ } }, "jest-cli": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.2.0.tgz", - "integrity": "sha512-/581TzbXeO+5kbtSlhXEthGiVJCC8AP0jgT0iZINAAMW+tTFj2uWU7z+HNUH5yIYdHV7AvRr0fWLrmHJGIruHg==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.3.1.tgz", + "integrity": "sha512-TO/ewvwyvPOiBBuWZ0gm04z3WWP8TIK8acgPzE4IxgsLKQgb377NYGrQLc3Wl/7ndWzIH2CDNNsUjGxwLL43VQ==", "dev": true, "requires": { - "@jest/core": "^29.2.0", - "@jest/test-result": "^29.2.0", - "@jest/types": "^29.2.0", + "@jest/core": "^29.3.1", + "@jest/test-result": "^29.3.1", + "@jest/types": "^29.3.1", "chalk": "^4.0.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", "import-local": "^3.0.2", - "jest-config": "^29.2.0", - "jest-util": "^29.2.0", - "jest-validate": "^29.2.0", + "jest-config": "^29.3.1", + "jest-util": "^29.3.1", + "jest-validate": "^29.3.1", "prompts": "^2.0.1", "yargs": "^17.3.1" }, @@ -12783,31 +13704,31 @@ } }, "jest-config": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.2.0.tgz", - "integrity": "sha512-IkdCsrHIoxDPZAyFcdtQrCQ3uftLqns6Joj0tlbxiAQW4k/zTXmIygqWBmPNxO9FbFkDrhtYZiLHXjaJh9rS+Q==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.3.1.tgz", + "integrity": "sha512-y0tFHdj2WnTEhxmGUK1T7fgLen7YK4RtfvpLFBXfQkh2eMJAQq24Vx9472lvn5wg0MAO6B+iPfJfzdR9hJYalg==", "dev": true, "requires": { "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.2.0", - "@jest/types": "^29.2.0", - "babel-jest": "^29.2.0", + "@jest/test-sequencer": "^29.3.1", + "@jest/types": "^29.3.1", + "babel-jest": "^29.3.1", "chalk": "^4.0.0", "ci-info": "^3.2.0", "deepmerge": "^4.2.2", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-circus": "^29.2.0", - "jest-environment-node": "^29.2.0", + "jest-circus": "^29.3.1", + "jest-environment-node": "^29.3.1", "jest-get-type": "^29.2.0", "jest-regex-util": "^29.2.0", - "jest-resolve": "^29.2.0", - "jest-runner": "^29.2.0", - "jest-util": "^29.2.0", - "jest-validate": "^29.2.0", + "jest-resolve": "^29.3.1", + "jest-runner": "^29.3.1", + "jest-util": "^29.3.1", + "jest-validate": "^29.3.1", "micromatch": "^4.0.4", "parse-json": "^5.2.0", - "pretty-format": "^29.2.0", + "pretty-format": "^29.3.1", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" }, @@ -12825,15 +13746,15 @@ } }, "jest-diff": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.2.0.tgz", - "integrity": "sha512-GsH07qQL+/D/GxlnU+sSg9GL3fBOcuTlmtr3qr2pnkiODCwubNN2/7slW4m3CvxDsEus/VEOfQKRFLyXsUlnZw==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.3.1.tgz", + "integrity": "sha512-vU8vyiO7568tmin2lA3r2DP8oRvzhvRcD4DjpXc6uGveQodyk7CKLhQlCSiwgx3g0pFaE88/KLZ0yaTWMc4Uiw==", "dev": true, "requires": { "chalk": "^4.0.0", - "diff-sequences": "^29.2.0", + "diff-sequences": "^29.3.1", "jest-get-type": "^29.2.0", - "pretty-format": "^29.2.0" + "pretty-format": "^29.3.1" }, "dependencies": { "chalk": { @@ -12858,16 +13779,16 @@ } }, "jest-each": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.2.0.tgz", - "integrity": "sha512-h4LeC3L/R7jIMfTdYowevPIssvcPYQ7Qzs+pCSYsJgPztIizXwKmnfhZXBA4WVqdmvMcpmseYEXb67JT7IJ2eg==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.3.1.tgz", + "integrity": "sha512-qrZH7PmFB9rEzCSl00BWjZYuS1BSOH8lLuC0azQE9lQrAx3PWGKHTDudQiOSwIy5dGAJh7KA0ScYlCP7JxvFYA==", "dev": true, "requires": { - "@jest/types": "^29.2.0", + "@jest/types": "^29.3.1", "chalk": "^4.0.0", "jest-get-type": "^29.2.0", - "jest-util": "^29.2.0", - "pretty-format": "^29.2.0" + "jest-util": "^29.3.1", + "pretty-format": "^29.3.1" }, "dependencies": { "chalk": { @@ -12883,17 +13804,17 @@ } }, "jest-environment-node": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.2.0.tgz", - "integrity": "sha512-b4qQGVStPMvtZG97Ac0rvnmSIjCZturFU7MQRMp4JDFl7zoaDLTtXmFjFP1tNmi9te6kR8d+Htbv3nYeoaIz6g==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.3.1.tgz", + "integrity": "sha512-xm2THL18Xf5sIHoU7OThBPtuH6Lerd+Y1NLYiZJlkE3hbE+7N7r8uvHIl/FkZ5ymKXJe/11SQuf3fv4v6rUMag==", "dev": true, "requires": { - "@jest/environment": "^29.2.0", - "@jest/fake-timers": "^29.2.0", - "@jest/types": "^29.2.0", + "@jest/environment": "^29.3.1", + "@jest/fake-timers": "^29.3.1", + "@jest/types": "^29.3.1", "@types/node": "*", - "jest-mock": "^29.2.0", - "jest-util": "^29.2.0" + "jest-mock": "^29.3.1", + "jest-util": "^29.3.1" } }, "jest-get-type": { @@ -12903,12 +13824,12 @@ "dev": true }, "jest-haste-map": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.2.0.tgz", - "integrity": "sha512-qu9lGFi7qJ8v37egS1phZZUJYiMyWnKwu83NlNT1qs50TbedIX2hFl+9ztsJ7U/ENaHwk1/Bs8fqOIQsScIRwg==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.3.1.tgz", + "integrity": "sha512-/FFtvoG1xjbbPXQLFef+WSU4yrc0fc0Dds6aRPBojUid7qlPqZvxdUBA03HW0fnVHXVCnCdkuoghYItKNzc/0A==", "dev": true, "requires": { - "@jest/types": "^29.2.0", + "@jest/types": "^29.3.1", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", @@ -12916,32 +13837,32 @@ "fsevents": "^2.3.2", "graceful-fs": "^4.2.9", "jest-regex-util": "^29.2.0", - "jest-util": "^29.2.0", - "jest-worker": "^29.2.0", + "jest-util": "^29.3.1", + "jest-worker": "^29.3.1", "micromatch": "^4.0.4", "walker": "^1.0.8" } }, "jest-leak-detector": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.2.0.tgz", - "integrity": "sha512-FXT9sCFdct42+oOqGIr/9kmUw3RbhvpkwidCBT5ySHHoWNGd3c9n7HXpFKjEz9UnUITRCGdn0q2s6Sxrq36kwg==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.3.1.tgz", + "integrity": "sha512-3DA/VVXj4zFOPagGkuqHnSQf1GZBmmlagpguxEERO6Pla2g84Q1MaVIB3YMxgUaFIaYag8ZnTyQgiZ35YEqAQA==", "dev": true, "requires": { "jest-get-type": "^29.2.0", - "pretty-format": "^29.2.0" + "pretty-format": "^29.3.1" } }, "jest-matcher-utils": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.2.0.tgz", - "integrity": "sha512-FcEfKZ4vm28yCdBsvC69EkrEhcfex+IYlRctNJXsRG9+WC3WxgBNORnECIgqUtj7o/h1d8o7xB/dFUiLi4bqtw==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.3.1.tgz", + "integrity": "sha512-fkRMZUAScup3txIKfMe3AIZZmPEjWEdsPJFK3AIy5qRohWqQFg1qrmKfYXR9qEkNc7OdAu2N4KPHibEmy4HPeQ==", "dev": true, "requires": { "chalk": "^4.0.0", - "jest-diff": "^29.2.0", + "jest-diff": "^29.3.1", "jest-get-type": "^29.2.0", - "pretty-format": "^29.2.0" + "pretty-format": "^29.3.1" }, "dependencies": { "chalk": { @@ -12957,18 +13878,18 @@ } }, "jest-message-util": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.2.0.tgz", - "integrity": "sha512-arBfk5yMFMTnMB22GyG601xGSGthA02vWSewPaxoFo0F9wBqDOyxccPbCcYu8uibw3kduSHXdCOd1PsLSgdomg==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.3.1.tgz", + "integrity": "sha512-lMJTbgNcDm5z+6KDxWtqOFWlGQxD6XaYwBqHR8kmpkP+WWWG90I35kdtQHY67Ay5CSuydkTBbJG+tH9JShFCyA==", "dev": true, "requires": { "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.2.0", + "@jest/types": "^29.3.1", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", - "pretty-format": "^29.2.0", + "pretty-format": "^29.3.1", "slash": "^3.0.0", "stack-utils": "^2.0.3" }, @@ -12986,20 +13907,20 @@ } }, "jest-mock": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.2.0.tgz", - "integrity": "sha512-aiWGR0P8ivssIO17xkehLGFtCcef2ZwQFNPwEer1jQLHxPctDlIg3Hs6QMq1KpPz5dkCcgM7mwGif4a9IPznlg==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.3.1.tgz", + "integrity": "sha512-H8/qFDtDVMFvFP4X8NuOT3XRDzOUTz+FeACjufHzsOIBAxivLqkB1PoLCaJx9iPPQ8dZThHPp/G3WRWyMgA3JA==", "dev": true, "requires": { - "@jest/types": "^29.2.0", + "@jest/types": "^29.3.1", "@types/node": "*", - "jest-util": "^29.2.0" + "jest-util": "^29.3.1" } }, "jest-pnp-resolver": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", - "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", "dev": true, "requires": {} }, @@ -13010,17 +13931,17 @@ "dev": true }, "jest-resolve": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.2.0.tgz", - "integrity": "sha512-f5c0ljNg2guDBCC7wi92vAhNuA0BtAG5vkY7Fob0c7sUMU1g87mTXqRmjrVFe2XvdwP5m5T/e5KJsCKu9hRvBA==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.3.1.tgz", + "integrity": "sha512-amXJgH/Ng712w3Uz5gqzFBBjxV8WFLSmNjoreBGMqxgCz5cH7swmBZzgBaCIOsvb0NbpJ0vgaSFdJqMdT+rADw==", "dev": true, "requires": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.2.0", + "jest-haste-map": "^29.3.1", "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.2.0", - "jest-validate": "^29.2.0", + "jest-util": "^29.3.1", + "jest-validate": "^29.3.1", "resolve": "^1.20.0", "resolve.exports": "^1.1.0", "slash": "^3.0.0" @@ -13039,40 +13960,40 @@ } }, "jest-resolve-dependencies": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.2.0.tgz", - "integrity": "sha512-Cd0Z39sDntEnfR9PoUdFHUAGDvtKI0/7Wt73l3lt03A3yQ+A6Qi3XmBuqGjdFl2QbXaPa937oLhilG612P8HGQ==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.3.1.tgz", + "integrity": "sha512-Vk0cYq0byRw2WluNmNWGqPeRnZ3p3hHmjJMp2dyyZeYIfiBskwq4rpiuGFR6QGAdbj58WC7HN4hQHjf2mpvrLA==", "dev": true, "requires": { "jest-regex-util": "^29.2.0", - "jest-snapshot": "^29.2.0" + "jest-snapshot": "^29.3.1" } }, "jest-runner": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.2.0.tgz", - "integrity": "sha512-VPBrCwl9fM2mc5yk6yZhNrgXzRJMD5jfLmntkMLlrVq4hQPWbRK998iJlR+DOGCO04TC9PPYLntOJ001Vnf28g==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.3.1.tgz", + "integrity": "sha512-oFvcwRNrKMtE6u9+AQPMATxFcTySyKfLhvso7Sdk/rNpbhg4g2GAGCopiInk1OP4q6gz3n6MajW4+fnHWlU3bA==", "dev": true, "requires": { - "@jest/console": "^29.2.0", - "@jest/environment": "^29.2.0", - "@jest/test-result": "^29.2.0", - "@jest/transform": "^29.2.0", - "@jest/types": "^29.2.0", + "@jest/console": "^29.3.1", + "@jest/environment": "^29.3.1", + "@jest/test-result": "^29.3.1", + "@jest/transform": "^29.3.1", + "@jest/types": "^29.3.1", "@types/node": "*", "chalk": "^4.0.0", - "emittery": "^0.10.2", + "emittery": "^0.13.1", "graceful-fs": "^4.2.9", "jest-docblock": "^29.2.0", - "jest-environment-node": "^29.2.0", - "jest-haste-map": "^29.2.0", - "jest-leak-detector": "^29.2.0", - "jest-message-util": "^29.2.0", - "jest-resolve": "^29.2.0", - "jest-runtime": "^29.2.0", - "jest-util": "^29.2.0", - "jest-watcher": "^29.2.0", - "jest-worker": "^29.2.0", + "jest-environment-node": "^29.3.1", + "jest-haste-map": "^29.3.1", + "jest-leak-detector": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-resolve": "^29.3.1", + "jest-runtime": "^29.3.1", + "jest-util": "^29.3.1", + "jest-watcher": "^29.3.1", + "jest-worker": "^29.3.1", "p-limit": "^3.1.0", "source-map-support": "0.5.13" }, @@ -13099,31 +14020,31 @@ } }, "jest-runtime": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.2.0.tgz", - "integrity": "sha512-+GDmzCrswQF+mvI0upTYMe/OPYnlRRNLLDHM9AFLp2y7zxWoDoYgb8DL3WwJ8d9m743AzrnvBV9JQHi/0ed7dg==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.3.1.tgz", + "integrity": "sha512-jLzkIxIqXwBEOZx7wx9OO9sxoZmgT2NhmQKzHQm1xwR1kNW/dn0OjxR424VwHHf1SPN6Qwlb5pp1oGCeFTQ62A==", "dev": true, "requires": { - "@jest/environment": "^29.2.0", - "@jest/fake-timers": "^29.2.0", - "@jest/globals": "^29.2.0", + "@jest/environment": "^29.3.1", + "@jest/fake-timers": "^29.3.1", + "@jest/globals": "^29.3.1", "@jest/source-map": "^29.2.0", - "@jest/test-result": "^29.2.0", - "@jest/transform": "^29.2.0", - "@jest/types": "^29.2.0", + "@jest/test-result": "^29.3.1", + "@jest/transform": "^29.3.1", + "@jest/types": "^29.3.1", "@types/node": "*", "chalk": "^4.0.0", "cjs-module-lexer": "^1.0.0", "collect-v8-coverage": "^1.0.0", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.2.0", - "jest-message-util": "^29.2.0", - "jest-mock": "^29.2.0", + "jest-haste-map": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-mock": "^29.3.1", "jest-regex-util": "^29.2.0", - "jest-resolve": "^29.2.0", - "jest-snapshot": "^29.2.0", - "jest-util": "^29.2.0", + "jest-resolve": "^29.3.1", + "jest-snapshot": "^29.3.1", + "jest-util": "^29.3.1", "slash": "^3.0.0", "strip-bom": "^4.0.0" }, @@ -13141,9 +14062,9 @@ } }, "jest-snapshot": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.2.0.tgz", - "integrity": "sha512-YCKrOR0PLRXROmww73fHO9oeY4tL+LPQXWR3yml1+hKbQDR8j1VUrVzB65hKSJJgxBOr1vWx+hmz2by8JjAU5w==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.3.1.tgz", + "integrity": "sha512-+3JOc+s28upYLI2OJM4PWRGK9AgpsMs/ekNryUV0yMBClT9B1DF2u2qay8YxcQd338PPYSFNb0lsar1B49sLDA==", "dev": true, "requires": { "@babel/core": "^7.11.6", @@ -13152,23 +14073,23 @@ "@babel/plugin-syntax-typescript": "^7.7.2", "@babel/traverse": "^7.7.2", "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.2.0", - "@jest/transform": "^29.2.0", - "@jest/types": "^29.2.0", + "@jest/expect-utils": "^29.3.1", + "@jest/transform": "^29.3.1", + "@jest/types": "^29.3.1", "@types/babel__traverse": "^7.0.6", "@types/prettier": "^2.1.5", "babel-preset-current-node-syntax": "^1.0.0", "chalk": "^4.0.0", - "expect": "^29.2.0", + "expect": "^29.3.1", "graceful-fs": "^4.2.9", - "jest-diff": "^29.2.0", + "jest-diff": "^29.3.1", "jest-get-type": "^29.2.0", - "jest-haste-map": "^29.2.0", - "jest-matcher-utils": "^29.2.0", - "jest-message-util": "^29.2.0", - "jest-util": "^29.2.0", + "jest-haste-map": "^29.3.1", + "jest-matcher-utils": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-util": "^29.3.1", "natural-compare": "^1.4.0", - "pretty-format": "^29.2.0", + "pretty-format": "^29.3.1", "semver": "^7.3.5" }, "dependencies": { @@ -13194,12 +14115,12 @@ } }, "jest-util": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.2.0.tgz", - "integrity": "sha512-8M1dx12ujkBbnhwytrezWY0Ut79hbflwodE+qZKjxSRz5qt4xDp6dQQJaOCFvCmE0QJqp9KyEK33lpPNjnhevw==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.3.1.tgz", + "integrity": "sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ==", "dev": true, "requires": { - "@jest/types": "^29.2.0", + "@jest/types": "^29.3.1", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", @@ -13220,17 +14141,17 @@ } }, "jest-validate": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.2.0.tgz", - "integrity": "sha512-4Vl51bPNeFeDok9aJiOnrC6tqJbOp4iMCYlewoC2ZzYJZ5+6pfr3KObAdx5wP8auHcg2MRaguiqj5OdScZa72g==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.3.1.tgz", + "integrity": "sha512-N9Lr3oYR2Mpzuelp1F8negJR3YE+L1ebk1rYA5qYo9TTY3f9OWdptLoNSPP9itOCBIRBqjt/S5XHlzYglLN67g==", "dev": true, "requires": { - "@jest/types": "^29.2.0", + "@jest/types": "^29.3.1", "camelcase": "^6.2.0", "chalk": "^4.0.0", "jest-get-type": "^29.2.0", "leven": "^3.1.0", - "pretty-format": "^29.2.0" + "pretty-format": "^29.3.1" }, "dependencies": { "camelcase": { @@ -13252,18 +14173,18 @@ } }, "jest-watcher": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.2.0.tgz", - "integrity": "sha512-bRh0JdUeN+cl9XfK7tMnXLm4Mv70hG2SZlqbkFe5CTs7oeCkbwlGBk/mEfEJ63mrxZ8LPbnfaMpfSmkhEQBEGA==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.3.1.tgz", + "integrity": "sha512-RspXG2BQFDsZSRKGCT/NiNa8RkQ1iKAjrO0//soTMWx/QUt+OcxMqMSBxz23PYGqUuWm2+m2mNNsmj0eIoOaFg==", "dev": true, "requires": { - "@jest/test-result": "^29.2.0", - "@jest/types": "^29.2.0", + "@jest/test-result": "^29.3.1", + "@jest/types": "^29.3.1", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", - "emittery": "^0.10.2", - "jest-util": "^29.2.0", + "emittery": "^0.13.1", + "jest-util": "^29.3.1", "string-length": "^4.0.1" }, "dependencies": { @@ -13280,13 +14201,13 @@ } }, "jest-worker": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.2.0.tgz", - "integrity": "sha512-mluOlMbRX1H59vGVzPcVg2ALfCausbBpxC8a2KWOzInhYHZibbHH8CB0C1JkmkpfurrkOYgF7FPmypuom1OM9A==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.3.1.tgz", + "integrity": "sha512-lY4AnnmsEWeiXirAIA0c9SDPbuCBq8IYuDVL8PMm0MZ2PEs2yPvRA/J64QBXuZp7CYKrDM/rmNrc9/i3KJQncw==", "dev": true, "requires": { "@types/node": "*", - "jest-util": "^29.2.0", + "jest-util": "^29.3.1", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, @@ -13308,6 +14229,12 @@ } } }, + "js-sdsl": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", + "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==", + "dev": true + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -13324,10 +14251,11 @@ "esprima": "^4.0.0" } }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + "jsdoc-type-pratt-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-3.1.0.tgz", + "integrity": "sha512-MgtD0ZiCDk9B+eI73BextfRrVQl0oyzRG8B2BjORts6jbunj4ScKPcyXGTbB6eXL4y9TzxCm6hyeLq/2ASzNdw==", + "dev": true }, "jsesc": { "version": "2.5.2", @@ -13347,15 +14275,11 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, - "json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" - }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -13363,11 +14287,6 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, "json5": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", @@ -13379,17 +14298,6 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-1.1.1.tgz", "integrity": "sha1-2k/WrXfxolUgPqY8e8Mtwx72RDM=" }, - "jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - } - }, "jsx-ast-utils": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.0.tgz", @@ -13647,13 +14555,13 @@ } }, "mathjs": { - "version": "11.3.0", - "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-11.3.0.tgz", - "integrity": "sha512-HAUFLxyH8WMSDisNljQFIKjp//d1iL0C36nhGEJFwzdMb46gACMG1E8Ze06sES4hJ5jHDv5ieq7r3mDDcU/d4g==", + "version": "11.5.0", + "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-11.5.0.tgz", + "integrity": "sha512-vJ/+SqWtxjW6/aeDRt8xL3TlOVKqwN15BIyTGVqGbIWuiqgY4SxZ0yLuna82YH9CB757iFP7uJ4m3KvVBX7Qcg==", "requires": { - "@babel/runtime": "^7.19.4", + "@babel/runtime": "^7.20.6", "complex.js": "^2.1.1", - "decimal.js": "^10.4.1", + "decimal.js": "^10.4.3", "escape-latex": "^1.2.0", "fraction.js": "^4.2.0", "javascript-natural-sort": "^0.7.1", @@ -13742,6 +14650,7 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "dev": true, + "peer": true, "requires": { "minimist": "^1.2.5" } @@ -13791,6 +14700,13 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true, + "peer": true + }, "ncp": { "version": "0.4.2", "resolved": "http://registry.npmjs.org/ncp/-/ncp-0.4.2.tgz", @@ -13860,11 +14776,6 @@ "path-key": "^3.0.0" } }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" - }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -14104,11 +15015,6 @@ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -14269,9 +15175,9 @@ "dev": true }, "pretty-format": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.2.0.tgz", - "integrity": "sha512-QCSUFdwOi924g24czhOH5eTkXxUCqlLGZBRCySlwDYHIXRJkdGyjJc9nZaqhlFBZws8dq5Dvk0lCilsmlfsPxw==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.3.1.tgz", + "integrity": "sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg==", "dev": true, "requires": { "@jest/schemas": "^29.0.0", @@ -14334,20 +15240,16 @@ "ipaddr.js": "1.9.1" } }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true }, "queue-microtask": { "version": "1.2.3", @@ -14419,9 +15321,9 @@ "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" }, "regenerator-runtime": { - "version": "0.13.10", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz", - "integrity": "sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw==" + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, "regexp.prototype.flags": { "version": "1.4.3", @@ -14445,68 +15347,6 @@ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - } - } - }, - "request-promise-core": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", - "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", - "requires": { - "lodash": "^4.17.19" - } - }, - "request-promise-native": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", - "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", - "requires": { - "request-promise-core": "1.1.4", - "stealthy-require": "^1.1.1", - "tough-cookie": "^2.3.3" - } - }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -14603,7 +15443,8 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true }, "safe-stable-stringify": { "version": "2.3.1", @@ -14638,7 +15479,8 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", - "dev": true + "dev": true, + "peer": true }, "send": { "version": "0.18.0", @@ -14778,28 +15620,34 @@ "source-map": "^0.6.0" } }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", + "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", + "dev": true + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, "stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", @@ -14855,11 +15703,6 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" }, - "stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" - }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -15070,15 +15913,6 @@ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, "triple-beam": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", @@ -15147,6 +15981,7 @@ "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", "dev": true, + "peer": true, "requires": { "@babel/code-frame": "^7.0.0", "builtin-modules": "^1.1.1", @@ -15163,70 +15998,12 @@ "tsutils": "^2.29.0" } }, - "tslint-config-standard": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/tslint-config-standard/-/tslint-config-standard-9.0.0.tgz", - "integrity": "sha512-CAw9J743RnPMemQV/XQ4YyNreC+A1NItACfkm+cBedrOkz6CQfwlnbKn8anUXBfoa4Zo4tjAhblRbsMNcSLfSw==", - "dev": true, - "requires": { - "tslint-eslint-rules": "^5.3.1" - } - }, - "tslint-eslint-rules": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/tslint-eslint-rules/-/tslint-eslint-rules-5.4.0.tgz", - "integrity": "sha512-WlSXE+J2vY/VPgIcqQuijMQiel+UtmXS+4nvK4ZzlDiqBfXse8FAvkNnTcYhnQyOTW5KFM+uRRGXxYhFpuBc6w==", - "dev": true, - "requires": { - "doctrine": "0.7.2", - "tslib": "1.9.0", - "tsutils": "^3.0.0" - }, - "dependencies": { - "doctrine": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-0.7.2.tgz", - "integrity": "sha1-fLhgNZujvpDgQLJrcpzkv6ZUxSM=", - "dev": true, - "requires": { - "esutils": "^1.1.6", - "isarray": "0.0.1" - } - }, - "esutils": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.1.6.tgz", - "integrity": "sha1-wBzKqa5LiXxtDD4hCuUvPHqEQ3U=", - "dev": true - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "tslib": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.0.tgz", - "integrity": "sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ==", - "dev": true - }, - "tsutils": { - "version": "3.17.1", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", - "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - } - } - }, "tsutils": { "version": "2.29.0", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", "dev": true, + "peer": true, "requires": { "tslib": "^1.8.1" } @@ -15239,19 +16016,6 @@ "tslib": "^1.9.3" } }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" - }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", @@ -15288,9 +16052,9 @@ "integrity": "sha512-DGwUl6cioBW5gw2L+6SMupGwH/kZOqivy17E4nsh1JI9fKF87orMmlQx3KISQPmg3sfnOUGlwVkroosvgddrlg==" }, "typescript": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", - "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", "dev": true }, "unbox-primitive": { @@ -15330,6 +16094,7 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, "requires": { "punycode": "^2.1.0" } @@ -15349,12 +16114,6 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==" }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, "v8-to-istanbul": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", @@ -15371,16 +16130,6 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, "walkdir": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.4.1.tgz", @@ -15506,9 +16255,9 @@ "dev": true }, "yargs": { - "version": "17.6.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.0.tgz", - "integrity": "sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==", + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", + "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", "dev": true, "requires": { "cliui": "^8.0.1", @@ -15517,13 +16266,13 @@ "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" + "yargs-parser": "^21.1.1" } }, "yargs-parser": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", - "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true }, "yauzl": { diff --git a/package.json b/package.json index f355c264..ec66a0b8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jspurefix", - "version": "2.2.1", + "version": "3.0.0", "description": "pure node js fix engine", "keywords": [ "typescript", @@ -24,57 +24,57 @@ }, "scripts": { "circular": "madge --circular --extensions js dist && madge --circular --extensions ts src", - "test": "jest", + "test": "jest --maxWorkers=1 --coverage", "build": "tsc", "test:debug": "node --inspect-brk node_modules/.bin/jest --runInBand", - "cmd": "cd dist && node jsfix-cmd", - "repo44": "cd dist && node jsfix-cmd --dict=repo44", - "repo44-unit": "cd dist && node jsfix-cmd --dict=repo44 --generate --density=0.8 --unit --delimiter=\"|\" --session=data/session/test-initiator.json", - "repo44-script": "cd dist && node jsfix-cmd --dict=repo44 --generate --density=0.8 --script --delimiter=\"|\" --session=data/session/test-initiator.json", - "repo44-replay": "cd dist && node jsfix-cmd --dict=repo44 --fix=data/examples/FIX.4.4/fix.txt --delimiter=\"|\" --session=data/session/test-initiator.json", - "repo44-repscr": "cd dist && node jsfix-cmd --dict=repo44 --fix=dist/fix.txt --delimiter=\"|\" --stats", - "repo44-bench": "cd dist && node jsfix-cmd --dict=repo44 --benchmark --delimiter=\"|\"", - "repo44-bench-er": "cd dist && node jsfix-cmd --dict=repo44 --fix=data/examples/FIX.4.4/repo/execution-report/fix.txt --benchmark --delimiter=\"|\" --repeats=125000", - "repo44-bench-tc": "cd dist && node jsfix-cmd ---dict=repo44 --fix=data/examples/FIX.4.4/repo/trade-capture/fix.txt --benchmark --delimiter=\"|\" --repeats=30000", - "repo44-bench-sd": "cd dist && node jsfix-cmd --dict=repo44 --fix=data/examples/FIX.4.4/repo/security-definition/fix.txt --benchmark --delimiter=\"|\" --repeats=150000", - "repo44-bench-lo": "cd dist && node jsfix-cmd --dict=repo44 --fix=data/examples/FIX.4.4/repo/logon/fix.txt --benchmark --delimiter=\"|\" --repeats=250000", - "qf-bench-lo": "cd dist && node jsfix-cmd --session=data/session/test-initiator.json --fix=data/examples/FIX.4.4/quickfix/logon/fix.txt --benchmark --delimiter=\"|\" --repeats=250000", - "fixml": "cd dist && node jsfix-cmd --dict=repofixml", - "tcp-qf-md": "cd dist/sample/tcp/qf-md && node app", - "tcp-tls-tc": "cd dist/sample/tcp/tls-trade-capture && node app", - "tcp-tc": "cd dist/sample/tcp/trade-capture && node app", - "tcp-rec-sk": "cd dist/sample/tcp/recovering-skeleton && node app", - "tcp-sk": "cd dist/sample/tcp/skeleton && node app", - "http-oms": "cd dist/sample/http/oms && node app", - "repo40-compile": "cd dist && node jsfix-cmd \"--dict=repo40\" \"--compile\"", - "repo41-compile": "cd dist && node jsfix-cmd \"--dict=repo41\" \"--compile\"", - "repo42-compile": "cd dist && node jsfix-cmd \"--dict=repo42\" \"--compile\"", - "repo43-compile": "cd dist && node jsfix-cmd \"--dict=repo43\" \"--compile\"", - "repo44-compile": "cd dist && node jsfix-cmd \"--dict=repo44\" \"--compile\"", - "repo50-compile": "cd dist && node jsfix-cmd \"--dict=repo50\" \"--compile\"", - "repo50sp1-compile": "cd dist && node jsfix-cmd \"--dict=repo50sp1\" \"--compile\"", - "repo50sp2-compile": "cd dist && node jsfix-cmd \"--dict=repo50sp2\" \"--compile\"", - "repofixml-compile": "cd dist && node jsfix-cmd \"--dict=repofixml\" \"--compile\"", - "qf44-compile": "cd dist && node jsfix-cmd \"--dict=qf44\" \"--compile\"", - "qf43-compile": "cd dist && node jsfix-cmd \"--dict=qf43\" \"--compile\"", - "qf42-compile": "cd dist && node jsfix-cmd \"--dict=qf42\" \"--compile\"", + "cmd": "node dist/jsfix-cmd", + "repo44": "node dist/jsfix-cmd --dict=repo44", + "repo44-unit": "node dist/jsfix-cmd --dict=repo44 --generate --density=0.8 --unit --delimiter=\"|\" --session=data/session/test-initiator.json", + "repo44-script": "node dist/jsfix-cmd --dict=repo44 --generate --density=0.8 --script --delimiter=\"|\" --session=data/session/test-initiator.json", + "repo44-replay": "node dist/jsfix-cmd --dict=repo44 --fix=data/examples/FIX.4.4/fix.txt --delimiter=\"|\" --stats--delimiter=\"|\" --objects", + "repo44-repscr": "node dist/jsfix-cmd --dict=repo44 --fix=data/examples/FIX.4.4/fix.txt --delimiter=\"|\" --stats--delimiter=\"|\" --stats", + "repo44-bench": "node dist/jsfix-cmd --dict=repo44 --benchmark --delimiter=\"|\"", + "repo44-bench-er": "node dist/jsfix-cmd --dict=repo44 --fix=data/examples/FIX.4.4/repo/execution-report/fix.txt --benchmark --delimiter=\"|\" --repeats=125000", + "repo44-bench-tc": "node dist/jsfix-cmd jsfix-cmd ---dict=repo44 --fix=data/examples/FIX.4.4/repo/trade-capture/fix.txt --benchmark --delimiter=\"|\" --repeats=30000", + "repo44-bench-sd": "node dist/jsfix-cmd --dict=repo44 --fix=data/examples/FIX.4.4/repo/security-definition/fix.txt --benchmark --delimiter=\"|\" --repeats=150000", + "repo44-bench-lo": "node dist/jsfix-cmd --dict=repo44 --fix=data/examples/FIX.4.4/repo/logon/fix.txt --benchmark --delimiter=\"|\" --repeats=250000", + "qf-bench-lo": "node dist/jsfix-cmd --session=data/session/test-initiator.json --fix=data/examples/FIX.4.4/quickfix/logon/fix.txt --benchmark --delimiter=\"|\" --repeats=250000", + "qf-bench-hb": "node dist/jsfix-cmd --session=data/session/test-initiator.json --fix=data/examples/FIX.4.4/quickfix/heartbeat/fix.txt --benchmark --delimiter=\"|\" --repeats=250000", + "fixml": "node dist/jsfix-cmd --dict=repofixml", + "tcp-qf-md": "node dist/sample/tcp/qf-md/app", + "tcp-tls-tc": "node dist/sample/tcp/tls-trade-capture/app", + "tcp-tc": "node dist/sample/tcp/trade-capture/app", + "tcp-rec-sk": "node dist/sample/tcp/recovering-skeleton/app", + "tcp-sk": "node dist/sample/tcp/skeleton/app", + "http-oms": "node dist/sample/http/oms/app", + "repo40-compile": "node dist/jsfix-cmd \"--dict=repo40\" \"--compile\"", + "repo41-compile": "node dist/jsfix-cmd \"--dict=repo41\" \"--compile\"", + "repo42-compile": "node dist/jsfix-cmd \"--dict=repo42\" \"--compile\"", + "repo43-compile": "node dist/jsfix-cmd \"--dict=repo43\" \"--compile\"", + "repo44-compile": "node dist/jsfix-cmd \"--dict=repo44\" \"--compile\"", + "repo50-compile": "node dist/jsfix-cmd \"--dict=repo50\" \"--compile\"", + "repo50sp1-compile": "node dist/jsfix-cmd \"--dict=repo50sp1\" \"--compile\"", + "repo50sp2-compile": "node dist/jsfix-cmd \"--dict=repo50sp2\" \"--compile\"", + "repofixml-compile": "node dist/jsfix-cmd \"--dict=repofixml\" \"--compile\"", + "qf44-compile": "node dist/jsfix-cmd \"--dict=qf44\" \"--compile\"", + "qf43-compile": "node dist/jsfix-cmd \"--dict=qf43\" \"--compile\"", + "qf42-compile": "node dist/jsfix-cmd \"--dict=qf42\" \"--compile\"", "unzip-repo": "cd data && node ../src/util/unzip fix_repo.zip", - "fixml-test-print": "cd dist && node jsfix-cmd --session=data/session/test-http-initiator.json --fix=data/examples/fixml/jsfix.test_http-client.txt --objects", - "fix-test-print": "cd dist && node jsfix-cmd --dict=repo44 --fix=data/examples/FIX.4.4/jsfix.test_client.txt --objects --delimiter=\"|\"" + "fixml-test-print": "node dist/jsfix-cmd --session=data/session/test-http-initiator.json --fix=data/examples/fixml/jsfix.test_http-client.txt --objects", + "fix-test-print": "node dist/jsfix-cmd --dict=repo44 --fix=data/examples/FIX.4.4/jsfix.test_client.txt --objects --delimiter=\"|\"" }, "author": "", "license": "MIT", "dependencies": { "align-text": "^1.0.2", + "axios": "^1.2.2", "express": "^4.18.2", "lodash": "^4.17.21", - "mathjs": "^11.3.0", + "mathjs": "^11.5.0", "minimist": "^1.2.7", "moment": "^2.29.4", "node-fs-extra": "^0.8.2", "reflect-metadata": "^0.1.13", - "request": "^2.88.2", - "request-promise-native": "^1.0.9", "sax": "^1.2.4", "tsyringe": "^4.7.0", "uuid": "^9.0.0", @@ -83,23 +83,27 @@ "yauzl": "^2.10.0" }, "devDependencies": { - "@types/express": "^4.17.14", - "@types/express-serve-static-core": "^4.17.31", - "@types/jest": "^29.1.2", - "@types/lodash": "^4.14.186", + "@types/express": "^4.17.15", + "@types/express-serve-static-core": "^4.17.32", + "@types/jest": "^29.2.5", + "@types/lodash": "^4.14.191", "@types/mathjs": "^9.4.1", "@types/minimist": "^1.2.2", - "@types/node": "^18.11.0", + "@types/node": "^18.11.18", "@types/request-promise-native": "^1.0.18", "@types/sax": "^1.2.4", - "@types/uuid": "^8.3.4", + "@types/uuid": "^9.0.0", "@types/winston": "^2.4.4", - "jest": "^29.2.0", + "@typescript-eslint/eslint-plugin-tslint": "^5.47.1", + "eslint": "^8.31.0", + "eslint-config-standard-with-typescript": "^24.0.0", + "eslint-plugin-jquery": "^1.5.1", + "eslint-plugin-jsdoc": "^39.6.4", + "eslint-plugin-node": "^11.1.0", + "jest": "^29.3.1", "madge": "^5.0.1", "standard": "^17.0.0", "ts-jest": "^29.0.3", - "tslint": "^6.1.3", - "tslint-config-standard": "^9.0.0", - "typescript": "^4.8.4" + "typescript": "^4.9.4" } } diff --git a/src/buffer/ascii/ascii-chars.ts b/src/buffer/ascii/ascii-chars.ts index 4ac15c04..1dc06ee4 100644 --- a/src/buffer/ascii/ascii-chars.ts +++ b/src/buffer/ascii/ascii-chars.ts @@ -1,5 +1,4 @@ export abstract class AsciiChars { - public static readonly Minus: number = '-'.charCodeAt(0) public static readonly Equal: number = '='.charCodeAt(0) public static readonly ForwardSlash: number = '/'.charCodeAt(0) diff --git a/src/buffer/ascii/ascii-encoder.ts b/src/buffer/ascii/ascii-encoder.ts index 0ea314cd..17544276 100644 --- a/src/buffer/ascii/ascii-encoder.ts +++ b/src/buffer/ascii/ascii-encoder.ts @@ -14,17 +14,16 @@ import { Tags } from '../tag/tags' import { TagType } from '../tag/tag-type' export class AsciiEncoder extends MsgEncoder { - public bodyLengthPos: number public msgTypePos: number public tags: Tags public checkGroups: boolean = true constructor (public readonly buffer: ElasticBuffer, - public readonly definitions: FixDefinitions, - public readonly timeFormatter: ITimeFormatter = new TimeFormatter(buffer), - public readonly delimiter: number = AsciiChars.Soh, - public readonly logDelimiter: number = AsciiChars.Pipe) { + public readonly definitions: FixDefinitions, + public readonly timeFormatter: ITimeFormatter = new TimeFormatter(buffer), + public readonly delimiter: number = AsciiChars.Soh, + public readonly logDelimiter: number = AsciiChars.Pipe) { super(definitions) this.tags = new Tags(definitions) } @@ -87,7 +86,7 @@ export class AsciiEncoder extends MsgEncoder { const keys: string[] = Object.keys(o) let j: number = 0 const fields: ContainedField[] = keys.reduce((a: ContainedField[], current: string) => { - const field: ContainedField = set.localNameToField.get(current) + const field: ContainedField | null = set.localNameToField.get(current) if (field) { a[j++] = field } @@ -98,7 +97,8 @@ export class AsciiEncoder extends MsgEncoder { } private encodeInstances (o: ILooseObject, gf: ContainedGroupField): void { - const noOfField: SimpleFieldDefinition = gf.definition.noOfField + const noOfField: SimpleFieldDefinition | null = gf.definition.noOfField + if (!noOfField) return const instances: ILooseObject[] = o[gf.name] || o[noOfField.name] const buffer = this.buffer @@ -167,7 +167,6 @@ export class AsciiEncoder extends MsgEncoder { } switch (tagType) { - case TagType.RawData: { // may need to first write raw message length (see below) break @@ -262,7 +261,7 @@ class GroupValidator { public readonly test: AsciiEncodeSetSummary = new AsciiEncodeSetSummary()) { } - getSummary (field: number) { + getSummary (field: number): AsciiEncodeSetSummary { return field === 0 ? this.first : this.test } @@ -270,31 +269,34 @@ class GroupValidator { const first = this.first const test = this.test if (field === 0 && first.empty()) { - throw new Error(`first group instance has no delimeter present ${this.gf.name}`) + throw new Error(`first group instance has no delimiter present ${this.gf.name}`) } if (field > 0 && test.empty()) { - throw new Error(`group instance [${field}] has no delimeter present ${this.gf.name}`) + throw new Error(`group instance [${field}] has no delimiter present ${this.gf.name}`) } if (field > 0) { - const firstTag = first.firstSimple.definition.tag - const tag = test.firstSimple.definition.tag + const firstTag = first.firstSimple?.definition.tag + const tag = test.firstSimple?.definition.tag if (firstTag !== tag) { - throw new Error(`group instance [${field}] inconsisent delimeter ${tag} expected tag ${firstTag}`) + const msg = `group instance [${field}] inconsistent delimiter ${tag} expected tag ${firstTag}` + throw new Error(msg) } } } } class AsciiEncodeSetSummary { - constructor (public firstSimple: ContainedSimpleField = null, - public lastSimple: ContainedSimpleField = null, + constructor (public firstSimple: ContainedSimpleField | null = null, + public lastSimple: ContainedSimpleField | null = null, public count: number = 0) { } + public reset (): void { this.firstSimple = null this.lastSimple = null this.count = 0 } + public empty (): boolean { return this.firstSimple === null || this.count === 0 } diff --git a/src/buffer/ascii/ascii-parser-state.ts b/src/buffer/ascii/ascii-parser-state.ts index 883dec28..2d0a0213 100644 --- a/src/buffer/ascii/ascii-parser-state.ts +++ b/src/buffer/ascii/ascii-parser-state.ts @@ -7,7 +7,7 @@ import { DITokens } from '../../runtime/di-tokens' @injectable() export class AsciiParserState { - public message: MessageDefinition + public message: MessageDefinition | null public locations: Tags public parseState: ParseState public bodyLen: number @@ -19,12 +19,12 @@ export class AsciiParserState { public currentTag: number public rawDataLen: number public rawDataRead: number - public msgType: string + public msgType: string | null constructor (@inject(DITokens.ParseBuffer) public readonly elasticBuffer: ElasticBuffer) { } - public beginTag (pos: number) { + public beginTag (pos: number): void { this.parseState = ParseState.ParsingTag this.tagStartPos = pos this.equalPos = this.valueEndPos = -1 @@ -54,7 +54,7 @@ export class AsciiParserState { public checkRawTag (): void { const msg = this.message - if (!msg || !msg.containsRaw) { + if (!msg?.containsRaw) { // optimisation as will never hit raw data this.parseState = ParseState.ParsingValue return diff --git a/src/buffer/ascii/ascii-parser.ts b/src/buffer/ascii/ascii-parser.ts index 8084c9c2..26bfaf52 100644 --- a/src/buffer/ascii/ascii-parser.ts +++ b/src/buffer/ascii/ascii-parser.ts @@ -28,12 +28,12 @@ export class AsciiParser extends MsgParser { // want to keep one slice of memory and constantly reuse it constructor (@inject(DITokens.IJsFixConfig) public readonly config: IJsFixConfig, - @inject(DITokens.readStream) public readonly readStream: Readable, - @inject(DITokens.ParseBuffer) protected readonly receivingBuffer: ElasticBuffer) { + @inject(DITokens.readStream) public readonly readStream: Readable | null, + @inject(DITokens.ParseBuffer) protected readonly receivingBuffer: ElasticBuffer) { super() - this.delimiter = config.delimiter - this.writeDelimiter = config.logDelimiter || AsciiChars.Pipe + this.delimiter = config.delimiter ?? AsciiChars.Soh + this.writeDelimiter = config.logDelimiter ?? AsciiChars.Pipe const definitions = config.definitions this.id = AsciiParser.nextId++ this.segmentParser = config.sessionContainer.resolve(AsciiSegmentParser) @@ -65,13 +65,13 @@ export class AsciiParser extends MsgParser { }) // receive from say a socket or file and pipe to parser which discovers messages - stream.pipe(receiver).on('finish', () => { + stream?.pipe(receiver).on('finish', () => { this.emit('done') }) - stream.on('error', (e) => { + stream?.on('error', (e) => { this.emit('error', e) }) - stream.on('end', () => { + stream?.on('end', () => { this.emit('end') }) } @@ -86,7 +86,7 @@ export class AsciiParser extends MsgParser { state.beginMessage() } - public parseText (text: string) { + public parseText (text: string): void { const buff = Buffer.from(text) this.parse(buff, buff.length) } @@ -103,11 +103,10 @@ export class AsciiParser extends MsgParser { let readPtr: number = 0 while (readPtr < end) { - let charAtPos: number = readBuffer[readPtr] + const charAtPos: number = readBuffer[readPtr] const writePtr = receivingBuffer.saveChar(charAtPos) - 1 switch (state.parseState) { - case ParseState.MsgComplete: { this.msg(writePtr) continue @@ -157,7 +156,8 @@ export class AsciiParser extends MsgParser { } default: { - throw new Error(`fix parser in unknown state ${state}`) + const st = state.parseState + throw new Error(`fix parser in unknown state ${st}`) } } readPtr++ @@ -171,15 +171,18 @@ export class AsciiParser extends MsgParser { } } - private getView (ptr: number): MsgView { + private getView (ptr: number): MsgView | null { const state = this.state const locations = state.locations const source = this.receivingBuffer const delimiter = this.delimiter const replace = this.writeDelimiter + const msgType = state.msgType ?? null + if (!msgType) return null if (state.message) { - const structure: Structure = this.segmentParser.parse(state.msgType, locations, + const structure: Structure | null = this.segmentParser.parse(msgType, locations, locations.nextTagPos - 1) + if (!structure) return null return new AsciiView(structure.msg(), source, structure, diff --git a/src/buffer/ascii/ascii-segment-parser.ts b/src/buffer/ascii/ascii-segment-parser.ts index 1c22ab7e..bb6d5463 100644 --- a/src/buffer/ascii/ascii-segment-parser.ts +++ b/src/buffer/ascii/ascii-segment-parser.ts @@ -20,14 +20,13 @@ import { SegmentType } from '../segment/segment-type' @injectable() export class AsciiSegmentParser { - constructor (@inject(DITokens.Definitions) public readonly definitions: FixDefinitions) { } - public parse (msgType: string, tags: Tags, last: number): Structure { + public parse (msgType: string, tags: Tags, last: number): Structure | null { // completed segments in that they are fully parsed const segments: SegmentDescription[] = [] - const msgDefinition: MessageDefinition = this.definitions.message.get(msgType) + const msgDefinition: MessageDefinition | null = this.definitions.message.get(msgType) if (!msgDefinition) { return null } @@ -41,11 +40,12 @@ export class AsciiSegmentParser { // having finished one segments keep unwinding until tag matches further up stack function unwind (tag: number): void { while (structureStack.length > 1) { - const done: SegmentDescription = structureStack.pop() + const done = structureStack.pop() + if (!done) continue done.end(segments.length, currentTagPosition - 1, tags.tagPos[currentTagPosition - 1].tag) segments.push(done) peek = structureStack[structureStack.length - 1] - if (peek.set.containedTag[tag]) { + if (peek.set?.containedTag[tag]) { // unwound to point this tag lives in this set. break } @@ -56,10 +56,10 @@ export class AsciiSegmentParser { } } - function examine (tag: number): SegmentDescription { - let structure: SegmentDescription = null - switch (peek.currentField.type) { - + function examine (tag: number): SegmentDescription | null { + let structure: SegmentDescription | null = null + const type = peek.currentField?.type + switch (type) { case ContainedFieldType.Simple: { const sf: ContainedSimpleField = peek.currentField as ContainedSimpleField if (sf.definition.tag === tag) { @@ -71,14 +71,14 @@ export class AsciiSegmentParser { case ContainedFieldType.Component: { const cf: ContainedComponentField = peek.currentField as ContainedComponentField structure = new SegmentDescription(cf.name, tag, cf.definition, - currentTagPosition, structureStack.length, SegmentType.Component) + currentTagPosition, structureStack.length, SegmentType.Component) break } // for a group also need to know where all delimiters are positioned case ContainedFieldType.Group: { const gf: ContainedComponentField = peek.currentField as ContainedGroupField structure = new SegmentDescription(gf.name, tag, gf.definition, - currentTagPosition, structureStack.length, SegmentType.Group) + currentTagPosition, structureStack.length, SegmentType.Group) currentTagPosition = currentTagPosition + 1 structure.startGroup(tags.tagPos[currentTagPosition].tag) break @@ -96,8 +96,8 @@ export class AsciiSegmentParser { if (tag === peek.delimiterTag) { peek.addDelimiterPosition(currentTagPosition) } else if (structureStack.length > 1) { - // if a group is represented by a repeated component, then the tag representing delimiter - // needs to be added further up stack to group itself. + // if a group is represented by a repeated component, then the tag representing delimiter + // needs to be added further up stack to group itself. delimiter = structureStack[structureStack.length - 2].groupAddDelimiter(tag, currentTagPosition) } return delimiter @@ -116,7 +116,7 @@ export class AsciiSegmentParser { const tag: number = tags.tagPos[currentTagPosition].tag peek = structureStack[structureStack.length - 1] peek.setCurrentField(tag) - if (!peek.set.containedTag[tag] || groupDelimiter(tag)) { + if (!peek.set?.containedTag[tag] || groupDelimiter(tag)) { // unravelled all way back to root hence this is not recognised const unknown = peek.type === SegmentType.Msg if (unknown) { @@ -137,7 +137,8 @@ export class AsciiSegmentParser { function clean (): void { // any remainder components can be closed. while (structureStack.length > 0) { - const done: SegmentDescription = structureStack.pop() + const done = structureStack.pop() + if (!done) continue done.end(segments.length, currentTagPosition - 1, tags.tagPos[currentTagPosition - 1].tag) segments[segments.length] = done } diff --git a/src/buffer/ascii/ascii-view.ts b/src/buffer/ascii/ascii-view.ts index b42f5c0e..457f8a7d 100644 --- a/src/buffer/ascii/ascii-view.ts +++ b/src/buffer/ascii/ascii-view.ts @@ -12,12 +12,11 @@ import { TagType } from '../tag/tag-type' export class AsciiView extends MsgView { private readonly timeFormatter: ITimeFormatter = new TimeFormatter(this.buffer) constructor (public readonly segment: SegmentDescription, - public readonly buffer: ElasticBuffer, - public readonly structure: Structure, - public readonly ptr: number, - public readonly delimiter: number, - public readonly writeDelimiter: number) { - + public readonly buffer: ElasticBuffer, + public readonly structure: Structure | null, + public readonly ptr: number, + public readonly delimiter: number, + public readonly writeDelimiter: number) { super(segment, structure) } @@ -35,6 +34,7 @@ export class AsciiView extends MsgView { } private replaceDelimiter (viewBuffer: Buffer, replaceDelimiter?: number): void { + if (this.structure == null) return const delimiter = replaceDelimiter ?? this.delimiter if (delimiter !== this.writeDelimiter) { const structure = this.structure @@ -55,6 +55,7 @@ export class AsciiView extends MsgView { } public checksum (): number { + if (this.structure == null) return -1 const t = this.getPosition(MsgTag.CheckSum) const structure = this.structure const prev = structure.tags.tagPos[t - 1] @@ -127,7 +128,8 @@ export class AsciiView extends MsgView { this.writeDelimiter) } - protected stringAtPosition (position: number): string { + protected stringAtPosition (position: number): string | null { + if (this.structure == null) return null const tags = this.structure.tags if (position < 0 || position >= tags.nextTagPos) { return null @@ -136,12 +138,14 @@ export class AsciiView extends MsgView { return this.buffer.getString(tag.start, tag.start + tag.len) } - private getBuffer (position: number): Buffer { + private getBuffer (position: number): Buffer | null { + if (this.structure == null) return null const tag: TagPos = this.structure.tags.tagPos[position] return this.buffer.getBuffer(tag.start, tag.start + tag.len) } - private getNumber (position: number, isFloat: boolean = false): number { + private getNumber (position: number, isFloat: boolean = false): number | null { + if (this.structure == null) return null const buffer = this.buffer const tag: TagPos = this.structure.tags.tagPos[position] if (isFloat) { @@ -151,7 +155,8 @@ export class AsciiView extends MsgView { } } - private getTime (tag: number, useUtc: boolean): Date { + private getTime (tag: number, useUtc: boolean): Date | null { + if (this.structure == null) return null const formatter = this.timeFormatter const position: number = this.getPosition(tag) const tagPos: TagPos = this.structure.tags.tagPos[position] @@ -165,7 +170,8 @@ export class AsciiView extends MsgView { } } - private getDate (tag: number, useUtc: boolean): Date { + private getDate (tag: number, useUtc: boolean): Date | null { + if (this.structure == null) return null const formatter = this.timeFormatter const position: number = this.getPosition(tag) if (position < 0) { @@ -183,14 +189,15 @@ export class AsciiView extends MsgView { } } - private getDateTime (tag: number, useUtc: boolean): Date { + private getDateTime (tag: number, useUtc: boolean): Date | null { + if (this.structure == null) return null const position: number = this.getPosition(tag) const tagPos: TagPos = this.structure.tags.tagPos[position] const formatter = this.timeFormatter if (position < 0) { return null } - // (SendingTime) = 20150417-01:00:08.201 + // (SendingTime) = 20150417-01:00:08.201 if (tagPos.len < 8) { return null } @@ -202,7 +209,8 @@ export class AsciiView extends MsgView { } } - private getBoolean (tag: number): boolean { + private getBoolean (tag: number): boolean | null { + if (this.structure == null) return null const position: number = this.getPosition(tag) if (position < 0) { return null diff --git a/src/buffer/ascii/itime-formatter.ts b/src/buffer/ascii/itime-formatter.ts index a767ce68..a85d1a6d 100644 --- a/src/buffer/ascii/itime-formatter.ts +++ b/src/buffer/ascii/itime-formatter.ts @@ -1,19 +1,19 @@ export interface ITimeFormatter { - writeLocalDate (v: Date): void - writeUtcDate (v: Date): void + writeLocalDate: (v: Date) => void + writeUtcDate: (v: Date) => void - writeLocalTimestamp (v: Date): void - writeUtcTimestamp (v: Date): void + writeLocalTimestamp: (v: Date) => void + writeUtcTimestamp: (v: Date) => void - writeLocalTime (v: Date): void - writeUtcTime (v: Date): void + writeLocalTime: (v: Date) => void + writeUtcTime: (v: Date) => void - getLocalDate (start: number): Date - getUtcDate (start: number): Date + getLocalDate: (start: number) => Date | null + getUtcDate: (start: number) => Date | null - getLocalTime (start: number): Date - getUtcTime (start: number): Date + getLocalTime: (start: number) => Date | null + getUtcTime: (start: number) => Date | null - getLocalTimestamp (start: number, end: number): Date - getUtcTimestamp (start: number, end: number): Date + getLocalTimestamp: (start: number, end: number) => Date | null + getUtcTimestamp: (start: number, end: number) => Date | null } diff --git a/src/buffer/ascii/time-formatter.ts b/src/buffer/ascii/time-formatter.ts index f9c2dfbb..a7f445e6 100644 --- a/src/buffer/ascii/time-formatter.ts +++ b/src/buffer/ascii/time-formatter.ts @@ -56,31 +56,31 @@ export class TimeFormatter implements ITimeFormatter { buffer.writePaddedHundreds(ms) } - public getLocalTime (start: number): Date { + public getLocalTime (start: number): Date | null { return this.getTime(start, false) } - public getUtcTime (start: number): Date { + public getUtcTime (start: number): Date | null { return this.getTime(start, true) } - public getUtcDate (start: number): Date { + public getUtcDate (start: number): Date | null { return this.getDate(start, true) } - public getLocalDate (start: number): Date { + public getLocalDate (start: number): Date | null { return this.getDate(start, false) } - public getUtcTimestamp (start: number, end: number): Date { - return this.getTimestamp(start, end,true) + public getUtcTimestamp (start: number, end: number): Date | null { + return this.getTimestamp(start, end, true) } - public getLocalTimestamp (start: number, end: number): Date { - return this.getTimestamp(start, end,false) + public getLocalTimestamp (start: number, end: number): Date | null { + return this.getTimestamp(start, end, false) } - private getTimestamp (start: number, end: number, useUtc: boolean): Date { + private getTimestamp (start: number, end: number, useUtc: boolean): Date | null { const buffer = this.buffer const n: number = buffer.getWholeNumber(start, start + 7) if (n == null) { @@ -145,7 +145,7 @@ export class TimeFormatter implements ITimeFormatter { return t } - private getTime (start: number, useUtc: boolean): Date { + private getTime (start: number, useUtc: boolean): Date | null { const buffer = this.buffer let offset = 0 const hh: number = buffer.getWholeNumber(start + offset, start + offset + 1) @@ -179,8 +179,7 @@ export class TimeFormatter implements ITimeFormatter { return t } - private getDate (start: number, useUtc: boolean): Date { - + private getDate (start: number, useUtc: boolean): Date | null { // = 20150417 const n: number = this.buffer.getWholeNumber(start, start + 7) diff --git a/src/buffer/elastic-buffer.ts b/src/buffer/elastic-buffer.ts index 94630f08..d09b6ab3 100644 --- a/src/buffer/elastic-buffer.ts +++ b/src/buffer/elastic-buffer.ts @@ -10,8 +10,7 @@ export class ElasticBuffer { private stretched: number constructor (@inject(DITokens.elasticBufferSize) public readonly size: number = 6 * 1024, - @inject(DITokens.elasticBufferReturnSize) public readonly returnTo: number = 6 * 1024) { - + @inject(DITokens.elasticBufferReturnSize) public readonly returnTo: number = 6 * 1024) { this.size = Math.max(1, this.size) this.buffer = Buffer.allocUnsafe(this.size) this.returnTo = Math.max(this.size, this.returnTo) @@ -117,7 +116,7 @@ export class ElasticBuffer { return this.ptr } - public writeNumber (v: number, places: number = 13) { + public writeNumber (v: number, places: number = 13): number { const rounded: number = Math.floor(v) const fraction: number = ElasticBuffer.precisionRound(v - rounded, places) if (fraction === 0) { @@ -141,7 +140,7 @@ export class ElasticBuffer { } public slice (): Buffer { - return this.buffer.slice(0, this.ptr) + return this.buffer.subarray(0, this.ptr) } public copy (): Buffer { @@ -251,7 +250,7 @@ export class ElasticBuffer { } public getBuffer (start: number, end: number): Buffer { - return this.buffer.slice(start, end) + return this.buffer.subarray(start, end) } public getBoolean (start: number): boolean { @@ -259,7 +258,7 @@ export class ElasticBuffer { return b === AsciiChars.Y } - public getFloat (start: number, vend: number): number { + public getFloat (start: number, vend: number): number | null { let n: number = 0 let digits: number = 0 let dotPosition: number = 0 diff --git a/src/buffer/encode-proxy.ts b/src/buffer/encode-proxy.ts index f3e49825..3db26099 100644 --- a/src/buffer/encode-proxy.ts +++ b/src/buffer/encode-proxy.ts @@ -33,14 +33,14 @@ export class EncodeProxy { } case TagType.Boolean: { - if (typeof(val) !== typeof(true)) { + if (typeof (val) !== typeof (true)) { throw new Error(`field ${field.name} expects boolean but receives "${typeof val}"`) } break } case TagType.String: { - if (typeof(val) !== 'string') { + if (typeof (val) !== 'string') { throw new Error(`field ${field.name} expects string but receives "${typeof val}"`) } break @@ -67,7 +67,7 @@ export class EncodeProxy { private static checkProperties (wrapped: ILooseObject, val: ILooseObject): ILooseObject { const keys: string[] = Object.keys(val) - for (let k of keys) { + for (const k of keys) { wrapped[k] = val[k] } return wrapped @@ -108,7 +108,7 @@ export class EncodeProxy { private static handler (set: ContainedFieldSet): Object { return { set (target: ILooseObject, prop: string, val: any): boolean { - const field: ContainedField = set.localNameToField.get(prop) + const field: ContainedField | null = set.localNameToField.get(prop) if (!field) { throw new Error(`type ${set.name} has no field named ${prop}`) } @@ -137,8 +137,9 @@ export class EncodeProxy { } return val } + public wrap (msgName: string): ILooseObject { - const msg: MessageDefinition = this.definitions.message.get(msgName) + const msg: MessageDefinition | null = this.definitions.message.get(msgName) if (!msg) { throw new Error(`no message defined for type ${msgName}`) } diff --git a/src/buffer/encoder-state.ts b/src/buffer/encoder-state.ts index 1772b642..badb319c 100644 --- a/src/buffer/encoder-state.ts +++ b/src/buffer/encoder-state.ts @@ -2,11 +2,15 @@ export class EncodedStatus { public ptr: number = 0 public bodyLengthPos: number public bodyEndPos: number - public begin: Date - public end: Date + public begin: Date | null + public end: Date | null public elapsed (): number { - return this.begin.getTime() - this.end.getTime() + if (this.begin && this.end) { + return this.begin.getTime() - this.end.getTime() + } + return 0 } + public reset (): void { this.ptr = 0 this.bodyLengthPos = 0 diff --git a/src/buffer/fixml/fixml-encoder.ts b/src/buffer/fixml/fixml-encoder.ts index 79312913..cfcf87f9 100644 --- a/src/buffer/fixml/fixml-encoder.ts +++ b/src/buffer/fixml/fixml-encoder.ts @@ -63,12 +63,13 @@ export class FixmlEncoder extends MsgEncoder { return moment(d).utc(true).format('YYYY-MM-DD') } } + return '' } public encodeSet (o: ILooseObject, set: ContainedFieldSet): void { const batch: ILooseObject[] = o.Batch const toWrite: ILooseObject[] = batch || [o] - let depth = batch ? 1 : 0 + const depth = batch ? 1 : 0 const buffer = this.buffer const begin = this.beginDoc const indent: string = '\t' @@ -80,7 +81,7 @@ export class FixmlEncoder extends MsgEncoder { this.batchStart(o, set, depth) } toWrite.forEach((next: ILooseObject) => { - this.toXml(next, set.abbreviation, set, depth + 1) + this.toXml(next, set.abbreviation ?? '', set, depth + 1) buffer.writeString(eol) }) if (batch) { @@ -90,7 +91,7 @@ export class FixmlEncoder extends MsgEncoder { buffer.writeString(this.endDoc) } - private batchStart (o: ILooseObject, set: ContainedFieldSet, depth: number) { + private batchStart (o: ILooseObject, set: ContainedFieldSet, depth: number): void { const buffer = this.buffer const indent: string = '\t' const beginBatch = this.beginBatch @@ -105,7 +106,6 @@ export class FixmlEncoder extends MsgEncoder { } private toXml (o: ILooseObject, name: string, set: ContainedFieldSet, depth: number): void { - const buffer = this.buffer const selfClose: string = '/>' const close: string = '>' @@ -131,7 +131,7 @@ export class FixmlEncoder extends MsgEncoder { private getPopulatedFields (set: ContainedFieldSet, o: ILooseObject): ContainedField[] { const keys: string[] = Object.keys(o) const fields: ContainedField[] = keys.reduce((a: ContainedField[], current: string) => { - const field: ContainedField = set.localNameToField.get(current) + const field: ContainedField | null = set.localNameToField.get(current) if (field && !set.nameToLocalAttribute.containsKey(current)) { a.push(field) } @@ -176,7 +176,7 @@ export class FixmlEncoder extends MsgEncoder { } private getPopulatedAttributes (o: ILooseObject, attributes: ContainedSimpleField[]): IPopulatedAttributes { - return attributes.reduce((a: IPopulatedAttributes, f: ContainedSimpleField) => { + return attributes.reduce((a: IPopulatedAttributes, f: ContainedSimpleField) => { let v: any = o[f.definition.name] if (v == null) { v = o[f.name] @@ -189,17 +189,17 @@ export class FixmlEncoder extends MsgEncoder { }, { values: [], fields: [] - } as IPopulatedAttributes) + }) } - private complexGroup (o: ILooseObject, field: ContainedField, depth: number) { + private complexGroup (o: ILooseObject, field: ContainedField, depth: number): void { const gf: ContainedGroupField = field as ContainedGroupField const elements: ILooseObject[] = o[gf.definition.name] if (elements) { if (Array.isArray(elements)) { for (const e of elements) { this.buffer.writeString(this.eol) - this.toXml(e, gf.name, gf.definition,depth + 1) + this.toXml(e, gf.name, gf.definition, depth + 1) } } else { throw new Error(`expected array for member ${gf.definition.name}`) @@ -207,7 +207,7 @@ export class FixmlEncoder extends MsgEncoder { } } - private complexComponent (o: ILooseObject, field: ContainedField, depth: number) { + private complexComponent (o: ILooseObject, field: ContainedField, depth: number): void { const cf: ContainedComponentField = field as ContainedComponentField const def = cf.definition const instance: ILooseObject = o[def.name] diff --git a/src/buffer/fixml/fixml-parser.ts b/src/buffer/fixml/fixml-parser.ts index a9e8a15f..5f288d69 100644 --- a/src/buffer/fixml/fixml-parser.ts +++ b/src/buffer/fixml/fixml-parser.ts @@ -2,9 +2,11 @@ import { MsgParser } from '../msg-parser' import { Tags } from '../tag/tags' import { SAXStream } from '../../dictionary' import { FixDefinitions, MessageDefinition } from '../../dictionary/definition' -import { ContainedField, ContainedComponentField, +import { + ContainedField, ContainedComponentField, ContainedFieldType, ContainedGroupField, - ContainedSimpleField, ContainedFieldSet } from '../../dictionary/contained' + ContainedSimpleField, ContainedFieldSet +} from '../../dictionary/contained' import { SegmentDescription } from '../segment/segment-description' import { IJsFixConfig, IJsFixLogger } from '../../config' import { MsgView } from '../msg-view' @@ -25,16 +27,16 @@ export class FiXmlParser extends MsgParser { private readonly segments: SegmentDescription[] = [] private readonly segmentStack: SegmentDescription[] = [] private readonly logger: IJsFixLogger - private last: SegmentDescription - private raw: string + private last: SegmentDescription | null + private raw: string | null constructor (@inject(DITokens.IJsFixConfig) public readonly config: IJsFixConfig, - @inject(DITokens.readStream) public readonly readStream: Readable, - @inject(DITokens.maxMessageLocations) public readonly maxMessageLocations: number = 10 * 1024) { + @inject(DITokens.readStream) public readonly readStream: Readable, + @inject(DITokens.maxMessageLocations) public readonly maxMessageLocations: number = 10 * 1024) { super() this.definitions = this.config.definitions const description = config.description - const me = description.application.name + const me = description?.application?.name this.logger = config.logFactory.logger(`${me}:FiXmlParser`) this.saxStream = require('sax').createStream(true, {}) this.locations = new Tags(this.definitions, maxMessageLocations) @@ -60,7 +62,7 @@ export class FiXmlParser extends MsgParser { private subscribe (): void { const writeStream = this.saxStream const readStream = this.readStream - let instance = this + const instance = this readStream.pipe(writeStream).on('ready', () => { this.logger.info('stream close event') this.emit('close') @@ -138,13 +140,17 @@ export class FiXmlParser extends MsgParser { if (stack.length === 0) { throw new Error(`Hdr not expected before batch or message ${saxNode.name}`) } - let peek: SegmentDescription = stack[stack.length - 1] + const peek: SegmentDescription = stack[stack.length - 1] switch (peek.type) { case SegmentType.Batch: { // manually handle this component const hdr = this.definitions.component.get('StandardHeader') - const segment: SegmentDescription = this.parseAttributes(saxNode.name, hdr, saxNode, SegmentType.Component) - this.segmentStack.push(segment) + if (hdr) { + const segment: SegmentDescription | null = this.parseAttributes(saxNode.name, hdr, saxNode, SegmentType.Component) + if (segment) { + this.segmentStack.push(segment) + } + } break } @@ -177,71 +183,75 @@ export class FiXmlParser extends MsgParser { return new FixmlView(last, this.values, structure) } - private pop (name: string): SegmentDescription { + private pop (name: string): SegmentDescription | null { const locations = this.locations const stack = this.segmentStack const segments = this.segments while (stack.length > 0) { const pop = stack.pop() const ptr = locations.nextTagPos - 1 - pop.end(segments.length, ptr, locations.tagPos[ptr].tag) - segments[segments.length] = pop - switch (pop.type) { - case SegmentType.Msg: { - // raise msg event - const last = segments[segments.length - 1] - this.last = last - this.emit('msg', last.name, this.getView()) - if (this.raw) { - this.emit('decoded', this.last.name, this.raw) - this.raw = null + pop?.end(segments.length, ptr, locations.tagPos[ptr].tag) + if (pop) { + segments[segments.length] = pop + switch (pop.type) { + case SegmentType.Msg: { + // raise msg event + const last = segments[segments.length - 1] + this.last = last + this.emit('msg', last.name, this.getView()) + if (this.raw) { + this.emit('decoded', this.last.name, this.raw) + this.raw = null + } + break + } + case SegmentType.Batch: { + const last = segments[segments.length - 1] + this.logger.debug(`emit batch with ${pop.delimiterPositions.length} elements`) + this.emit('batch', last?.set?.abbreviation, this.getView()) + break } - break } - case SegmentType.Batch: { - const last = segments[segments.length - 1] - this.logger.debug(`emit batch with ${pop.delimiterPositions.length} elements`) - this.emit('batch', last.set.abbreviation, this.getView()) - break + if (pop.name === name) { + return pop } } - if (pop.name === name) { - return pop - } } return null } - private startGroup (saxNode: ISaxNode, gf: ContainedGroupField) { + private startGroup (saxNode: ISaxNode, gf: ContainedGroupField): void { const locations = this.locations const stack: SegmentDescription[] = this.segmentStack const ptr = locations.nextTagPos const def = gf.definition - const segment: SegmentDescription = this.parseAttributes(saxNode.name, def, saxNode, SegmentType.Component) - const group: SegmentDescription = new SegmentDescription(def.name, - locations.tagPos[ptr].tag, - def, - ptr, - stack.length, - SegmentType.Group) - group.startGroup(locations.tagPos[ptr].tag) - group.addDelimiterPosition(ptr) - stack.push(group) - stack.push(segment) + const segment: SegmentDescription | null = this.parseAttributes(saxNode.name, def, saxNode, SegmentType.Component) + if (segment) { + const group: SegmentDescription = new SegmentDescription(def.name, + locations.tagPos[ptr].tag, + def, + ptr, + stack.length, + SegmentType.Group) + group.startGroup(locations.tagPos[ptr].tag) + group.addDelimiterPosition(ptr) + stack.push(group) + stack.push(segment) + } } - private getNextField (saxNode: ISaxNode): ContainedField { + private getNextField (saxNode: ISaxNode): ContainedField | null { const stack: SegmentDescription[] = this.segmentStack while (stack.length > 0) { - let peek: SegmentDescription = stack[stack.length - 1] - let field = peek.set.localNameToField.get(saxNode.name) + const peek: SegmentDescription = stack[stack.length - 1] + const field = peek?.set?.localNameToField.get(saxNode.name) if (field) { return field } // if this is a group of the same type as already on stack // take the field from the next level up if (peek.type === SegmentType.Group && stack.length > 1) { - const contained = stack[stack.length - 2].set.localNameToField.get(saxNode.name) + const contained = stack[stack.length - 2]?.set?.localNameToField.get(saxNode.name) if (contained instanceof ContainedGroupField) { if (contained.definition.name === peek.name) { // this is the same type for next instance in the same group. @@ -254,18 +264,22 @@ export class FiXmlParser extends MsgParser { const ptr = locations.nextTagPos - 1 const pop = stack.pop() const segments = this.segments - pop.end(segments.length, ptr, locations.tagPos[ptr].tag) - segments[segments.length] = pop + if (pop) { + pop.end(segments.length, ptr, locations.tagPos[ptr].tag) + segments[segments.length] = pop + } } return null } - private dispatch (saxNode: ISaxNode, field: ContainedField) { + private dispatch (saxNode: ISaxNode, field: ContainedField): void { switch (field.type) { case ContainedFieldType.Component: { const cf: ContainedComponentField = field as ContainedComponentField - const segment: SegmentDescription = this.parseAttributes(saxNode.name, cf.definition, saxNode, SegmentType.Component) - this.segmentStack.push(segment) + const segment: SegmentDescription | null = this.parseAttributes(saxNode.name, cf.definition, saxNode, SegmentType.Component) + if (segment) { + this.segmentStack.push(segment) + } break } @@ -290,7 +304,10 @@ export class FiXmlParser extends MsgParser { if (gf.name === saxNode.name) { const ptr = this.locations.nextTagPos peek.addDelimiterPosition(ptr) - stack[stack.length] = this.parseAttributes(saxNode.name, gf.definition, saxNode, SegmentType.Component) + const a = this.parseAttributes(saxNode.name, gf.definition, saxNode, SegmentType.Component) + if (a) { + stack.push(a) + } } else { throw new Error(`expected another group instance of ${gf.name} but got ${saxNode.name}`) } @@ -307,8 +324,8 @@ export class FiXmlParser extends MsgParser { const field = this.getNextField(saxNode) if (!field) { const stack: SegmentDescription[] = this.segmentStack - let peek: SegmentDescription = stack[stack.length - 1] - throw new Error(`field ${saxNode.name} not known in set ${peek.set.name}`) + const peek: SegmentDescription = stack[stack.length - 1] + throw new Error(`field ${saxNode.name} not known in set ${peek?.set?.name}`) } this.dispatch(saxNode, field) } @@ -316,7 +333,7 @@ export class FiXmlParser extends MsgParser { private msg (saxNode: ISaxNode, inBatch: boolean = false): void { this.logger.debug(`${saxNode.name}: begin parse msg`) const type: string = saxNode.name - const def: MessageDefinition = this.definitions.message.get(type) + const def: MessageDefinition | null = this.definitions.message.get(type) if (!def) { throw new Error(`unknown message type ${type}`) } @@ -324,11 +341,13 @@ export class FiXmlParser extends MsgParser { const batch = this.segmentStack[0] batch.set = def } - const segment: SegmentDescription = this.parseAttributes(type, def, saxNode, SegmentType.Msg) - this.segmentStack.push(segment) + const segment: SegmentDescription | null = this.parseAttributes(type, def, saxNode, SegmentType.Msg) + if (segment) { + this.segmentStack.push(segment) + } } - private parseAttributes (name: string, set: ContainedFieldSet, saxNode: ISaxNode, type: SegmentType): SegmentDescription { + private parseAttributes (name: string, set: ContainedFieldSet, saxNode: ISaxNode, type: SegmentType): SegmentDescription | null { const locations = this.locations const attributes = saxNode.attributes const values = this.values @@ -350,5 +369,6 @@ export class FiXmlParser extends MsgParser { } return new SegmentDescription(name, locations.tagPos[ptr].tag, set, ptr, this.segmentStack.length, type) } + return null } } diff --git a/src/buffer/fixml/fixml-view.ts b/src/buffer/fixml/fixml-view.ts index c455007e..15c77a27 100644 --- a/src/buffer/fixml/fixml-view.ts +++ b/src/buffer/fixml/fixml-view.ts @@ -7,26 +7,25 @@ import * as moment from 'moment' import { TagType } from '../tag/tag-type' export class FixmlView extends MsgView { - constructor (public readonly segment: SegmentDescription, - public readonly values: string[], - public readonly structure: Structure) { + public readonly values: string[], + public readonly structure: Structure) { super(segment, structure) } private static getTimestamp (s: string, useUtc: boolean): Date { - let m = moment(s) + const m = moment(s) return useUtc ? m.utc(true).toDate() : m.toDate() } // 01:00:08.201 private static getTimeOnly (s: string, useUtc: boolean): Date { - let m = moment(s, [moment.HTML5_FMT.TIME_MS, moment.HTML5_FMT.TIME_SECONDS]) + const m = moment(s, [moment.HTML5_FMT.TIME_MS, moment.HTML5_FMT.TIME_SECONDS]) return useUtc ? m.utc(true).toDate() : m.toDate() } private static getDateOnly (s: string, useUtc: boolean): Date { - let m = moment(s) + const m = moment(s) let d: Date if (useUtc) { d = m.utc(true).toDate() @@ -45,7 +44,7 @@ export class FixmlView extends MsgView { return 0 } - protected create (singleton: SegmentDescription) { + protected create (singleton: SegmentDescription): FixmlView { return new FixmlView(singleton, this.values, this.structure) diff --git a/src/buffer/fixml/populated-attributes.ts b/src/buffer/fixml/populated-attributes.ts index 4f4cf1c2..43071663 100644 --- a/src/buffer/fixml/populated-attributes.ts +++ b/src/buffer/fixml/populated-attributes.ts @@ -1,6 +1,6 @@ import { ContainedSimpleField } from '../../dictionary/contained' export interface IPopulatedAttributes { - fields: ContainedSimpleField[], + fields: ContainedSimpleField[] values: any[] } diff --git a/src/buffer/msg-encoder.ts b/src/buffer/msg-encoder.ts index 49ab8c34..ff2a8240 100644 --- a/src/buffer/msg-encoder.ts +++ b/src/buffer/msg-encoder.ts @@ -9,7 +9,8 @@ export abstract class MsgEncoder extends events.EventEmitter { } public encode (o: ILooseObject, name: string): void { - const set: ContainedFieldSet = this.definitions.message.get(name) || this.definitions.component.get(name) + const set: ContainedFieldSet | null = this.definitions.message.get(name) ?? + this.definitions.component.get(name) if (!set) { return } diff --git a/src/buffer/msg-view.ts b/src/buffer/msg-view.ts index ad31c199..a30414b2 100644 --- a/src/buffer/msg-view.ts +++ b/src/buffer/msg-view.ts @@ -3,8 +3,13 @@ import { SegmentDescription } from './segment/segment-description' import { Structure } from './structure' import { Dictionary } from '../collections' import { Tags } from './tag/tags' -import { ContainedGroupField, ContainedComponentField, ContainedField, - ContainedFieldSet, ContainedSimpleField } from '../dictionary/contained' +import { + ContainedComponentField, + ContainedField, + ContainedFieldSet, + ContainedGroupField, + ContainedSimpleField +} from '../dictionary/contained' import { SetReduce } from '../dictionary' import { ILooseObject } from '../collections/collection' import { ElasticBuffer } from './elastic-buffer' @@ -16,7 +21,7 @@ export abstract class MsgView { protected sortedTagPosForwards: TagPos[] protected sortedTagPosBackwards: TagPos[] - protected constructor (public readonly segment: SegmentDescription, public readonly structure: Structure) { + protected constructor (public readonly segment: SegmentDescription, public readonly structure: Structure | null) { } protected static asVerbose (field: SimpleFieldDefinition, val: string, i: number, count: number, tp: TagPos): string { @@ -26,12 +31,13 @@ export abstract class MsgView { if (field) { name = field.name || 'unknown' if (field.isEnum()) { - desc = `${val}[${field.resolveEnum(val)}]${newLine}\t${field.description || ''}${newLine}${newLine}` + desc = `${val}[${field.resolveEnum(val)}]${newLine}\t${field.description ?? ''}${newLine}${newLine}` } else { - desc = `${val}${newLine}t${field.description || ''}${newLine}${newLine}` + desc = `${val}${newLine}t${field.description ?? ''}${newLine}${newLine}` } } else { name = 'unknown' + desc = 'na' } return `[${i}] ${tp.tag} (${name}) = ${desc}` } @@ -66,7 +72,9 @@ export abstract class MsgView { public invalid (): number[] { const invalidTags: number[] = [] const set = this.segment.set - const tags = this.structure.tags + if (set == null) return [] + const tags = this.structure?.tags ?? null + if (tags == null) return [] for (let i = 0; i < tags.nextTagPos; ++i) { const tag = tags.tagPos[i].tag if (tag <= 0 || !set.containedTag[tag]) { @@ -78,6 +86,8 @@ export abstract class MsgView { // list of tags that must be present public missing (): number[] { + if (this.segment == null) return [] + if (this.segment.set == null) return [] return this.missingRequired(this.segment.set, []) } @@ -87,24 +97,24 @@ export abstract class MsgView { return position >= 0 } - public getGroupInstance (i: number): MsgView { - const instance: SegmentDescription = this.segment.getInstance(i) + public getGroupInstance (i: number): MsgView | null { + const instance: SegmentDescription | null = this.segment?.getInstance(i) if (!instance) { return null } return this.create(instance) } - public getUndefined (): SegmentDescription | SegmentDescription[] { - return this.structure.layout['.undefined'] + public getUndefined (): SegmentDescription | SegmentDescription[] | null { + return this.structure?.layout['.undefined'] } - public undefinedForMsg (): string { - let msg: string = null + public undefinedForMsg (): string | null { + let msg: string | null = null const undefinedTags = this.getUndefined() if (undefinedTags) { if (Array.isArray(undefinedTags)) { - msg = `undefined tags = ` + undefinedTags.map((e: SegmentDescription) => e.startTag.toString()).join(', ') + msg = 'undefined tags = ' + undefinedTags.map((e: SegmentDescription) => e.startTag.toString()).join(', ') } else { msg = `undefined tag = ${undefinedTags.startTag}` } @@ -117,7 +127,7 @@ export abstract class MsgView { return positions ? positions.length : 0 } - public getString (tagOrName: number | string): string { + public getString (tagOrName: number | string): string | null { const tag: number = this.resolveTag(tagOrName) if (tag == null) { return null @@ -129,7 +139,7 @@ export abstract class MsgView { return this.stringAtPosition(position) } - public getStrings (tagOrName: number | string = -1): string[] { + public getStrings (tagOrName: number | string = -1): Array<(string | null)> | null { if (tagOrName < 0) { return this.allStrings() } @@ -151,7 +161,7 @@ export abstract class MsgView { if (tag == null) { return null } - const field: SimpleFieldDefinition = this.structure.tags.definitions.tagToSimple[tag] + const field: SimpleFieldDefinition | null = this.structure?.tags.definitions.tagToSimple[tag] ?? null if (field == null) { return null } @@ -164,6 +174,7 @@ export abstract class MsgView { public toObject (): any { const segment: SegmentDescription = this.segment + if (segment.set == null) return null if (segment.delimiterTag) { switch (segment.set.type) { case ContainedSetType.Group: { @@ -174,7 +185,8 @@ export abstract class MsgView { const hdrView = this.getView('Hdr') const batch: ILooseObject = {} if (hdrView) { - batch[hdrView.segment.set.name] = hdrView.toObject() + const name = hdrView.segment.set?.name ?? 'na' + batch[name] = hdrView.toObject() } batch[segment.name] = this.asInstances(segment.name) return batch @@ -196,27 +208,29 @@ export abstract class MsgView { return JSON.stringify(this.toObject(), null, 4) } - public getView (name: string): MsgView { + public getView (name: string): MsgView | null { const parts: string[] = name.split('.') - return parts.reduce((a: MsgView, current: string) => { + const reducer = (a: MsgView, current: string): MsgView | null => { if (!a) { return a } const structure = a.structure - const singleton: SegmentDescription = structure.firstContainedWithin(current, a.segment) + const singleton: SegmentDescription | null = structure?.firstContainedWithin(current, a.segment) ?? null if (singleton) { return a.create(singleton) } // is this a full name where abbreviation exists - const component: ContainedField = a.segment.set.localNameToField.get(current) + const component: ContainedField | null = a.segment.set?.localNameToField.get(current) ?? null + // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions if (component) { - const abbreviated: SegmentDescription = structure.firstContainedWithin(component.name, a.segment) + const abbreviated: SegmentDescription | null = structure?.firstContainedWithin(component.name, a.segment) ?? null if (abbreviated) { return a.create(abbreviated) } } return null - }, this as MsgView) + } + return parts.reduce(reducer, this) } public abstract checksum (): number @@ -225,17 +239,18 @@ export abstract class MsgView { protected abstract create (singleton: SegmentDescription): MsgView - protected abstract stringAtPosition (position: number): string + protected abstract stringAtPosition (position: number): string | null protected abstract toTyped (field: SimpleFieldDefinition): any protected resolveTag (tagOrName: number | string): number { let tag: number - if (typeof(tagOrName) === 'string') { - let cf = this.segment.set.simple.get(tagOrName) - const f: SimpleFieldDefinition = cf ? cf.definition : this.structure.tags.definitions.simple.get(tagOrName) + if (typeof (tagOrName) === 'string') { + if (this.segment.set == null) return 0 + const cf = this.segment.set.simple.get(tagOrName) + const f: SimpleFieldDefinition | null = cf ? cf.definition : this.structure?.tags.definitions.simple.get(tagOrName) ?? null if (f == null) { - return null + return -1 } tag = f.tag } else { @@ -250,7 +265,7 @@ export abstract class MsgView { const backwards: TagPos[] = this.sortedTagPosBackwards const position: number = this.binarySearch(tag) if (position < 0) { - return null + return [] } const count: number = forwards.length @@ -288,7 +303,7 @@ export abstract class MsgView { } } - private allStrings (): string[] { + private allStrings (): Array<(string | null)> { const segment: SegmentDescription = this.segment const range: number[] = [] for (let i: number = segment.startPosition; i <= segment.endPosition; ++i) { @@ -297,73 +312,75 @@ export abstract class MsgView { return range.map((i: number) => this.stringAtPosition(i)) } - private asInstances (name: string): ILooseObject[] { - const groupView: MsgView = this.getView(name) + private asInstances (name: string): ILooseObject[] | null { + const groupView: MsgView | null = this.getView(name) if (groupView == null) { - return + return null } const groupArray: ILooseObject[] = new Array(groupView.groupCount()) const count: number = groupView.groupCount() for (let j: number = 0; j < count; ++j) { - const instance: MsgView = groupView.getGroupInstance(j) - groupArray[j] = instance.toObject() + const instance: MsgView | null = groupView.getGroupInstance(j) + groupArray[j] = instance?.toObject() } return groupArray } private asLoose (def: ContainedFieldSet): ILooseObject { const reducer = new SetReduce() + // eslint-disable-next-line return reducer.reduce(def, { group: (a: ILooseObject, field: ContainedGroupField) => this.asLooseGroup(a, field), simple: (a: ILooseObject, field: ContainedSimpleField) => this.asLooseSimple(a, field), component: (a: ILooseObject, field: ContainedComponentField) => this.asLooseComponent(a, field) - } as ITypeDispatcher, def.localAttribute.reduce((a: ILooseObject, sf: ContainedSimpleField) => { + } as ITypeDispatcher, def.localAttribute.reduce((a: ILooseObject, sf: ContainedSimpleField) => { const def = sf.definition const position: number = this.getPosition(def.tag) if (position >= 0) { a[def.name] = this.toTyped(def) } return a - }, {} as ILooseObject)) + }, {})) } private missingRequired (def: ContainedFieldSet, tags: number []): number[] { const reducer = new SetReduce() - return reducer.reduce(def, { + const dispatcher: ITypeDispatcher = { group: (a: number[], field: ContainedGroupField) => this.missingGroup(def, field, a), simple: (a: number[], field: ContainedSimpleField) => this.missingSimple(field, a), component: (a: number[], field: ContainedComponentField) => this.missingComponent(field, a) - } as ITypeDispatcher, tags) + } + return reducer.reduce(def, dispatcher, tags) } - private missingSimple (sf: ContainedSimpleField, a: number[]) { + private missingSimple (sf: ContainedSimpleField, a: number[]): void { if (sf.required && this.getPosition(sf.definition.tag) < 0) { a.push(sf.definition.tag) } } - private missingComponent (cf: ContainedComponentField, a: number[]) { - const view: MsgView = this.getView(cf.name) + private missingComponent (cf: ContainedComponentField, a: number[]): void { + const view: MsgView | null = this.getView(cf.name) if (view) { view.missingRequired(cf.definition, a) } } - private missingGroup (def: ContainedFieldSet, gf: ContainedGroupField, tags: number []) { + private missingGroup (def: ContainedFieldSet, gf: ContainedGroupField, tags: number []): void { const name = gf.definition.noOfField ? gf.definition.noOfField.name : def.name - const groupView: MsgView = this.getView(name) || this.getView(gf.definition.name) + const groupView: MsgView | null = this.getView(name) ?? this.getView(gf.definition.name) if (groupView == null) { return } const count: number = groupView.groupCount() for (let j: number = 0; j < count; ++j) { - const instance: MsgView = groupView.getGroupInstance(j) - instance.missingRequired(gf.definition, tags) + const instance: MsgView | null = groupView.getGroupInstance(j) + instance?.missingRequired(gf.definition, tags) } } - private asLooseComponent (a: ILooseObject, cf: ContainedComponentField) { - const view: MsgView = this.getView(cf.name) + private asLooseComponent (a: ILooseObject, cf: ContainedComponentField): void { + const view: MsgView | null = this.getView(cf.name) if (view) { const component = view.toObject() if (component) { @@ -372,7 +389,7 @@ export abstract class MsgView { } } - private asLooseSimple (a: ILooseObject, sf: ContainedSimpleField) { + private asLooseSimple (a: ILooseObject, sf: ContainedSimpleField): void { const def = sf.definition const position: number = this.getPosition(def.tag) if (position >= 0) { @@ -383,16 +400,17 @@ export abstract class MsgView { } } - private asLooseGroup (a: ILooseObject, gf: ContainedGroupField) { + private asLooseGroup (a: ILooseObject, gf: ContainedGroupField): void { const def = gf.definition const name = def.noOfField ? def.noOfField.name : def.name - const instances: ILooseObject = this.asInstances(name) || this.asInstances(def.name) + const instances: ILooseObject | null = this.asInstances(name) ?? this.asInstances(def.name) if (instances) { a[def.name] = instances } } private binarySearch (tag: number): number { + if (this.structure == null) return -1 let forwards = this.sortedTagPosForwards if (!forwards) { const segment = this.segment @@ -407,14 +425,16 @@ export abstract class MsgView { const structure = this.structure const buffer: ElasticBuffer = new ElasticBuffer() const segment: SegmentDescription = this.segment + if (structure == null) return '' const tags: Tags = structure.tags const count: number = segment.endPosition - segment.startPosition const simple: Dictionary = tags.definitions.simple for (let i: number = segment.startPosition; i <= segment.endPosition; ++i) { const tagPos: TagPos = tags.tagPos[i] - const field: SimpleFieldDefinition = simple.get(tagPos.tag.toString()) - const val: string = this.stringAtPosition(i) + const field: SimpleFieldDefinition | null = simple.get(tagPos.tag.toString()) + const val: string | null = this.stringAtPosition(i) ?? '' + if (!field) return '' const token = getToken(field, val, i - segment.startPosition, count, tagPos) buffer.writeString(token) } diff --git a/src/buffer/segment/segment-description.ts b/src/buffer/segment/segment-description.ts index d52a2b83..3cd8fa1e 100644 --- a/src/buffer/segment/segment-description.ts +++ b/src/buffer/segment/segment-description.ts @@ -9,23 +9,23 @@ export class SegmentDescription { public endPosition: number = 0 public delimiterTag: number = 0 public delimiterPositions: number[] - public currentField: ContainedField + public currentField: ContainedField | null public containedDelimiterPositions: INumericKeyed constructor ( - public name: string, - public startTag: number, - public set: ContainedFieldSet, - public startPosition: number, - public readonly depth: number, - public readonly type: SegmentType) { + public name: string, + public startTag: number, + public set: ContainedFieldSet | null, + public startPosition: number, + public readonly depth: number, + public readonly type: SegmentType) { } public contains (segment: SegmentDescription): boolean { return segment.startPosition >= this.startPosition && segment.endPosition <= this.endPosition } - public getInstance (instance: number): SegmentDescription { + public getInstance (instance: number): SegmentDescription | null { const delimiters: number[] = this.delimiterPositions if (!delimiters) { return null @@ -34,10 +34,10 @@ export class SegmentDescription { return null } const start: number = delimiters[instance] - const end: number = instance < delimiters.length - 1 ? - delimiters[instance + 1] - 1 : - this.endPosition - const name = this.type === SegmentType.Batch ? this.set.abbreviation : this.name + const end: number = instance < delimiters.length - 1 + ? delimiters[instance + 1] - 1 + : this.endPosition + const name: string = this.type === SegmentType.Batch ? this.set?.abbreviation ?? this.name : this.name ?? this.name ?? 'na' const d: SegmentDescription = new SegmentDescription(name, this.startTag, this.set, start, this.depth, this.type) d.endPosition = end d.endTag = this.endTag @@ -60,7 +60,7 @@ export class SegmentDescription { } public setCurrentField (tag: number): void { - this.currentField = this.set.localTag[tag] || this.set.tagToField[tag] + this.currentField = this.set?.localTag[tag] ?? this.set?.tagToField[tag] ?? null } public groupAddDelimiter (tag: number, position: number): boolean { diff --git a/src/buffer/segment/segment-summary.ts b/src/buffer/segment/segment-summary.ts index 30466377..3420db56 100644 --- a/src/buffer/segment/segment-summary.ts +++ b/src/buffer/segment/segment-summary.ts @@ -11,8 +11,9 @@ export class SegmentSummary { public readonly delimiterTag: number, public readonly delimiterPositions: number[]) { } + public static fromDescription (d: SegmentDescription): SegmentSummary { - return new SegmentSummary(d.set.name, d.depth, d.startTag, d.startPosition, - d.endTag, d.endPosition, d.delimiterTag, d.delimiterPositions || []) + return new SegmentSummary(d.set?.name ?? 'na', d.depth, d.startTag, d.startPosition, + d.endTag, d.endPosition, d.delimiterTag, d.delimiterPositions || []) } } diff --git a/src/buffer/structure.ts b/src/buffer/structure.ts index 340e4ee4..41fe7f1c 100644 --- a/src/buffer/structure.ts +++ b/src/buffer/structure.ts @@ -4,28 +4,28 @@ import { SegmentSummary } from './segment/segment-summary' import { Tags } from './tag/tags' export class Structure { - public readonly layout: ILooseObject = null + public readonly layout: ILooseObject constructor (public readonly tags: Tags, - public readonly segments: SegmentDescription[]) { + public readonly segments: SegmentDescription[]) { this.layout = this.boundLayout() } public msg (): SegmentDescription { - // trailer = -1, msg = -2 + // trailer = -1, msg = -2 return this.segments[this.segments.length - 2] } public summary (): SegmentSummary[] { - return this.segments.map(((s: SegmentDescription) => SegmentSummary.fromDescription(s))) + return this.segments.map((s: SegmentDescription) => SegmentSummary.fromDescription(s)) } - public firstContainedWithin (name: string, segment: SegmentDescription): SegmentDescription { + public firstContainedWithin (name: string, segment: SegmentDescription): SegmentDescription | null { const all: SegmentDescription | SegmentDescription[] = this.layout[name] if (!all) { return null } - let ret: SegmentDescription = null + let ret: SegmentDescription | null = null if (!Array.isArray(all)) { const instance: SegmentDescription = all ret = segment.contains(instance) ? instance : null diff --git a/src/buffer/time-format-template.ts b/src/buffer/time-format-template.ts index 9b1281ca..3b18f737 100644 --- a/src/buffer/time-format-template.ts +++ b/src/buffer/time-format-template.ts @@ -2,5 +2,5 @@ export abstract class TimeFormatTemplate { public static readonly DateYearMonthDay: string = 'YYYYMMDD' public static readonly TimeHourMinSecMilliColon: string = 'HH:mm:ss.SSS' public static readonly TimeHourMinSecColon: string = 'HH:mm:ss' - public static readonly DateYearMonthDayHyphen: string = 'YYYY-MM-DD' // 2015-03-11 + public static readonly DateYearMonthDayHyphen: string = 'YYYY-MM-DD' // 2015-03-11 } diff --git a/src/collections/collection.ts b/src/collections/collection.ts index 6a6e0ec0..48f21a02 100644 --- a/src/collections/collection.ts +++ b/src/collections/collection.ts @@ -1,9 +1,5 @@ -export interface INumericKeyed { - [prop: number]: T -} +export type INumericKeyed = Record -export interface ILooseObject { - [prop: string]: any -} +export type ILooseObject = Record export type IDictIteratorCb = (key: string, val: T) => void diff --git a/src/collections/dictionary.ts b/src/collections/dictionary.ts index 116a1b1f..32415dfc 100644 --- a/src/collections/dictionary.ts +++ b/src/collections/dictionary.ts @@ -1,8 +1,7 @@ import { IDictIteratorCb } from './collection' export class Dictionary { - - private container: { [id: string]: T; } = {} + private container: Record = {} public count (): number { const keys: string[] = Object.keys(this.container) @@ -23,7 +22,7 @@ export class Dictionary { public toString (): string { return this.keys().reduce((a: string, current: string) => { const token = a.length > 0 ? ', ' : '' - return a + `${token}${this.container[current].toString() || ''}` + return a + `${token}${this.container[current]?.toString() ?? ''}` }, '') } @@ -47,11 +46,11 @@ export class Dictionary { delete this.container[key] } - public get (key: string): T { + public get (key: string): T | null { return this.container[key] } - public clear () { + public clear (): void { this.keys().forEach(k => { this.remove(k) }) diff --git a/src/config/empty-log-factory.ts b/src/config/empty-log-factory.ts index 348e04e2..970c6844 100644 --- a/src/config/empty-log-factory.ts +++ b/src/config/empty-log-factory.ts @@ -5,6 +5,7 @@ export class EmptyLogFactory extends JsFixLoggerFactory { public logger (type: string): IJsFixLogger { return new EmptyLogger(type) } + public plain (fileName: string, maxSize?: number): IJsFixLogger { return new EmptyLogger() } diff --git a/src/config/get-js-fx-logger.ts b/src/config/get-js-fx-logger.ts index 161700bc..f289f293 100644 --- a/src/config/get-js-fx-logger.ts +++ b/src/config/get-js-fx-logger.ts @@ -1,4 +1,3 @@ import { IJsFixLogger } from './js-fix-logger' -export interface GetJsFixLogger { (type: string): IJsFixLogger -} +export type GetJsFixLogger = (type: string) => IJsFixLogger diff --git a/src/config/js-fix-config.ts b/src/config/js-fix-config.ts index 9841c722..77c6a9bc 100644 --- a/src/config/js-fix-config.ts +++ b/src/config/js-fix-config.ts @@ -6,7 +6,7 @@ import { AsciiChars } from '../buffer/ascii' import { DependencyContainer } from 'tsyringe' export interface IJsFixConfig { - factory: ISessionMsgFactory + factory: ISessionMsgFactory | null definitions: FixDefinitions description: ISessionDescription delimiter?: number @@ -19,10 +19,10 @@ export class JsFixConfig implements IJsFixConfig { public logDelimiter: number = AsciiChars.Pipe public sessionContainer: DependencyContainer constructor ( - public readonly factory: ISessionMsgFactory, - public readonly definitions: FixDefinitions, - public readonly description: ISessionDescription, - public readonly delimiter: number = AsciiChars.Soh, - public readonly logFactory: JsFixLoggerFactory = new EmptyLogFactory()) { + public readonly factory: ISessionMsgFactory | null, + public readonly definitions: FixDefinitions, + public readonly description: ISessionDescription, + public readonly delimiter: number = AsciiChars.Soh, + public readonly logFactory: JsFixLoggerFactory = new EmptyLogFactory()) { } } diff --git a/src/config/js-fix-logger.ts b/src/config/js-fix-logger.ts index 08a4be6a..ea21300e 100644 --- a/src/config/js-fix-logger.ts +++ b/src/config/js-fix-logger.ts @@ -1,27 +1,32 @@ export interface IJsFixLogger { - info (message: string): void - warning (message: string): void - debug (message: string): void - error (e: Error): void + info: (message: string) => void + warning: (message: string) => void + debug: (message: string) => void + error: (e: Error) => void } export class EmptyLogger implements IJsFixLogger { constructor (public readonly type: string = '') { } - public info (message: string) { + + public info (message: string): void { // nothing } - public warning (message: string) { + + public warning (message: string): void { // nothing } - public debug (message: string) { + + public debug (message: string): void { // nothing } - public error (error: Error) { + + // eslint-disable-next-line n/handle-callback-err + public error (error: Error): void { // nothing } } -export function makeEmptyLogger (type: string) { +export function makeEmptyLogger (type: string): IJsFixLogger { return new EmptyLogger(type) } diff --git a/src/config/js-fix-winston-log-factory.ts b/src/config/js-fix-winston-log-factory.ts index f9fc79f8..8c924593 100644 --- a/src/config/js-fix-winston-log-factory.ts +++ b/src/config/js-fix-winston-log-factory.ts @@ -3,7 +3,7 @@ import { WinstonLogger } from './winston-logger' import { JsFixLoggerFactory } from './js-fix-logger-factory' export class JsFixWinstonLogFactory extends JsFixLoggerFactory { - private wl: WinstonLogger + private readonly wl: WinstonLogger constructor (public readonly options: any = WinstonLogger.consoleOptions()) { super() @@ -13,6 +13,7 @@ export class JsFixWinstonLogFactory extends JsFixLoggerFactory { public logger (type: string): IJsFixLogger { return this.wl.make(type) } + public plain (fileName: string, maxSize?: number): IJsFixLogger { return this.wl.plain(fileName, maxSize) } diff --git a/src/config/winston-logger.ts b/src/config/winston-logger.ts index 99f292ad..749eefa7 100644 --- a/src/config/winston-logger.ts +++ b/src/config/winston-logger.ts @@ -7,9 +7,11 @@ export class WinstonLogger { public static readonly appFormat = printf((info: any) => { return `${info.timestamp} [${info.type}] ${info.level}: ${info.message}` }) + public static readonly plainFormat = printf((info: any) => { return `${info.message}` }) + private readonly appLogger: Logger constructor (public readonly options: any = WinstonLogger.consoleOptions()) { @@ -22,7 +24,7 @@ export class WinstonLogger { timestamp(), WinstonLogger.appFormat ), - level: level, + level, transports: [ new transports.Console() ] @@ -34,8 +36,8 @@ export class WinstonLogger { WinstonLogger.appFormat )): any { return { - format: format, - level: level, + format, + level, transports: [ new transports.File({ filename: fileName, @@ -85,25 +87,25 @@ export class WinstonLogger { return { info: function (msg: string): void { logger.info({ - type: type, + type, message: msg }) }, debug: function (msg: string): void { logger.debug({ - type: type, + type, message: msg }) }, warning: function (msg: string): void { logger.warn({ - type: type, + type, message: msg }) }, error: function (e: Error): void { logger.error({ - type: type, + type, message: `${e.message} : ${e.stack}` }) } diff --git a/src/dict-parser.ts b/src/dict-parser.ts index d088ab1b..ad7e66e2 100644 --- a/src/dict-parser.ts +++ b/src/dict-parser.ts @@ -8,7 +8,7 @@ import { ReadStream } from 'fs' import { ISessionDescription, StringDuplex, MsgPayload } from './transport' import { FixDefinitions } from './dictionary/definition' import { MsgType } from './types' -import { JsFixWinstonLogFactory, JsFixConfig, WinstonLogger } from './config' +import { JsFixWinstonLogFactory, JsFixConfig, WinstonLogger, IJsFixConfig } from './config' import { BusinessRejectReason, IBusinessMessageReject } from './types/FIXML50SP2' import * as rp from 'request-promise-native' import { EnumCompiler, ICompilerSettings, MsgCompiler } from './dictionary/compiler' @@ -18,7 +18,7 @@ async function testEncodeDecode (): Promise { const msgType: string = 'W' const root: string = path.join(__dirname, '../') const sessionDescription: ISessionDescription = require('../data/session/test-initiator.json') - const definitions = await new DefinitionFactory().getDefinitions(path.join(root, sessionDescription.application.dictionary)) + const definitions = await new DefinitionFactory().getDefinitions(path.join(root, sessionDescription.application?.dictionary ?? '')) const jh: JsonHelper = new JsonHelper(definitions) const msg: ILooseObject = jh.fromJson(path.join(root, 'data/examples/FIXML/cme/tc/Initial Single Side Submission/fix.xml'), msgType) const config = new JsFixConfig(null, definitions, sessionDescription, AsciiChars.Pipe) @@ -30,7 +30,7 @@ async function testEncodeDecode (): Promise { const parser: MsgParser = new AsciiParser(config, encoderStream, new ElasticBuffer(160 * 1024)) const fix: string = session.buffer.toString() console.log(fix) - return new Promise(async (resolve, reject) => { + return await new Promise(async (resolve, reject) => { parser.on('msg', (msgType: string, view: AsciiView) => { resolve(view.toObject()) }) @@ -49,7 +49,7 @@ async function testGenerator (): Promise { const msgType: string = MsgType.NewOrderSingle const example: ILooseObject = generator.generate(msgType) console.log(JSON.stringify(example, null, 4)) - const config = new JsFixConfig(null, definitions, sessionDescription, AsciiChars.Pipe) + const config: IJsFixConfig = new JsFixConfig(null, definitions, sessionDescription, AsciiChars.Pipe) const session: AsciiMsgTransmitter = new AsciiMsgTransmitter(config) session.encodeMessage(msgType, example) const fix: string = session.buffer.toString() @@ -82,7 +82,7 @@ async function repository (): Promise { // const file: string = path.join(root,'data/examples/FIXML/cme/alloc/Claiming Firm Requests Sub-allocation with Allocation Instructions/') // const file: string = path.join(root,'data/examples/FIXML/cme/md/settle') // const file: string = path.join(root, 'data/examples/FIXML/cme/alloc/Clearing System Notifies Allocation to the Claiming Firm - Cross-Exchange/') - const file: string = path.join(root,'data/examples/FIXML/cme/tc/Delivery Fixed Commodity Swap/') + const file: string = path.join(root, 'data/examples/FIXML/cme/tc/Delivery Fixed Commodity Swap/') // const file: string = path.join(root, 'data/examples/FIXML/cme/tc/Trading Firm Continued Subscription') // const file: string = path.join(root,'data/examples/FIXML/cme/md/futures') // const file: string = path.join(root, 'data/examples/FIXML/cme/tc/Delivery Fixed Commodity Swap') @@ -95,7 +95,7 @@ async function repository (): Promise { const t855 = definitions.simple.get('SecondaryTrdType') const reject = { - Text: `no response`, + Text: 'no response', BusinessRejectReason: BusinessRejectReason.ApplicationNotAvailable } as IBusinessMessageReject const fe = new FixmlEncoder(new ElasticBuffer(), definitions) @@ -104,7 +104,7 @@ async function repository (): Promise { // console.log(fixml) const jh: JsonHelper = new JsonHelper(definitions) const fs: any = require('fs') - let readStream: ReadStream = fs.createReadStream(`${file}/fix.xml`) + const readStream: ReadStream = fs.createReadStream(`${file}/fix.xml`) const sessionDescription: ISessionDescription = require('../data/session/test-initiator.json') const config = new JsFixConfig(null, definitions, sessionDescription, AsciiChars.Pipe) const xmlParser: MsgParser = new FiXmlParser(config, readStream) @@ -131,10 +131,10 @@ async function repository (): Promise { } async function runTest (): Promise { - return new Promise(async (accept, reject) => { + return await new Promise(async (resolve, reject) => { try { const res: any = await testGenerator() - accept(res) + resolve(res) } catch (e) { console.log(e.message) reject(e) @@ -142,15 +142,15 @@ async function runTest (): Promise { }) } -function streamExample () { +async function streamExample (): Promise { const fs: any = require('fs') const root: string = path.join(__dirname, '../') - const file: string = path.join(root,'data/examples/FIXML/cme/Claiming Firm Requests Sub-allocation with Allocation Instructions/') - let readStream: ReadStream = fs.createReadStream(`${file}/fix.xml`) + const file: string = path.join(root, 'data/examples/FIXML/cme/Claiming Firm Requests Sub-allocation with Allocation Instructions/') + const readStream: ReadStream = fs.createReadStream(`${file}/fix.xml`) const Writable = require('stream').Writable const receiver = new Writable({ write: (data: Buffer, _: any, done: Function) => { - console.log('receive ' + data) + console.log('receive ' + data.toString()) done() } }) @@ -159,7 +159,7 @@ function streamExample () { }) } -async function compileDefinitions (definitionPath: string, outputPath: string) { +async function compileDefinitions (definitionPath: string, outputPath: string): Promise { const definitions = await new DefinitionFactory().getDefinitions(definitionPath) const compilerSettings: ICompilerSettings = require('../data/compiler.json') compilerSettings.output = outputPath @@ -173,14 +173,14 @@ async function compileDefinitions (definitionPath: string, outputPath: string) { await enumCompiler.generate(writeFile) } -async function compiler () { +async function compiler (): Promise { // 'C:/Users/Stephen/dev/js/jsfix/data/fix_repo/fixmlschema_FIX.5.0SP2_EP228' // await compileDefinitions('data/fix_repo/FIX.4.4/Base', 'C:/Users/Stephen/dev/ts/jsfix/src/types/FIX4.4/repo') // await compileDefinitions('data/FIX44.xml', 'C:/Users/Stephen/dev/ts/jsfix/src/types/FIX4.4/quickfix') await compileDefinitions('data/fix_repo/fixmlschema_FIX.5.0SP2_EP228', 'C:/Users/Stephen/dev/ts/jsfix/src/types/FIXML50SP2') } -async function generateMessage () { +async function generateMessage (): Promise { await testGenerator() } @@ -205,7 +205,7 @@ async function decode (): Promise { async function http (): Promise { const sessionDescription: ISessionDescription = require('../data/session/test-http-acceptor.json') - const definitions = await new DefinitionFactory().getDefinitions(sessionDescription.application.dictionary) + const definitions = await new DefinitionFactory().getDefinitions(sessionDescription.application?.dictionary ?? '') const logFactory = new JsFixWinstonLogFactory(WinstonLogger.consoleOptions('info')) const config = new JsFixConfig(null, definitions, sessionDescription, AsciiChars.Pipe, logFactory) // const acceptor = acceptor(config) @@ -220,7 +220,7 @@ async function http (): Promise { method: 'POST', uri: 'http://localhost:2343/session', body: { - fixml : xml + fixml: xml }, json: true // Automatically stringifies the body to JSON }).then(function (parsedBody) { @@ -240,7 +240,9 @@ async function http (): Promise { // stronglyTyped() // streamExample() // testEncodeDecode() -repository() +repository().then(() => {}).catch(e => { + console.error(e) +}) // testEncodeDecode() // runTest(); // testSocket() diff --git a/src/dictionary/compiler/compiler-settings.ts b/src/dictionary/compiler/compiler-settings.ts index 1c0bc001..9441b791 100644 --- a/src/dictionary/compiler/compiler-settings.ts +++ b/src/dictionary/compiler/compiler-settings.ts @@ -1,8 +1,8 @@ export interface ICompilerSettings { - standard: boolean, - spaces: number, - comment: boolean, - tags: true, - output: string, + standard: boolean + spaces: number + comment: boolean + tags: true + output: string types?: string[] } diff --git a/src/dictionary/compiler/compiler-type.ts b/src/dictionary/compiler/compiler-type.ts index 1f39e890..b109a035 100644 --- a/src/dictionary/compiler/compiler-type.ts +++ b/src/dictionary/compiler/compiler-type.ts @@ -51,7 +51,7 @@ export class CompilerType { } } - public getFieldGroupName (field: ContainedField) { + public getFieldGroupName (field: ContainedField): string { switch (field.type) { case ContainedFieldType.Group: { const gf = field as ContainedGroupField diff --git a/src/dictionary/compiler/enum-compiler.ts b/src/dictionary/compiler/enum-compiler.ts index 7cca0378..a15d59ca 100644 --- a/src/dictionary/compiler/enum-compiler.ts +++ b/src/dictionary/compiler/enum-compiler.ts @@ -21,8 +21,8 @@ export class EnumCompiler { private static getIndex (): string { const lines = [ - `export * from './all-enum'`, - `export * from './msg_tag'`, + 'export * from \'./all-enum\'', + 'export * from \'./msg_tag\'', '' ] return lines.join(require('os').EOL) @@ -36,12 +36,11 @@ export class EnumCompiler { } */ - public async generate (asOneFile: string = null) { + public async generate (asOneFile: string | null = null): Promise { this.generateTagEnum('MsgTag').then(async () => { const toDo: SimpleFieldDefinition[] = this.toDo() - toDo.forEach(async (field: SimpleFieldDefinition) => { - await this.oneEnum(field, asOneFile) - }) + const promised = toDo.map(async field => await this.oneEnum(field, asOneFile)) + await Promise.all(promised) if (asOneFile) { await this.writeAsOne(asOneFile) await this.writeFile('index', EnumCompiler.getIndex()) @@ -51,7 +50,7 @@ export class EnumCompiler { }) } - public async oneEnum (field: SimpleFieldDefinition, asOneFile: string) { + public async oneEnum (field: SimpleFieldDefinition, asOneFile: string | null): Promise { const newLine = require('os').EOL const api = this.generateEnum(field) if (!asOneFile) { @@ -75,7 +74,7 @@ export class EnumCompiler { return this.create(field.name, field.description, () => { const newLine = require('os').EOL return field.enums.keys().reduce((a: number, latest: string, i: number, arr: string[]) => { - let k = field.resolveEnum(latest) + const k = field.resolveEnum(latest) let v: any = latest switch (field.tagType) { case TagType.Int: { @@ -94,7 +93,7 @@ export class EnumCompiler { }) } - public async generateTagEnum (name: string) { + public async generateTagEnum (name: string): Promise { const newLine = require('os').EOL const tags = this.definitions.tagToSimple const snippets = this.snippets @@ -130,7 +129,7 @@ export class EnumCompiler { }, []) } - private async writeAsOne (asOneFile: string) { + private async writeAsOne (asOneFile: string): Promise { const writer = util.promisify(fs.writeFile) await writer(asOneFile, this.consolidated.toString(), { encoding: 'utf8' @@ -140,7 +139,7 @@ export class EnumCompiler { }) } - private commentBlock (comment: string) { + private commentBlock (comment: string): void { const buffer = this.buffer const snippets = this.snippets const newLine = require('os').EOL @@ -152,11 +151,10 @@ export class EnumCompiler { buffer.writeString(newLine) } - private async writeFile (name: string, api: string) { + private async writeFile (name: string, api: string): Promise { const writer = util.promisify(fs.writeFile) const fullName = this.getFileName(name) - await writer(fullName, api, { - encoding: 'utf8'} + await writer(fullName, api, { encoding: 'utf8' } ).catch((e: Error) => { throw e }) @@ -166,11 +164,11 @@ export class EnumCompiler { const snake = _.snakeCase(name) const settings = this.settings const fileName = `${snake}.ts` - let path: string = `${settings.output}/enum/` + const path: string = `${settings.output}/enum/` return Path.join(path, fileName) } - private create (name: string, description: string, populateMembers: Function): string { + private create (name: string, description: string | null, populateMembers: Function): string { const newLine = require('os').EOL const buffer = this.buffer buffer.reset() diff --git a/src/dictionary/compiler/msg-compiler.ts b/src/dictionary/compiler/msg-compiler.ts index efc7c29b..65071988 100644 --- a/src/dictionary/compiler/msg-compiler.ts +++ b/src/dictionary/compiler/msg-compiler.ts @@ -34,9 +34,9 @@ export class MsgCompiler { this.snippets = new StandardSnippet(this.settings) } - public async generate () { - const types: string[] = this.settings.types || this.definitions.message.keys() - return this.createTypes(types) + public async generate (): Promise { + const types: string[] = this.settings.types ?? this.definitions.message.keys() + return await this.createTypes(types) } private getFileName (compilerType: CompilerType): string { @@ -45,7 +45,7 @@ export class MsgCompiler { return Path.join(settings.output, fileName) } - private async createTypes (types: string[]) { + private async createTypes (types: string[]): Promise { const definitions = this.definitions types.forEach((type: string) => { const definition = definitions.containedSet(type) @@ -59,35 +59,33 @@ export class MsgCompiler { await this.index() } - private async work () { + private async work (): Promise { const q = this.queue const writeFile = Util.promisify(fs.writeFile) while (q.length > 0) { - const compilerType: CompilerType = q.pop() + const compilerType = q.pop() + if (!compilerType) continue const api: string = this.generateMessages(compilerType) const fullName = this.getFileName(compilerType) - await writeFile(fullName, api, { - encoding: 'utf8'} + await writeFile(fullName, api, { encoding: 'utf8' } ) } } - private async index () { + private async index (): Promise { const writeFile = Util.promisify(fs.writeFile) const settings = this.settings const fileName = 'index.ts' const done = this.completed.values() - const exports: string[] = done.reduce((prev: string[], current: CompilerType) => { + const exports: string[] = done.reduce((prev: string[], current: CompilerType) => { prev.push(`export * from '${current.snaked}'`) return prev - }, [`export * from './enum'`] as string[]) + }, ['export * from \'./enum\'']) exports.sort() exports.push('') const api: string = exports.join(newLine) const fullName: string = Path.join(settings.output, fileName) - await writeFile(fullName, api, { - encoding: 'utf8'} - ) + return await writeFile(fullName, api, { encoding: 'utf8' }) } private generateMessages (compilerType: CompilerType): string { @@ -96,7 +94,7 @@ export class MsgCompiler { buffer.reset() const snippets = this.snippets // a single class with dependencies included - let ptr: number = this.imports(compilerType) + const ptr: number = this.imports(compilerType) if (ptr > 0) { buffer.writeString(newLine) buffer.writeString(newLine) @@ -154,12 +152,12 @@ export class MsgCompiler { } private tagSummary (definition: ContainedFieldSet, max: number = 3): string { - function tagTxt (tag: number) { + function tagTxt (tag: number): string { const name = definition.getFieldName(tag) return `${name}.${tag}` } - function setTxt (flattened: number[]) { + function setTxt (flattened: number[]): string { return flattened.map(f => tagTxt(f)).join(', ') } diff --git a/src/dictionary/compiler/standard-snippet.ts b/src/dictionary/compiler/standard-snippet.ts index 214f4bb2..b146943c 100644 --- a/src/dictionary/compiler/standard-snippet.ts +++ b/src/dictionary/compiler/standard-snippet.ts @@ -5,8 +5,8 @@ export class StandardSnippet { constructor (public readonly settings: ICompilerSettings) { } - private static rhsJustify (txt: string, width: number) { - let align = require('align-text') + private static rhsJustify (txt: string, width: number): string { + const align = require('align-text') return align(txt, width) } @@ -18,27 +18,27 @@ export class StandardSnippet { return `${spaces}import { I${name} } from '${path}'` } - public interface (name: string, indent: number) { + public interface (name: string, indent: number): string { const spaces: string = this.spaces(indent) return `${spaces}export interface I${name}` } - public startBlockComment (indent: number) { + public startBlockComment (indent: number): string { const spaces: string = this.spaces(indent) return `${spaces}/*` } - public endBlockComment (indent: number) { + public endBlockComment (indent: number): string { const spaces: string = this.spaces(indent) return `${spaces}*/` } - public startBlock (indent: number) { + public startBlock (indent: number): string { const spaces: string = this.spaces(indent) return `${spaces}{` } - public endBlock (indent: number) { + public endBlock (indent: number): string { const spaces: string = this.spaces(indent) return `${spaces}}` } @@ -58,50 +58,51 @@ export class StandardSnippet { return `${spaces}${name}${required ? '' : '?'}: ${type}` } - public enum (name: string, indent: number) { + public enum (name: string, indent: number): string { const spaces: string = this.spaces(indent) return `export ${spaces}enum ${name}` } - public enumValue (name: string, val: any, indent: number) { + public enumValue (name: string, val: any, indent: number): string { const spaces: string = this.spaces(indent) - if (typeof(val) === 'string') { + if (typeof (val) === 'string') { return `${spaces}${name} = '${val}'` } return `${spaces}${name} = ${val}` } - public spaces (indent: number) { + public spaces (indent: number): string { return ' '.repeat(this.settings.spaces * indent) } - public commentLine (line: string, justify: number) { + public commentLine (line: string, justify: number): string { return StandardSnippet.rhsJustify(`// ${line}`, justify) } - public commentBox (str: string) { - - let wrap = require('word-wrap') - let text = wrap(str, { + public commentBox (str: string): string { + const wrap = require('word-wrap') + const text = wrap(str, { width: 60, - indent: ' '} - ) + indent: ' ' + } + ) let lines = text.split('\n').map((a: string) => a.trim()) - let max = this.longest(lines).length + 2 - const newLine = require('os').EOL + const max = this.longest(lines).length + 2 + const newLine: string = require('os').EOL lines = lines.map(function (line: string) { line = ` ${line} ` - let diff = max - line.length + const diff = max - line.length return '*' + line + ' '.repeat(diff) + '*' }) - let stars = '*'.repeat(lines[0].length) - return stars + newLine - + lines.join(newLine) - + newLine - + stars + const stars: string = '*'.repeat(lines[0].length) + const joined: string = lines.join(newLine) + return stars + newLine + + joined + + newLine + + stars } - private longest (arr: string[]) { + private longest (arr: string[]): string { return arr.reduce(function (a, b) { return a.length > b.length ? a : b }) diff --git a/src/dictionary/contained/contained-component-field.ts b/src/dictionary/contained/contained-component-field.ts index 79df2ef7..5bc42c68 100644 --- a/src/dictionary/contained/contained-component-field.ts +++ b/src/dictionary/contained/contained-component-field.ts @@ -4,11 +4,12 @@ import { ContainedFieldType } from './contained-field-type' export class ContainedComponentField extends ContainedField { constructor (public readonly definition: ComponentFieldDefinition, - public position: number, - public readonly required: boolean, - public readonly override?: string) { - super(override || definition.name, position, ContainedFieldType.Component, required) + public position: number, + public readonly required: boolean, + public readonly override?: string) { + super(override ?? definition.name, position, ContainedFieldType.Component, required) } + public toString (): string { return `[${this.position}]=C.${this.definition.fields.length} (${this.name})` } diff --git a/src/dictionary/contained/contained-field-dispatcher.ts b/src/dictionary/contained/contained-field-dispatcher.ts index 5193d045..492267f3 100644 --- a/src/dictionary/contained/contained-field-dispatcher.ts +++ b/src/dictionary/contained/contained-field-dispatcher.ts @@ -3,7 +3,7 @@ import { ContainedSimpleField } from './contained-simple-field' import { ContainedComponentField } from './contained-component-field' export interface IContainedFieldDispatcher { - group?: { (field: ContainedGroupField): void } - simple?: { (field: ContainedSimpleField): void } - component?: { (field: ContainedComponentField): void } + group?: (field: ContainedGroupField) => void + simple?: (field: ContainedSimpleField) => void + component?: (field: ContainedComponentField) => void } diff --git a/src/dictionary/contained/contained-field-set.ts b/src/dictionary/contained/contained-field-set.ts index 98a0ce1e..80e6a662 100644 --- a/src/dictionary/contained/contained-field-set.ts +++ b/src/dictionary/contained/contained-field-set.ts @@ -13,45 +13,45 @@ export abstract class ContainedFieldSet { public readonly groups: Dictionary = new Dictionary() public readonly components: Dictionary = new Dictionary() public readonly simple: Dictionary = new Dictionary() - // sequence of fields representing this type - can be simple, group or component + // sequence of fields representing this type - can be simple, group or component public readonly fields: ContainedField[] = [] - // any tag at any level i.e. does this set contain a tag + // any tag at any level i.e. does this set contain a tag public readonly containedTag: INumericKeyed = {} - // any tag at any level ordered ie. all tags flattened to list + // any tag at any level ordered ie. all tags flattened to list public readonly flattenedTag: number[] = [] - // any data tags contained length within this set. + // any data tags contained length within this set. public readonly containedLength: INumericKeyed = {} - // tags only in repository at this level, not from any at deeper levels + // tags only in repository at this level, not from any at deeper levels public readonly localTag: INumericKeyed = {} // tags marked required at this level only public readonly localRequired: INumericKeyed = {} - // all tags contained within this field set flattened from all levels + // all tags contained within this field set flattened from all levels public readonly tagToSimple: INumericKeyed = {} - // direct any tag contained within this set to field one level down where it belongs. + // direct any tag contained within this set to field one level down where it belongs. public readonly tagToField: INumericKeyed = {} - // only repository directly in this set indexed by name + // only repository directly in this set indexed by name public readonly localNameToField: Dictionary = new Dictionary() - // for FixMl notation this set of fields appear as attributes i.e. + // for FixMl notation this set of fields appear as attributes i.e. public readonly nameToLocalAttribute: Dictionary = new Dictionary() - // all attributes in order of being declared + // all attributes in order of being declared public readonly localAttribute: ContainedSimpleField[] = [] - // at any level on this set, first declared simple field + // at any level on this set, first declared simple field public firstSimple: ContainedSimpleField - // parser needs to know about raw fields + // parser needs to know about raw fields public containsRaw: boolean = false protected constructor (public readonly type: ContainedSetType, - public readonly name: string, - public readonly category: string, - public readonly abbreviation: string, - public readonly description: string) { + public readonly name: string, + public readonly category: string | null, + public readonly abbreviation: string | null, + public readonly description: string | null) { } - public toString () { + public toString (): string { const buffer = new ElasticBuffer(2 * 1024) const fields = this.fields buffer.writeString(`Set: ${this.name}(${this.getPrefix()}) fields [${fields.length}]: `) - const set = fields.map(f => f.toString()) + const set: string[] = fields.map((f: ContainedField) => f.toString()) const s = set.join(', ') buffer.writeString(s) return buffer.toString() @@ -59,7 +59,7 @@ export abstract class ContainedFieldSet { public abstract getPrefix (): string - public getFieldName (tag: number) { + public getFieldName (tag: number): string { const s = this.tagToSimple[tag] if (s == null) { const gf = this.tagToField[tag] as ContainedGroupField @@ -108,7 +108,7 @@ export abstract class ContainedFieldSet { } } - private addLocalSimple (field: ContainedSimpleField) { + private addLocalSimple (field: ContainedSimpleField): void { const definition = field.definition if (definition.abbreviation && definition.abbreviation !== definition.name) { this.localNameToField.addUpdate(definition.abbreviation, field) @@ -194,7 +194,7 @@ export abstract class ContainedFieldSet { } private mapAllBelow (set: ContainedFieldSet, field: ContainedField): void { - // point all tags in this component to this field. + // point all tags in this component to this field. const tagsBelow: number[] = set.keys() for (const t of tagsBelow) { this.tagToField[t] = field diff --git a/src/dictionary/contained/contained-field.ts b/src/dictionary/contained/contained-field.ts index 0ee74187..ce215776 100644 --- a/src/dictionary/contained/contained-field.ts +++ b/src/dictionary/contained/contained-field.ts @@ -2,7 +2,10 @@ import { ContainedFieldType } from './contained-field-type' export class ContainedField { constructor (public readonly name: string, public readonly position: number, - public readonly type: ContainedFieldType, readonly required: boolean) { + public readonly type: ContainedFieldType, readonly required: boolean) { + } + public toString (): string { + return this.name } } diff --git a/src/dictionary/contained/contained-group-field.ts b/src/dictionary/contained/contained-group-field.ts index 0f4d39c2..14dfdfb9 100644 --- a/src/dictionary/contained/contained-group-field.ts +++ b/src/dictionary/contained/contained-group-field.ts @@ -4,10 +4,10 @@ import { ContainedFieldType } from './contained-field-type' export class ContainedGroupField extends ContainedField { constructor (public readonly definition: GroupFieldDefinition, - public readonly position: number, - public readonly required: boolean, - public readonly override?: string) { - super(override || definition.name, position, ContainedFieldType.Group, required) + public readonly position: number, + public readonly required: boolean, + public readonly override?: string) { + super(override ?? definition.name, position, ContainedFieldType.Group, required) } public toString (): string { diff --git a/src/dictionary/contained/contained-simple-field.ts b/src/dictionary/contained/contained-simple-field.ts index 9afe821c..19e0d68a 100644 --- a/src/dictionary/contained/contained-simple-field.ts +++ b/src/dictionary/contained/contained-simple-field.ts @@ -4,11 +4,11 @@ import { ContainedFieldType } from './contained-field-type' export class ContainedSimpleField extends ContainedField { constructor (public readonly definition: SimpleFieldDefinition, - public readonly position: number, - public readonly required: boolean, - public readonly attribute: boolean, - public readonly override?: string) { - super(override || definition.name, position, ContainedFieldType.Simple, required) + public readonly position: number, + public readonly required: boolean, + public readonly attribute: boolean, + public readonly override?: string) { + super(override ?? definition.name, position, ContainedFieldType.Simple, required) } public toString (): string { diff --git a/src/dictionary/contained/field-dispatcher.ts b/src/dictionary/contained/field-dispatcher.ts index 1a30850e..d01e0103 100644 --- a/src/dictionary/contained/field-dispatcher.ts +++ b/src/dictionary/contained/field-dispatcher.ts @@ -3,7 +3,7 @@ import { ContainedSimpleField } from './contained-simple-field' import { ContainedComponentField } from './contained-component-field' export interface IFieldDispatcher { - group?: { (field: ContainedGroupField): void } - simple?: { (field: ContainedSimpleField): void } - component?: { (field: ContainedComponentField): void } + group?: (field: ContainedGroupField) => void + simple?: (field: ContainedSimpleField) => void + component?: (field: ContainedComponentField) => void } diff --git a/src/dictionary/contained/fields-dispatch.ts b/src/dictionary/contained/fields-dispatch.ts index eebd6f5c..5a6d9611 100644 --- a/src/dictionary/contained/fields-dispatch.ts +++ b/src/dictionary/contained/fields-dispatch.ts @@ -20,7 +20,6 @@ export class FieldsDispatch { try { dispatcher.simple(field as ContainedSimpleField) } catch (ex) { - let x = 0 } } break diff --git a/src/dictionary/definition/component-field-definition.ts b/src/dictionary/definition/component-field-definition.ts index 29265aa8..7c464927 100644 --- a/src/dictionary/definition/component-field-definition.ts +++ b/src/dictionary/definition/component-field-definition.ts @@ -3,13 +3,13 @@ import { ContainedSetType } from '../contained-set-type' export class ComponentFieldDefinition extends ContainedFieldSet { constructor (public readonly name: string, - public readonly abbreviation: string, - public readonly category: string, - public readonly description: string) { + public readonly abbreviation: string, + public readonly category: string | null, + public readonly description: string | null) { super(ContainedSetType.Component, name, abbreviation, category, description) } public getPrefix (): string { - return `C` + return 'C' } } diff --git a/src/dictionary/definition/fix-definitions.ts b/src/dictionary/definition/fix-definitions.ts index 6b482e0c..b0f0ccb8 100644 --- a/src/dictionary/definition/fix-definitions.ts +++ b/src/dictionary/definition/fix-definitions.ts @@ -18,8 +18,8 @@ export class FixDefinitions { constructor (public readonly source: FixDefinitionSource, public readonly version: FixVersion) { } - public containedSet (type: string): ContainedFieldSet { - return this.message.get(type) || this.component.get(type) + public containedSet (type: string): ContainedFieldSet | null { + return this.message.get(type) ?? this.component.get(type) } public addMessage (message: MessageDefinition): void { @@ -39,10 +39,10 @@ export class FixDefinitions { this.component.addUpdate(field.name, field) } - public getSimple (name: string, cat?: string): SimpleFieldDefinition { - let sf: SimpleFieldDefinition = null + public getSimple (name: string, cat?: string | null): SimpleFieldDefinition | null { + let sf: SimpleFieldDefinition | null = null if (cat) { - let category: CategorySimpleSet = this.categorySimple.get(cat) + const category: CategorySimpleSet | null = this.categorySimple.get(cat) if (category) { sf = category.simple.get(name) } @@ -60,7 +60,7 @@ export class FixDefinitions { } } - public addSimpleFieldDef (field: SimpleFieldDefinition, typeName: string = null): void { + public addSimpleFieldDef (field: SimpleFieldDefinition, typeName: string | null = null): void { this.assignCategory(field) const simple = this.simple simple.addUpdate(field.num, field) @@ -76,9 +76,9 @@ export class FixDefinitions { } } - private assignCategory (field: SimpleFieldDefinition) { + private assignCategory (field: SimpleFieldDefinition): void { if (field.baseCategory && field.baseCategoryAbbreviation) { - let category: CategorySimpleSet = this.categorySimple.get(field.baseCategory) + let category: CategorySimpleSet | null = this.categorySimple.get(field.baseCategory) if (!category) { category = new CategorySimpleSet(field.baseCategory) this.categorySimple.add(field.baseCategory, category) diff --git a/src/dictionary/definition/group-field-definition.ts b/src/dictionary/definition/group-field-definition.ts index 5f9a383d..da16cae9 100644 --- a/src/dictionary/definition/group-field-definition.ts +++ b/src/dictionary/definition/group-field-definition.ts @@ -4,10 +4,10 @@ import { ContainedSetType } from '../contained-set-type' export class GroupFieldDefinition extends ContainedFieldSet { constructor (public readonly name: string, - public readonly abbreviation: string, - public readonly category: string, - public readonly noOfField: SimpleFieldDefinition, - public readonly description: string) { + public readonly abbreviation: string, + public readonly category: string | null, + public readonly noOfField: SimpleFieldDefinition | null, + public readonly description: string | null) { super(ContainedSetType.Group, name, abbreviation, category, description) if (this.noOfField) { this.containedTag[this.noOfField.tag] = true @@ -15,6 +15,6 @@ export class GroupFieldDefinition extends ContainedFieldSet { } public getPrefix (): string { - return `G` + return 'G' } } diff --git a/src/dictionary/definition/message-definition.ts b/src/dictionary/definition/message-definition.ts index 83b67e14..10c1ee55 100644 --- a/src/dictionary/definition/message-definition.ts +++ b/src/dictionary/definition/message-definition.ts @@ -3,10 +3,10 @@ import { ContainedSetType } from '../contained-set-type' export class MessageDefinition extends ContainedFieldSet { constructor (public readonly name: string, - public readonly abbreviation: string, - public readonly msgType: string, - public readonly category: string, - public readonly description: string) { + public readonly abbreviation: string, + public readonly msgType: string, + public readonly category: string, + public readonly description: string | null) { super(ContainedSetType.Msg, name, category, abbreviation, description) } diff --git a/src/dictionary/definition/simple-field-definition.ts b/src/dictionary/definition/simple-field-definition.ts index 824e1b3d..5d193f6c 100644 --- a/src/dictionary/definition/simple-field-definition.ts +++ b/src/dictionary/definition/simple-field-definition.ts @@ -12,12 +12,12 @@ export class SimpleFieldDefinition { public enumVals: Dictionary constructor (public readonly num: string, - public readonly name: string, - public readonly abbreviation: string, - public readonly baseCategory: string, - public readonly baseCategoryAbbreviation: string, - public readonly type: string, - public readonly description: string) { + public readonly name: string, + public readonly abbreviation: string, + public readonly baseCategory: string | null, + public readonly baseCategoryAbbreviation: string | null, + public readonly type: string, + public readonly description: string | null) { this.tag = parseInt(num, 10) this.tagType = Tags.toType(type) } @@ -39,7 +39,7 @@ export class SimpleFieldDefinition { if (!enums) { return key } - const e: FieldEnum = enums.get(key) + const e: FieldEnum | null = enums.get(key) if (e) { return e.val } @@ -69,7 +69,7 @@ export class SimpleFieldDefinition { this.enumVals = enumVals = new Dictionary() } val = this.patchEnumValue(val) - enums.add(key, new FieldEnum(key, val, description)) + enums.add(key, new FieldEnum(key, val, description ?? '')) enumVals.add(val, true) } @@ -85,6 +85,6 @@ export class SimpleFieldDefinition { if (baseCategoryAbbreviation.length > 0 && this.baseCategory) { baseCategoryAbbreviation = `${this.baseCategory} ${baseCategoryAbbreviation}` } - return `${this.num} ${this.name} ${abbreviation} ${baseCategoryAbbreviation} ${this.type} ${this.description || ''} ${this.enums ? `enumerated = [ ${this.enums.toString()} ]` : ''}` + return `${this.num} ${this.name} ${abbreviation} ${baseCategoryAbbreviation} ${this.type} ${this.description ?? ''} ${this.enums ? `enumerated = [ ${this.enums.toString()} ]` : ''}` } } diff --git a/src/dictionary/dict-primitive.ts b/src/dictionary/dict-primitive.ts index d64c1687..4fcc61ec 100644 --- a/src/dictionary/dict-primitive.ts +++ b/src/dictionary/dict-primitive.ts @@ -2,9 +2,9 @@ import { SAXOptions, SAXParser } from 'sax' import * as stream from 'stream' import { FixDefinitions } from './definition' -export type IDictDoneCb = (err: Error, done: FixDefinitions) => void +export type IDictDoneCb = (err?: Error, done?: FixDefinitions | null) => void export declare class SAXStream extends stream.Duplex { public _parser: SAXParser - constructor (strict: boolean, opt: SAXOptions); + constructor (strict: boolean, opt: SAXOptions) } diff --git a/src/dictionary/field-enum.ts b/src/dictionary/field-enum.ts index 8765d0a3..43da866f 100644 --- a/src/dictionary/field-enum.ts +++ b/src/dictionary/field-enum.ts @@ -1,8 +1,9 @@ export class FieldEnum { constructor (public readonly key: string, - public readonly val: string, - public readonly description: string) { + public readonly val: string, + public readonly description: string) { } + public toString (): string { return `${this.key} = ${this.val}` } diff --git a/src/dictionary/fix-versions.ts b/src/dictionary/fix-versions.ts index d64ed5e6..db4b8f43 100644 --- a/src/dictionary/fix-versions.ts +++ b/src/dictionary/fix-versions.ts @@ -1,12 +1,12 @@ export enum FixVersion { - Unknown = 1, - FIX40 = 1, - FIX41 = 2, - FIX42 = 3, - FIX43 = 4, - FIX44 = 5, - FIX50 = 6, - FIX50SP1 = 7, - FIX50SP2 = 8, - FIXML50SP2 = 9 + Unknown = 1, + FIX40 = 1, + FIX41 = 2, + FIX42 = 3, + FIX43 = 4, + FIX44 = 5, + FIX50 = 6, + FIX50SP1 = 7, + FIX50SP2 = 8, + FIXML50SP2 = 9 } diff --git a/src/dictionary/parser/fix-repository/repository-xml-parser.ts b/src/dictionary/parser/fix-repository/repository-xml-parser.ts index e11c5a6e..43c2a1a8 100644 --- a/src/dictionary/parser/fix-repository/repository-xml-parser.ts +++ b/src/dictionary/parser/fix-repository/repository-xml-parser.ts @@ -30,8 +30,8 @@ export class RepositoryXmlParser extends FixParser { } private static subscribe (instance: RepositoryXmlParser, saxStream: SAXStream, done: IDictDoneCb): void { - let parser: BaseParser - let pending: string + let parser: BaseParser | null + let pending: string | null const saxParser: SAXParser = saxStream._parser saxStream.on('error', (e: Error) => { @@ -39,7 +39,6 @@ export class RepositoryXmlParser extends FixParser { }) saxStream.on('closetag', (name) => { - switch (name) { case 'Datatypes': case 'Abbreviations': @@ -79,7 +78,7 @@ export class RepositoryXmlParser extends FixParser { saxStream.on('text', (t: string) => { t = t.trim() if (pending) { - parser.value(saxParser.line, pending, t) + parser?.value(saxParser.line, pending, t) } }) @@ -143,13 +142,13 @@ export class RepositoryXmlParser extends FixParser { saxStream.on('ready', () => { if (done) { parser = null - done(null, instance.repository.definitions) + done(undefined, instance.repository.definitions) } }) } - public parse (): Promise { - return new Promise(async (accept, reject) => { + public async parse (): Promise { + return await new Promise(async (resolve, reject) => { try { await this.onePass('Datatypes.xml') await this.onePass('Fields.xml') @@ -160,7 +159,7 @@ export class RepositoryXmlParser extends FixParser { if (this.repository.includesAbbreviations) { await this.onePass('Abbreviations.xml') } - accept(this.repository.definitions) + resolve(this.repository.definitions) } catch (e) { reject(e) } diff --git a/src/dictionary/parser/fix-repository/repository.ts b/src/dictionary/parser/fix-repository/repository.ts index 91b509b3..4bb454b9 100644 --- a/src/dictionary/parser/fix-repository/repository.ts +++ b/src/dictionary/parser/fix-repository/repository.ts @@ -1,6 +1,8 @@ import { ILooseObject } from '../../../collections/collection' -import { SimpleFieldDefinition, GroupFieldDefinition, ComponentFieldDefinition, - MessageDefinition, FixDefinitions } from '../../definition' +import { + SimpleFieldDefinition, GroupFieldDefinition, ComponentFieldDefinition, + MessageDefinition, FixDefinitions +} from '../../definition' import { Dictionary } from '../../../collections' import { ContainedFieldSet, ContainedComponentField, ContainedGroupField, ContainedSimpleField } from '../../contained' import { FixVersion } from '../../fix-versions' @@ -24,7 +26,7 @@ export class Repository { public MsgContents: IRepositoryMsgContent[] public Abbreviations: IRepositoryAbbreviation[] public includesAbbreviations: boolean - // derived from above + // derived from above public readonly definitions: FixDefinitions private readonly groupLookup: Dictionary = new Dictionary() private contentLookup: Dictionary @@ -127,13 +129,17 @@ export class Repository { } private header (): void { - const h: ComponentFieldDefinition = this.definitions.component.get('StandardHeader') - this.definitions.component.add('header', h) + const h: ComponentFieldDefinition | null = this.definitions.component.get('StandardHeader') + if (h) { + this.definitions.component.add('header', h) + } } private trailer (): void { - const t: ComponentFieldDefinition = this.definitions.component.get('StandardTrailer') - this.definitions.component.add('trailer', t) + const t: ComponentFieldDefinition | null = this.definitions.component.get('StandardTrailer') + if (t) { + this.definitions.component.add('trailer', t) + } } private static isNative (f: IRepositoryField): boolean { @@ -144,7 +150,7 @@ export class Repository { f.Type === 'UTCTimestamp' } - private static makeSimple (f: IRepositoryField, type: string) { + private static makeSimple (f: IRepositoryField, type: string): SimpleFieldDefinition { return new SimpleFieldDefinition( f.Tag, f.Name, @@ -166,7 +172,7 @@ export class Repository { return type } - private fieldEnums () { + private fieldEnums (): void { const definitions = this.definitions for (const e of this.Enums) { const field: SimpleFieldDefinition = definitions.tagToSimple[parseInt(e.Tag, 10)] @@ -194,7 +200,7 @@ export class Repository { private contents (): Dictionary { return this.MsgContents.reduce((a, current) => { - let content: IRepositoryMsgContent[] = a.get(current.ComponentID) + let content: IRepositoryMsgContent[] | null = a.get(current.ComponentID) if (!content) { content = [] a.add(current.ComponentID, content) @@ -214,27 +220,29 @@ export class Repository { parentSet.add(new ContainedSimpleField(sf, parentSet.fields.length, required, false)) } } else { - // is there a definition for this type yet create. - let childSet: ContainedFieldSet = this.definitions.component.get(current.TagText) + // is there a definition for this type yet create. + let childSet: ContainedFieldSet | null = this.definitions.component.get(current.TagText) if (!childSet) { - const cl: IRepositoryComponent = this.componentLookup.get(current.TagText) + const cl: IRepositoryComponent | null = this.componentLookup.get(current.TagText) if (cl) { childSet = this.resolve(cl) } } - switch (childSet.type) { - case ContainedSetType.Component: { - parentSet.add(new ContainedComponentField(childSet, parentSet.fields.length, required)) - break - } + if (childSet) { + switch (childSet.type) { + case ContainedSetType.Component: { + parentSet.add(new ContainedComponentField(childSet as ComponentFieldDefinition, parentSet.fields.length, required)) + break + } - case ContainedSetType.Group: { - parentSet.add(new ContainedGroupField(childSet as GroupFieldDefinition, parentSet.fields.length, required)) - break - } + case ContainedSetType.Group: { + parentSet.add(new ContainedGroupField(childSet as GroupFieldDefinition, parentSet.fields.length, required)) + break + } - default: { - throw new Error(`unknown set type ${childSet.type}`) + default: { + throw new Error(`unknown set type ${childSet.type}`) + } } } } @@ -246,9 +254,9 @@ export class Repository { switch (c.ComponentType) { case 'ImplicitBlockRepeating': case 'BlockRepeating': { - const content: IRepositoryMsgContent[] = this.contentLookup.get(c.ComponentID) + const content: IRepositoryMsgContent[] | null = this.contentLookup.get(c.ComponentID) ?? [] const noField: SimpleFieldDefinition = this.definitions.tagToSimple[parseInt(content[0].TagText, 10)] - let def: GroupFieldDefinition = this.groupLookup.get(c.ComponentID) + let def: GroupFieldDefinition | null = this.groupLookup.get(c.ComponentID) if (!def) { def = new GroupFieldDefinition(c.Name, c.AbbrName, c.CategoryID, noField, c.Description) this.resolveToFieldSet(content.slice(1), def) @@ -258,8 +266,8 @@ export class Repository { } default: { - const content: IRepositoryMsgContent[] = this.contentLookup.get(c.ComponentID) - let def: ComponentFieldDefinition = this.definitions.component.get(c.Name) + const content: IRepositoryMsgContent[] | null = this.contentLookup.get(c.ComponentID) ?? [] + let def: ComponentFieldDefinition | null = this.definitions.component.get(c.Name) if (!def) { def = new ComponentFieldDefinition(c.Name, c.AbbrName, c.CategoryID, c.Description) this.resolveToFieldSet(content, def) @@ -272,8 +280,8 @@ export class Repository { private message (m: IRepositoryMessage): MessageDefinition { const definitions = this.definitions - const content: IRepositoryMsgContent[] = this.contentLookup.get(m.ComponentID) - let def: MessageDefinition = definitions.message.get(m.Name) + const content: IRepositoryMsgContent[] = this.contentLookup.get(m.ComponentID) ?? [] + let def: MessageDefinition | null = definitions.message.get(m.Name) if (!def) { def = new MessageDefinition(m.Name, m.AbbrName, m.MsgType, m.CategoryID, m.Description) this.resolveToFieldSet(content, def) diff --git a/src/dictionary/parser/fixml/components-parser.ts b/src/dictionary/parser/fixml/components-parser.ts index 2f41efcc..837f9df6 100644 --- a/src/dictionary/parser/fixml/components-parser.ts +++ b/src/dictionary/parser/fixml/components-parser.ts @@ -1,10 +1,14 @@ import { Dictionary } from '../../../collections' import { XsdParser } from './xsd-parser' -import { ComponentFieldDefinition, SimpleFieldDefinition, - GroupFieldDefinition, FixDefinitions, MessageDefinition } from '../../definition' -import { ContainedSimpleField, ContainedComponentField, - ContainedFieldSet, ContainedGroupField } from '../../contained' +import { + ComponentFieldDefinition, SimpleFieldDefinition, + GroupFieldDefinition, FixDefinitions, MessageDefinition +} from '../../definition' +import { + ContainedSimpleField, ContainedComponentField, + ContainedFieldSet, ContainedGroupField +} from '../../contained' import { ISaxNode } from '../../sax-node' interface IElement { @@ -35,7 +39,7 @@ interface IGroup { interface IAppInfo { Protocol: string MsgID: string - name: string, + name: string ComponentType: string Category: string } @@ -73,9 +77,9 @@ export class ComponentsParser extends XsdParser { private readonly complexTypes: Dictionary = new Dictionary() private newComplexTypes: IComplexType[] - private currentGroup: IGroup - private currentAttributeGroupStack: IAttributeGroup[] = [] - private currentComplexType: IComplexType + private currentGroup: IGroup | null + private readonly currentAttributeGroupStack: IAttributeGroup[] = [] + private currentComplexType: IComplexType | null private previousComplexType: IComplexType public constructor (public readonly definitions: FixDefinitions) { @@ -84,7 +88,7 @@ export class ComponentsParser extends XsdParser { private static getName (group: IGroup, attributeGroup: IAttributeGroup, type: IComplexType): string { let name: string - if (type && type.appInfo) { + if (type?.appInfo) { name = type.appInfo.name } else if (group) { name = group.name @@ -100,7 +104,7 @@ export class ComponentsParser extends XsdParser { // do nothing switch (n) { case 'xs:documentation': { - if (this.currentComplexType && this.currentComplexType.annotation) { + if (this.currentComplexType?.annotation) { this.currentComplexType.annotation.documentation = v } break @@ -111,7 +115,7 @@ export class ComponentsParser extends XsdParser { public close (line: number, node: string): void { switch (node) { - case'xs:complexType': { + case 'xs:complexType': { if (this.currentComplexType != null) { const complex: IComplexType = this.currentComplexType this.previousComplexType = complex @@ -133,8 +137,10 @@ export class ComponentsParser extends XsdParser { case 'xs:attributeGroup': { const attributeStack = this.currentAttributeGroupStack if (attributeStack.length > 0) { - const group: IAttributeGroup = attributeStack.pop() - this.attributeGroups.addUpdate(group.name, group) + const group: IAttributeGroup | null = attributeStack.pop() ?? null + if (group) { + this.attributeGroups.addUpdate(group.name, group) + } } break } @@ -148,7 +154,6 @@ export class ComponentsParser extends XsdParser { public open (line: number, node: ISaxNode): void { switch (node.name) { - case 'xs:schema': { this.newComplexTypes = [] break @@ -170,8 +175,10 @@ export class ComponentsParser extends XsdParser { } case 'xs:appinfo': { - this.currentComplexType.appInfo = { - } as IAppInfo + const ct = this.currentComplexType + if (ct) { + ct.appInfo = {} as IAppInfo + } break } @@ -197,7 +204,7 @@ export class ComponentsParser extends XsdParser { } case 'fm:Xref': { - this.assign(node, this.currentComplexType.appInfo) + this.assign(node, this.currentComplexType?.appInfo) break } @@ -217,7 +224,7 @@ export class ComponentsParser extends XsdParser { } } - private xsAttribute (node: ISaxNode) { + private xsAttribute (node: ISaxNode): void { const attribute: IAttribute = {} as IAttribute this.assign(node, attribute) const stack = this.currentAttributeGroupStack @@ -228,10 +235,10 @@ export class ComponentsParser extends XsdParser { } } - private xsGroup (node: ISaxNode) { + private xsGroup (node: ISaxNode): void { if (node.attributes.name) { this.currentGroup = { - name: node.attributes['name'], + name: node.attributes.name, elements: [] as IElement[] } as IGroup } else if (node.attributes.ref) { @@ -248,7 +255,7 @@ export class ComponentsParser extends XsdParser { } } - private xsAttributeGroup (node: ISaxNode) { + private xsAttributeGroup (node: ISaxNode): void { const attributeStack = this.currentAttributeGroupStack if (node.attributes.name) { attributeStack.push({ @@ -268,7 +275,7 @@ export class ComponentsParser extends XsdParser { } } - private xsElement (node: ISaxNode) { + private xsElement (node: ISaxNode): void { const element: IElement = {} as IElement this.assign(node, element) const currentComplex = this.currentComplexType @@ -291,12 +298,12 @@ export class ComponentsParser extends XsdParser { } } - private addElement (set: ContainedFieldSet, element: IElement) { + private addElement (set: ContainedFieldSet, element: IElement): void { const minOccurs: number = parseInt(element.minOccurs, 10) const isGroup: boolean = element.maxOccurs === 'unbounded' const isComponent: boolean = element.maxOccurs === '1' || !isGroup const key = element.type || element.ref || element.name - const containedType: IComplexType = this.complexTypes.get(key) + const containedType: IComplexType | null = this.complexTypes.get(key) if (containedType) { if (isComponent) { const containedDefinition: ComponentFieldDefinition = this.getComponent(containedType) @@ -322,18 +329,19 @@ export class ComponentsParser extends XsdParser { } } - private addElements (set: ContainedFieldSet, elements: IElement[]) { + private addElements (set: ContainedFieldSet, elements: IElement[]): void { if (elements) { elements.forEach((element: IElement) => { switch (element.type) { - case 'xs:group': - const groupElements: IGroup = this.groups.get(element.name) + case 'xs:group': { + const groupElements: IGroup | null = this.groups.get(element.name) if (groupElements) { this.addElements(set, groupElements.elements) } else { throw new Error(`unable to get xs:group ${element.name}`) } break + } default: { this.addElement(set, element) @@ -344,7 +352,7 @@ export class ComponentsParser extends XsdParser { } private addSimpleAttribute (set: ContainedFieldSet, attribute: IAttribute): void { - let sf: SimpleFieldDefinition = this.definitions.getSimple(attribute.type) + let sf: SimpleFieldDefinition | null = this.definitions.getSimple(attribute.type) if (!sf) { sf = this.definitions.getSimple(attribute.name, set.category) } @@ -360,12 +368,11 @@ export class ComponentsParser extends XsdParser { } } - private addAttributes (set: ContainedFieldSet, attributes: IAttribute[]) { - + private addAttributes (set: ContainedFieldSet, attributes: IAttribute[]): void { attributes.forEach((attribute: IAttribute) => { switch (attribute.type) { case 'xs:attributeGroup': { - const attributeGroup: IAttributeGroup = this.attributeGroups.get(attribute.name) + const attributeGroup: IAttributeGroup | null = this.attributeGroups.get(attribute.name) if (attributeGroup) { this.addAttributes(set, attributeGroup.attributes) } else { @@ -382,10 +389,10 @@ export class ComponentsParser extends XsdParser { } private getGroup (type: IComplexType): GroupFieldDefinition { - let group: IGroup = this.groups.get(type.group) - const attributeGroup: IAttributeGroup = this.attributeGroups.get(type.attributeGroup) - const name: string = ComponentsParser.getName(group, attributeGroup, type) - const category: string = type.appInfo != null ? type.appInfo.Category : null + const group: IGroup | null = this.groups.get(type.group) + const attributeGroup: IAttributeGroup | null = this.attributeGroups.get(type.attributeGroup) + const name: string = group && attributeGroup ? ComponentsParser.getName(group, attributeGroup, type) : '' + const category: string | null = type.appInfo != null ? type.appInfo.Category : null const groupDefinition: GroupFieldDefinition = new GroupFieldDefinition(name, name, category, null, null) this.populateSet(type, groupDefinition) return groupDefinition @@ -393,17 +400,17 @@ export class ComponentsParser extends XsdParser { private getComponent (type: IComplexType): ComponentFieldDefinition { const definitions = this.definitions - let group: IGroup = this.groups.get(type.group) - const attributeGroup: IAttributeGroup = this.attributeGroups.get(type.attributeGroup) - let name: string = ComponentsParser.getName(group, attributeGroup, type) - const cached: ComponentFieldDefinition = definitions.component.get(name) + const group: IGroup | null = this.groups.get(type.group) + const attributeGroup: IAttributeGroup | null = this.attributeGroups.get(type.attributeGroup) + let name: string = group && attributeGroup ? ComponentsParser.getName(group, attributeGroup, type) : '' + const cached: ComponentFieldDefinition | null = definitions.component.get(name) if (cached) { return cached } - const category: string = type.appInfo != null ? type.appInfo.Category : null + const category: string | null = type.appInfo != null ? type.appInfo.Category : null if (type.extensionBase) { - let base = this.complexTypes.get(type.extensionBase) + const base = this.complexTypes.get(type.extensionBase) if (base) { name = base.appInfo.name } @@ -424,8 +431,8 @@ export class ComponentsParser extends XsdParser { type.appInfo.MsgID, type.appInfo.Category, type.annotation.documentation) - const abstractMessage: ComponentFieldDefinition = definitions.component.get('Message') - abstractMessage.fields.forEach((f) => { + const abstractMessage: ComponentFieldDefinition | null = definitions.component.get('Message') + abstractMessage?.fields.forEach((f) => { message.add(f) }) this.populateSet(type, message) @@ -437,11 +444,11 @@ export class ComponentsParser extends XsdParser { return message } - private getBaseAttributes (type: IComplexType): IAttributeGroup { + private getBaseAttributes (type: IComplexType): IAttributeGroup | null { const attributeGroups = this.attributeGroups - let baseGroup: IAttributeGroup = null + let baseGroup: IAttributeGroup | null = null if (type.extensionBase) { - let base = this.complexTypes.get(type.extensionBase) + const base = this.complexTypes.get(type.extensionBase) if (base) { baseGroup = attributeGroups.get(base.attributeGroup) } @@ -451,11 +458,11 @@ export class ComponentsParser extends XsdParser { } private populateSet (type: IComplexType, set: ContainedFieldSet): void { - const group: IGroup = this.groups.get(type.group) + const group: IGroup | null = this.groups.get(type.group) const elements: IElement[] = group ? group.elements : type.element const attributeGroups = this.attributeGroups - const attributeGroup: IAttributeGroup = attributeGroups.get(type.attributeGroup) - const baseGroup: IAttributeGroup = this.getBaseAttributes(type) + const attributeGroup: IAttributeGroup | null = attributeGroups.get(type.attributeGroup) + const baseGroup: IAttributeGroup | null = this.getBaseAttributes(type) // if a base is specified add the attributes from there if (baseGroup) { this.addAttributes(set, baseGroup.attributes) @@ -466,10 +473,9 @@ export class ComponentsParser extends XsdParser { this.addElements(set, elements) } - private constructType (type: IComplexType) { + private constructType (type: IComplexType): void { const componentType: string = type.appInfo != null ? type.appInfo.ComponentType : 'Block' switch (componentType) { - case 'Message': { const message: MessageDefinition = this.getMessage(type) if (!message) { diff --git a/src/dictionary/parser/fixml/fields-parser.ts b/src/dictionary/parser/fixml/fields-parser.ts index 7f053ef7..e513c33b 100644 --- a/src/dictionary/parser/fixml/fields-parser.ts +++ b/src/dictionary/parser/fixml/fields-parser.ts @@ -10,8 +10,8 @@ interface ISimpleField extends ILooseObject { Protocol: string simpleTypeName: string name: string - ComponentType: string, - Tag: string, + ComponentType: string + Tag: string Type: string AbbrName: string Category: string @@ -26,7 +26,7 @@ interface IAlias { } export class FieldsParser extends XsdParser { - private alias: IAlias[] = [] + private readonly alias: IAlias[] = [] public constructor (public readonly definitions: FixDefinitions) { super(definitions) } @@ -39,7 +39,7 @@ export class FieldsParser extends XsdParser { break } case 'xs:documentation': { - this.current['documentation'] = v + this.current.documentation = v break } } @@ -48,7 +48,7 @@ export class FieldsParser extends XsdParser { public close (line: number, node: string): void { const current: ISimpleField = this.current as ISimpleField switch (node) { - case'xs:simpleType': { + case 'xs:simpleType': { this.data[this.data.length] = current break } @@ -62,10 +62,9 @@ export class FieldsParser extends XsdParser { public open (line: number, node: ISaxNode): void { switch (node.name) { - case 'xs:simpleType': { this.current = { - simpleTypeName: node.attributes['name'] + simpleTypeName: node.attributes.name } as ISimpleField break } @@ -76,7 +75,7 @@ export class FieldsParser extends XsdParser { } case 'xs:restriction': { - this.current.restrictionBase = node.attributes['base'] + this.current.restrictionBase = node.attributes.base break } @@ -84,7 +83,7 @@ export class FieldsParser extends XsdParser { if (!this.current.enums) { this.current.enums = new Dictionary() } - this.current.currentEnum = node.attributes['value'] + this.current.currentEnum = node.attributes.value this.pending = node.name break } @@ -120,7 +119,7 @@ export class FieldsParser extends XsdParser { } alias.push({ name: f.simpleTypeName, - mapped: mapped + mapped }) } }) diff --git a/src/dictionary/parser/fixml/fix-xsd-parser.ts b/src/dictionary/parser/fixml/fix-xsd-parser.ts index 996204d8..70b90e70 100644 --- a/src/dictionary/parser/fixml/fix-xsd-parser.ts +++ b/src/dictionary/parser/fixml/fix-xsd-parser.ts @@ -10,7 +10,6 @@ import { FixDefinitionSource } from '../../fix-definition-source' import { FixVersion } from '../../fix-versions' export class FixXsdParser extends FixParser { - public readonly definitions: FixDefinitions private readonly logger: IJsFixLogger @@ -28,7 +27,7 @@ export class FixXsdParser extends FixParser { logger.info(`resolve includes ${main} from root ${this.rootPath}`) const t = new IncludeGraph(this.rootPath, main) await t.build() - const resolved: string[] = t.resolve(main) + const resolved: string[] = t.resolve(main) ?? [] const fields: FieldsParser = new FieldsParser(definitions) const components: ComponentsParser = new ComponentsParser(definitions) const filtered: string[] = resolved.reduce((a: string[], f: string) => { @@ -38,7 +37,7 @@ export class FixXsdParser extends FixParser { return a }, []) - for (let f of filtered) { + for (const f of filtered) { const parser: XsdParser = f.indexOf('-fields-') > 0 ? fields : components logger.info(`parsing file ${f}`) await parser.parse(path.join(this.rootPath, f)) diff --git a/src/dictionary/parser/fixml/include-graph.ts b/src/dictionary/parser/fixml/include-graph.ts index b7c32faf..b89e5389 100644 --- a/src/dictionary/parser/fixml/include-graph.ts +++ b/src/dictionary/parser/fixml/include-graph.ts @@ -33,8 +33,8 @@ export class IncludeGraph { this.getGraph() } - public resolve (file: string): string[] { - const label: number = this.nodes.get(file) + public resolve (file: string): string[] | null { + const label: number | null = this.nodes.get(file) if (label == null) { return null } @@ -47,18 +47,21 @@ export class IncludeGraph { private resolve_nodes (label: number, depends: number[]): void { const node: IGraphNode = this.graph[label] - for (let e of node.edges) { - if (depends.indexOf(e) < 0) { - this.resolve_nodes(e, depends) - } + if (node) { + node.edges.forEach(e => { + if (!depends.includes(e)) { + this.resolve_nodes(e, depends) + } + }) } depends.push(label) } - private getGraph () { + private getGraph (): void { const nodes = this.nodes - this.graph = this.includes.reduce((a: INumericKeyed, current: IInclude) => { - const parent: number = nodes.get(current.parent) + this.graph = this.includes.reduce>((a: INumericKeyed, current: IInclude) => { + const parent: number | null = nodes.get(current.parent) + if (parent == null) return a let parentNode = a[parent] if (!parentNode) { a[parent] = parentNode = { @@ -68,16 +71,18 @@ export class IncludeGraph { } as IGraphNode } current.children.forEach((s: string) => { - const child: number = nodes.get(s) - if (parentNode.edges.indexOf(child) < 0) { - parentNode.edges.push(child) + const child: number | null = nodes.get(s) + if (child) { + if (!parentNode.edges.includes(child)) { + parentNode.edges.push(child) + } } }) return a - }, {} as INumericKeyed) + }, {}) } - private assignNodes () { + private assignNodes (): void { let next: number = 0 this.nodes = this.includes.reduce((a: Dictionary, current: IInclude) => { if (!a.containsKey(current.parent)) { @@ -92,8 +97,8 @@ export class IncludeGraph { }, new Dictionary()) } - private scanIncludes (file: string): Promise { - return new Promise((accept, reject) => { + private async scanIncludes (file: string): Promise { + return await new Promise((resolve, reject) => { const includes: string[] = [] const pass: fs.ReadStream = fs.createReadStream(path.join(this.root, file)) const saxStream: SAXStream = require('sax').createStream(true, {}) @@ -107,7 +112,7 @@ export class IncludeGraph { } }) saxStream.on('ready', () => { - accept(includes) + resolve(includes) }) saxStream.on('error', (r) => { reject(r) @@ -116,16 +121,16 @@ export class IncludeGraph { }) } - private getIncludes (main: string): Promise { + private async getIncludes (main: string): Promise { const q: any[] = [] q.push([main]) const ordered: IInclude[] = [] const seen: Dictionary = new Dictionary() - return new Promise(async (accept, reject) => { + return await new Promise(async (resolve, reject) => { try { while (q.length > 0) { const batch: string[] = q.pop() - for (let next of batch) { + for (const next of batch) { if (seen.containsKey(next)) { continue } @@ -138,7 +143,7 @@ export class IncludeGraph { q.push(includes) } } - accept(ordered) + resolve(ordered) } catch (e) { reject(e) } diff --git a/src/dictionary/parser/fixml/xsd-parser.ts b/src/dictionary/parser/fixml/xsd-parser.ts index 70329680..063b7716 100644 --- a/src/dictionary/parser/fixml/xsd-parser.ts +++ b/src/dictionary/parser/fixml/xsd-parser.ts @@ -8,13 +8,13 @@ import { ISaxNode } from '../../sax-node' export abstract class XsdParser { public readonly data: ILooseObject[] = [] public current: ILooseObject - protected pending: string + protected pending: string | null protected constructor (public readonly definitions: FixDefinitions) { } - public parse (file: string): Promise { - return new Promise((accept, reject) => { + public async parse (file: string): Promise { + return await new Promise((resolve, reject) => { const pass: fs.ReadStream = fs.createReadStream(file) const saxStream: SAXStream = require('sax').createStream(true, {}) const saxParser: SAXParser = saxStream._parser @@ -27,7 +27,7 @@ export abstract class XsdParser { }) saxStream.on('ready', () => { - accept(this.definitions) + resolve(this.definitions) }) saxStream.on('text', (t: string) => { @@ -53,7 +53,7 @@ export abstract class XsdParser { target = this.current } const keys: string[] = Object.keys(node.attributes) - for (let k of keys) { + for (const k of keys) { target[k] = node.attributes[k] } } diff --git a/src/dictionary/parser/quickfix/field-definition-parser.ts b/src/dictionary/parser/quickfix/field-definition-parser.ts index 1da69694..11a76fc3 100644 --- a/src/dictionary/parser/quickfix/field-definition-parser.ts +++ b/src/dictionary/parser/quickfix/field-definition-parser.ts @@ -13,9 +13,9 @@ export class FieldDefinitionParser extends NodeParser { switch (node.name) { case 'field': { this.currentField = new SimpleFieldDefinition( - node.attributes.number, - node.attributes.name, - node.attributes.name, + node.attributes.number, + node.attributes.name, + node.attributes.name, null, null, node.attributes.type, @@ -32,7 +32,5 @@ export class FieldDefinitionParser extends NodeParser { } public close (line: number, name: string): void { - switch (name) { - } } } diff --git a/src/dictionary/parser/quickfix/field-set-parser.ts b/src/dictionary/parser/quickfix/field-set-parser.ts index 2230452a..1e93cb29 100644 --- a/src/dictionary/parser/quickfix/field-set-parser.ts +++ b/src/dictionary/parser/quickfix/field-set-parser.ts @@ -55,14 +55,14 @@ export class FieldSetParser extends NodeParser { case 'component': case 'header': case 'trailer': { - const latest: ParseContext = this.parseContexts.pop() + const latest: ParseContext | null = this.parseContexts.pop() ?? null if (latest == null) { throw new Error(`component field ${name} closes yet does not exist.`) } if (!latest.defining) { return } - const asComponent: ComponentFieldDefinition = latest.asComponent() + const asComponent: ComponentFieldDefinition | null = latest.asComponent() ?? null if (asComponent != null) { this.definitions.addComponentFieldDef(asComponent) } else { diff --git a/src/dictionary/parser/quickfix/message-parser.ts b/src/dictionary/parser/quickfix/message-parser.ts index 548a431d..61d8ae37 100644 --- a/src/dictionary/parser/quickfix/message-parser.ts +++ b/src/dictionary/parser/quickfix/message-parser.ts @@ -5,7 +5,6 @@ import { ContainedComponentField } from '../../contained' import { ISaxNode } from '../../sax-node' export class MessageParser extends NodeParser { - constructor (definitions: FixDefinitions, public passes: number) { super(definitions, passes) } @@ -17,9 +16,13 @@ export class MessageParser extends NodeParser { const msg: MessageDefinition = new MessageDefinition(att.name, att.name, att.msgtype, att.msgcat, null) const context: ParseContext = new ParseContext(msg.name, true, msg) const hdr = this.definitions.component.get('StandardHeader') - const contained = new ContainedComponentField(hdr, msg.fields.length, true) - msg.add(contained) - this.parseContexts.push(context) + const contained = hdr + ? new ContainedComponentField(hdr, msg.fields.length, true) + : null + if (contained) { + msg.add(contained) + this.parseContexts.push(context) + } break } @@ -47,8 +50,8 @@ export class MessageParser extends NodeParser { public close (line: number, name: string): void { switch (name) { case 'message': { - const parent: ParseContext = this.parseContexts.pop() - const message: MessageDefinition = parent.asMessage() + const parent: ParseContext | null = this.parseContexts.pop() ?? null + const message: MessageDefinition | null = parent?.asMessage() ?? null if (message != null) { this.definitions.addMessage(message) } diff --git a/src/dictionary/parser/quickfix/node-parser.ts b/src/dictionary/parser/quickfix/node-parser.ts index eb9da55e..26e24d85 100644 --- a/src/dictionary/parser/quickfix/node-parser.ts +++ b/src/dictionary/parser/quickfix/node-parser.ts @@ -15,31 +15,31 @@ export abstract class NodeParser { protected addSimple (node: ISaxNode): void { if (node.isSelfClosing) { - const parent: ParseContext = this.peek() + const parent: ParseContext | null = this.peek() if (parent == null) { throw new Error(`simple field ${node.name} has no parent on which to add.`) } const fieldName: string = node.attributes.name - const fieldDefinition: SimpleFieldDefinition = this.definitions.simple.get(fieldName) + const fieldDefinition: SimpleFieldDefinition | null = this.definitions.simple.get(fieldName) if (fieldDefinition == null) { throw new Error(`simple field ${fieldName} has no declaration in dictionary.`) } const containedField: ContainedSimpleField = new ContainedSimpleField(fieldDefinition, - parent.set.fields.length, node.attributes.required === 'Y', false) - parent.set.add(containedField) + parent?.set?.fields?.length ?? 0, node.attributes.required === 'Y', false) + parent?.set?.add(containedField) } } protected addComponentField (componentName: string, node: ISaxNode): void { - const parent: ParseContext = this.peek() + const parent: ParseContext | null = this.peek() if (parent == null) { throw new Error(`component ${node.name} has no parent on which to add.`) } - const fieldDef: ComponentFieldDefinition = this.definitions.component.get(componentName) + const fieldDef: ComponentFieldDefinition | null = this.definitions.component.get(componentName) if (fieldDef != null) { const containedField: ContainedComponentField = - new ContainedComponentField(fieldDef, parent.set.fields.length, parent.required) - parent.set.add(containedField) + new ContainedComponentField(fieldDef, parent?.set?.fields?.length ?? 0, parent.required) + parent?.set?.add(containedField) } else { if (this.passes >= 4) { throw new Error(`field ${node.name} includes unknown component ${componentName}.`) @@ -48,16 +48,18 @@ export abstract class NodeParser { } protected addGroupField (name: string): void { - const group: ParseContext = this.parseContexts.pop() + const group: ParseContext | null = this.parseContexts.pop() ?? null if (group == null) { throw new Error(`group field ${name} closes yet does not exist.`) } - const parent: ParseContext = this.peek() + const parent: ParseContext | null = this.peek() if (parent != null) { - const asGroup: GroupFieldDefinition = group.asGroup() - const containedField: ContainedGroupField = - new ContainedGroupField(asGroup, parent.set.fields.length, group.required) - parent.set.add(containedField) + const asGroup: GroupFieldDefinition | null = group.asGroup() + if (asGroup) { + const containedField: ContainedGroupField = + new ContainedGroupField(asGroup, parent?.set?.fields?.length ?? 0, group.required) + parent?.set?.add(containedField) + } } else { throw new Error(`group field ${group.name} has no parent on which to add.`) } @@ -65,21 +67,21 @@ export abstract class NodeParser { protected beginGroupDefinition (node: ISaxNode): void { if (!node.isSelfClosing) { - // a group should have a field that matches its name + // a group should have a field that matches its name const groupName: string = node.attributes.name - const noOfField: SimpleFieldDefinition = this.definitions.simple.get(groupName) + const noOfField: SimpleFieldDefinition | null = this.definitions.simple.get(groupName) ?? null if (noOfField == null) { const msg: string = `group ${groupName} has no field defined.` throw new Error(msg) } - const set: GroupFieldDefinition = new GroupFieldDefinition(groupName, groupName, null, noOfField,null) + const set: GroupFieldDefinition = new GroupFieldDefinition(groupName, groupName, null, noOfField, null) const context: ParseContext = new ParseContext(node.attributes.name, true, set) context.required = node.attributes.required === 'Y' this.parseContexts.push(context) } } - private peek (): ParseContext { + private peek (): ParseContext | null { return this.parseContexts.length > 0 ? this.parseContexts[this.parseContexts.length - 1] : null } } diff --git a/src/dictionary/parser/quickfix/parse-context.ts b/src/dictionary/parser/quickfix/parse-context.ts index 3df29b83..8e74fefc 100644 --- a/src/dictionary/parser/quickfix/parse-context.ts +++ b/src/dictionary/parser/quickfix/parse-context.ts @@ -4,11 +4,11 @@ import { ComponentFieldDefinition, GroupFieldDefinition, MessageDefinition } fro export class ParseContext { public required: boolean - constructor (public name: string, public defining: boolean, public set: ContainedFieldSet) { + constructor (public name: string, public defining: boolean, public set: ContainedFieldSet | null) { this.required = false } - public asMessage (): MessageDefinition { + public asMessage (): MessageDefinition | null { const res: boolean = this.set != null && this.set instanceof MessageDefinition if (res) { return (this.set) as MessageDefinition @@ -17,7 +17,7 @@ export class ParseContext { } } - public asComponent (): ComponentFieldDefinition { + public asComponent (): ComponentFieldDefinition | null { const res: boolean = this.set != null && this.set instanceof ComponentFieldDefinition if (res) { return (this.set) as ComponentFieldDefinition @@ -26,7 +26,7 @@ export class ParseContext { } } - public asGroup (): GroupFieldDefinition { + public asGroup (): GroupFieldDefinition | null { const res: boolean = this.set != null && this.set instanceof GroupFieldDefinition if (res) { return (this.set) as GroupFieldDefinition diff --git a/src/dictionary/parser/quickfix/quick-fix-xml-file-parser.ts b/src/dictionary/parser/quickfix/quick-fix-xml-file-parser.ts index ab68adfd..c31fa8b8 100644 --- a/src/dictionary/parser/quickfix/quick-fix-xml-file-parser.ts +++ b/src/dictionary/parser/quickfix/quick-fix-xml-file-parser.ts @@ -16,7 +16,6 @@ import { VersionUtil } from '../../version-util' import { ParseState } from './parse-state' export class QuickFixXmlFileParser extends FixParser { - public parseState: ParseState = ParseState.Begin public numberPasses: number = 0 public definitions: FixDefinitions @@ -27,7 +26,7 @@ export class QuickFixXmlFileParser extends FixParser { } private static subscribe (instance: QuickFixXmlFileParser, saxStream: SAXStream, done: IDictDoneCb): void { - let parser: NodeParser + let parser: NodeParser | null instance.numberPasses++ switch (instance.parseState) { @@ -79,7 +78,6 @@ export class QuickFixXmlFileParser extends FixParser { const saxNode: ISaxNode = node as ISaxNode switch (saxNode.name) { - case 'fix': { switch (instance.parseState) { case ParseState.FieldDefinitions: { @@ -120,9 +118,9 @@ export class QuickFixXmlFileParser extends FixParser { } case 'components': { - // can have a group containing components which contain themselves components of groups - // each step will build forward references to a deeper level to ensure final messages - // have all dependencies correctly defined. + // can have a group containing components which contain themselves components of groups + // each step will build forward references to a deeper level to ensure final messages + // have all dependencies correctly defined. switch (instance.parseState) { case ParseState.ComponentsFirstPass: case ParseState.ComponentsSecondPass: @@ -175,7 +173,7 @@ export class QuickFixXmlFileParser extends FixParser { saxStream.on('ready', () => { if (done) { parser = null - done(null, instance.definitions) + done(undefined, instance.definitions) } }) } @@ -187,15 +185,15 @@ export class QuickFixXmlFileParser extends FixParser { keys.forEach(k => { const message = messages.get(k) const trailer = this.definitions.component.get(trailerName) - if (trailer && !message.components.containsKey(trailerName)) { - const contained = new ContainedComponentField(trailer, message.fields.length, true) - message.add(contained) + if (trailer && !message?.components.containsKey(trailerName)) { + const contained = new ContainedComponentField(trailer, message?.fields?.length ?? 0, true) + message?.add(contained) } }) } - public parse (): Promise { - return new Promise(async (accept, reject) => { + public async parse (): Promise { + return await new Promise(async (resolve, reject) => { try { await this.onePass() // first fetch all field definitions await this.onePass() // first pass of components, will not know about forward components. @@ -203,17 +201,17 @@ export class QuickFixXmlFileParser extends FixParser { await this.onePass() // third pass of components all fully resolved i.e. pick up versions from pass above await this.onePass() // lastly messages with all dependencies this.encloseMessages() - accept(this.definitions) + resolve(this.definitions) } catch (e) { reject(e) } }) } - private async onePass (): Promise { + private async onePass (): Promise { const pass: fs.ReadStream = fs.createReadStream(this.xmlPath) const saxStream: SAXStream = require('sax').createStream(true, {}) pass.pipe(saxStream) - return this.singlePass(this, saxStream) + return await this.singlePass(this, saxStream) } } diff --git a/src/dictionary/set-reduce.ts b/src/dictionary/set-reduce.ts index 4a3c366b..59753197 100644 --- a/src/dictionary/set-reduce.ts +++ b/src/dictionary/set-reduce.ts @@ -1,9 +1,10 @@ -import { ContainedField, ContainedFieldSet, - ContainedGroupField, ContainedSimpleField, ContainedComponentField, ContainedFieldType } from './contained' +import { + ContainedField, ContainedFieldSet, + ContainedGroupField, ContainedSimpleField, ContainedComponentField, ContainedFieldType +} from './contained' import { ITypeDispatcher } from './type-dispatcher' export class SetReduce { - reduceField (a: T, field: ContainedField, dispatcher: ITypeDispatcher): void { switch (field.type) { case ContainedFieldType.Group: { diff --git a/src/dictionary/type-dispatcher.ts b/src/dictionary/type-dispatcher.ts index f77152c0..2e9d10bf 100644 --- a/src/dictionary/type-dispatcher.ts +++ b/src/dictionary/type-dispatcher.ts @@ -1,7 +1,7 @@ import { ContainedComponentField, ContainedGroupField, ContainedSimpleField } from './contained' export interface ITypeDispatcher { - group?: { (a: T, field: ContainedGroupField): void } - simple?: { (a: T, field: ContainedSimpleField): void } - component?: { (a: T, field: ContainedComponentField): void } + group?: (a: T, field: ContainedGroupField) => void + simple?: (a: T, field: ContainedSimpleField) => void + component?: (a: T, field: ContainedComponentField) => void } diff --git a/src/dictionary/version-util.ts b/src/dictionary/version-util.ts index fcd6400a..07279aef 100644 --- a/src/dictionary/version-util.ts +++ b/src/dictionary/version-util.ts @@ -3,21 +3,21 @@ import { FixVersion } from './fix-versions' export abstract class VersionUtil { public static resolve (description: string): FixVersion { let version: FixVersion = FixVersion.Unknown - if (description.indexOf('FIX.4.0') >= 0) { + if (description.includes('FIX.4.0')) { version = FixVersion.FIX40 - } else if (description.indexOf('FIX.4.1') >= 0) { + } else if (description.includes('FIX.4.1')) { version = FixVersion.FIX41 - } else if (description.indexOf('FIX.4.2') >= 0) { + } else if (description.includes('FIX.4.2')) { version = FixVersion.FIX42 - } else if (description.indexOf('FIX.4.3') >= 0) { + } else if (description.includes('FIX.4.3')) { version = FixVersion.FIX43 - } else if (description.indexOf('FIX.4.4') >= 0) { + } else if (description.includes('FIX.4.4')) { version = FixVersion.FIX44 - } else if (description.indexOf('FIX.5.0') >= 0) { + } else if (description.includes('FIX.5.0')) { version = FixVersion.FIX50 - } else if (description.indexOf('FIX.5.0SP1') >= 0) { + } else if (description.includes('FIX.5.0SP1')) { version = FixVersion.FIX50SP1 - } else if (description.indexOf('FIX.5.0SP1') >= 0) { + } else if (description.includes('FIX.5.0SP1')) { version = FixVersion.FIX50SP2 } return version diff --git a/src/jsfix-cmd.ts b/src/jsfix-cmd.ts index 6db5dfbf..1e716228 100644 --- a/src/jsfix-cmd.ts +++ b/src/jsfix-cmd.ts @@ -11,7 +11,6 @@ import { MsgTag } from './types' import { IJsFixConfig } from './config' import * as util from 'util' -const fs = require('node-fs-extra') import * as minimist from 'minimist' import * as path from 'path' import { MsgTransport } from './transport/factory' @@ -19,6 +18,7 @@ import { EnumCompiler, ICompilerSettings, MsgCompiler } from './dictionary/compi import { AsciiMsgTransmitter } from './transport/ascii/ascii-msg-transmitter' import { SessionContainer } from './runtime' import { DITokens } from './runtime/di-tokens' +const fs = require('node-fs-extra') const argv: any = minimist(process.argv.slice(2)) @@ -49,7 +49,7 @@ export class JsfixCmd { private sessionDescription: ISessionDescription private delimiter: number = AsciiChars.Soh private stats: ILooseObject = {} - private filter: string = null + private filter: string | null = null private messages: number = 0 private print: boolean = true @@ -89,20 +89,19 @@ export class JsfixCmd { return mode } - private static async writeFile (name: string, api: string) { + private static async writeFile (name: string, api: string): Promise { const writer = util.promisify(fs.writeFile) - await writer(name, api, { - encoding: 'utf8'} + await writer(name, api, { encoding: 'utf8' } ).catch((e: Error) => { throw e }) } - public exec (): Promise { - return new Promise((resolve, reject) => { + public async exec (): Promise { + return await new Promise((resolve, reject) => { this.init().then(async () => { let actioned: boolean = true - let command = JsfixCmd.getCommand() + const command = JsfixCmd.getCommand() switch (command) { case Command.Generate: { @@ -168,8 +167,8 @@ export class JsfixCmd { }) } - private firstMessage (t: MsgTransport): Promise { - return new Promise((resolve, reject) => { + private async firstMessage (t: MsgTransport): Promise { + return await new Promise((resolve, reject) => { t.receiver.on('msg', (msgType: string, msgView: MsgView) => { resolve(msgView.clone()) }) @@ -179,7 +178,7 @@ export class JsfixCmd { }) } - private async generate () { + protected async generate (): Promise { const lipPath: string = path.join(this.root, 'data/examples/lipsum.txt') const words: string[] = await getWords(lipPath) const generator = new MessageGenerator(words, this.definitions) @@ -199,7 +198,7 @@ export class JsfixCmd { } } - private async single (generator: MessageGenerator, density: number) { + private async single (generator: MessageGenerator, density: number): Promise { if (!argv.type) { console.log('specify type to generate e.g. --type = AE') return @@ -220,14 +219,14 @@ export class JsfixCmd { } } - private async script (generator: MessageGenerator, density: number) { - let buffer: ElasticBuffer = new ElasticBuffer() + private async script (generator: MessageGenerator, density: number): Promise { + const buffer: ElasticBuffer = new ElasticBuffer() const repeats: number = argv.repeats || 50 const key: string = MsgTag.MsgType.toString() const sf = this.definitions.simple.get(key) const session: AsciiMsgTransmitter = this.session for (let i = 0; i < repeats; ++i) { - const msgType: string = MessageGenerator.getRandomEnum(sf).toString() + const msgType: string = sf ? MessageGenerator.getRandomEnum(sf).toString() : '' console.log(`i = ${i} ${msgType}`) const obj: ILooseObject = generator.generate(msgType, density) session.encodeMessage(msgType, obj) @@ -237,9 +236,9 @@ export class JsfixCmd { await JsfixCmd.writeFile('./fix.txt', buffer.slice().toString('utf8')) } - private async unitTest (fix: string, obj: ILooseObject, ft: MsgTransport) { + private async unitTest (fix: string, obj: ILooseObject, ft: MsgTransport): Promise { const view: MsgView = await this.firstMessage(ft) - const summary = view.structure.summary() + const summary = view?.structure?.summary() await JsfixCmd.writeFile('./fix.txt', fix) await JsfixCmd.writeFile('./object.json', JSON.stringify(obj, null, 4)) @@ -262,7 +261,7 @@ export class JsfixCmd { } private field (): void { - let sf: SimpleFieldDefinition + let sf: SimpleFieldDefinition | null const tag: number = parseInt(argv.field, 10) const definitions = this.definitions if (!isNaN(tag)) { @@ -275,19 +274,19 @@ export class JsfixCmd { } } - ensureExists (path: string): Promise { - return new Promise((accept, reject) => { + async ensureExists (path: string): Promise { + return await new Promise((resolve, reject) => { fs.mkdirp(path, (err: Error) => { if (err) { reject(err) } else { - accept(true) + resolve(true) } }) }) } - private async compileDefinitions (outputPath: string) { + private async compileDefinitions (outputPath: string): Promise { await this.ensureExists(path.join(outputPath, 'set')) await this.ensureExists(path.join(outputPath, 'enum')) const definitions = this.definitions @@ -300,7 +299,7 @@ export class JsfixCmd { await enumCompiler.generate(writeFile) } - private async compile () { + private async compile (): Promise { let output = argv.output const dp = new DefinitionFactory().getDictPath(argv.dict) if (dp) { @@ -361,13 +360,12 @@ export class JsfixCmd { } } - private subscribe (ft: MsgTransport) { + private subscribe (ft: MsgTransport): void { this.messages = 0 this.stats = {} const filter = this.filter // the receiver is message parser which is piped from an input stream - file, socket ft.receiver.on('msg', (msgType: string, m: AsciiView) => { - if (filter) { if (msgType !== filter) { return @@ -378,17 +376,19 @@ export class JsfixCmd { }) } - private onMsg (msgType: string, m: MsgView) { + private onMsg (msgType: string, m: MsgView): void { const mode: PrintMode = JsfixCmd.getPrintMode() const print = this.print const stats = this.stats switch (mode) { case PrintMode.Stats: { + let i: number = 0 if (!stats[msgType]) { - stats[msgType] = 1 + i = 1 } else { - stats[msgType] = stats[msgType] + 1 + i = stats[msgType] as number + 1 } + stats[msgType] = i break } @@ -404,14 +404,14 @@ export class JsfixCmd { const asObject: ILooseObject = m.toObject() if (print) { const def = this.definitions.message.get(msgType) - console.log(`${msgType} [${def.name}] = ${JSON.stringify(asObject, null, 4)}`) + console.log(`${msgType} [${def?.name}] = ${JSON.stringify(asObject, null, 4)}`) console.log() } break } case PrintMode.Structure: { - const summary = m.structure.summary() + const summary = m?.structure?.summary() if (print) { console.log(JSON.stringify(summary, null, 4)) } @@ -448,35 +448,59 @@ export class JsfixCmd { await this.dispatch(ft) } + private async promisedRead (f: string): Promise { + return new Promise((resolve, reject) => { + fs.readFile(f, 'utf8', async (err: Error, contents: string) => { + if (err) { + reject(err) + } + resolve(contents) + }) + }) + } + + async benchParse (contents: string, iterations: number, print: boolean): Promise { + return new Promise((resolve, reject) => { + const toParse = new StringDuplex(contents.repeat(iterations), false) + const startsAt: Date = new Date() + let i = 0 + const config = this.config + const buffer = config.sessionContainer.resolve(DITokens.ParseBuffer) + const asciiParser: MsgParser = new AsciiParser(config, toParse.readable, buffer) + function printer (msgType: string, v: MsgView): void { + const elapsed: number = new Date().getTime() - startsAt.getTime() + console.log(contents) + console.log(v.toString()) + console.log(`[${msgType}]: iterations = ${iterations}, fields = ${v?.structure?.tags.nextTagPos}, length = ${contents.length} chars, elapsed ms ${elapsed}, ${(elapsed / iterations) * 1000} micros per msg`) + } + asciiParser.on('msg', (msgType: string, v: MsgView) => { + ++i + if (i === iterations) { + if (print) { + printer(msgType, v) + } + resolve() + } + }) + asciiParser.on('error', e => reject(e)) + }) + } + private async benchmark (repeats: number): Promise { if (!argv.fix) { console.log('provide a path to fix file i.e. --fix=data/examples/execution-report/fix.txt') return } - return new Promise((accept, reject) => { + return await new Promise((resolve, reject) => { const fix: string = this.norm(argv.fix) - const fs = require('fs') - fs.readFile(fix, 'utf8', async (err: Error, contents: string) => { - if (err) { - reject(err) - } - const toParse = new StringDuplex(contents.repeat(repeats), false) - const startsAt: Date = new Date() - let i = 0 - const config = this.config - const buffer = config.sessionContainer.resolve(DITokens.ParseBuffer) - const asciiParser: MsgParser = new AsciiParser(config, toParse.readable, buffer) - asciiParser.on('msg', (msgType: string, v: MsgView) => { - ++i - if (i === repeats) { - const elapsed: number = new Date().getTime() - startsAt.getTime() - console.log(contents) - console.log(v.toString()) - console.log(`[${msgType}]: repeats = ${repeats}, fields = ${v.structure.tags.nextTagPos}, length = ${contents.length} chars, elapsed ms ${elapsed}, ${(elapsed / repeats) * 1000} micros per msg`) - accept(true) - } + this.promisedRead(fix) + .then(contents => { + this.benchParse(contents, repeats, true) + .then((a: any) => resolve(a)) + .catch(e => reject(e)) + }).catch(e => { + reject(e) }) - }) }) } @@ -521,23 +545,23 @@ function showHelp (): void { console.log() console.log('token format use fix repo dictionary') - console.log('jsfix-cmd --dict=data/fix_repo/FIX.4.4/Base --fix=data/examples/quickfix/FIX.4.4/execution-report/fix.txt' - + ' --delimiter="|" --tokens') + console.log('jsfix-cmd --dict=data/fix_repo/FIX.4.4/Base --fix=data/examples/quickfix/FIX.4.4/execution-report/fix.txt' + + ' --delimiter="|" --tokens') console.log() console.log('structure format i.e. show locations of components etc.') console.log('jsfix-cmd --dict=data/FIX44.xml --fix=data/examples/FIX.4.4/quickfix/execution-report/fix.txt' + - ' --delimiter="|" --tokens --structures') + ' --delimiter="|" --tokens --structures') console.log() console.log('full JS object in JSON format.') console.log('jsfix-cmd --dict=data/FIX44.xml --fix=data/examples/FIX.4.4/quickfix/execution-report/fix.txt' + - ' --delimiter="|" --tokens --objects') + ' --delimiter="|" --tokens --objects') console.log() console.log('full JS object in JSON format - filter only type messages.') console.log('jsfix-cmd --dict=data/FIX44.xml --fix=data/examples/FIX.4.4/quickfix/execution-report/fix.txt' + - ' --delimiter="|" --tokens --type=8 --objects') + ' --delimiter="|" --tokens --type=8 --objects') console.log() console.log('timing stats and message counts. Structured parsing of all messages.') @@ -546,7 +570,7 @@ function showHelp (): void { console.log('encode a json object to fix format') console.log('jsfix-cmd --json=data/examples/FIX.4.4/quickfix/execution-report/object.json' + - ' --session=data/session.json --type=8 --delimiter="|"') + ' --session=data/session.json --type=8 --delimiter="|"') console.log() console.log('display field definition') diff --git a/src/runtime/make-config.ts b/src/runtime/make-config.ts index 1f380e60..c8938013 100644 --- a/src/runtime/make-config.ts +++ b/src/runtime/make-config.ts @@ -14,19 +14,24 @@ export class RuntimeFactory { @inject(DITokens.ISessionMsgFactory) public readonly msgFactory: ISessionMsgFactory, @inject(DITokens.ISessionDescription) public readonly description: ISessionDescription) {} - makeConfig (): Promise { - const description = this.description - return new Promise((accept, reject) => { + async makeConfig (): Promise { + const description = this.description || null + return await new Promise((resolve, reject) => { try { - this.definitionFactory.getDefinitions(description.application.dictionary, + if (!description) reject(new Error('no description object')) + const path = description.application?.dictionary ?? 'na' + if (!path) reject(new Error('no dictionary in application object')) + const type = description.application?.type ?? null + if (!type) reject(new Error('no application type in application object')) + this.definitionFactory.getDefinitions(path, (t: string) => { - return this.logFactory.logger(`${description.application.type}.${t}`) + return this.logFactory.logger(`${type}.${t}`) }).then((definitions: FixDefinitions) => { - const config = new JsFixConfig(this.msgFactory, definitions, description, AsciiChars.Soh, this.logFactory) - accept(config) - }).catch(e => { - reject(e) - }) + const config = new JsFixConfig(this.msgFactory, definitions, description, AsciiChars.Soh, this.logFactory) + resolve(config) + }).catch(e => { + reject(e) + }) } catch (e) { reject(e) } diff --git a/src/runtime/session-container.ts b/src/runtime/session-container.ts index 820b31ec..c66062a2 100644 --- a/src/runtime/session-container.ts +++ b/src/runtime/session-container.ts @@ -4,8 +4,10 @@ import { IJsFixConfig, JsFixLoggerFactory, JsFixWinstonLogFactory, WinstonLogger import { DITokens } from './di-tokens' import { RuntimeFactory } from './make-config' -import { TcpAcceptorListener, RecoveringTcpInitiator, - TcpInitiatorConnector, TcpInitiator } from '../transport/tcp' +import { + TcpAcceptorListener, RecoveringTcpInitiator, + TcpInitiatorConnector, TcpInitiator +} from '../transport/tcp' import { HttpInitiator, HttpAcceptorListener, HttpJsonSampleAdapter } from '../transport/http' import { ISessionMsgFactory, MsgTransmitter, ISessionDescription } from '../transport' import { AsciiMsgTransmitter } from '../transport/ascii/ascii-msg-transmitter' @@ -19,20 +21,19 @@ import { FixEntity } from '../transport/fix-entity' import { IHttpAdapter } from '../transport/http/http-adapter' export class SessionContainer { - public reset (): void { container.reset() } - public registerGlobal (loggerFactory?: JsFixLoggerFactory): void; - public registerGlobal (level: string): void; + public registerGlobal (loggerFactory?: JsFixLoggerFactory): void + public registerGlobal (level: string): void public registerGlobal (levelOrLoggerFactory: string | JsFixLoggerFactory = 'info'): void { container.registerInstance(DefinitionFactory, new DefinitionFactory()) - let lf: JsFixLoggerFactory; + let lf: JsFixLoggerFactory if (typeof levelOrLoggerFactory === 'string') { lf = new JsFixWinstonLogFactory(WinstonLogger.consoleOptions(levelOrLoggerFactory)) } else { - lf = levelOrLoggerFactory; + lf = levelOrLoggerFactory } container.registerInstance(DITokens.JsFixLoggerFactory, lf) container.register(RuntimeFactory, { @@ -45,9 +46,9 @@ export class SessionContainer { protected makeSessionFactory (description: ISessionDescription): ISessionMsgFactory { const fixml = !this.isAscii(description) - return fixml ? - new FixmlSessionMsgFactory(description) : - new AsciiSessionMsgFactory(description) + return fixml + ? new FixmlSessionMsgFactory(description) + : new AsciiSessionMsgFactory(description) } public newChild (description: ISessionDescription): DependencyContainer { @@ -58,8 +59,8 @@ export class SessionContainer { return sessionContainer } - public makeSystem (description: ISessionDescription): Promise { - return new Promise((resolve, reject) => { + public async makeSystem (description: ISessionDescription): Promise { + return await new Promise((resolve, reject) => { const sessionContainer = this.newChild(description) const factory = sessionContainer.resolve(RuntimeFactory) factory.makeConfig().then((c: IJsFixConfig) => { @@ -72,14 +73,14 @@ export class SessionContainer { } public isAscii (description: ISessionDescription): boolean { - return description.application.protocol === 'ascii' + return description.application?.protocol === 'ascii' } public isInitiator (description: ISessionDescription): boolean { - return description.application.type === 'initiator' + return description.application?.type === 'initiator' } - public asAscii (description: ISessionDescription, sessionContainer: DependencyContainer) { + public asAscii (description: ISessionDescription, sessionContainer: DependencyContainer): void { sessionContainer.register(DITokens.MsgTransmitter, { useClass: AsciiMsgTransmitter }) @@ -112,7 +113,7 @@ export class SessionContainer { sessionContainer.register(TcpInitiatorConnector, { useClass: TcpInitiatorConnector }) - if (description.application.resilient) { + if (description.application?.resilient) { sessionContainer.register(DITokens.FixEntity, { useClass: RecoveringTcpInitiator }) @@ -128,7 +129,7 @@ export class SessionContainer { } } - public asFixml (description: ISessionDescription, sessionContainer: DependencyContainer) { + public asFixml (description: ISessionDescription, sessionContainer: DependencyContainer): void { sessionContainer.register(DITokens.MsgTransmitter, { useClass: FixmlMsgTransmitter }) @@ -164,7 +165,7 @@ export class SessionContainer { } } - protected asciiConstants (c: IJsFixConfig, sessionContainer: DependencyContainer) { + protected asciiConstants (c: IJsFixConfig, sessionContainer: DependencyContainer): void { sessionContainer.register(DITokens.delimiter, { useValue: c.delimiter }) @@ -173,7 +174,7 @@ export class SessionContainer { }) } - public registerSession (c: IJsFixConfig, sessionContainer: DependencyContainer) { + public registerSession (c: IJsFixConfig, sessionContainer: DependencyContainer): void { if (this.isAscii(c.description)) { this.asAscii(c.description, sessionContainer) this.asciiConstants(c, sessionContainer) diff --git a/src/runtime/session-launcher.ts b/src/runtime/session-launcher.ts index 38830bb1..b6380279 100644 --- a/src/runtime/session-launcher.ts +++ b/src/runtime/session-launcher.ts @@ -11,28 +11,32 @@ const defaultLoggerFactory = new JsFixWinstonLogFactory(WinstonLogger.consoleOpt export abstract class SessionLauncher { public root: string = '../../' protected readonly logger: IJsFixLogger - public readonly initiatorConfig: ISessionDescription; - public readonly acceptorConfig: ISessionDescription; + public readonly initiatorConfig: ISessionDescription + public readonly acceptorConfig: ISessionDescription | null protected constructor (initiatorConfig: string | ISessionDescription, - acceptorConfig: string | ISessionDescription = null, - private readonly loggerFactory: JsFixLoggerFactory = defaultLoggerFactory) { + acceptorConfig: string | ISessionDescription | null = null, + private readonly loggerFactory: JsFixLoggerFactory = defaultLoggerFactory) { this.logger = this.loggerFactory.logger('launcher') this.initiatorConfig = this.loadConfig(initiatorConfig) - this.acceptorConfig = this.loadConfig(acceptorConfig) + this.acceptorConfig = acceptorConfig ? this.loadConfig(acceptorConfig) : null } protected sessionContainer: SessionContainer = new SessionContainer() - private empty (): Promise { - return new Promise((resolve, _) => { - setImmediate(() => { - this.logger.info('resolving an empty promise') - resolve(null) - }) + private async empty (): Promise { + return await new Promise((resolve, reject) => { + try { + setImmediate(() => { + this.logger.info('resolving an empty promise') + resolve(null) + }) + } catch (e) { + reject(e) + } }) } - protected getAcceptor (sessionContainer: DependencyContainer): Promise { + protected async getAcceptor (sessionContainer: DependencyContainer): Promise { if (sessionContainer.isRegistered(DITokens.FixEntity)) { const entity = sessionContainer.resolve(DITokens.FixEntity) return entity.start() @@ -41,7 +45,7 @@ export abstract class SessionLauncher { } } - protected getInitiator (sessionContainer: DependencyContainer): Promise { + protected async getInitiator (sessionContainer: DependencyContainer): Promise { if (sessionContainer.isRegistered(DITokens.FixEntity)) { const entity = sessionContainer.resolve(DITokens.FixEntity) return entity.start() @@ -50,17 +54,17 @@ export abstract class SessionLauncher { } } - protected makeFactory (config: IJsFixConfig): EngineFactory { + protected makeFactory (config: IJsFixConfig): EngineFactory | null { return null } - public run () { - return new Promise((accept, reject) => { + public async run (): Promise { + return await new Promise((resolve, reject) => { const logger = this.logger logger.info('launching ..') this.setup().then(() => { logger.info('.. done') - accept(true) + resolve(true) }).catch((e: Error) => { logger.error(e) reject(e) @@ -68,7 +72,7 @@ export abstract class SessionLauncher { }) } - public exec () { + public exec (): void { this.run().then(() => { console.log('finished.') }).catch(e => { @@ -88,9 +92,11 @@ export abstract class SessionLauncher { this.logger.info('bypass register via DI') } - private makeSystem (description: ISessionDescription): Promise { - this.logger.info(`creating app ${description.application.name} [protocol ${description.application.protocol}]`) - return this.sessionContainer.makeSystem(description) + private async makeSystem (description: ISessionDescription): Promise { + const name = description.application?.name ?? 'na' + const protocol = description.application?.protocol ?? 'ascii' + this.logger.info(`creating app ${name} [protocol ${protocol}]`) + return await this.sessionContainer.makeSystem(description) } private register (container: DependencyContainer): void { @@ -111,25 +117,36 @@ export abstract class SessionLauncher { const sessionContainer = await this.makeSystem(this.initiatorConfig) this.register(sessionContainer) this.logger.info('create initiator') - return this.getInitiator(sessionContainer) + return await this.getInitiator(sessionContainer) } private async makeServer (): Promise { + if (!this.acceptorConfig) return const sessionContainer = await this.makeSystem(this.acceptorConfig) this.register(sessionContainer) this.logger.info('create acceptor') - return this.getAcceptor(sessionContainer) + return await this.getAcceptor(sessionContainer) } - private async setup () { - this.sessionContainer.registerGlobal(this.loggerFactory) + async serverOrEmpty (): Promise { const server = this.acceptorConfig ? this.makeServer() : this.empty() + return server + } + + async clientOrEmpty (): Promise { const client = this.initiatorConfig ? this.makeClient() : this.empty() + return client + } + + private async setup (): Promise { + this.sessionContainer.registerGlobal(this.loggerFactory) + const server = this.serverOrEmpty() + const client = this.clientOrEmpty() this.logger.info('launching ....') - return Promise.all([server, client]) + return await Promise.all([server, client]) } - private loadConfig(config: string | ISessionDescription): ISessionDescription { + private loadConfig (config: string | ISessionDescription): ISessionDescription { if (typeof config === 'string') { return require(path.join(this.root, config)) } diff --git a/src/sample/http/oms/app.ts b/src/sample/http/oms/app.ts index 3dfc323c..5445cf04 100644 --- a/src/sample/http/oms/app.ts +++ b/src/sample/http/oms/app.ts @@ -7,6 +7,7 @@ import { DITokens, EngineFactory, SessionLauncher } from '../../../runtime' import { DependencyContainer } from 'tsyringe' import { FixEntity } from '../../../transport' import { IHttpAdapter } from '../../../transport/http/http-adapter' +import * as util from 'util' class AppLauncher extends SessionLauncher { public constructor () { @@ -18,17 +19,33 @@ class AppLauncher extends SessionLauncher { protected override makeFactory (config: IJsFixConfig): EngineFactory { const isInitiator = this.isInitiator(config.description) return { - makeSession: () => isInitiator ? - new HttpClient(config) : - new HttpServer(config) + makeSession: () => isInitiator + ? new HttpClient(config) + : new HttpServer(config) } as EngineFactory } - protected override getInitiator (sessionContainer: DependencyContainer): Promise { + async waitFor (ms: number): Promise { + const to = util.promisify(setTimeout) + return new Promise((resolve, reject) => { + to(ms).then(() => { + resolve(null) + }).catch(e => { + reject(e) + }) + }) + } + + protected override async getInitiator (sessionContainer: DependencyContainer): Promise { const config: IJsFixConfig = sessionContainer.resolve(DITokens.IJsFixConfig) - config.description.application.http.adapter = sessionContainer.resolve(DITokens.IHttpAdapter) - const initiator = sessionContainer.resolve(DITokens.FixEntity) - return initiator.start() + const http = config?.description?.application?.http + if (http) { + http.adapter = sessionContainer.resolve(DITokens.IHttpAdapter) + const initiator = sessionContainer.resolve(DITokens.FixEntity) + return this.waitFor(1000).then(async () => { + return initiator.start() + }) + } } } diff --git a/src/sample/http/oms/http-client.ts b/src/sample/http/oms/http-client.ts index 8c03fc73..5f39eb11 100644 --- a/src/sample/http/oms/http-client.ts +++ b/src/sample/http/oms/http-client.ts @@ -12,10 +12,10 @@ export class HttpClient extends FixmlSession { private readonly fixLog: IJsFixLogger private readonly factory: OmsFactory = new OmsFactory('TradersRUs') constructor (@inject(DITokens.IJsFixConfig) public readonly config: IJsFixConfig, - @inject('logoutSeconds') public readonly logoutSeconds: number = 45) { + @inject('logoutSeconds') public readonly logoutSeconds: number = 45) { super(config) this.logReceivedMsgs = true - this.fixLog = config.logFactory.plain(`jsfix.${config.description.application.name}.txt`) + this.fixLog = config.logFactory.plain(`jsfix.${config?.description?.application?.name}.txt`) this.logger = config.logFactory.logger(`${this.me}`) } @@ -30,7 +30,7 @@ export class HttpClient extends FixmlSession { case 'ExecRpt': { const fill: IExecutionReport = view.toObject() - this.logger.warning(`received execution report ${fill.OrderQtyData.OrderQty}@${fill.Price}`) + this.logger.info(`received execution report ${fill?.OrderQtyData?.OrderQty}@${fill.Price}`) break } } diff --git a/src/sample/http/oms/http-server.ts b/src/sample/http/oms/http-server.ts index bef5de1e..3b991fa5 100644 --- a/src/sample/http/oms/http-server.ts +++ b/src/sample/http/oms/http-server.ts @@ -15,7 +15,7 @@ export class HttpServer extends FixmlSession { constructor (@inject(DITokens.IJsFixConfig) public readonly config: IJsFixConfig) { super(config) this.logReceivedMsgs = true - this.fixLog = config.logFactory.plain(`jsfix.${config.description.application.name}.txt`) + this.fixLog = config.logFactory.plain(`jsfix.${config?.description?.application?.name}.txt`) this.logger = config.logFactory.logger(`${this.me}`) } @@ -28,6 +28,12 @@ export class HttpServer extends FixmlSession { this.logger.info(`received order id ${order.ClOrdID}`) const fill: IExecutionReport = this.factory.fillOrder(order) this.send('ExecutionReport', fill) + break + } + + default: { + this.logger.warning(`received unknown msgType ${msgType}`) + break } } } diff --git a/src/sample/http/oms/oms-factory.ts b/src/sample/http/oms/oms-factory.ts index 006c3214..e39b68b9 100644 --- a/src/sample/http/oms/oms-factory.ts +++ b/src/sample/http/oms/oms-factory.ts @@ -52,11 +52,11 @@ export class OmsFactory { Side: order.Side, Price: order.Price, OrderQtyData: { - OrderQty: order.OrderQtyData.OrderQty + OrderQty: order?.OrderQtyData?.OrderQty } as IOrderQtyData, Instrument: { - Symbol: order.Instrument.Symbol, - SecurityID: order.Instrument.SecurityID, + Symbol: order?.Instrument?.Symbol, + SecurityID: order?.Instrument?.SecurityID, SecurityIDSource: SecurityIDSource.IsinNumber } as IInstrument } as IExecutionReport diff --git a/src/sample/tcp/qf-md/app.ts b/src/sample/tcp/qf-md/app.ts index af6f4226..5121b8b7 100644 --- a/src/sample/tcp/qf-md/app.ts +++ b/src/sample/tcp/qf-md/app.ts @@ -15,9 +15,9 @@ class AppLauncher extends SessionLauncher { protected override makeFactory (config: IJsFixConfig): EngineFactory { const isInitiator = this.isInitiator(config.description) return { - makeSession: () => isInitiator ? - new MDClient(config) : - new MDServer(config) + makeSession: () => isInitiator + ? new MDClient(config) + : new MDServer(config) } as EngineFactory } } diff --git a/src/sample/tcp/qf-md/md-client.ts b/src/sample/tcp/qf-md/md-client.ts index 92cb9fdb..37dda218 100644 --- a/src/sample/tcp/qf-md/md-client.ts +++ b/src/sample/tcp/qf-md/md-client.ts @@ -15,7 +15,7 @@ export class MDClient extends AsciiSession { constructor (@inject(DITokens.IJsFixConfig) public readonly config: IJsFixConfig) { super(config) this.logReceivedMsgs = true - this.fixLog = config.logFactory.plain(`jsfix.${config!.description!.application!.name}.txt`) + this.fixLog = config.logFactory.plain(`jsfix.${config?.description?.application?.name}.txt`) this.logger = config.logFactory.logger(`${this.me}:MDClient`) } diff --git a/src/sample/tcp/qf-md/md-factory.ts b/src/sample/tcp/qf-md/md-factory.ts index 79fc5bf8..0069c1b2 100644 --- a/src/sample/tcp/qf-md/md-factory.ts +++ b/src/sample/tcp/qf-md/md-factory.ts @@ -1,10 +1,11 @@ import { IMarketDataRequest, MDEntryType, - SubscriptionRequestType } from '../../../types/FIX4.4/quickfix' + SubscriptionRequestType +} from '../../../types/FIX4.4/quickfix' export class MDFactory { - public static BidOfferRequest (symbol: string) { + public static BidOfferRequest (symbol: string): IMarketDataRequest { return { MDReqID: '1', SubscriptionRequestType: SubscriptionRequestType.SnapshotPlusUpdates, diff --git a/src/sample/tcp/qf-md/md-server.ts b/src/sample/tcp/qf-md/md-server.ts index 5745a57f..c429f294 100644 --- a/src/sample/tcp/qf-md/md-server.ts +++ b/src/sample/tcp/qf-md/md-server.ts @@ -11,13 +11,13 @@ import { inject, injectable } from 'tsyringe' export class MDServer extends AsciiSession { private readonly logger: IJsFixLogger private readonly fixLog: IJsFixLogger - private timerHandle: NodeJS.Timer = null + private readonly timerHandle: NodeJS.Timer | null = null constructor (@inject('IJsFixConfig') public readonly config: IJsFixConfig) { super(config) this.logReceivedMsgs = true this.logger = config.logFactory.logger(`${this.me}:MDServer`) - this.fixLog = config.logFactory.plain(`jsfix.${config!.description!.application!.name}.txt`) + this.fixLog = config.logFactory.plain(`jsfix.${config?.description?.application?.name}.txt`) } protected onApplicationMsg (msgType: string, view: MsgView): void { diff --git a/src/sample/tcp/recovering-skeleton/app.ts b/src/sample/tcp/recovering-skeleton/app.ts index cf7a91e0..c452c677 100644 --- a/src/sample/tcp/recovering-skeleton/app.ts +++ b/src/sample/tcp/recovering-skeleton/app.ts @@ -8,40 +8,57 @@ import { RespawnAcceptor } from './respawn-acceptor' import { AsciiChars } from '../../../buffer/ascii' import { DependencyContainer } from 'tsyringe' import { SkeletonServer } from './skeleton-server' -import { FixSession, FixEntity } from '../../../transport' +import { FixSession, FixEntity, ISessionDescription } from '../../../transport' +import * as path from 'path' -class AppLauncher extends SessionLauncher { +const root: string = '../../../../' + +function makeConfig (config: string): ISessionDescription { + const o = require(path.join(root, config)) + o.ResetSeqNumFlag = false + return o as ISessionDescription +} +class AppLauncher extends SessionLauncher { public constructor () { super( - 'data/session/test-initiator.json', - 'data/session/test-acceptor.json') + makeConfig('data/session/test-initiator.json'), + makeConfig('data/session/test-acceptor.json')) + } + + private asInitiator (sessionContainer: DependencyContainer): void { + sessionContainer.register(DITokens.FixSession, { + useClass: SkeletonClient + }) + sessionContainer.register(DITokens.FixEntity, { + useClass: RecoveringTcpInitiator + }) } - protected override registerApplication (sessionContainer: DependencyContainer) { + private asAcceptor (sessionContainer: DependencyContainer): void { + sessionContainer.register(DITokens.FixEntity, { + useClass: RespawnAcceptor + }) + sessionContainer.register(DITokens.FixSession, { + useClass: SkeletonServer + }) + sessionContainer.register('logoutSeconds', { + useValue: 45 + }) + } + + protected override registerApplication (sessionContainer: DependencyContainer): void { const config: IJsFixConfig = sessionContainer.resolve(DITokens.IJsFixConfig) // use a different log delimiter as an example config.logDelimiter = AsciiChars.Carat const isInitiator = this.isInitiator(config.description) if (isInitiator) { - sessionContainer.register(DITokens.FixSession, { - useClass: SkeletonClient - }) - sessionContainer.register(DITokens.FixEntity, { - useClass: RecoveringTcpInitiator - }) + this.asInitiator(sessionContainer) } else { - sessionContainer.register(DITokens.FixEntity, { - useClass: RespawnAcceptor - }) - sessionContainer.register(DITokens.FixSession, { - useClass: SkeletonServer - }) - sessionContainer.register('logoutSeconds', { - useValue: 45 - }) + this.asAcceptor(sessionContainer) } + sessionContainer.register('logoutSeconds', { useValue: 45 }) diff --git a/src/sample/tcp/recovering-skeleton/respawn-acceptor.ts b/src/sample/tcp/recovering-skeleton/respawn-acceptor.ts index a90ebe80..a8070392 100644 --- a/src/sample/tcp/recovering-skeleton/respawn-acceptor.ts +++ b/src/sample/tcp/recovering-skeleton/respawn-acceptor.ts @@ -1,11 +1,16 @@ import { IJsFixConfig, IJsFixLogger } from '../../../config' import { TcpAcceptorListener } from '../../../transport/tcp' import { inject, injectable } from 'tsyringe' -import { FixEntity } from '../../../transport' +import { FixEntity, FixSession } from '../../../transport' +import { Dictionary } from '../../../collections' +import { MsgView } from '../../../buffer' +import { MsgTransport } from '../../../transport/factory' +import { ILooseObject } from '../../../collections/collection' @injectable() export class RespawnAcceptor extends FixEntity { private readonly logger: IJsFixLogger + private readonly sessions: Dictionary = new Dictionary() constructor (@inject('IJsFixConfig') public readonly config: IJsFixConfig) { super(config) @@ -15,17 +20,74 @@ export class RespawnAcceptor extends FixEntity { // if acceptor errors e.g. via a forced connection drop, then respawn // a set number of times. - public start (): Promise { - return this.waitFor() + public async start (): Promise { + return await this.waitFor() + } + + protected rxOnMsg (session: FixSession, msgType: string, view: MsgView): void { + this.logger.info(`rxOnMsg msgType = ${msgType}`) + const o: ILooseObject = view.toObject() + const key: string = o.StandardHeader.SenderCompID + if (!this.sessions.containsKey(key)) { + this.logger.info(`onSession: new session acceptor SenderCompID = ${key} created, count = ${this.sessions.count()}}`) + this.sessions.add(key, session) + } + } + + private resetSessionSeqNo (session: FixSession): void { + const key = this.config.description.TargetCompID + if (this.sessions.containsKey(key)) { + const lastSession = this.sessions.get(key) + const lastPeerSeqNum = lastSession?.lastPeerSeqNum() ?? 0 + this.logger.info(`resetSessionSeqNo: set lastPeerSeqNum ${lastPeerSeqNum} for key ${key}`) + session.reset(lastPeerSeqNum) + this.sessions.remove(key) + } + } + + private resetLastSentSeqNo (): void { + if (!this.resetSeqNo()) { + const key = this.config.description.TargetCompID + if (this.sessions.containsKey(key)) { + const lastSession = this.sessions.get(key) + const lastSentSeqNum = lastSession?.lastSentSeqNum() ?? 0 + this.logger.info(`resetLastSentSeqNo: set seqNo ${lastSentSeqNum} for key ${key}`) + this.config.description.LastSentSeqNum = lastSentSeqNum + } + } + } + + private onSession (session: FixSession, transport: MsgTransport): void { + this.resetSessionSeqNo(session) + const rx = transport.receiver + rx?.on('msg', (msgType: string, view: MsgView) => this.rxOnMsg(session, msgType, view)) + } + + private subscribe (listener: TcpAcceptorListener): void { + listener.on('session', (s, transport) => this.onSession(s, transport)) + } + + /* + private unsubscribe (listener: TcpAcceptorListener): void { + listener.removeListener('session', this.onSession) + } + */ + + private resetSeqNo (): boolean { + return this.config.description.ResetSeqNumFlag } public async waitFor (respawns: number = 1): Promise { - return new Promise(async (resolve, reject) => { + return await new Promise(async (resolve, reject) => { let respawned = 0 while (respawned <= respawns) { try { const sessionContainer = this.config.sessionContainer + // if previously running a session then retrieve its last state + // as need to adjust seqNo if not resetting. + this.resetLastSentSeqNo() const listener = sessionContainer.resolve(TcpAcceptorListener) + this.subscribe(listener) const dropConnectionTimeout = respawned === 0 ? 5 : -1 sessionContainer.register('dropConnectionTimeout', { useValue: dropConnectionTimeout }) this.logger.info(`waitFor: waiting for acceptor respawned = ${respawned}`) diff --git a/src/sample/tcp/recovering-skeleton/skeleton-client.ts b/src/sample/tcp/recovering-skeleton/skeleton-client.ts index c6881ed3..8fac0f95 100644 --- a/src/sample/tcp/recovering-skeleton/skeleton-client.ts +++ b/src/sample/tcp/recovering-skeleton/skeleton-client.ts @@ -9,10 +9,10 @@ export class SkeletonClient extends AsciiSession { private readonly fixLog: IJsFixLogger constructor (@inject('IJsFixConfig') public readonly config: IJsFixConfig, - @inject('logoutSeconds') public readonly logoutSeconds: number) { + @inject('logoutSeconds') public readonly logoutSeconds: number) { super(config) this.logReceivedMsgs = true - this.fixLog = config.logFactory.plain(`jsfix.${config.description.application.name}.txt`) + this.fixLog = config.logFactory.plain(`jsfix.${config?.description?.application?.name}.txt`) this.logger = config.logFactory.logger(`${this.me}`) } diff --git a/src/sample/tcp/recovering-skeleton/skeleton-server.ts b/src/sample/tcp/recovering-skeleton/skeleton-server.ts index 50c0cf4d..021b7ee7 100644 --- a/src/sample/tcp/recovering-skeleton/skeleton-server.ts +++ b/src/sample/tcp/recovering-skeleton/skeleton-server.ts @@ -9,10 +9,10 @@ export class SkeletonServer extends AsciiSession { private readonly fixLog: IJsFixLogger constructor (@inject('IJsFixConfig') public readonly config: IJsFixConfig, - @inject('dropConnectionTimeout') public readonly dropConnectionTimeout: number) { + @inject('dropConnectionTimeout') public readonly dropConnectionTimeout: number) { super(config) this.logReceivedMsgs = true - this.fixLog = config.logFactory.plain(`jsfix.${config.description.application.name}.txt`) + this.fixLog = config.logFactory.plain(`jsfix.${config?.description?.application?.name}.txt`) this.logger = config.logFactory.logger(`${this.me}`) } @@ -47,12 +47,12 @@ export class SkeletonServer extends AsciiSession { this.logger.info(`acceptor is ready for requests - drop connection in ${this.dropConnectionTimeout}`) setTimeout(() => { setImmediate(() => { - this.logger.info(`kill transport`) + this.logger.info('kill transport') this.stop(new Error(`loss of tcp. ${this.me}`)) }) }, this.dropConnectionTimeout * 1000) } else { - this.logger.info(`acceptor is ready for requests`) + this.logger.info('acceptor is ready for requests') } } diff --git a/src/sample/tcp/skeleton/app.ts b/src/sample/tcp/skeleton/app.ts index 87482218..9ab76d89 100644 --- a/src/sample/tcp/skeleton/app.ts +++ b/src/sample/tcp/skeleton/app.ts @@ -13,7 +13,7 @@ class AppLauncher extends SessionLauncher { protected override makeFactory (config: IJsFixConfig): EngineFactory { return { - makeSession: () => new SkeletonSession(config, 45,false) + makeSession: () => new SkeletonSession(config, 45, false) } as EngineFactory } } diff --git a/src/sample/tcp/skeleton/skeleton-session.ts b/src/sample/tcp/skeleton/skeleton-session.ts index 7fd2a9d3..407227fa 100644 --- a/src/sample/tcp/skeleton/skeleton-session.ts +++ b/src/sample/tcp/skeleton/skeleton-session.ts @@ -12,11 +12,11 @@ export class SkeletonSession extends AsciiSession { private readonly fixLog: IJsFixLogger constructor (@inject(DITokens.IJsFixConfig) public readonly config: IJsFixConfig, - @inject('logoutSeconds') public readonly logoutSeconds: number, - @inject('useInMemoryStore') public useInMemoryStore: boolean) { + @inject('logoutSeconds') public readonly logoutSeconds: number, + @inject('useInMemoryStore') public useInMemoryStore: boolean) { super(config) this.logReceivedMsgs = true - this.fixLog = config.logFactory.plain(`jsfix.${config.description.application.name}.txt`) + this.fixLog = config.logFactory.plain(`jsfix.${config?.description?.application?.name}.txt`) this.logger = config.logFactory.logger(`${this.me}`) } @@ -24,7 +24,7 @@ export class SkeletonSession extends AsciiSession { // dispatch messages if (this.useInMemoryStore) { const rec = FixMsgStoreRecord.toMsgStoreRecord(view) - this.store.put(rec).then(r => { + this.store?.put(rec).then(r => { this.logger.info(`store state ${JSON.stringify(r, null, 4)}`) this.dispatch(msgType, view) }).catch(e => { @@ -35,7 +35,7 @@ export class SkeletonSession extends AsciiSession { } } - private dispatch (msgType: string, view: MsgView) { + private dispatch (msgType: string, view: MsgView): void { const o = view.toObject() switch (msgType) { default: { @@ -67,7 +67,7 @@ export class SkeletonSession extends AsciiSession { protected onReady (view: MsgView): void { this.logger.info('onReady') const logoutSeconds = this.logoutSeconds - const t = this.config.description.application.type + const t = this.config?.description?.application?.type switch (t) { case 'initiator': { this.logger.info(`will logout after ${logoutSeconds}`) @@ -78,7 +78,7 @@ export class SkeletonSession extends AsciiSession { } case 'acceptor': { - this.logger.info(`acceptor is ready for requests`) + this.logger.info('acceptor is ready for requests') break } diff --git a/src/sample/tcp/tls-trade-capture/app.ts b/src/sample/tcp/tls-trade-capture/app.ts index 0f2c9118..d5c909a1 100644 --- a/src/sample/tcp/tls-trade-capture/app.ts +++ b/src/sample/tcp/tls-trade-capture/app.ts @@ -4,5 +4,5 @@ import { AppLauncher } from '../trade-capture/app-launcher' const l = new AppLauncher( 'data/session/test-initiator-tls.json', 'data/session/test-acceptor-tls.json' - ) +) l.exec() diff --git a/src/sample/tcp/trade-capture/app-launcher.ts b/src/sample/tcp/trade-capture/app-launcher.ts index ec6c252f..f0a6c083 100644 --- a/src/sample/tcp/trade-capture/app-launcher.ts +++ b/src/sample/tcp/trade-capture/app-launcher.ts @@ -5,7 +5,7 @@ import { TradeCaptureServer } from './trade-capture-server' export class AppLauncher extends SessionLauncher { public constructor (client: string = 'data/session/test-initiator.json', - server: string = 'data/session/test-acceptor.json') { + server: string = 'data/session/test-acceptor.json') { super( client, server) @@ -14,9 +14,9 @@ export class AppLauncher extends SessionLauncher { protected override makeFactory (config: IJsFixConfig): EngineFactory { const isInitiator = this.isInitiator(config.description) return { - makeSession: () => isInitiator ? - new TradeCaptureClient(config) : - new TradeCaptureServer(config) + makeSession: () => isInitiator + ? new TradeCaptureClient(config) + : new TradeCaptureServer(config) } as EngineFactory } } diff --git a/src/sample/tcp/trade-capture/trade-capture-client.ts b/src/sample/tcp/trade-capture/trade-capture-client.ts index c9bb9746..2e43a43a 100644 --- a/src/sample/tcp/trade-capture/trade-capture-client.ts +++ b/src/sample/tcp/trade-capture/trade-capture-client.ts @@ -1,8 +1,10 @@ import { MsgView } from '../../../buffer' import { AsciiSession } from '../../../transport' import { MsgType } from '../../../types' -import { ITradeCaptureReport, ITradeCaptureReportRequest, - ITradeCaptureReportRequestAck } from '../../../types/FIX4.4/repo' +import { + ITradeCaptureReport, ITradeCaptureReportRequest, + ITradeCaptureReportRequestAck +} from '../../../types/FIX4.4/repo' import { IJsFixLogger, IJsFixConfig } from '../../../config' import { Dictionary } from '../../../collections' import { TradeFactory } from './trade-factory' @@ -10,13 +12,13 @@ import { TradeFactory } from './trade-factory' export class TradeCaptureClient extends AsciiSession { private readonly logger: IJsFixLogger private readonly fixLog: IJsFixLogger - private reports: Dictionary + private readonly reports: Dictionary constructor (public readonly config: IJsFixConfig) { super(config) this.logReceivedMsgs = true this.reports = new Dictionary() - this.fixLog = config.logFactory.plain(`jsfix.${config.description.application.name}.txt`) + this.fixLog = config.logFactory.plain(`jsfix.${config?.description?.application?.name}.txt`) this.logger = config.logFactory.logger(`${this.me}:TradeCaptureClient`) } @@ -53,7 +55,7 @@ export class TradeCaptureClient extends AsciiSession { this.fixLog.info(txt) } - private logoutTimer (logoutSeconds: number = 32) { + private logoutTimer (logoutSeconds: number = 32): void { setTimeout(() => { this.done() }, logoutSeconds * 1000) diff --git a/src/sample/tcp/trade-capture/trade-capture-server.ts b/src/sample/tcp/trade-capture/trade-capture-server.ts index 39c71110..0929ca34 100644 --- a/src/sample/tcp/trade-capture/trade-capture-server.ts +++ b/src/sample/tcp/trade-capture/trade-capture-server.ts @@ -3,21 +3,23 @@ import { AsciiSession } from '../../../transport' import { MsgType } from '../../../types' import { IJsFixLogger, IJsFixConfig } from '../../../config' // interfaces generated by compiler to make messages easy in an IDE -import { ITradeCaptureReportRequest, ITradeCaptureReport, MsgTag, SessionRejectReason, - SubscriptionRequestType, TradeRequestStatus } from '../../../types/FIX4.4/repo' +import { + ITradeCaptureReportRequest, ITradeCaptureReport, MsgTag, SessionRejectReason, + SubscriptionRequestType, TradeRequestStatus +} from '../../../types/FIX4.4/repo' import { TradeFactory } from './trade-factory' export class TradeCaptureServer extends AsciiSession { private readonly logger: IJsFixLogger private readonly fixLog: IJsFixLogger private readonly tradeFactory: TradeFactory = new TradeFactory() - private timerHandle: NodeJS.Timer = null + private timerHandle: NodeJS.Timer | null = null constructor (public readonly config: IJsFixConfig) { super(config) this.logReceivedMsgs = true this.logger = config.logFactory.logger(`${this.me}:TradeCaptureServer`) - this.fixLog = config.logFactory.plain(`jsfix.${config.description.application.name}.txt`) + this.fixLog = config.logFactory.plain(`jsfix.${config?.description?.application?.name}.txt`) } protected onApplicationMsg (msgType: string, view: MsgView): void { @@ -30,7 +32,10 @@ export class TradeCaptureServer extends AsciiSession { default: { const seqNum = view.getTyped(MsgTag.MsgSeqNum) - this.send(msgType, this.config.factory.reject(msgType, seqNum, `${this.me}: unexpected msg type '${msgType}'`, SessionRejectReason.InvalidMsgType)) + const msg = this.config.factory?.reject(msgType, seqNum, `${this.me}: unexpected msg type '${msgType}'`, SessionRejectReason.InvalidMsgType) + if (msg) { + this.send(msgType, msg) + } break } } diff --git a/src/sample/tcp/trade-capture/trade-factory.ts b/src/sample/tcp/trade-capture/trade-factory.ts index 1cb089d2..a3badde9 100644 --- a/src/sample/tcp/trade-capture/trade-factory.ts +++ b/src/sample/tcp/trade-capture/trade-factory.ts @@ -54,7 +54,7 @@ export class TradeFactory { } as ITradeCaptureReportRequest } - private static getRandomInt (min: number, max: number) { + private static getRandomInt (min: number, max: number): number { min = Math.ceil(min) max = Math.floor(max) return Math.floor(Math.random() * (max - min)) + min diff --git a/src/store/fix-msg-ascii-store-resend.ts b/src/store/fix-msg-ascii-store-resend.ts index 2bf686bb..55f9babe 100644 --- a/src/store/fix-msg-ascii-store-resend.ts +++ b/src/store/fix-msg-ascii-store-resend.ts @@ -12,13 +12,12 @@ export class FixMsgAsciiStoreResend { this.parser = new AsciiParser(this.config, null, new ElasticBuffer(160 * 1024)) } - public getResendRequest (startSeq: number, endSeq: number): Promise { - + public async getResendRequest (startSeq: number, endSeq: number): Promise { // need to cover request from start to end where any missing numbers are // included as gaps to allow vector of messages to be sent by the session // on a request - return new Promise((resolve, reject) => { + return await new Promise((resolve, reject) => { this.store.getSeqNumRange(startSeq, endSeq).then(res => { resolve(this.inflateRange(startSeq, endSeq, res)) }).catch(e => { @@ -49,7 +48,7 @@ export class FixMsgAsciiStoreResend { return toResend } - public gap (beginGap: number, seqNum: number, arr: IFixMsgStoreRecord[]) { + public gap (beginGap: number, seqNum: number, arr: IFixMsgStoreRecord[]): void { if (beginGap > 0) { arr.push(this.sequenceResetGap(beginGap, seqNum)) } @@ -74,8 +73,8 @@ export class FixMsgAsciiStoreResend { public sequenceResetGap (startGap: number, newSeq: number): IFixMsgStoreRecord { const factory = this.config.factory - const gapFill: ISequenceReset = factory.sequenceReset(newSeq, true) as ISequenceReset - gapFill.StandardHeader = factory.header(MsgType.SequenceReset, startGap) as IStandardHeader + const gapFill: ISequenceReset = factory?.sequenceReset(newSeq, true) as ISequenceReset + gapFill.StandardHeader = factory?.header(MsgType.SequenceReset, startGap) as IStandardHeader gapFill.StandardHeader.PossDupFlag = true gapFill.NewSeqNo = newSeq return new FixMsgStoreRecord( diff --git a/src/store/fix-msg-memory-store.ts b/src/store/fix-msg-memory-store.ts index 7b014b56..0ef2acd3 100644 --- a/src/store/fix-msg-memory-store.ts +++ b/src/store/fix-msg-memory-store.ts @@ -9,9 +9,9 @@ export class FixMsgMemoryStore implements IFixMsgStore { protected readonly logger: IJsFixLogger public heartbeat: boolean = true private sortedBySeqNum: IFixMsgStoreRecord[] = [] - private excluded: Dictionary = new Dictionary() + private readonly excluded: Dictionary = new Dictionary() public length: number = 0 - private sessionMessages: string[] = [ + private readonly sessionMessages: string[] = [ MsgType.Logon, MsgType.Logout, MsgType.ResendRequest, @@ -25,7 +25,7 @@ export class FixMsgMemoryStore implements IFixMsgStore { this.setExcMsgType([]) } - public static search (ar: IFixMsgStoreRecord[], target?: number, isDate?: boolean): number { + public static search (ar: IFixMsgStoreRecord[], target: number, isDate?: boolean): number { let m: number = 0 let n: number = ar.length - 1 while (m <= n) { @@ -43,8 +43,8 @@ export class FixMsgMemoryStore implements IFixMsgStore { return -m - 1 } - public getMsgType (msgType: string): Promise { - return new Promise((resolve, reject: any) => { + public async getMsgType (msgType: string): Promise { + return await new Promise((resolve, reject: any) => { const data = this.sortedBySeqNum if (data === null) reject(new Error('no store')) const required = data.filter(x => x.msgType === msgType) @@ -61,13 +61,13 @@ export class FixMsgMemoryStore implements IFixMsgStore { return index } - private bounded (fromIdx: number, toIdx: number) { + private bounded (fromIdx: number, toIdx: number): boolean { const arr = this.sortedBySeqNum return fromIdx >= 0 && fromIdx <= arr.length && toIdx >= fromIdx && toIdx <= arr.length } - public get (from: number): Promise { - return new Promise((resolve, reject) => { + public async get (from: number): Promise { + return await new Promise((resolve, reject) => { this.getSeqNumRange(from, from).then(res => { if (res.length > 0) { const record = res[0].clone() @@ -81,14 +81,15 @@ export class FixMsgMemoryStore implements IFixMsgStore { }) } - public getSeqNumRange (from: number, to?: number): Promise { - return new Promise((resolve, reject) => { + public async getSeqNumRange (from: number, to?: number): Promise { + return await new Promise((resolve, reject) => { + to = to ?? 0 const arr = this.sortedBySeqNum if (from < 0) reject(new Error(`illegal from ${from}`)) if (to < 0) reject(new Error(`illegal to ${to}`)) - let fromIdx = this.getIndex(from) + const fromIdx = this.getIndex(from) const toEnd = to === 0 || isNaN(to) - let toIdx = toEnd ? arr.length - 1 : this.getIndex(to) + const toIdx = toEnd ? arr.length - 1 : this.getIndex(to) if (this.bounded(fromIdx, toIdx)) { resolve(arr.slice(fromIdx, toIdx + 1)) } else { @@ -107,8 +108,8 @@ export class FixMsgMemoryStore implements IFixMsgStore { } as IFixMsgStoreState } - public getState (): Promise { - return new Promise((resolve, reject) => { + public async getState (): Promise { + return await new Promise((resolve, reject) => { try { resolve(this.buildState()) } catch (e) { @@ -117,9 +118,9 @@ export class FixMsgMemoryStore implements IFixMsgStore { }) } - public clear (): Promise { + public async clear (): Promise { this.sortedBySeqNum = [] - return new Promise((resolve, reject) => { + return await new Promise((resolve, reject) => { try { resolve(this.buildState()) } catch (e) { @@ -128,8 +129,8 @@ export class FixMsgMemoryStore implements IFixMsgStore { }) } - public put (record: IFixMsgStoreRecord): Promise { - return new Promise((resolve, reject) => { + public async put (record: IFixMsgStoreRecord): Promise { + return await new Promise((resolve, reject) => { if (this.excluded.containsKey(record.msgType)) { resolve(this.buildState()) } else { @@ -157,11 +158,11 @@ export class FixMsgMemoryStore implements IFixMsgStore { }) } - exists (seqNum: number): Promise { - return new Promise((resolve, reject) => { + async exists (seqNum: number): Promise { + return await new Promise((resolve, reject) => { try { const arr = this.sortedBySeqNum - let index = FixMsgMemoryStore.search(arr, seqNum) + const index = FixMsgMemoryStore.search(arr, seqNum) resolve(index >= 0) } catch (e) { reject(e) diff --git a/src/store/fix-msg-store-record.ts b/src/store/fix-msg-store-record.ts index c2f8857f..0872f203 100644 --- a/src/store/fix-msg-store-record.ts +++ b/src/store/fix-msg-store-record.ts @@ -6,21 +6,21 @@ export interface IFixMsgStoreRecord { readonly msgType: string readonly timestamp: Date readonly seqNum: number - obj?: ILooseObject - readonly encoded?: string - clone (): IFixMsgStoreRecord + obj?: ILooseObject | null + readonly encoded?: string | null + clone: () => IFixMsgStoreRecord } export class FixMsgStoreRecord implements IFixMsgStoreRecord { constructor (public readonly msgType: string, - public readonly timestamp: Date, - public readonly seqNum: number, - public obj?: ILooseObject, - public readonly encoded?: string) { + public readonly timestamp: Date, + public readonly seqNum: number, + public obj?: ILooseObject, + public readonly encoded?: string | null) { } static toMsgStoreRecord (v: MsgView): IFixMsgStoreRecord { - return new FixMsgStoreRecord(v.getString(MsgTag.MsgType), v.getTyped(MsgTag.SendingTime), v.getTyped(MsgTag.MsgSeqNum), v.toObject()) + return new FixMsgStoreRecord(v.getString(MsgTag.MsgType) ?? '', v.getTyped(MsgTag.SendingTime), v.getTyped(MsgTag.MsgSeqNum), v.toObject()) } clone (): IFixMsgStoreRecord { diff --git a/src/store/fix-msg-store-state.ts b/src/store/fix-msg-store-state.ts index bc790ff9..a05972d9 100644 --- a/src/store/fix-msg-store-state.ts +++ b/src/store/fix-msg-store-state.ts @@ -1,6 +1,6 @@ export interface IFixMsgStoreState { - readonly length: number, - readonly firstSeq: number, + readonly length: number + readonly firstSeq: number readonly lastSeq: number readonly id: string } diff --git a/src/store/fix-msg-store.ts b/src/store/fix-msg-store.ts index e0383d86..e11bddf7 100644 --- a/src/store/fix-msg-store.ts +++ b/src/store/fix-msg-store.ts @@ -6,12 +6,12 @@ import { IFixMsgStoreRecord } from './fix-msg-store-record' import { IFixMsgStoreState } from './fix-msg-store-state' export interface IFixMsgStore { - clear (): Promise - getState (): Promise - put (record: IFixMsgStoreRecord): Promise - get (seq: number): Promise - exists (seq: number): Promise + clear: () => Promise + getState: () => Promise + put: (record: IFixMsgStoreRecord) => Promise + get: (seq: number) => Promise + exists: (seq: number) => Promise // if to = 0, then to the end of sequence - getSeqNumRange (from: number, to?: number): Promise - getMsgType (msgType: string): Promise + getSeqNumRange: (from: number, to?: number) => Promise + getMsgType: (msgType: string) => Promise } diff --git a/src/test/ascii/ascii-encoder.test.ts b/src/test/ascii/ascii-encoder.test.ts index 4e9bfb69..91056616 100644 --- a/src/test/ascii/ascii-encoder.test.ts +++ b/src/test/ascii/ascii-encoder.test.ts @@ -28,7 +28,7 @@ import { ParsingResult } from '../env/parsing-result' let definitions: FixDefinitions let session: AsciiMsgTransmitter let encoder: AsciiEncoder -let nos: MessageDefinition +let nos: MessageDefinition | null let er: MessageDefinition const localDate: Date = new Date(2018, 6, 25) @@ -48,7 +48,10 @@ beforeAll(async () => { buffer = setup.client.txBuffer encoder = session.encoder as AsciiEncoder nos = definitions.message.get('NewOrderSingle') - er = definitions.message.get('ExecutionReport') + const erd = definitions.message.get('ExecutionReport') + if (erd) { + er = erd + } }, 45000) test('expect a definition ', () => { @@ -56,12 +59,12 @@ test('expect a definition ', () => { }) function toFix (o: ILooseObject, set?: ContainedFieldSet, enc?: AsciiEncoder): string { - const theEncode = enc ? enc : encoder + const theEncode = enc ?? encoder session.buffer.reset() if (set) { theEncode.encode(o, set.name) } else { - theEncode.encode(o, nos.name) + theEncode.encode(o, nos?.name ?? '') } return session.buffer.toString() } @@ -73,14 +76,18 @@ function toFixMessage (o: ILooseObject, msg: MessageDefinition): string { test('encode heartbeat', async () => { const factory = session.config.factory + expect(factory).toBeTruthy() + if (!factory) return const hb = factory.heartbeat('test01') const hbd = definitions.message.get('Heartbeat') + expect(hbd).toBeTruthy() + if (!hbd) return const fix = toFixMessage(hb, hbd) expect(fix).toBeTruthy() const res: ParsingResult = await setup.client.parseText(fix) expect(res.event).toEqual('msg') expect(res.msgType).toEqual('0') - const len = res.view.getTyped(Tags.BodyLengthTag) + const len = res.view?.getTyped(Tags.BodyLengthTag) const expected = fix.length - '8=FIX.4.4|9=0000081|'.length - '10=159|'.length expect(len).toEqual(expected) }) @@ -91,7 +98,8 @@ test('encode custom header PossDupFlag', () => { PossDupFlag: true } } - const fix: string = toFixMessage(no, definitions.message.get('Heartbeat')) + const hb = definitions.message.get('Heartbeat') + const fix: string = hb ? toFixMessage(no, hb) : '' expect(fix).toMatch('43=Y|') }) @@ -101,7 +109,8 @@ test('encode custom header PossDupFlag', () => { MsgSeqNum: 9999 } } - const fix: string = toFixMessage(no, definitions.message.get('Heartbeat')) + const hb = definitions.message.get('Heartbeat') + const fix: string = hb ? toFixMessage(no, hb) : '' expect(fix).toMatch('34=9999|') }) @@ -190,8 +199,9 @@ test('encode UTCTIMESTAMP ExpireTime - check padding', () => { }) test('encode UTCDATEONLY MDEntryDate', () => { - const mdGrp: ComponentFieldDefinition = definitions.component.get('MDFullGrp') + const mdGrp: ComponentFieldDefinition | null = definitions.component.get('MDFullGrp') expect(mdGrp).toBeTruthy() + if (!mdGrp) return const grp: ILooseObject = { NoMDEntries: [ { @@ -205,7 +215,7 @@ test('encode UTCDATEONLY MDEntryDate', () => { }) test('encode UTCTIMEONLY MDEntryTime', () => { - const mdGrp: ComponentFieldDefinition = definitions.component.get('MDFullGrp') + const mdGrp: ComponentFieldDefinition | null = definitions.component.get('MDFullGrp') expect(mdGrp).toBeTruthy() const grp: ILooseObject = { NoMDEntries: [ @@ -215,6 +225,7 @@ test('encode UTCTIMEONLY MDEntryTime', () => { } ] } + if (!mdGrp) return const fix: string = toFix(grp, mdGrp) expect(fix).toEqual('268=1|269=0|273=16:35:00.246|') }) @@ -242,6 +253,7 @@ function getTCR1 (): ITradeCaptureReportRequest { test('encode TradeCaptureReportRequest with TransactTime', () => { const tcr = getTCR1() const d = definitions.message.get('TradeCaptureReportRequest') + if (!d) return const fix: string = toFix(tcr, d) expect(fix).toEqual('568=all-trades|569=0|263=1|580=2|60=20181201-00:00:00.000|60=20181202-00:00:00.000|') }) @@ -308,17 +320,17 @@ test('encode empty RawData EncodedText', () => { function getParties (): ILooseObject { return { - 'Parties': { - 'NoPartyIDs': [ + Parties: { + NoPartyIDs: [ { - 'PartyID': 'magna.', - 'PartyIDSource': '9', - 'PartyRole': 28 + PartyID: 'magna.', + PartyIDSource: '9', + PartyRole: 28 }, { - 'PartyID': 'iaculis', - 'PartyIDSource': 'F', - 'PartyRole': 2 + PartyID: 'iaculis', + PartyIDSource: 'F', + PartyRole: 2 }] } } @@ -326,12 +338,12 @@ function getParties (): ILooseObject { function getPartiesNoPartyID (): ILooseObject { return { - 'Parties': { - 'NoPartyIDs': [ + Parties: { + NoPartyIDs: [ { - // missing PartyID - 'PartyIDSource': '9', - 'PartyRole': 28 + // missing PartyID + PartyIDSource: '9', + PartyRole: 28 } ] } @@ -373,8 +385,8 @@ test('encode repeated group with no PartyID - should encode', () => { test('encode repeated group with no array - should throw', () => { expect(er).toBeTruthy() const e: ILooseObject = { - 'Parties': { - 'NoPartyIDs': 'should be an array' + Parties: { + NoPartyIDs: 'should be an array' } } function run (): void { @@ -386,8 +398,8 @@ test('encode repeated group with no array - should throw', () => { test('encode repeated group with empty array', () => { expect(er).toBeTruthy() const e: ILooseObject = { - 'Parties': { - 'NoPartyIDs': [] + Parties: { + NoPartyIDs: [] } } expect(toFix(e, er)).toEqual('453=0|') @@ -395,36 +407,36 @@ test('encode repeated group with empty array', () => { function getInstrument (): ILooseObject { return { - 'Instrument': { - 'Symbol': 'ac,', - 'SymbolSfx': 'non', - 'SecurityID': 'Pellentesque', - 'SecurityIDSource': 'B', - 'Product': 2 + Instrument: { + Symbol: 'ac,', + SymbolSfx: 'non', + SecurityID: 'Pellentesque', + SecurityIDSource: 'B', + Product: 2 } } } function getInstrumentNestedGroup (): ILooseObject { return { - 'Instrument': { - 'Symbol': 'ac,', - 'SymbolSfx': 'non', - 'SecurityID': 'Pellentesque', - 'SecurityIDSource': 'B', - 'SecAltIDGrp': { - 'NoSecurityAltID': [ + Instrument: { + Symbol: 'ac,', + SymbolSfx: 'non', + SecurityID: 'Pellentesque', + SecurityIDSource: 'B', + SecAltIDGrp: { + NoSecurityAltID: [ { - 'SecurityAltID': 'lorem', - 'SecurityAltIDSource': 'consequat' + SecurityAltID: 'lorem', + SecurityAltIDSource: 'consequat' }, { - 'SecurityAltID': 'sapien', - 'SecurityAltIDSource': 'tempor' + SecurityAltID: 'sapien', + SecurityAltIDSource: 'tempor' } ] }, - 'Product': 2 + Product: 2 } } } @@ -444,30 +456,30 @@ test('encode component nested group', () => { test('encode group missing delimiter', () => { expect(er).toBeTruthy() const e: ILooseObject = getInstrumentNestedGroup() - delete e.Instrument.SecAltIDGrp.NoSecurityAltID[0]['SecurityAltID'] - function run () { + delete e.Instrument.SecAltIDGrp.NoSecurityAltID[0].SecurityAltID + function run (): void { toFix(e, er) } - expect(run).toThrow(/group instance \[1] inconsisent delimeter 455 expected tag 456/) + expect(run).toThrow(/group instance \[1] inconsistent delimiter 455 expected tag 456/) }) test('encode group not an array of', () => { expect(er).toBeTruthy() const e: ILooseObject = { - 'Instrument': { - 'Symbol': 'ac,', - 'SymbolSfx': 'non', - 'SecurityID': 'Pellentesque', - 'SecurityIDSource': 'B', - 'SecAltIDGrp': { - 'NoSecurityAltID': { + Instrument: { + Symbol: 'ac,', + SymbolSfx: 'non', + SecurityID: 'Pellentesque', + SecurityIDSource: 'B', + SecAltIDGrp: { + NoSecurityAltID: { elements: [] } }, - 'Product': 2 + Product: 2 } } - function run () { + function run (): void { toFix(e, er) } expect(run).toThrow(/expected array instance for group NoSecurityAltID/) @@ -515,33 +527,36 @@ function createOrder (id: number, symbol: string, securityType: SecurityType, si } as INewOrderSingle } -test('encode custom header 1 - expect DeliverToCompID DepA', async () => { - const type = SecurityType.CommonStock - const o1 = createOrder(1, 'MS', type, Side.Buy, 100, 1000.0) +async function getNewOrderSingle (o1: INewOrderSingle): Promise { const nosd = definitions.message.get('NewOrderSingle') + expect(nosd).toBeTruthy() + if (!nosd) return const fix = toFixMessage(o1, nosd) expect(fix).toBeTruthy() const res: ParsingResult = await setup.client.parseText(fix) - const tag = res.view.getTyped('DeliverToCompID') + const tag = res.view?.getTyped('DeliverToCompID') + return { res, tag } +} + +test('encode custom header 1 - expect DeliverToCompID DepA', async () => { + const type = SecurityType.CommonStock + const o1 = createOrder(1, 'MS', type, Side.Buy, 100, 1000.0) + const { res, tag } = await getNewOrderSingle(o1) expect(tag).toEqual('DepA') expect(res.event).toEqual('msg') expect(res.msgType).toEqual(MsgType.NewOrderSingle) - const parsed: INewOrderSingle = res.view.toObject() + const parsed: INewOrderSingle = res.view?.toObject() expect(parsed.StandardHeader.DeliverToCompID).toEqual('DepA') }) test('encode custom header 2 - expect DeliverToCompID DepC', async () => { const type = SecurityType.ConvertibleBond const o1 = createOrder(1, 'MSCb', type, Side.Buy, 100, 1000.0) - const nosd = definitions.message.get('NewOrderSingle') - const fix = toFixMessage(o1, nosd) - expect(fix).toBeTruthy() - const res: ParsingResult = await setup.client.parseText(fix) - const tag = res.view.getTyped('DeliverToCompID') + const { res, tag } = await getNewOrderSingle(o1) expect(tag).toEqual('DepC') expect(res.event).toEqual('msg') expect(res.msgType).toEqual(MsgType.NewOrderSingle) - const parsed: INewOrderSingle = res.view.toObject() + const parsed: INewOrderSingle = res.view?.toObject() expect(parsed.StandardHeader.DeliverToCompID).toEqual('DepC') }) @@ -553,12 +568,13 @@ test('encode custom header - include MsgSeqNum (for resends we do not want to ov o1.StandardHeader.PossDupFlag = true const nosd = definitions.message.get('NewOrderSingle') expect(nosd).toBeTruthy() + if (!nosd) return const fix = toFixMessage(o1, nosd) expect(fix).toBeTruthy() const res: ParsingResult = await setup.client.parseText(fix) expect(res.event).toEqual('msg') expect(res.msgType).toEqual(MsgType.NewOrderSingle) - const parsed: INewOrderSingle = res.view.toObject() + const parsed: INewOrderSingle = res.view?.toObject() const h: IStandardHeader = parsed.StandardHeader expect(h.DeliverToCompID).toEqual('DepC') expect(h.MsgSeqNum).toEqual(seqNum) diff --git a/src/test/ascii/ascii-parser.test.ts b/src/test/ascii/ascii-parser.test.ts index 671c1351..9c389ff0 100644 --- a/src/test/ascii/ascii-parser.test.ts +++ b/src/test/ascii/ascii-parser.test.ts @@ -37,7 +37,7 @@ const expectedTagPos = [ new TagPos(20, 554, 196, 11), new TagPos(21, 10, 211, 2)] -let setup: Setup = null +let setup: Setup beforeAll(async () => { expect.assertions(1) setup = new Setup('session/test-initiator-tls.json', 'session/test-acceptor-tls.json') @@ -48,38 +48,38 @@ beforeAll(async () => { jsonHelper = new JsonHelper(definitions) }, 45000) -test('begin string incorrectly placed', () => { - return expect(setup.client.parseText('8=FIX4.4|8=FIX4.4|')).rejects.toEqual( +test('begin string incorrectly placed', async () => { + return await expect(setup.client.parseText('8=FIX4.4|8=FIX4.4|')).rejects.toEqual( new Error('BeginString: not expected at position [2]') ) }) -test('body length incorrectly placed', () => { - return expect(setup.client.parseText('8=FIX4.4|9=101|9=101|')).rejects.toEqual( +test('body length incorrectly placed', async () => { + return await expect(setup.client.parseText('8=FIX4.4|9=101|9=101|')).rejects.toEqual( new Error('BodyLengthTag: not expected at position [3]') ) }) -test('msg type incorrectly placed', () => { - return expect(setup.client.parseText('8=FIX4.4|9=101|35=A|35=A|')).rejects.toEqual( +test('msg type incorrectly placed', async () => { + return await expect(setup.client.parseText('8=FIX4.4|9=101|35=A|35=A|')).rejects.toEqual( new Error('MsgTag: not expected at position [4]') ) }) -test('do not start with 8=', () => { - return expect(setup.client.parseText('59=FIX4.4|')).rejects.toEqual( +test('do not start with 8=', async () => { + return await expect(setup.client.parseText('59=FIX4.4|')).rejects.toEqual( new Error('position 1 [59] must be BeginString: 8=') ) }) -test('body length incorrectly placed', () => { - return expect(setup.client.parseText('8=FIX4.4|59=101|9=101|')).rejects.toEqual( +test('body length incorrectly placed', async () => { + return await expect(setup.client.parseText('8=FIX4.4|59=101|9=101|')).rejects.toEqual( new Error('position 2 [59] must be BodyLengthTag: 9=') ) }) -test('msgTag incorrectly placed', () => { - return expect(setup.client.parseText('8=FIX4.4|9=101|59=A|')).rejects.toEqual( +test('msgTag incorrectly placed', async () => { + return await expect(setup.client.parseText('8=FIX4.4|9=101|59=A|')).rejects.toEqual( new Error('position 3 [59] must be MsgTag: 35=') ) }) @@ -110,13 +110,13 @@ test('msg sent in chunks matches parser buffer', async () => { test('logon parsers to correct tag set', async () => { const res: ParsingResult = await setup.client.parseText(logon, true) expect(res.msgType).toEqual(MsgType.Logon) - expect(res.view.structure.tags.tagPos).toEqual(expectedTagPos) + expect(res?.view?.structure?.tags.tagPos).toEqual(expectedTagPos) }) test('tags other than 10 past body length', async () => { const begin = '8=FIX4.4|9=0000208|' - const changed = logon.replace('10=49|','555=you know nothin|10=49') - return expect(setup.client.parseText(changed)).rejects.toEqual( + const changed = logon.replace('10=49|', '555=you know nothin|10=49') + return await expect(setup.client.parseText(changed)).rejects.toEqual( new Error(`Tag: [555] cant be after ${208 + begin.length - 1}`) ) }) @@ -125,23 +125,22 @@ test('unknown message type', async () => { const changed = logon.replace('35=A', '35=ZZ') const res = await setup.client.parseText(changed) expect(res.view).toBeTruthy() - expect(res.view.segment.type).toEqual(SegmentType.Unknown) + expect(res?.view?.segment.type).toEqual(SegmentType.Unknown) }) test('missing 1 required tag', async () => { - const changed = logon.replace('108=62441|','000=62441|') + const changed = logon.replace('108=62441|', '000=62441|') const res = await setup.client.parseText(changed) expect(res.view).toBeTruthy() - const missing = res.view.missing() + const missing = res?.view?.missing() expect(missing).toEqual([108]) }) test('missing 2 required tags', async () => { // const changed = logon.replace('108=62441|','000=62441|') - const changed = logon.replace('98=2|108=62441|','01=2|000=62441|') + const changed = logon.replace('98=2|108=62441|', '01=2|000=62441|') const res = await setup.client.parseText(changed) expect(res.view).toBeTruthy() - const missing = res.view.missing() + const missing = res?.view?.missing() expect(missing).toEqual([98, 108]) }) - diff --git a/src/test/ascii/ascii-segment.test.ts b/src/test/ascii/ascii-segment.test.ts index 2d667c32..4c45ee79 100644 --- a/src/test/ascii/ascii-segment.test.ts +++ b/src/test/ascii/ascii-segment.test.ts @@ -13,7 +13,7 @@ let jsonHelper: JsonHelper const logon: string = '8=FIX4.4|9=0000208|35=A|49=sender-10|56=target-20|34=1|57=sub-a|52=20180610-10:39:01.621|98=2|108=62441|95=20|96=VgfoSqo56NqSVI1fLdlI|141=Y|789=4886|383=20|384=1|372=ipsum|385=R|464=N|553=sit|554=consectetur|10=49|' let config: IJsFixConfig -let setup: Setup = null +let setup: Setup beforeAll(async () => { setup = new Setup() await setup.init() @@ -26,9 +26,9 @@ test('0 gaps', async () => { const res: ParsingResult = await setup.client.parseText(logon) expect(res.event).toEqual('msg') expect(res.msgType).toEqual(MsgType.Logon) - const unknowns: SegmentDescription[] = res.view.structure.layout['.undefined'] + const unknowns: SegmentDescription[] = res.view?.structure?.layout['.undefined'] expect(unknowns).toBeFalsy() - const o: ILogon = res.view.toObject() + const o: ILogon = res?.view?.toObject() expect(o).toBeTruthy() expect(o.Password).toEqual('consectetur') expect(o.Username).toEqual('sit') @@ -39,11 +39,11 @@ test('1 gap', async () => { const res: ParsingResult = await setup.client.parseText(gap) expect(res.event).toEqual('msg') expect(res.msgType).toEqual(MsgType.Logon) - const unknown: SegmentDescription = res.view.structure.layout['.undefined'] + const unknown: SegmentDescription = res?.view?.structure?.layout['.undefined'] expect(unknown).toBeTruthy() expect(unknown.startTag).toEqual(9999) expect(unknown.startPosition).toEqual(10) - const o: ILogon = res.view.toObject() + const o: ILogon = res?.view?.toObject() expect(o).toBeTruthy() expect(o.Password).toEqual('consectetur') expect(o.Username).toEqual('sit') @@ -54,14 +54,14 @@ test('1 gap next to 1 gap', async () => { const res: ParsingResult = await setup.client.parseText(gap) expect(res.event).toEqual('msg') expect(res.msgType).toEqual(MsgType.Logon) - const unknowns: SegmentDescription[] = res.view.structure.layout['.undefined'] + const unknowns: SegmentDescription[] = res?.view?.structure?.layout['.undefined'] expect(unknowns).toBeTruthy() expect(Array.isArray(unknowns)).toEqual(true) expect(unknowns[0].startTag).toEqual(1) expect(unknowns[0].startPosition).toEqual(10) expect(unknowns[1].startTag).toEqual(2) expect(unknowns[1].startPosition).toEqual(11) - const o: ILogon = res.view.toObject() + const o: ILogon = res?.view?.toObject() expect(o).toBeTruthy() expect(o.Password).toEqual('consectetur') expect(o.Username).toEqual('sit') @@ -72,8 +72,8 @@ test('1 gap undefined msg', async () => { const res: ParsingResult = await setup.client.parseText(gap) expect(res.event).toEqual('msg') expect(res.msgType).toEqual(MsgType.Logon) - expect(res.view.getUndefined()).toBeTruthy() - expect(res.view.undefinedForMsg()).toEqual('undefined tag = 9999') + expect(res?.view?.getUndefined()).toBeTruthy() + expect(res?.view?.undefinedForMsg()).toEqual('undefined tag = 9999') }) test('2 gap undefined msg', async () => { @@ -81,6 +81,6 @@ test('2 gap undefined msg', async () => { const res: ParsingResult = await setup.client.parseText(gap) expect(res.event).toEqual('msg') expect(res.msgType).toEqual(MsgType.Logon) - expect(res.view.getUndefined()).toBeTruthy() - expect(res.view.undefinedForMsg()).toEqual('undefined tags = 1, 2') + expect(res?.view?.getUndefined()).toBeTruthy() + expect(res?.view?.undefinedForMsg()).toEqual('undefined tags = 1, 2') }) diff --git a/src/test/ascii/ascii-store-replay.test.ts b/src/test/ascii/ascii-store-replay.test.ts index 344d0bbb..0bcbea01 100644 --- a/src/test/ascii/ascii-store-replay.test.ts +++ b/src/test/ascii/ascii-store-replay.test.ts @@ -18,7 +18,7 @@ let definitions: FixDefinitions let server: TestRecovery let client: TestRecovery -let setup: Setup = null +let setup: Setup beforeAll(async () => { setup = new Setup( @@ -61,12 +61,12 @@ server: (application only) 8=FIX4.4|9=000206|35=AE|49=accept-tls-comp|56=init-tls-comp|34=10|57=fix|52=20210307-16:17:14.477|571=100006|487=0|856=0|828=0|17=600006|39=2|570=N|55=Silver|48=Silver.INC|32=105|31=61.2|75=20210307|60=20210307-16:17:14.477|10=191| */ -test('server store states',async () => { +test('server store states', async () => { const s1 = await server.recovery.store.getState() expect(s1.length).toEqual(9) }) -test('client store states',async () => { +test('client store states', async () => { const s1 = await client.recovery.store.getState() expect(s1.length).toEqual(1) }) @@ -110,7 +110,7 @@ test('client replay request from seq=1 to seq=10', async () => { checkSeqReset(vec[2], 3, 11) }) -function checkSeqReset (rec: IFixMsgStoreRecord, from: number, to: number) { +function checkSeqReset (rec: IFixMsgStoreRecord, from: number, to: number): void { const reset: ISequenceReset = rec.obj as ISequenceReset expect(rec.msgType).toEqual(MsgType.SequenceReset) expect(rec.obj).toBeTruthy() diff --git a/src/test/ascii/ascii-tag-pos.test.ts b/src/test/ascii/ascii-tag-pos.test.ts index fa3c4dd7..e161b4c2 100644 --- a/src/test/ascii/ascii-tag-pos.test.ts +++ b/src/test/ascii/ascii-tag-pos.test.ts @@ -11,8 +11,8 @@ const root: string = path.join(__dirname, '../../../data') let definitions: FixDefinitions let session: AsciiMsgTransmitter let views: MsgView[] -let structure: Structure -let tp: TagPos[] +let structure: Structure | null +let tp: TagPos[] | undefined const testTags: TagPos[] = [ new TagPos(0, 120, 0, 1), // 3 @@ -54,7 +54,7 @@ const unsortedLogon = [ new TagPos(20, 554, 196, 11) ] -let setup: Setup = null +let setup: Setup beforeAll(async () => { setup = new Setup('session/test-initiator.json', null) await setup.init() @@ -63,7 +63,7 @@ beforeAll(async () => { views = await setup.client.replayer.replayFixFile(path.join(root, 'examples/FIX.4.4/quickfix/logon/fix.txt')) if (views && views.length > 0) { structure = views[0].structure - tp = views[0].structure.tags.tagPos.slice(0, views[0].segment.endPosition) + tp = views[0].structure?.tags.tagPos.slice(0, views[0].segment.endPosition) } }, 45000) @@ -104,7 +104,9 @@ test('binary search duplicate tag', () => { }) test('check logon', () => { - const sorted = tp.slice().sort(TagPos.compare) + const sorted = tp?.slice().sort(TagPos.compare) + expect(sorted).toBeTruthy() + if (!sorted) return expect(sorted[0].tag).toEqual(8) expect(sorted[sorted.length - 1].tag).toEqual(789) }) diff --git a/src/test/ascii/execution-report.test.ts b/src/test/ascii/execution-report.test.ts index 61abd814..d7cbc4ac 100644 --- a/src/test/ascii/execution-report.test.ts +++ b/src/test/ascii/execution-report.test.ts @@ -4,7 +4,7 @@ import * as path from 'path' import { SegmentDescription, MsgView, Structure } from '../../buffer' import { ILooseObject } from '../../collections/collection' import { FixDefinitions } from '../../dictionary/definition' -import { IUndInstrmtGrp, IUnderlyingInstrument } from '../../types/FIX4.4/quickfix' +import { IUndInstrmtGrp, IUnderlyingInstrument, IUndInstrmtGrpNoUnderlyings } from '../../types/FIX4.4/quickfix' import { SegmentType } from '../../buffer/segment/segment-type' import { Setup } from '../env/setup' @@ -12,9 +12,9 @@ const root: string = path.join(__dirname, '../../../data') let definitions: FixDefinitions let views: MsgView[] -let structure: Structure +let structure: Structure | undefined -let setup: Setup = null +let setup: Setup beforeAll(async () => { setup = new Setup('session/qf-fix44.json', null) await setup.init() @@ -23,7 +23,7 @@ beforeAll(async () => { views = await setup.client.replayer.replayFixFile(path.join(root, 'examples/FIX.4.4/quickfix/execution-report/fix.txt')) if (views && views.length > 0) { - structure = views[0].structure + structure = views[0].structure ?? undefined } }, 45000) @@ -48,8 +48,8 @@ test('expect a structure from fix msg', () => { */ test('Parties structure', () => { - const parties: SegmentDescription = structure.layout.Parties - const noPartyIDs: SegmentDescription = structure.layout.NoPartyIDs + const parties: SegmentDescription = structure?.layout.Parties + const noPartyIDs: SegmentDescription = structure?.layout.NoPartyIDs expect(parties).toBeTruthy() expect(parties.startPosition === 20) expect(parties.endPosition === 44) @@ -62,7 +62,7 @@ test('Parties structure', () => { }) test('Parties PartySubIDType sub-structure', () => { - const ptysSubGrp: SegmentDescription[] = structure.layout.PtysSubGrp + const ptysSubGrp: SegmentDescription[] = structure?.layout.PtysSubGrp expect(ptysSubGrp).toBeTruthy() expect(Array.isArray(ptysSubGrp)).toEqual(true) expect(ptysSubGrp.length).toEqual(3) @@ -73,7 +73,7 @@ test('Parties PartySubIDType sub-structure', () => { expect(ptysSubGrp[2].startPosition).toEqual(42) expect(ptysSubGrp[2].type).toEqual(SegmentType.Component) - const noPartySubIDs: SegmentDescription[] = structure.layout.NoPartySubIDs + const noPartySubIDs: SegmentDescription[] = structure?.layout.NoPartySubIDs expect(noPartySubIDs).toBeTruthy() expect(Array.isArray(noPartySubIDs)).toEqual(true) expect(noPartySubIDs.length).toEqual(3) @@ -106,20 +106,19 @@ test('Parties PartySubIDType sub-structure', () => { */ test('ContraGrp structure', () => { - const contraGrp: SegmentDescription = structure.layout.ContraGrp + const contraGrp: SegmentDescription = structure?.layout.ContraGrp expect(contraGrp).toBeTruthy() expect(contraGrp.depth).toEqual(1) expect(contraGrp.type).toEqual(SegmentType.Component) expect(contraGrp.startPosition).toEqual(46) expect(contraGrp.endPosition).toEqual(61) - const noContraBrokers: SegmentDescription = structure.layout.NoContraBrokers + const noContraBrokers: SegmentDescription = structure?.layout.NoContraBrokers expect(noContraBrokers).toBeTruthy() expect(noContraBrokers.depth).toEqual(2) expect(noContraBrokers.type).toEqual(SegmentType.Group) expect(noContraBrokers.delimiterTag).toEqual(375) expect(noContraBrokers.delimiterPositions).toEqual([47, 52, 57]) - }) /* @@ -152,7 +151,7 @@ test('ContraGrp structure', () => { */ test('Instrument structure', () => { - const instrument: SegmentDescription = structure.layout.Instrument + const instrument: SegmentDescription = structure?.layout.Instrument expect(instrument).toBeTruthy() expect(instrument.type).toEqual(SegmentType.Component) expect(instrument.startPosition).toEqual(83) @@ -160,7 +159,7 @@ test('Instrument structure', () => { expect(instrument.endPosition).toEqual(133) expect(instrument.endTag).toEqual(874) - const noSecurityAltID: SegmentDescription = structure.layout.NoSecurityAltID + const noSecurityAltID: SegmentDescription = structure?.layout.NoSecurityAltID expect(noSecurityAltID).toBeTruthy() expect(noSecurityAltID.type).toEqual(SegmentType.Group) expect(noSecurityAltID.delimiterTag).toEqual(455) @@ -176,7 +175,7 @@ test('Instrument structure', () => { */ test('FinancingDetails structure', () => { - const financingDetails: SegmentDescription = structure.layout.FinancingDetails + const financingDetails: SegmentDescription = structure?.layout.FinancingDetails expect(financingDetails).toBeTruthy() expect(financingDetails.depth).toEqual(1) expect(financingDetails.type).toEqual(SegmentType.Component) @@ -193,7 +192,7 @@ test('FinancingDetails structure', () => { */ test('PegInstructions structure', () => { - const pegInstructions: SegmentDescription = structure.layout.PegInstructions + const pegInstructions: SegmentDescription = structure?.layout.PegInstructions expect(pegInstructions).toBeTruthy() expect(pegInstructions.depth).toEqual(1) expect(pegInstructions.type).toEqual(SegmentType.Component) @@ -211,7 +210,7 @@ test('PegInstructions structure', () => { */ test('DiscretionInstructions structure', () => { - const discretionInstructions: SegmentDescription = structure.layout.DiscretionInstructions + const discretionInstructions: SegmentDescription = structure?.layout.DiscretionInstructions expect(discretionInstructions).toBeTruthy() expect(discretionInstructions.depth).toEqual(1) expect(discretionInstructions.type).toEqual(SegmentType.Component) @@ -226,7 +225,7 @@ test('DiscretionInstructions structure', () => { [326] 479 (CommCurrency) = 25841, [327] 497 (FundRenewWaiv) = N[NO] */ test('CommissionData structure', () => { - const commisionData: SegmentDescription = structure.layout.CommissionData + const commisionData: SegmentDescription = structure?.layout.CommissionData expect(commisionData).toBeTruthy() expect(commisionData.depth).toEqual(1) expect(commisionData.type).toEqual(SegmentType.Component) @@ -244,7 +243,7 @@ test('CommissionData structure', () => { */ test('SpreadOrBenchmarkCurveData structure', () => { - const spreadBenchData: SegmentDescription = structure.layout.SpreadOrBenchmarkCurveData + const spreadBenchData: SegmentDescription = structure?.layout.SpreadOrBenchmarkCurveData expect(spreadBenchData).toBeTruthy() expect(spreadBenchData.depth).toEqual(1) expect(spreadBenchData.type).toEqual(SegmentType.Component) @@ -261,7 +260,7 @@ test('SpreadOrBenchmarkCurveData structure', () => { */ test('YieldData structure', () => { - const yieldData: SegmentDescription = structure.layout.YieldData + const yieldData: SegmentDescription = structure?.layout.YieldData expect(yieldData).toBeTruthy() expect(yieldData.depth).toEqual(1) expect(yieldData.type).toEqual(SegmentType.Component) @@ -281,7 +280,7 @@ test('YieldData structure', () => { */ test('ContAmtGrp structure', () => { - const contAmtGrp: SegmentDescription = structure.layout.ContAmtGrp + const contAmtGrp: SegmentDescription = structure?.layout.ContAmtGrp expect(contAmtGrp).toBeTruthy() expect(contAmtGrp.depth).toEqual(1) expect(contAmtGrp.type).toEqual(SegmentType.Component) @@ -290,7 +289,7 @@ test('ContAmtGrp structure', () => { expect(contAmtGrp.endPosition).toEqual(394) expect(contAmtGrp.endTag).toEqual(521) - const noContAmts: SegmentDescription = structure.layout.NoContAmts + const noContAmts: SegmentDescription = structure?.layout.NoContAmts expect(noContAmts).toBeTruthy() expect(noContAmts.depth).toEqual(2) expect(noContAmts.type).toEqual(SegmentType.Group) @@ -311,7 +310,7 @@ test('ContAmtGrp structure', () => { */ test('MiscFeesGrp structure', () => { - const miscFees: SegmentDescription = structure.layout.MiscFeesGrp + const miscFees: SegmentDescription = structure?.layout.MiscFeesGrp expect(miscFees).toBeTruthy() expect(miscFees.depth).toEqual(1) expect(miscFees.type).toEqual(SegmentType.Component) @@ -320,7 +319,7 @@ test('MiscFeesGrp structure', () => { expect(miscFees.endPosition).toEqual(644) expect(miscFees.endTag).toEqual(891) - const noMiscfees: SegmentDescription = structure.layout.NoMiscFees + const noMiscfees: SegmentDescription = structure?.layout.NoMiscFees expect(noMiscfees).toBeTruthy() expect(noMiscfees.depth).toEqual(2) expect(noMiscfees.type).toEqual(SegmentType.Group) @@ -394,7 +393,7 @@ test('MiscFeesGrp structure', () => { */ test('UndInstrmtGrp structure', () => { - const undInstrmtGrp: SegmentDescription = structure.layout.UndInstrmtGrp + const undInstrmtGrp: SegmentDescription = structure?.layout.UndInstrmtGrp expect(undInstrmtGrp).toBeTruthy() expect(undInstrmtGrp.type).toEqual(SegmentType.Component) expect(undInstrmtGrp.startPosition).toEqual(143) @@ -403,14 +402,14 @@ test('UndInstrmtGrp structure', () => { expect(undInstrmtGrp.endTag).toEqual(889) expect(undInstrmtGrp.depth).toEqual(1) - const noUnderlyings: SegmentDescription = structure.layout.NoUnderlyings + const noUnderlyings: SegmentDescription = structure?.layout.NoUnderlyings expect(noUnderlyings).toBeTruthy() expect(noUnderlyings.delimiterTag).toEqual(311) expect(noUnderlyings.delimiterPositions).toEqual([144, 203]) expect(noUnderlyings.depth).toEqual(2) expect(noUnderlyings.type).toEqual(SegmentType.Group) - const underlyingInstrument: SegmentDescription[] = structure.layout.UnderlyingInstrument + const underlyingInstrument: SegmentDescription[] = structure?.layout.UnderlyingInstrument expect(underlyingInstrument).toBeTruthy() expect(underlyingInstrument.length).toEqual(2) expect(underlyingInstrument).toBeTruthy() @@ -429,7 +428,7 @@ test('UndInstrmtGrp structure', () => { expect(underlyingInstrument[1].depth).toEqual(3) expect(underlyingInstrument[1].type).toEqual(SegmentType.Component) - const undSecAltIDGrp: SegmentDescription[] = structure.layout.UndSecAltIDGrp + const undSecAltIDGrp: SegmentDescription[] = structure?.layout.UndSecAltIDGrp expect(undSecAltIDGrp.length).toEqual(2) expect(undSecAltIDGrp[0].startPosition).toEqual(148) expect(undSecAltIDGrp[0].startTag).toEqual(457) @@ -445,7 +444,7 @@ test('UndInstrmtGrp structure', () => { expect(undSecAltIDGrp[1].depth).toEqual(4) expect(undSecAltIDGrp[1].type).toEqual(SegmentType.Component) - const noUnderlyingSecurityAltID: SegmentDescription[] = structure.layout.NoUnderlyingSecurityAltID + const noUnderlyingSecurityAltID: SegmentDescription[] = structure?.layout.NoUnderlyingSecurityAltID expect(noUnderlyingSecurityAltID.length).toEqual(2) expect(noUnderlyingSecurityAltID).toBeTruthy() @@ -463,9 +462,9 @@ test('UndInstrmtGrp structure', () => { expect(noUnderlyingSecurityAltID[1].endPosition).toEqual(209) expect(noUnderlyingSecurityAltID[1].delimiterPositions).toEqual([208]) - const boundNoUnderlyingSecurityAltID: SegmentDescription = structure.firstContainedWithin( - 'NoUnderlyingSecurityAltID', - underlyingInstrument[1]) + const boundNoUnderlyingSecurityAltID: SegmentDescription | null = structure?.firstContainedWithin( + 'NoUnderlyingSecurityAltID', + underlyingInstrument[1]) ?? null expect(boundNoUnderlyingSecurityAltID).toBeTruthy() }) @@ -594,7 +593,7 @@ test('UndInstrmtGrp structure', () => { */ test('InstrmtLegExecGrp structure', () => { - const instrmtLegExecGrp: SegmentDescription = structure.layout.InstrmtLegExecGrp + const instrmtLegExecGrp: SegmentDescription = structure?.layout.InstrmtLegExecGrp expect(instrmtLegExecGrp).toBeTruthy() expect(instrmtLegExecGrp.type).toEqual(SegmentType.Component) expect(instrmtLegExecGrp.startPosition).toEqual(395) @@ -603,7 +602,7 @@ test('InstrmtLegExecGrp structure', () => { expect(instrmtLegExecGrp.endPosition).toEqual(634) expect(instrmtLegExecGrp.depth).toEqual(1) - const noLegs: SegmentDescription = structure.layout.NoLegs + const noLegs: SegmentDescription = structure?.layout.NoLegs expect(noLegs).toBeTruthy() expect(noLegs.type).toEqual(SegmentType.Group) expect(noLegs.startPosition).toEqual(395) @@ -616,7 +615,7 @@ test('InstrmtLegExecGrp structure', () => { }) test('instrumentLeg structure', () => { - const instrumentLeg: SegmentDescription[] = structure.layout.InstrumentLeg + const instrumentLeg: SegmentDescription[] = structure?.layout.InstrumentLeg expect(instrumentLeg).toBeTruthy() expect(Array.isArray(instrumentLeg)).toEqual(true) expect(instrumentLeg.length).toEqual(3) @@ -643,8 +642,20 @@ test('instrumentLeg structure', () => { expect(instrumentLeg[2].endTag).toEqual(956) }) +function getnoLegSecurityAltID (index: number): SegmentDescription[] { + const noLegSecurityAltID: SegmentDescription[] = structure?.layout.NoLegSecurityAltID + expect(noLegSecurityAltID).toBeTruthy() + expect(Array.isArray(noLegSecurityAltID)).toEqual(true) + expect(noLegSecurityAltID.length).toEqual(3) + + expect(noLegSecurityAltID[index].type).toEqual(SegmentType.Group) + expect(noLegSecurityAltID[index].depth).toEqual(5) + expect(noLegSecurityAltID[index].startTag).toEqual(604) + return noLegSecurityAltID +} + test('LegSecAltIDGrp [0] structure', () => { - const legSecAltIDGrp: SegmentDescription[] = structure.layout.LegSecAltIDGrp + const legSecAltIDGrp: SegmentDescription[] = structure?.layout.LegSecAltIDGrp expect(legSecAltIDGrp).toBeTruthy() expect(Array.isArray(legSecAltIDGrp)).toEqual(true) expect(legSecAltIDGrp.length).toEqual(3) @@ -656,14 +667,7 @@ test('LegSecAltIDGrp [0] structure', () => { expect(legSecAltIDGrp[index].endPosition).toEqual(406) expect(legSecAltIDGrp[index].endTag).toEqual(606) - const noLegSecurityAltID: SegmentDescription[] = structure.layout.NoLegSecurityAltID - expect(noLegSecurityAltID).toBeTruthy() - expect(Array.isArray(noLegSecurityAltID)).toEqual(true) - expect(noLegSecurityAltID.length).toEqual(3) - - expect(noLegSecurityAltID[index].type).toEqual(SegmentType.Group) - expect(noLegSecurityAltID[index].depth).toEqual(5) - expect(noLegSecurityAltID[index].startTag).toEqual(604) + const noLegSecurityAltID: SegmentDescription[] = getnoLegSecurityAltID(index) expect(noLegSecurityAltID[index].startPosition).toEqual(400) expect(noLegSecurityAltID[index].endPosition).toEqual(406) expect(noLegSecurityAltID[index].endTag).toEqual(606) @@ -672,7 +676,7 @@ test('LegSecAltIDGrp [0] structure', () => { }) test('LegSecAltIDGrp [1] structure', () => { - const legSecAltIDGrp: SegmentDescription[] = structure.layout.LegSecAltIDGrp + const legSecAltIDGrp: SegmentDescription[] = structure?.layout.LegSecAltIDGrp expect(legSecAltIDGrp).toBeTruthy() expect(Array.isArray(legSecAltIDGrp)).toEqual(true) expect(legSecAltIDGrp.length).toEqual(3) @@ -684,14 +688,7 @@ test('LegSecAltIDGrp [1] structure', () => { expect(legSecAltIDGrp[index].endPosition).toEqual(475) expect(legSecAltIDGrp[index].endTag).toEqual(606) - const noLegSecurityAltID: SegmentDescription[] = structure.layout.NoLegSecurityAltID - expect(noLegSecurityAltID).toBeTruthy() - expect(Array.isArray(noLegSecurityAltID)).toEqual(true) - expect(noLegSecurityAltID.length).toEqual(3) - - expect(noLegSecurityAltID[index].type).toEqual(SegmentType.Group) - expect(noLegSecurityAltID[index].depth).toEqual(5) - expect(noLegSecurityAltID[index].startTag).toEqual(604) + const noLegSecurityAltID: SegmentDescription[] = getnoLegSecurityAltID(index) expect(noLegSecurityAltID[index].startPosition).toEqual(469) expect(noLegSecurityAltID[index].endPosition).toEqual(475) expect(noLegSecurityAltID[index].endTag).toEqual(606) @@ -700,7 +697,7 @@ test('LegSecAltIDGrp [1] structure', () => { }) test('LegSecAltIDGrp [2] structure', () => { - const legSecAltIDGrp: SegmentDescription[] = structure.layout.LegSecAltIDGrp + const legSecAltIDGrp: SegmentDescription[] = structure?.layout.LegSecAltIDGrp expect(legSecAltIDGrp).toBeTruthy() expect(Array.isArray(legSecAltIDGrp)).toEqual(true) expect(legSecAltIDGrp.length).toEqual(3) @@ -712,14 +709,7 @@ test('LegSecAltIDGrp [2] structure', () => { expect(legSecAltIDGrp[index].endPosition).toEqual(564) expect(legSecAltIDGrp[index].endTag).toEqual(606) - const noLegSecurityAltID: SegmentDescription[] = structure.layout.NoLegSecurityAltID - expect(noLegSecurityAltID).toBeTruthy() - expect(Array.isArray(noLegSecurityAltID)).toEqual(true) - expect(noLegSecurityAltID.length).toEqual(3) - - expect(noLegSecurityAltID[index].type).toEqual(SegmentType.Group) - expect(noLegSecurityAltID[index].depth).toEqual(5) - expect(noLegSecurityAltID[index].startTag).toEqual(604) + const noLegSecurityAltID: SegmentDescription[] = getnoLegSecurityAltID(index) expect(noLegSecurityAltID[index].startPosition).toEqual(558) expect(noLegSecurityAltID[index].endPosition).toEqual(564) expect(noLegSecurityAltID[index].endTag).toEqual(606) @@ -754,9 +744,9 @@ test('simple repeated tag decoding', () => { test('repeated group decoding of Parties', () => { const erView: MsgView = views[0] - const partyView: MsgView = erView.getView('Parties') + const partyView: MsgView | null = erView.getView('Parties') expect(partyView).toBeTruthy() - const partyViewAsObject: ILooseObject = partyView.toObject() + const partyViewAsObject: ILooseObject = partyView?.toObject() expect(partyViewAsObject).toBeTruthy() expect(partyViewAsObject.NoPartyIDs.length).toEqual(3) expect(partyViewAsObject.NoPartyIDs[0]).toEqual({ @@ -776,42 +766,48 @@ test('repeated group decoding of Parties', () => { ] } }) - const noParties: MsgView = partyView.getView('NoPartyIDs') + const noParties: MsgView | null = partyView?.getView('NoPartyIDs') ?? null expect(noParties).toBeTruthy() - expect(noParties.groupCount()).toEqual(3) - const np0View: MsgView = noParties.getGroupInstance(0) + expect(noParties?.groupCount()).toEqual(3) + const np0View: MsgView | null = noParties?.getGroupInstance(0) ?? null expect(np0View).toBeTruthy() - expect(np0View.getString('PartyID')).toEqual('magna.') - expect(np0View.getString('PartyIDSource')).toEqual('9') - const np0ViewPtysSubGrp: MsgView = np0View.getView('PtysSubGrp') - const np0ViewPtysSubGrpAsObject: ILooseObject = np0ViewPtysSubGrp.toObject() + expect(np0View?.getString('PartyID')).toEqual('magna.') + expect(np0View?.getString('PartyIDSource')).toEqual('9') + const np0ViewPtysSubGrp: MsgView | null = np0View?.getView('PtysSubGrp') ?? null + const np0ViewPtysSubGrpAsObject: ILooseObject = np0ViewPtysSubGrp?.toObject() expect(np0ViewPtysSubGrpAsObject).toBeTruthy() expect(np0ViewPtysSubGrpAsObject).toEqual(partyViewAsObject.NoPartyIDs[0].PtysSubGrp) }, 1000) test('instrument component decode', () => { const erView: MsgView = views[0] - // check the instrument component - const instrumentView: MsgView = erView.getView('Instrument') + // check the instrument component + const instrumentView: MsgView | null = erView.getView('Instrument') expect(instrumentView).toBeTruthy() - expect(instrumentView.getString('Symbol')).toEqual('ac,') - const secAltIDGrpAsObject: ILooseObject = instrumentView.getView('SecAltIDGrp').toObject() + expect(instrumentView?.getString('Symbol')).toEqual('ac,') + const secAltIDGrpAsObject: ILooseObject | null = instrumentView?.getView('SecAltIDGrp')?.toObject() ?? null expect(secAltIDGrpAsObject).toBeTruthy() - expect(secAltIDGrpAsObject.NoSecurityAltID.length).toEqual(2) + expect(secAltIDGrpAsObject?.NoSecurityAltID.length).toEqual(2) }, 1000) test('UndInstrmtGrp component decode', () => { const erView: MsgView = views[0] - // check the instrument component - const undInstrmtGrpView: MsgView = erView.getView('UndInstrmtGrp') + // check the instrument component + const undInstrmtGrpView: MsgView | null = erView.getView('UndInstrmtGrp') expect(undInstrmtGrpView).toBeTruthy() - const undInstrmtGrpViewAsObject: IUndInstrmtGrp = undInstrmtGrpView.toObject() + const undInstrmtGrpViewAsObject: IUndInstrmtGrp | null = undInstrmtGrpView?.toObject() expect(undInstrmtGrpViewAsObject).toBeTruthy() - expect(undInstrmtGrpViewAsObject.NoUnderlyings.length).toEqual(2) - const underlying0: IUnderlyingInstrument = undInstrmtGrpViewAsObject.NoUnderlyings[0].UnderlyingInstrument + expect(undInstrmtGrpViewAsObject?.NoUnderlyings?.length).toEqual(2) + const underlyings: IUndInstrmtGrpNoUnderlyings[] | null = undInstrmtGrpViewAsObject?.NoUnderlyings ?? null + expect(underlyings).toBeTruthy() + if (!underlyings) return + const u0: IUndInstrmtGrpNoUnderlyings | null = underlyings ? underlyings[0] : null + const underlying0: IUnderlyingInstrument | null = u0 + ? u0.UnderlyingInstrument ?? null + : null expect(underlying0).toBeTruthy() - expect(underlying0.UnderlyingSymbol).toEqual('massa.') - expect(underlying0.UndSecAltIDGrp).toEqual( + expect(underlying0?.UnderlyingSymbol).toEqual('massa.') + expect(underlying0?.UndSecAltIDGrp).toEqual( { NoUnderlyingSecurityAltID: [ { @@ -827,10 +823,13 @@ test('UndInstrmtGrp component decode', () => { UnderlyingSecurityAltIDSource: 'Pellentesque' }] }) - const underlying1: ILooseObject = undInstrmtGrpViewAsObject.NoUnderlyings[1].UnderlyingInstrument + const underlyings2: IUndInstrmtGrpNoUnderlyings[] | null = undInstrmtGrpViewAsObject?.NoUnderlyings ?? null + expect(underlyings2).toBeTruthy() + if (!underlyings2) return + const underlying1: ILooseObject | null = underlyings2[1].UnderlyingInstrument ?? null expect(underlying1).toBeTruthy() - expect(underlying1.UnderlyingSymbol).toEqual('erat') - expect(underlying1.UndSecAltIDGrp).toEqual( + expect(underlying1?.UnderlyingSymbol).toEqual('erat') + expect(underlying1?.UndSecAltIDGrp).toEqual( { NoUnderlyingSecurityAltID: [ { diff --git a/src/test/ascii/fix-log-replay.test.ts b/src/test/ascii/fix-log-replay.test.ts index 30bad61d..92c98cca 100644 --- a/src/test/ascii/fix-log-replay.test.ts +++ b/src/test/ascii/fix-log-replay.test.ts @@ -11,10 +11,10 @@ const root: string = path.join(__dirname, '../../../data') let definitions: FixDefinitions let views: MsgView[] let expected: ILooseObject -let setup: Setup = null +let setup: Setup beforeAll(async () => { - setup = new Setup('session/test-initiator.json',null) + setup = new Setup('session/test-initiator.json', null) await setup.init() definitions = setup.client.config.definitions expected = require(path.join(root, 'examples/FIX.4.4/fix.json')) @@ -26,8 +26,8 @@ test('expect 50 messages in log', () => { }) test('expect 50 messages of specific types in log', () => { - const layout = views.reduce((a: ILooseObject, latest: MsgView) => { - const def: MessageDefinition = definitions.message.get(latest.segment.name) + const layout = views.reduce((a: ILooseObject, latest: MsgView) => { + const def: MessageDefinition | null = definitions.message.get(latest.segment.name) if (def) { let lookup = a[def.msgType] if (!lookup) { @@ -38,6 +38,6 @@ test('expect 50 messages of specific types in log', () => { a[def.msgType] = lookup } return a - }, {} as ILooseObject) + }, {}) expect(layout).toEqual(expected) }) diff --git a/src/test/ascii/fix-repo-dict.test.ts b/src/test/ascii/fix-repo-dict.test.ts index bb8f26aa..77a6a977 100644 --- a/src/test/ascii/fix-repo-dict.test.ts +++ b/src/test/ascii/fix-repo-dict.test.ts @@ -12,261 +12,261 @@ let definitions: FixDefinitions beforeAll(async () => { const sessionDescription: ISessionDescription = require(path.join(root, 'session/test-initiator.json')) - definitions = await new DefinitionFactory().getDefinitions(sessionDescription.application.dictionary) + definitions = await new DefinitionFactory().getDefinitions(sessionDescription?.application?.dictionary ?? '') }, 45000) - +// eslint-disable no-tabs /* - 1 - Account - String - Acct - 0 - Account mnemonic as agreed between buy and sell sides, e.g. broker and institution or investor/intermediary and fund manager. - + 1 + Account + String + Acct + 0 + Account mnemonic as agreed between buy and sell sides, e.g. broker and institution or investor/intermediary and fund manager. + */ test('field check tag 1', () => { const simple = definitions.simple.get('1') expect(simple).toBeTruthy() - expect(simple.tag).toEqual(1) - expect(simple.name).toEqual('Account') - expect(simple.type.toLowerCase()).toEqual('string') + expect(simple?.tag).toEqual(1) + expect(simple?.name).toEqual('Account') + expect(simple?.type.toLowerCase()).toEqual('string') }) /* - - 15 - Currency - Currency - Ccy - 0 - Identifies currency used for price. Absence of this field is interpreted as the default for the security. It is recommended that systems provide the currency value whenever possible. See "Appendix 6-A: Valid Currency Codes" for information on obtaining valid values. - + + 15 + Currency + Currency + Ccy + 0 + Identifies currency used for price. Absence of this field is interpreted as the default for the security. It is recommended that systems provide the currency value whenever possible. See "Appendix 6-A: Valid Currency Codes" for information on obtaining valid values. + */ test('field check tag 15', () => { const simple = definitions.simple.get('15') expect(simple).toBeTruthy() - expect(simple.tag).toEqual(15) - expect(simple.name).toEqual('Currency') - expect(simple.type.toLowerCase()).toEqual('string') // maps to string + expect(simple?.tag).toEqual(15) + expect(simple?.name).toEqual('Currency') + expect(simple?.type.toLowerCase()).toEqual('string') // maps to string }) /* - - 35 - MsgType - String - MsgTyp - 1 - Defines message type ALWAYS THIRD FIELD IN MESSAGE. (Always unencrypted) + + 35 + MsgType + String + MsgTyp + 1 + Defines message type ALWAYS THIRD FIELD IN MESSAGE. (Always unencrypted) Note: A "U" as the first character in the MsgType field (i.e. U, U2, etc) indicates that the message format is privately defined between the sender and receiver. - + */ test('field check tag 35', () => { const simple = definitions.simple.get('35') expect(simple).toBeTruthy() - expect(simple.tag).toEqual(35) - expect(simple.name).toEqual('MsgType') - expect(simple.type.toLowerCase()).toEqual('string') - expect(simple.enums).toBeTruthy() - expect(simple.enums.get('ZZZ')).toBeUndefined() - expect(simple.enums.get('0').val).toEqual('Heartbeat') - expect(simple.enums.get('8').val).toEqual('ExecutionReport') - expect(simple.enums.get('AE').val).toEqual('TradeCaptureReport') + expect(simple?.tag).toEqual(35) + expect(simple?.name).toEqual('MsgType') + expect(simple?.type.toLowerCase()).toEqual('string') + expect(simple?.enums).toBeTruthy() + expect(simple?.enums.get('ZZZ')).toBeUndefined() + expect(simple?.enums.get('0')?.val).toEqual('Heartbeat') + expect(simple?.enums.get('8')?.val).toEqual('ExecutionReport') + expect(simple?.enums.get('AE')?.val).toEqual('TradeCaptureReport') }) /* - - 54 - Side - char - Side - 0 - Side of order - + + 54 + Side + char + Side + 0 + Side of order + */ test('field check tag 54', () => { const simple = definitions.simple.get('54') expect(simple).toBeTruthy() - expect(simple.tag).toEqual(54) - expect(simple.name).toEqual('Side') - expect(simple.type.toLowerCase()).toEqual('char') - expect(simple.enums).toBeTruthy() - expect(simple.enums.get('1').val).toEqual('Buy') - expect(simple.enums.get('2').val).toEqual('Sell') - expect(simple.enums.get('3').val).toEqual('BuyMinus') - expect(simple.enums.get('8').val).toEqual('Cross') + expect(simple?.tag).toEqual(54) + expect(simple?.name).toEqual('Side') + expect(simple?.type.toLowerCase()).toEqual('char') + expect(simple?.enums).toBeTruthy() + expect(simple?.enums?.get('1')?.val).toEqual('Buy') + expect(simple?.enums?.get('2')?.val).toEqual('Sell') + expect(simple?.enums?.get('3')?.val).toEqual('BuyMinus') + expect(simple?.enums?.get('8')?.val).toEqual('Cross') }) /* - - 99 - StopPx - Price - StopPx - 0 - Price per unit of quantity (e.g. per share) - + + 99 + StopPx + Price + StopPx + 0 + Price per unit of quantity (e.g. per share) + */ test('field check tag 99', () => { const simple = definitions.simple.get('99') expect(simple).toBeTruthy() - expect(simple.tag).toEqual(99) - expect(simple.name).toEqual('StopPx') - expect(simple.type.toLowerCase()).toEqual('float') // maps to float + expect(simple?.tag).toEqual(99) + expect(simple?.name).toEqual('StopPx') + expect(simple?.type.toLowerCase()).toEqual('float') // maps to float }) /* - - 113 - ReportToExch - Boolean - RptToExch - 0 - Identifies party of trade responsible for exchange reporting. - + + 113 + ReportToExch + Boolean + RptToExch + 0 + Identifies party of trade responsible for exchange reporting. + */ test('field check tag 113', () => { const simple = definitions.simple.get('113') expect(simple).toBeTruthy() - expect(simple.tag).toEqual(113) - expect(simple.name).toEqual('ReportToExch') - expect(simple.type.toLowerCase()).toEqual('boolean') + expect(simple?.tag).toEqual(113) + expect(simple?.name).toEqual('ReportToExch') + expect(simple?.type.toLowerCase()).toEqual('boolean') }) /* - - 119 - SettlCurrAmt - Amt - SettlCurrAmt - 0 - Total amount due expressed in settlement currency (includes the effect of the forex transaction) - + + 119 + SettlCurrAmt + Amt + SettlCurrAmt + 0 + Total amount due expressed in settlement currency (includes the effect of the forex transaction) + */ test('field check tag 119', () => { const simple = definitions.simple.get('119') expect(simple).toBeTruthy() - expect(simple.tag).toEqual(119) - expect(simple.name).toEqual('SettlCurrAmt') - expect(simple.type.toLowerCase()).toEqual('float') // maps to float + expect(simple?.tag).toEqual(119) + expect(simple?.name).toEqual('SettlCurrAmt') + expect(simple?.type.toLowerCase()).toEqual('float') // maps to float }) /* - - 135 - OfferSize - Qty - OfrSz - 0 - Quantity of offer + + 135 + OfferSize + Qty + OfrSz + 0 + Quantity of offer (Prior to FIX 4.2 this field was of type int) - + */ test('field check tag 135', () => { const simple = definitions.simple.get('135') expect(simple).toBeTruthy() - expect(simple.tag).toEqual(135) - expect(simple.name).toEqual('OfferSize') - expect(simple.type.toLowerCase()).toEqual('float') // maps to float + expect(simple?.tag).toEqual(135) + expect(simple?.name).toEqual('OfferSize') + expect(simple?.type.toLowerCase()).toEqual('float') // maps to float }) /* - - 168 - EffectiveTime - UTCTimestamp - EfctvTm - 0 - Time the details within the message should take effect (always expressed in UTC (Universal Time Coordinated, also known as "GMT") - + + 168 + EffectiveTime + UTCTimestamp + EfctvTm + 0 + Time the details within the message should take effect (always expressed in UTC (Universal Time Coordinated, also known as "GMT") + */ test('field check tag 168', () => { const simple = definitions.simple.get('168') expect(simple).toBeTruthy() - expect(simple.tag).toEqual(168) - expect(simple.name).toEqual('EffectiveTime') - expect(simple.type.toLowerCase()).toEqual('utctimestamp') + expect(simple?.tag).toEqual(168) + expect(simple?.name).toEqual('EffectiveTime') + expect(simple?.type.toLowerCase()).toEqual('utctimestamp') }) /* - - 95 - RawDataLength - Length - 96 - RawDataLength - 0 - Number of bytes in raw data field. - + + 95 + RawDataLength + Length + 96 + RawDataLength + 0 + Number of bytes in raw data field. + */ test('field check tag 95', () => { const simple = definitions.simple.get('95') expect(simple).toBeTruthy() - expect(simple.tag).toEqual(95) - expect(simple.name).toEqual('RawDataLength') - expect(simple.type.toLowerCase()).toEqual('int') + expect(simple?.tag).toEqual(95) + expect(simple?.name).toEqual('RawDataLength') + expect(simple?.type.toLowerCase()).toEqual('int') }) /* - - 96 - RawData - data - RawData - 0 - Unformatted raw data, can include bitmaps, word processor documents, etc. - + + 96 + RawData + data + RawData + 0 + Unformatted raw data, can include bitmaps, word processor documents, etc. + */ test('field check tag 96', () => { const simple = definitions.simple.get('96') expect(simple).toBeTruthy() - expect(simple.tag).toEqual(96) - expect(simple.name).toEqual('RawData') - expect(simple.type.toLowerCase()).toEqual('data') + expect(simple?.tag).toEqual(96) + expect(simple?.name).toEqual('RawData') + expect(simple?.type.toLowerCase()).toEqual('data') }) /* - - 100 - ExDestination - Exchange - ExDest - 0 - Execution destination as defined by institution when order is entered. + + 100 + ExDestination + Exchange + ExDest + 0 + Execution destination as defined by institution when order is entered. Valid values: See "Appendix 6-C" - + */ test('field check tag 100', () => { const simple = definitions.simple.get('100') expect(simple).toBeTruthy() - expect(simple.tag).toEqual(100) - expect(simple.name).toEqual('ExDestination') - expect(simple.type.toLowerCase()).toEqual('string') + expect(simple?.tag).toEqual(100) + expect(simple?.name).toEqual('ExDestination') + expect(simple?.type.toLowerCase()).toEqual('string') }) test('message check TestRequest', () => { const msg = definitions.message.get('TestRequest') expect(msg).toBeTruthy() - expect(msg.msgType).toEqual('1') - expect(msg.fields.length).toEqual(3) - expect(msg.fields[0].type).toEqual(ContainedFieldType.Component) - expect(msg.fields[0].name).toEqual('StandardHeader') - expect(msg.fields[1].type).toEqual(ContainedFieldType.Simple) - expect(msg.fields[1].name).toEqual('TestReqID') - expect(msg.fields[2].type).toEqual(ContainedFieldType.Component) - expect(msg.fields[2].name).toEqual('StandardTrailer') + expect(msg?.msgType).toEqual('1') + expect(msg?.fields.length).toEqual(3) + expect(msg?.fields[0].type).toEqual(ContainedFieldType.Component) + expect(msg?.fields[0].name).toEqual('StandardHeader') + expect(msg?.fields[1].type).toEqual(ContainedFieldType.Simple) + expect(msg?.fields[1].name).toEqual('TestReqID') + expect(msg?.fields[2].type).toEqual(ContainedFieldType.Component) + expect(msg?.fields[2].name).toEqual('StandardTrailer') }) diff --git a/src/test/ascii/includes.test.ts b/src/test/ascii/includes.test.ts index 69679b87..7f42f163 100644 --- a/src/test/ascii/includes.test.ts +++ b/src/test/ascii/includes.test.ts @@ -5,7 +5,7 @@ import * as path from 'path' let includes: IncludeGraph const ver: string = '5-0-SP2' -const root: string = path.join(__dirname, `../../../data/fix_repo/fixmlschema_FIX.5.0SP2_EP228`) +const root: string = path.join(__dirname, '../../../data/fix_repo/fixmlschema_FIX.5.0SP2_EP228') const dataTypes: string = `fixml-datatypes-${ver}.xsd` const fieldsBase: string = `fixml-fields-base-${ver}.xsd` const fieldsImpl: string = `fixml-fields-impl-${ver}.xsd` diff --git a/src/test/ascii/logon.test.ts b/src/test/ascii/logon.test.ts index 43997f37..e598490a 100644 --- a/src/test/ascii/logon.test.ts +++ b/src/test/ascii/logon.test.ts @@ -12,8 +12,8 @@ const root: string = path.join(__dirname, '../../../data') let definitions: FixDefinitions let views: MsgView[] -let structure: Structure -let setup: Setup = null +let structure: Structure | null +let setup: Setup const asStrings: string[] = [ 'FIX4.4', @@ -41,7 +41,7 @@ const asStrings: string[] = [ ] beforeAll(async () => { - setup = new Setup('session/qf-fix44.json',null) + setup = new Setup('session/qf-fix44.json', null) await setup.init() definitions = setup.client.config.definitions views = await setup.client.replayer.replayFixFile(path.join(root, 'examples/FIX.4.4/quickfix/logon/fix.txt')) @@ -55,7 +55,7 @@ test('expect a structure from fix msg', () => { }) test('Logon structure', () => { - const logon: SegmentDescription = structure.layout.Logon + const logon: SegmentDescription = structure?.layout.Logon expect(logon).toBeTruthy() expect(logon.type).toEqual(SegmentType.Msg) expect(logon.startPosition).toEqual(0) @@ -65,7 +65,7 @@ test('Logon structure', () => { }) test('Logon MsgTypes', () => { - const msgTypes: SegmentDescription = structure.layout.NoMsgTypes + const msgTypes: SegmentDescription = structure?.layout.NoMsgTypes expect(msgTypes).toBeTruthy() expect(msgTypes.type).toEqual(SegmentType.Group) expect(msgTypes.delimiterTag).toEqual(372) @@ -87,6 +87,6 @@ test('Logon Object', () => { test('values as strings', () => { const view: MsgView = views[0] - const strings: string[] = view.getStrings() + const strings: Array<(string | null)> | null = view.getStrings() expect(strings).toEqual(asStrings) }) diff --git a/src/test/ascii/memory-store.test.ts b/src/test/ascii/memory-store.test.ts index a67ef653..52ac95a4 100644 --- a/src/test/ascii/memory-store.test.ts +++ b/src/test/ascii/memory-store.test.ts @@ -5,7 +5,7 @@ import { FixDefinitions } from '../../dictionary/definition' import { MsgView } from '../../buffer' import { AsciiView } from '../../buffer/ascii' import { ILooseObject } from '../../collections/collection' -import { FixMsgMemoryStore, FixMsgStoreRecord, IFixMsgStore } from '../../store' +import { FixMsgMemoryStore, FixMsgStoreRecord, IFixMsgStore, IFixMsgStoreRecord } from '../../store' import { MsgTag } from '../../types' import { Setup } from '../env/setup' @@ -15,23 +15,23 @@ let definitions: FixDefinitions let views: MsgView[] let expected: ILooseObject let store: IFixMsgStore -let records: FixMsgStoreRecord[] -let setup: Setup = null +let records: IFixMsgStoreRecord[] +let setup: Setup beforeAll(async () => { - setup = new Setup('session/test-initiator.json',null) + setup = new Setup('session/test-initiator.json', null) await setup.init() definitions = setup.clientConfig.definitions expected = require(path.join(root, 'examples/FIX.4.4/fix.json')) views = await setup.client.replayer.replayFixFile(path.join(root, 'examples/FIX.4.4/jsfix.test_client.txt')) store = new FixMsgMemoryStore('test', setup.clientConfig) - records = views.reduce((agg: FixMsgStoreRecord[], v: AsciiView) => { + records = views.reduce((agg: IFixMsgStoreRecord[], v: AsciiView) => { if (v.getString(MsgTag.SenderCompID) === 'accept-comp') { agg.push(FixMsgStoreRecord.toMsgStoreRecord(v)) } return agg - }, []) - const toWrite = records.map(r => store.put(r)) + }, new Array()) + const toWrite = records.map(async r => await store.put(r)) await Promise.all(toWrite) }, 45000) diff --git a/src/test/ascii/qf-full-msg.test.ts b/src/test/ascii/qf-full-msg.test.ts index 33a803d4..8630c455 100644 --- a/src/test/ascii/qf-full-msg.test.ts +++ b/src/test/ascii/qf-full-msg.test.ts @@ -25,9 +25,9 @@ beforeAll(async () => { }, 45000) async function testEncodeDecode (msgType: string, msg: ILooseObject): Promise { - // encode to FIX format from provided object. - return new Promise(async (resolve, reject) => { - let session: AsciiMsgTransmitter = new AsciiMsgTransmitter(config) + // encode to FIX format from provided object. + return await new Promise(async function (resolve, reject) { + const session: AsciiMsgTransmitter = new AsciiMsgTransmitter(config) const parseBuffer = config.sessionContainer.resolve(DITokens.ParseBuffer) const parser: AsciiParser = new AsciiParser(config, session.encodeStream, parseBuffer) parser.on('msg', (msgType: string, view: AsciiView) => { @@ -83,8 +83,8 @@ test('parse MD snapshot msg', async () => { const res: ParsingResult = await setup.client.parseText(msg) expect(res.event).toEqual('msg') expect(res.msgType).toEqual(MsgType.MarketDataSnapshotFullRefresh) - const v2 = res.view.getView('MDFullGrp') - const o = v2.toObject() + const v2 = res.view?.getView('MDFullGrp') + const o = v2?.toObject() expect(o).toBeTruthy() // console.log(JSON.stringify(o, null, 4)) }) diff --git a/src/test/ascii/repo-full-ascii-msg.test.ts b/src/test/ascii/repo-full-ascii-msg.test.ts index e7e80b3c..d7fa11bc 100644 --- a/src/test/ascii/repo-full-ascii-msg.test.ts +++ b/src/test/ascii/repo-full-ascii-msg.test.ts @@ -15,7 +15,7 @@ let jsonHelper: JsonHelper let session: AsciiMsgTransmitter const root: string = path.join(__dirname, '../../../data/examples/FIX.4.4/repo/') let config: IJsFixConfig -let setup: Setup = null +let setup: Setup beforeAll(async () => { setup = new Setup() await setup.init() @@ -27,13 +27,13 @@ beforeAll(async () => { async function testEncodeDecode (msgType: string, msg: ILooseObject): Promise { // encode to FIX format from provided object. - return new Promise(async (resolve, reject) => { + return await new Promise(async (resolve, reject) => { const rxBuffer = config.sessionContainer.resolve(DITokens.ParseBuffer) const parser: AsciiParser = new AsciiParser(config, session.encodeStream, rxBuffer) parser.on('msg', (msgType: string, view: AsciiView) => { const o = view.toObject() - delete o['StandardHeader'] - delete o['StandardTrailer'] + delete o.StandardHeader + delete o.StandardTrailer resolve(o) }) parser.on('error', (e: Error) => { @@ -46,20 +46,20 @@ async function testEncodeDecode (msgType: string, msg: ILooseObject): Promise { const factory = session.config.factory - const cs = factory.trailer(1) - expect(cs.CheckSum).toEqual('001') + const cs = factory?.trailer(1) + expect(cs?.CheckSum).toEqual('001') }) test('check 2 digit checksum format', async () => { const factory = session.config.factory - const cs = factory.trailer(10) - expect(cs.CheckSum).toEqual('010') + const cs = factory?.trailer(10) + expect(cs?.CheckSum).toEqual('010') }) test('check 3 digit checksum format', async () => { const factory = session.config.factory - const cs = factory.trailer(100) - expect(cs.CheckSum).toEqual('100') + const cs = factory?.trailer(100) + expect(cs?.CheckSum).toEqual('100') }) test('AE object to ascii fix to object', async () => { @@ -69,7 +69,7 @@ test('AE object to ascii fix to object', async () => { const o: ILooseObject = await testEncodeDecode(msgType, msg) expect(o).toEqual(msg) -}, 1000) +}, 60000) test('d object to ascii fix to object', async () => { const msgType: string = MsgType.SecurityDefinition diff --git a/src/test/ascii/session-state.test.ts b/src/test/ascii/session-state.test.ts index b489e1e6..6a4e7fec 100644 --- a/src/test/ascii/session-state.test.ts +++ b/src/test/ascii/session-state.test.ts @@ -1,6 +1,6 @@ import 'reflect-metadata' -import { SessionState,TickAction, FixSessionState } from '../../transport' +import { SessionState, TickAction, FixSessionState } from '../../transport' let state: FixSessionState let now: Date diff --git a/src/test/ascii/session.test.ts b/src/test/ascii/session.test.ts index 7af4ff57..f50c2e3b 100644 --- a/src/test/ascii/session.test.ts +++ b/src/test/ascii/session.test.ts @@ -14,8 +14,8 @@ import { SkeletonRunner } from '../env/skeleton-runner' const logonMsg: string = '8=FIX4.4|9=0000136|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20180902-12:25:28.980|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=177|' const heartbeat: string = '8=FIX4.4|9=0000123|35=0|49=init-comp|56=accept-comp|34=1|57=fix|52=20180902-12:25:59.161|112=Sun, 02 Sep 2018 12:25:59 GMT|10=95|' -let setup: Setup = null -let experiment: Experiment = null +let setup: Setup +let experiment: Experiment beforeEach(async () => { setup = new Setup() @@ -31,8 +31,8 @@ class ParsingResult { } } -function clientToServerWaitFirstMessage (type: string, obj: ILooseObject): Promise { - return new Promise((resolve, reject) => { +async function clientToServerWaitFirstMessage (type: string, obj: ILooseObject): Promise { + return await new Promise((resolve, reject) => { const clt = experiment.client.transport const svt = experiment.server.transport clt.transmitter.on('error', (e: Error) => { @@ -49,28 +49,32 @@ function clientToServerWaitFirstMessage (type: string, obj: ILooseObject): Promi }) } -async function runSkeletons (logoutSeconds: number = 1, followOn: string = null) { +async function runSkeletons (logoutSeconds: number = 1, followOn: string | null = null): Promise { const runner: SkeletonRunner = new SkeletonRunner(experiment, logoutSeconds) runner.sendText(followOn) await runner.wait() } test('end to end logon', async () => { - const lo = experiment.client.config.factory.logon() + const lo = experiment?.client?.config?.factory?.logon() + expect(lo).toBeTruthy() + if (!lo) return const res: ParsingResult = await clientToServerWaitFirstMessage(MsgType.Logon, lo) expect(res.event).toEqual('msg') expect(res.msgType).toEqual('A') const received = res.view.toObject() expect(received).toBeTruthy() - delete received['StandardHeader'] - delete received['StandardTrailer'] + delete received.StandardHeader + delete received.StandardTrailer expect(received).toEqual(lo) }) test('session send resendRequest when logged on', async () => { const runner: SkeletonRunner = new SkeletonRunner(experiment, 2) const factory = experiment.client.config.factory - const resend = factory.resendRequest(1, 2) + const resend = factory?.resendRequest(1, 2) + expect(resend).toBeTruthy() + if (!resend) return runner.sendMsg(MsgType.ResendRequest, resend) try { const cViews = experiment.client.views @@ -89,7 +93,9 @@ test('session send resendRequest when logged on', async () => { test('session send logon when logged on', async () => { const runner: SkeletonRunner = new SkeletonRunner(experiment, 2) - const logon = experiment.client.config.factory.logon() + const logon = experiment?.client?.config?.factory?.logon() + expect(logon).toBeTruthy() + if (!logon) return runner.sendMsg(MsgType.Logon, logon) try { await runner.wait() @@ -117,8 +123,8 @@ test('session logon / logout', async () => { expect(sViews[1].segment.name).toEqual('Logout') }) -function checkSeqNos (views: MsgView[]) { - const seqNo: number[] = views.map((v: MsgView) => (v.getView('StandardHeader').toObject() as IStandardHeader).MsgSeqNum) +function checkSeqNos (views: MsgView[]): void { + const seqNo: number[] = views.map((v: MsgView) => (v.getView('StandardHeader')?.toObject() as IStandardHeader)?.MsgSeqNum) expect(seqNo).toBeTruthy() const delta = seqNo.reduce((c: number, latest: number) => { return latest - c === 1 ? c + 1 : c - 1 @@ -173,7 +179,8 @@ function mutateRemoveRequiredHeartBtInt (description: ISessionDescription, type: switch (type) { case 'A': { const logon = o as ILogon - delete logon['HeartBtInt'] + // @ts-expect-error + delete logon.HeartBtInt break } } @@ -197,9 +204,7 @@ test('client logon reject missing 108', async () => { // transport.transmitter -test('client unknown msg type', async () => { - const at = experiment.client.transport.transmitter as AsciiMsgTransmitter - const changed = logonMsg.replace('35=A', '35=ZZ').replace('34=1', `34=${at.msgSeqNum + 1}`) +async function runCheckReject (experiment: Experiment, changed: string): Promise { await runSkeletons(2, changed) const cviews = experiment.client.views const sviews = experiment.server.views @@ -209,6 +214,16 @@ test('client unknown msg type', async () => { expect(cviews[0].segment.name).toEqual('Logon') expect(cviews[1].segment.name).toEqual('Reject') expect(sviews[0].segment.name).toEqual('Logon') +} + +test('client unknown msg type', async () => { + const at = experiment.client.transport.transmitter as AsciiMsgTransmitter + const changed = logonMsg + .replace('35=A', '35=ZZ') + .replace('34=1', `34=${at.msgSeqNum + 1}`) + const cviews = experiment.client.views + const sviews = experiment.server.views + await runCheckReject(experiment, changed) expect(sviews[1].segment.name).toEqual('unknown') const reject: IReject = cviews[1].toObject() expect(reject.SessionRejectReason === SessionRejectReason.InvalidMsgType) @@ -217,15 +232,13 @@ test('client unknown msg type', async () => { test('heartbeat invalid tag', async () => { const at = experiment.client.transport.transmitter as AsciiMsgTransmitter - const changed = heartbeat.replace('112=', '999=').replace('34=1', `34=${at.msgSeqNum + 1}`) + const changed = heartbeat + .replace('112=', '999=') + .replace('34=1', `34=${at.msgSeqNum + 1}`) await runSkeletons(2, changed) const cviews = experiment.client.views const sviews = experiment.server.views - expect(cviews.length === 3).toEqual(true) - expect(sviews.length === 3).toEqual(true) - expect(cviews[0].segment.name).toEqual('Logon') - expect(cviews[1].segment.name).toEqual('Reject') - expect(sviews[0].segment.name).toEqual('Logon') + await runCheckReject(experiment, changed) expect(sviews[1].segment.name).toEqual('Heartbeat') const reject: IReject = experiment.client.views[1].toObject() expect(reject.SessionRejectReason === SessionRejectReason.InvalidTagNumber) @@ -235,15 +248,12 @@ test('heartbeat invalid tag', async () => { test('heartbeat invalid sender comp', async () => { const at = experiment.client.transport.transmitter as AsciiMsgTransmitter - const changed = heartbeat.replace('49=init-comp', '49=init-not!').replace('34=1', `34=${at.msgSeqNum + 1}`) - await runSkeletons(2, changed) + const changed = heartbeat + .replace('49=init-comp', '49=init-not!') + .replace('34=1', `34=${at.msgSeqNum + 1}`) const cviews = experiment.client.views const sviews = experiment.server.views - expect(cviews.length === 3).toEqual(true) - expect(sviews.length === 3).toEqual(true) - expect(cviews[0].segment.name).toEqual('Logon') - expect(cviews[1].segment.name).toEqual('Reject') - expect(sviews[0].segment.name).toEqual('Logon') + await runCheckReject(experiment, changed) expect(sviews[1].segment.name).toEqual('Heartbeat') const reject: IReject = cviews[1].toObject() expect(reject.SessionRejectReason === SessionRejectReason.CompIDProblem) diff --git a/src/test/ascii/view-decode.test.ts b/src/test/ascii/view-decode.test.ts index 1b7e134f..760eb9eb 100644 --- a/src/test/ascii/view-decode.test.ts +++ b/src/test/ascii/view-decode.test.ts @@ -15,12 +15,12 @@ const root: string = path.join(__dirname, '../../../data') let definitions: FixDefinitions let session: AsciiMsgTransmitter let views: MsgView[] -let structure: Structure +let structure: Structure | null let view: MsgView -let setup: Setup = null +let setup: Setup beforeAll(async () => { - setup = new Setup('session/qf-fix44.json',null) + setup = new Setup('session/qf-fix44.json', null) await setup.init() definitions = setup.definitions session = setup.client.transmitter as AsciiMsgTransmitter @@ -36,26 +36,26 @@ test('expect a structure from fix msg', () => { }) test('get NoMDEntries directly - expect an array', () => { - const noMDEntriesView: MsgView = view.getView('NoMDEntries') + const noMDEntriesView: MsgView | null = view?.getView('NoMDEntries') expect(noMDEntriesView).toBeTruthy() - const noMDEntries: ILooseObject[] = noMDEntriesView.toObject() + const noMDEntries: ILooseObject[] = noMDEntriesView?.toObject() expect(Array.isArray(noMDEntries)).toEqual(true) expect(noMDEntries.length).toEqual(2) }) test('get NoMDEntries via MDFullGrp - array within a component', () => { - const mdFullGrp: MsgView = view.getView('MDFullGrp') + const mdFullGrp: MsgView | null = view.getView('MDFullGrp') expect(mdFullGrp).toBeTruthy() - const mdFullGrpAsObject: ILooseObject = mdFullGrp.toObject() + const mdFullGrpAsObject: ILooseObject = mdFullGrp?.toObject() const noMDEntries: ILooseObject[] = mdFullGrpAsObject.NoMDEntries expect(Array.isArray(noMDEntries)).toEqual(true) expect(noMDEntries.length).toEqual(2) }) function getMdEntriesObjects (): ILooseObject[] { - const noMDEntriesView: MsgView = view.getView('NoMDEntries') + const noMDEntriesView: MsgView | null = view.getView('NoMDEntries') expect(noMDEntriesView).toBeTruthy() - const noMDEntries: ILooseObject[] = noMDEntriesView.toObject() + const noMDEntries: ILooseObject[] = noMDEntriesView?.toObject() expect(Array.isArray(noMDEntries)).toEqual(true) expect(noMDEntries.length).toEqual(2) return noMDEntries @@ -65,13 +65,13 @@ function getMdEntriesObjects (): ILooseObject[] { test('get UTCDATEONLY from NoMDEntries instance 1', () => { const noMdEntriesAsObjects: ILooseObject[] = getMdEntriesObjects() - const noMDEntriesView: MsgView = view.getView('NoMDEntries') - const mmEntryView: MsgView = noMDEntriesView.getGroupInstance(1) + const noMDEntriesView: MsgView | null = view.getView('NoMDEntries') + const mmEntryView: MsgView | null = noMDEntriesView?.getGroupInstance(1) ?? null const instance: ILooseObject = noMdEntriesAsObjects[1] - const mmEntryDateAsString: string = mmEntryView.getString('MDEntryDate') + const mmEntryDateAsString: string | null = mmEntryView?.getString('MDEntryDate') ?? null expect(mmEntryDateAsString).toEqual('20210129') - expect(mmEntryView.getString(272)).toEqual('20210129') + expect(mmEntryView?.getString(272)).toEqual('20210129') const asUtc: Date = new Date(Date.UTC(2021, 0, 29)) expect(instance.MDEntryDate).toEqual(asUtc) }) @@ -80,13 +80,12 @@ test('get UTCDATEONLY from NoMDEntries instance 1', () => { test('get UTCTIMEONLY from NoMDEntries instance 0', () => { const noMdEntriesAsObjects: ILooseObject[] = getMdEntriesObjects() - const noMDEntriesView: MsgView = view.getView('NoMDEntries') - const mmEntryView: MsgView = noMDEntriesView.getGroupInstance(0) - + const noMDEntriesView: MsgView | null = view.getView('NoMDEntries') + const mmEntryView: MsgView | null = noMDEntriesView?.getGroupInstance(0) ?? null const instance: ILooseObject = noMdEntriesAsObjects[0] - const mmEntryTimeAsString: string = mmEntryView.getString('MDEntryTime') + const mmEntryTimeAsString: string | null = mmEntryView?.getString('MDEntryTime') ?? null expect(mmEntryTimeAsString).toEqual('19:45:19.852') - expect(mmEntryView.getString(273)).toEqual('19:45:19.852') + expect(mmEntryView?.getString(273)).toEqual('19:45:19.852') const asUtc: Date = new Date(Date.UTC(0, 0, 0, 19, 45, 19, 852)) expect(instance.MDEntryTime).toEqual(asUtc) }) @@ -95,13 +94,13 @@ test('get UTCTIMEONLY from NoMDEntries instance 0', () => { test('get UTCTIMESTAMP from NoMDEntries instance 1', () => { const noMdEntriesAsObjects: ILooseObject[] = getMdEntriesObjects() - const noMDEntriesView: MsgView = view.getView('NoMDEntries') - const mmEntryView: MsgView = noMDEntriesView.getGroupInstance(1) + const noMDEntriesView: MsgView | null = view.getView('NoMDEntries') + const mmEntryView: MsgView | null = noMDEntriesView?.getGroupInstance(1) ?? null const instance: ILooseObject = noMdEntriesAsObjects[1] - const mmEntryExpireTimeAsString: string = mmEntryView.getString('ExpireTime') + const mmEntryExpireTimeAsString: string | null = mmEntryView?.getString('ExpireTime') ?? null expect(mmEntryExpireTimeAsString).toEqual('20210129-19:45:19.000') - expect(mmEntryView.getString(126)).toEqual('20210129-19:45:19.000') + expect(mmEntryView?.getString(126)).toEqual('20210129-19:45:19.000') const asUtc: Date = new Date(Date.UTC(2021, 0, 29, 19, 45, 19, 0)) const d: Date = instance.ExpireTime expect(d).toEqual(asUtc) @@ -111,13 +110,13 @@ test('get UTCTIMESTAMP from NoMDEntries instance 1', () => { test('get MinQty from NoMDEntries instance 1', () => { const noMdEntriesAsObjects: ILooseObject[] = getMdEntriesObjects() - const noMDEntriesView: MsgView = view.getView('NoMDEntries') - const mmEntryView: MsgView = noMDEntriesView.getGroupInstance(1) + const noMDEntriesView: MsgView | null = view.getView('NoMDEntries') + const mmEntryView: MsgView | null = noMDEntriesView?.getGroupInstance(1) ?? null const instance: ILooseObject = noMdEntriesAsObjects[1] - const mmEntryMinQtyAsString: string = mmEntryView.getString('MinQty') + const mmEntryMinQtyAsString: string | null = mmEntryView?.getString('MinQty') ?? null expect(mmEntryMinQtyAsString).toEqual('9.6478') - expect(mmEntryView.getString(110)).toEqual('9.6478') + expect(mmEntryView?.getString(110)).toEqual('9.6478') expect(instance.MinQty).toEqual(9.6478) }) @@ -153,16 +152,16 @@ test('get selection tags one call - tag names', () => { expect(f).toEqual('ipsum') }) -test('nested view fetch' , () => { +test('nested view fetch', () => { const legGrpView = view.getView('InstrmtLegGrp.NoLegs') expect(legGrpView).toBeTruthy() - const legGrp: IInstrumentLeg[] = legGrpView.toObject() + const legGrp: IInstrumentLeg[] = legGrpView?.toObject() expect(legGrp).toBeTruthy() expect(Array.isArray(legGrp)) expect(legGrp.length).toEqual(3) }) -test('view buffer' , () => { +test('view buffer', () => { const asciiView: AsciiView = view as AsciiView const buffer = asciiView.toBuffer('?'.charCodeAt(0)) const txt = buffer.toString() @@ -205,18 +204,20 @@ function BidOfferRequest (symbol: string): IMarketDataRequest { test('market data request', async () => { const mdr = BidOfferRequest('EUR/USD') const def = definitions.message.get('MarketDataRequest') + expect(def).toBeTruthy() + if (!def) return const fix = toFixMessage(mdr, def) expect(fix).toBeTruthy() const res: ParsingResult = await setup.client.parseText(fix) expect(res.event).toEqual('msg') expect(res.msgType).toEqual(def.msgType) - const gv = res.view.getView('MDReqGrp') + const gv = res?.view?.getView('MDReqGrp') expect(gv).toBeTruthy() - const s = gv.toString() + const s = gv?.toString() const newLine = require('os').EOL expect(s).toEqual(`[0] 267 (NoMDEntryTypes) = 2, [1] 269 (MDEntryType) = 0[Bid]${newLine}[2] 269 (MDEntryType) = 1[Offer]`) - const iv = res.view.getView('InstrmtMDReqGrp.NoRelatedSym') + const iv = res?.view?.getView('InstrmtMDReqGrp.NoRelatedSym') expect(iv).toBeTruthy() - const s2 = iv.toString() + const s2 = iv?.toString() expect(s2).toEqual(`[0] 146 (NoRelatedSym) = 1, [1] 55 (Symbol) = EUR/USD${newLine}`) }) diff --git a/src/test/encode-proxy.test.ts b/src/test/encode-proxy.test.ts index f0ce2fdc..421d1147 100644 --- a/src/test/encode-proxy.test.ts +++ b/src/test/encode-proxy.test.ts @@ -9,7 +9,7 @@ import { Setup } from './env/setup' let definitions: FixDefinitions let session: AsciiMsgTransmitter let proxyFactory: EncodeProxy -let setup: Setup = null +let setup: Setup beforeAll(async () => { setup = new Setup('session/qf-fix44.json', 'session/qf-fix44.json') @@ -109,7 +109,7 @@ test('check wrapper will accept Buffer field when given Buffer', () => { const proxy: ILooseObject = proxyFactory.wrap('ExecutionReport') expect(proxy).toBeTruthy() function run (): void { - proxy.EncodedText = new Buffer('I am a buffer.', 'utf8') + proxy.EncodedText = Buffer.from('I am a buffer.', 'utf8') } run() expect(proxy.EncodedText).toBeTruthy() @@ -184,10 +184,10 @@ test('check wrapper when assigned component wraps so it can be checked.', () => function run2 (): void { proxy.YieldData.GrossTradeAmt = 999 } - // reject this + // reject this expect(run2).toThrow(/no field named GrossTradeAmt/) proxy.YieldData.YieldCalcDate = new Date() - // accept this + // accept this expect(proxy.YieldData).toBeTruthy() }) @@ -199,7 +199,7 @@ test('check wrapper when given populated component.', () => { ClOrdID: "I don't belong here." } } - // reject this + // reject this expect(run).toThrow(/no field named ClOrdID/) }) @@ -215,13 +215,13 @@ test('check wrapper accepts number for group field and wraps n elements', () => expect(proxy.Parties).toBeTruthy() expect(Array.isArray(proxy.Parties.NoPartyIDs)).toEqual(true) expect(proxy.Parties.NoPartyIDs.length).toEqual(2) - // now assign a field, this should also be wrapped + // now assign a field, this should also be wrapped proxy.Parties.NoPartyIDs[0].PartyID = 'hello' expect(proxy.Parties.NoPartyIDs[0].PartyID).toBeTruthy() function run2 (): void { proxy.Parties.NoPartyIDs[0].GrossTradeAmt = 999 } - // reject this + // reject this expect(run2).toThrow(/no field named GrossTradeAmt/) }) @@ -237,59 +237,59 @@ test('check wrapper accepts array of objects in group component', () => { function run2 (): void { proxy.Parties.NoPartyIDs[0].GrossTradeAmt = 999 } - // reject this + // reject this expect(run2).toThrow(/no field named GrossTradeAmt/) }) function getParties (): ILooseObject { return [ { - 'PartyID': 'magna.', - 'PartyIDSource': '9', - 'PartyRole': 28, - 'PtysSubGrp': { - 'NoPartySubIDs': [ + PartyID: 'magna.', + PartyIDSource: '9', + PartyRole: 28, + PtysSubGrp: { + NoPartySubIDs: [ { - 'PartySubID': 'et', - 'PartySubIDType': 22 + PartySubID: 'et', + PartySubIDType: 22 }, { - 'PartySubID': 'leo,', - 'PartySubIDType': 10 + PartySubID: 'leo,', + PartySubIDType: 10 } ] } }, { - 'PartyID': 'iaculis', - 'PartyIDSource': 'F', - 'PartyRole': 2, - 'PtysSubGrp': { - 'NoPartySubIDs': [ + PartyID: 'iaculis', + PartyIDSource: 'F', + PartyRole: 2, + PtysSubGrp: { + NoPartySubIDs: [ { - 'PartySubID': 'Nullam', - 'PartySubIDType': 12 + PartySubID: 'Nullam', + PartySubIDType: 12 }, { - 'PartySubID': 'lectus,', - 'PartySubIDType': 13 + PartySubID: 'lectus,', + PartySubIDType: 13 }, { - 'PartySubID': 'eget', - 'PartySubIDType': 18 + PartySubID: 'eget', + PartySubIDType: 18 } ] } }, { - 'PartyID': 'vitae,', - 'PartyIDSource': '9', - 'PartyRole': 5, - 'PtysSubGrp': { - 'NoPartySubIDs': [ + PartyID: 'vitae,', + PartyIDSource: '9', + PartyRole: 5, + PtysSubGrp: { + NoPartySubIDs: [ { - 'PartySubID': 'ac', - 'PartySubIDType': 6 + PartySubID: 'ac', + PartySubIDType: 6 } ] } diff --git a/src/test/env/experiment.ts b/src/test/env/experiment.ts index 90d18a17..2d815daf 100644 --- a/src/test/env/experiment.ts +++ b/src/test/env/experiment.ts @@ -11,8 +11,8 @@ class FixEntity { public readonly errors: Error[] = [] constructor (public readonly config: IJsFixConfig, - public readonly duplex: FixDuplex = new StringDuplex(), - public readonly transport: MsgTransport = new MsgTransport(0, config, duplex)) { + public readonly duplex: FixDuplex = new StringDuplex(), + public readonly transport: MsgTransport = new MsgTransport(0, config, duplex)) { } } @@ -22,7 +22,7 @@ export class Experiment { public readonly serverFactory: AsciiSessionMsgFactory public readonly server: FixEntity - loopBack (lhs: FixDuplex, rhs: FixDuplex) { + loopBack (lhs: FixDuplex, rhs: FixDuplex): void { lhs.writable.on('data', (data: Buffer) => { rhs.readable.push(data) }) @@ -38,7 +38,7 @@ export class Experiment { this.client = new FixEntity(clientConfig) this.server = new FixEntity(serverConfig) - // using a string duplex so pipe a write to client to read to server + // using a string duplex so pipe to client to read to server // to simulate a tcp connection. this.loopBack(this.client.duplex, this.server.duplex) this.loopBack(this.server.duplex, this.client.duplex) diff --git a/src/test/env/parsing-result.ts b/src/test/env/parsing-result.ts index bf6cfcec..fa97818a 100644 --- a/src/test/env/parsing-result.ts +++ b/src/test/env/parsing-result.ts @@ -3,9 +3,9 @@ import { AsciiParser } from '../../buffer/ascii' export class ParsingResult { constructor (public readonly event: string, - public readonly msgType: string, - public readonly view: MsgView, - public readonly contents: string, - public readonly parser: AsciiParser) { + public readonly msgType: string | null, + public readonly view: MsgView | null, + public readonly contents: string, + public readonly parser: AsciiParser) { } } diff --git a/src/test/env/setup.ts b/src/test/env/setup.ts index 1961332a..fecb925f 100644 --- a/src/test/env/setup.ts +++ b/src/test/env/setup.ts @@ -28,15 +28,15 @@ export class TestEntity { } async getViews (fix: string = 'examples/FIX.4.4/fix.txt'): Promise { - return this.replayer.replayFixFile(path.join(root, fix)) + return await this.replayer.replayFixFile(path.join(root, fix)) } getAsciiParser (text: string, chunks: boolean = false): AsciiParser { return new AsciiParser(this.config, new StringDuplex(text, chunks).readable, this.rxBuffer) } - parseText (text: string, chunks: boolean = false): Promise { - return new Promise((resolve, reject) => { + async parseText (text: string, chunks: boolean = false): Promise { + return await new Promise((resolve, reject) => { const parser = this.getAsciiParser(text, chunks) parser.on('error', (e: Error) => { reject(e) @@ -45,12 +45,12 @@ export class TestEntity { resolve(new ParsingResult('msg', msgType, view.clone(), parser.state.elasticBuffer.toString(), parser)) }) parser.on('done', () => { - resolve(new ParsingResult('done', null,null, parser.state.elasticBuffer.toString(), parser)) + resolve(new ParsingResult('done', null, null, parser.state.elasticBuffer.toString(), parser)) }) }) } - async make () { + async make (): Promise { this.fixContainer.reset() this.fixContainer.registerGlobal('error') this.sessionContainer = await this.fixContainer.makeSystem(this.description) @@ -82,15 +82,14 @@ export class Setup { clientSessionContainer: DependencyContainer constructor (public readonly clientPath: string = 'session/test-initiator.json', - public readonly serverPath: string = 'session/test-acceptor.json') { - + public readonly serverPath: string | null = 'session/test-acceptor.json') { this.client = new TestEntity(clientPath) if (serverPath) { this.server = new TestEntity(serverPath) } } - async init () { + async init (): Promise { if (this.client) { await this.client.make() this.definitions = this.client.config.definitions diff --git a/src/test/env/skeleton-runner.ts b/src/test/env/skeleton-runner.ts index cf16da91..98e50bbe 100644 --- a/src/test/env/skeleton-runner.ts +++ b/src/test/env/skeleton-runner.ts @@ -33,7 +33,7 @@ export class SkeletonRunner { }) } - watchdog () { + watchdog (): void { const experiment = this.experiment const cviews = experiment.client.views const sviews = experiment.server.views @@ -52,7 +52,7 @@ export class SkeletonRunner { sendMsg (msgType: string, o: ILooseObject): void { let count = 0 - this.experiment.client.transport.receiver.on('msg', m => { + this.experiment.client.transport.receiver.on('msg', _ => { if (count === 0) { count++ this.clientSkeleton.sendMessage(msgType, o) @@ -60,39 +60,38 @@ export class SkeletonRunner { }) } - sendText (followOn: string): void { + sendText (followOn: string | null): void { const experiment = this.experiment - if (followOn) { - let sent: boolean = false - experiment.client.transport.transmitter.on('encoded', () => { - const b1 = new ElasticBuffer() - b1.writeString(followOn) - if (!sent) { - experiment.client.transport.duplex.writable.write(b1.slice()) - const at = experiment.client.transport.transmitter as AsciiMsgTransmitter - at.msgSeqNum++ - sent = true - } - }) - } + if (!followOn) return + let sent: boolean = false + experiment.client.transport.transmitter.on('encoded', () => { + const b1 = new ElasticBuffer() + b1.writeString(followOn) + if (!sent) { + experiment.client.transport.duplex.writable.write(b1.slice()) + const at = experiment.client.transport.transmitter as AsciiMsgTransmitter + at.msgSeqNum++ + sent = true + } + }) } - done () { + done (): void { this.clientSkeleton.done() this.serverSkeleton.done() } - async wait () { + async wait (): Promise { const experiment = this.experiment await Promise.all([ this.clientSkeleton.run(experiment.client.transport), this.serverSkeleton.run(experiment.server.transport), - new Promise((accept, reject) => { + new Promise((resolve, reject) => { let handle = null try { handle = setTimeout(() => { this.done() - accept(true) + resolve(true) }, (this.logoutSeconds + 2) * 1000) } catch (e) { if (handle) { diff --git a/src/test/env/test-recovery.ts b/src/test/env/test-recovery.ts index 542a1d10..fbb554b2 100644 --- a/src/test/env/test-recovery.ts +++ b/src/test/env/test-recovery.ts @@ -1,4 +1,10 @@ -import { FixMsgAsciiStoreResend, FixMsgMemoryStore, FixMsgStoreRecord, IFixMsgStore } from '../../store' +import { + FixMsgAsciiStoreResend, + FixMsgMemoryStore, + FixMsgStoreRecord, + IFixMsgStore, + IFixMsgStoreRecord +} from '../../store' import { MsgView } from '../../buffer' import { IJsFixConfig } from '../../config' import { AsciiView } from '../../buffer/ascii' @@ -6,7 +12,7 @@ import { MsgTag } from '../../types' export class TestRecovery { public readonly store: IFixMsgStore - public readonly records: FixMsgStoreRecord[] + public readonly records: IFixMsgStoreRecord[] public readonly recovery: FixMsgAsciiStoreResend constructor (public readonly views: MsgView[], public readonly config: IJsFixConfig) { @@ -17,14 +23,14 @@ export class TestRecovery { this.recovery = new FixMsgAsciiStoreResend(this.store, config) } - async populate () { + async populate (): Promise { const records = this.records - const toWrite = records.map(r => this.store.put(r)) + const toWrite = records.map(async r => await this.store.put(r)) await Promise.all(toWrite) } - getRecords (comp: string) { - return this.views.reduce((agg: FixMsgStoreRecord[], v: AsciiView) => { + getRecords (comp: string): IFixMsgStoreRecord[] { + return this.views.reduce((agg: IFixMsgStoreRecord[], v: AsciiView) => { if (v.getString(MsgTag.SenderCompID) === comp) { agg.push(FixMsgStoreRecord.toMsgStoreRecord(v)) } diff --git a/src/test/env/to-views.ts b/src/test/env/to-views.ts index ddbc3e7c..0a2eb809 100644 --- a/src/test/env/to-views.ts +++ b/src/test/env/to-views.ts @@ -11,7 +11,7 @@ import { DefinitionFactory } from '../../util' export class ToViews { public definitions: FixDefinitions public readonly views: MsgView[] = [] - public batch: MsgView = null + public batch: MsgView | null = null private readonly root: string = path.join(__dirname, '../../../data') constructor (public readonly testFolder: string) { @@ -29,7 +29,7 @@ export class ToViews { const sessionDescription: ISessionDescription = require(path.join(root, 'session/test-initiator.json')) const config = new JsFixConfig(null, definitions, sessionDescription, AsciiChars.Pipe) const xmlParser: MsgParser = new FiXmlParser(config, readStream) - return new Promise((accept, reject) => { + return await new Promise((resolve, reject) => { xmlParser.on('msg', (msgType: string, v: MsgView) => { views.push(v.clone()) }) @@ -37,7 +37,7 @@ export class ToViews { this.batch = v.clone() }) xmlParser.on('close', () => { - accept(true) + resolve(true) }) xmlParser.on('error', (e: Error) => { reject(e) diff --git a/src/test/fixml/fixml-alloc-parse.test.ts b/src/test/fixml/fixml-alloc-parse.test.ts index 18847511..395adf17 100644 --- a/src/test/fixml/fixml-alloc-parse.test.ts +++ b/src/test/fixml/fixml-alloc-parse.test.ts @@ -5,7 +5,8 @@ import { MsgView } from '../../buffer' import { IStandardHeader, IInstrument, IPtysSubGrp, IParties, INstdPtysSubGrp, - IAllocationInstruction, IAllocationReport, IOrdAllocGrp } from '../../types/FIXML50SP2' + IAllocationInstruction, IAllocationReport, IOrdAllocGrp +} from '../../types/FIXML50SP2' import { ToViews } from '../env/to-views' import * as moment from 'moment' @@ -25,7 +26,7 @@ test('expect a view from fix msg', () => { test('expect Hdr view to be available', () => { const views = toViews.views const v = views[0] - const hdr: MsgView = v.getView('Hdr') + const hdr: MsgView | null = v.getView('Hdr') expect(hdr).toBeTruthy() }) @@ -42,7 +43,7 @@ test('expect Hdr view to be available', () => { test('fetch attributes from Hdr', () => { const views = toViews.views const v = views[0] - const hdr: IStandardHeader = v.getView('Hdr').toObject() as IStandardHeader + const hdr: IStandardHeader | null = v?.getView('Hdr')?.toObject() as IStandardHeader expect(hdr).toBeTruthy() expect(hdr.SenderCompID).toEqual('CME') expect(v.getTyped('SID')).toEqual('CME') @@ -76,7 +77,11 @@ test('test complex sub structure OrdAllocGrp', () => { const v = views[0] const allocInstruction: IAllocationInstruction = v.toObject() expect(allocInstruction).toBeTruthy() - expect(allocInstruction.OrdAllocGrp[0].ClOrdID).toEqual('SX1') + const ag = allocInstruction?.OrdAllocGrp + expect(ag).toBeTruthy() + if (!ag) return + const f0 = ag[0] + expect(f0?.ClOrdID).toEqual('SX1') }) /* @@ -93,14 +98,14 @@ test('test complex sub structure OrdAllocGrp', () => { test('test instrument on fixml allocation - use abbreviation', () => { const views = toViews.views const v = views[0] - const instrument: IInstrument = v.getView('Instrmt').toObject() + const instrument: IInstrument = v.getView('Instrmt')?.toObject() expect(instrument).toBeTruthy() }) test('test instrument attributes', () => { const views = toViews.views const v = views[0] - const instrument: IInstrument = v.getView('Instrmt').toObject() + const instrument: IInstrument = v.getView('Instrmt')?.toObject() expect(instrument).toBeTruthy() expect(instrument.SecurityID).toEqual('ED') expect(instrument.CFICode).toEqual('FFDCSO') @@ -113,7 +118,7 @@ test('test instrument attributes', () => { test('test instrument on fixml allocation - use full name', () => { const views = toViews.views const v = views[0] - const instrument: IInstrument = v.getView('Instrument').toObject() + const instrument: IInstrument = v.getView('Instrument')?.toObject() expect(instrument).toBeTruthy() }) @@ -123,19 +128,21 @@ test('test complex sub structure AllocGrp', () => { const allocInstruction: IAllocationInstruction = v.toObject() expect(allocInstruction).toBeTruthy() expect(Array.isArray(allocInstruction.AllocGrp)).toBeTruthy() - expect(allocInstruction.AllocGrp.length).toEqual(1) - expect(allocInstruction.AllocGrp[0].AllocQty).toEqual(4) - expect(allocInstruction.AllocGrp[0].IndividualAllocID).toEqual('307006') - expect(allocInstruction.AllocGrp[0].SecondaryIndividualAllocID).toEqual('178004') - expect(allocInstruction.AllocGrp[0].AllocCustomerCapacity).toEqual('4') - expect(allocInstruction.AllocGrp[0].OriginalSecondaryTradeID).toEqual('12A80D9ED85HI040083A') - + expect(allocInstruction.AllocGrp?.length).toEqual(1) + const fa = allocInstruction.AllocGrp + const f0 = fa ? fa[0] : null + expect(f0).toBeTruthy() + expect(f0?.AllocQty).toEqual(4) + expect(f0?.IndividualAllocID).toEqual('307006') + expect(f0?.SecondaryIndividualAllocID).toEqual('178004') + expect(f0?.AllocCustomerCapacity).toEqual('4') + expect(f0?.OriginalSecondaryTradeID).toEqual('12A80D9ED85HI040083A') }) test('test OrdAllocGrp', () => { const views = toViews.views const v = views[0] - const ordAlloc: IOrdAllocGrp[] = v.getView('OrdAllocGrp').toObject() + const ordAlloc: IOrdAllocGrp[] = v.getView('OrdAllocGrp')?.toObject() expect(ordAlloc).toBeTruthy() expect(Array.isArray(ordAlloc)).toBeTruthy() expect(ordAlloc.length).toEqual(1) @@ -145,7 +152,7 @@ test('test OrdAllocGrp', () => { test('main Party Group', () => { const views = toViews.views const v = views[0] - const parties: IParties[] = v.getView('Parties').toObject() + const parties: IParties[] = v.getView('Parties')?.toObject() expect(parties).toBeTruthy() expect(Array.isArray(parties)).toBeTruthy() expect(parties.length).toEqual(4) @@ -166,7 +173,7 @@ test('main Party Group', () => { test('test party sub group', () => { const views = toViews.views const v = views[0] - const parties: IPtysSubGrp[] = v.getView('Parties.PtysSubGrp').toObject() + const parties: IPtysSubGrp[] = v.getView('Parties.PtysSubGrp')?.toObject() expect(parties).toBeTruthy() expect(Array.isArray(parties)).toBeTruthy() expect(parties.length).toEqual(1) @@ -180,7 +187,7 @@ test('test party sub group', () => { test('test AllocGrp.NestedParties.NstdPtysSubGrp', () => { const views = toViews.views const v = views[0] - const parties: INstdPtysSubGrp[] = v.getView('AllocGrp.NestedParties.NstdPtysSubGrp').toObject() + const parties: INstdPtysSubGrp[] = v.getView('AllocGrp.NestedParties.NstdPtysSubGrp')?.toObject() expect(parties).toBeTruthy() expect(Array.isArray(parties)).toBeTruthy() expect(parties.length).toEqual(1) diff --git a/src/test/fixml/fixml-mkt-data-fut-parse.test.ts b/src/test/fixml/fixml-mkt-data-fut-parse.test.ts index d03154b6..f45a5326 100644 --- a/src/test/fixml/fixml-mkt-data-fut-parse.test.ts +++ b/src/test/fixml/fixml-mkt-data-fut-parse.test.ts @@ -31,7 +31,7 @@ test('expect an instrument from fix msg', () => { const views = toViews.views const v0 = views[0].getView('Instrument') expect(v0).toBeTruthy() - const i: IInstrument = v0.toObject() + const i: IInstrument = v0?.toObject() expect(i).toBeTruthy() expect(i.Symbol).toEqual('ZCZ9') expect(i.SecurityID).toEqual('01') @@ -46,7 +46,7 @@ test('expect an md group from fix msg', () => { const views = toViews.views const v0 = views[0].getView('MDFullGrp') expect(v0).toBeTruthy() - const fullGrp: IMDFullGrp[] = v0.toObject() + const fullGrp: IMDFullGrp[] = v0?.toObject() expect(fullGrp).toBeTruthy() expect(Array.isArray(fullGrp)) expect(fullGrp.length).toEqual(1) diff --git a/src/test/fixml/fixml-mkt-data-settle-parse.test.ts b/src/test/fixml/fixml-mkt-data-settle-parse.test.ts index 96d28c73..6bcf6746 100644 --- a/src/test/fixml/fixml-mkt-data-settle-parse.test.ts +++ b/src/test/fixml/fixml-mkt-data-settle-parse.test.ts @@ -21,7 +21,7 @@ test('expect 2 views from fix msg', () => { }) test('expect 1 batch from fix msg', () => { - const batch: IBatch = toViews.batch.toObject() + const batch: IBatch = toViews?.batch?.toObject() expect(batch).toBeTruthy() expect(batch.Batch).toBeTruthy() expect(Array.isArray(batch.Batch)).toBeTruthy() diff --git a/src/test/fixml/fixml-tc-bi-lateral-parse.test.ts b/src/test/fixml/fixml-tc-bi-lateral-parse.test.ts index 55a5daea..5ff7e8ad 100644 --- a/src/test/fixml/fixml-tc-bi-lateral-parse.test.ts +++ b/src/test/fixml/fixml-tc-bi-lateral-parse.test.ts @@ -21,7 +21,7 @@ test('expect a view from fix msg', () => { test('expect a batch view ', () => { const batch = toViews.batch expect(batch).toBeTruthy() - const o: IBatch = batch.toObject() + const o: IBatch = batch?.toObject() expect(o).toBeTruthy() const instances: ILooseObject[] = o.Batch expect(Array.isArray(instances)).toEqual(true) @@ -43,30 +43,32 @@ test('check instrument attributes', () => { expect(i).toBeTruthy() const iv = views[0].getView('Instrument') expect(iv).toBeTruthy() - expect(i.SecurityExchange).toEqual('XXXX') - expect(iv.getString('SecurityExchange')).toEqual('XXXX') - expect(iv.getString('SecurityType')).toEqual('CMDTYSWAP') - expect(i.SecurityType).toEqual('CMDTYSWAP') + expect(i?.SecurityExchange).toEqual('XXXX') + expect(iv?.getString('SecurityExchange')).toEqual('XXXX') + expect(iv?.getString('SecurityType')).toEqual('CMDTYSWAP') + expect(i?.SecurityType).toEqual('CMDTYSWAP') }) test('check instrument groups', () => { const views = toViews.views const t: ITradeCaptureReport = views[0].toObject() - const i: IInstrument = t.Instrument - const stream = i.StreamGrp + const i: IInstrument | null = t.Instrument ?? null + const stream = i?.StreamGrp expect(stream).toBeTruthy() expect(Array.isArray(stream)) - expect(stream.length).toEqual(2) + expect(stream?.length).toEqual(2) const grpView = views[0].getView('Instrument.StreamGrp') expect(grpView).toBeTruthy() - const g0 = grpView.getGroupInstance(0) - const g1 = grpView.getGroupInstance(1) + const g0 = grpView?.getGroupInstance(0) + const g1 = grpView?.getGroupInstance(1) expect(g0).toBeTruthy() expect(g1).toBeTruthy() // TotNotlUOM="MMBtu" TotNotl="310000.10" NotlUOM="MMBtu" NotlUnit="D" NotlPeriod="1" Notl="10000.20" RcvSide="1" PaySide="2" Typ="1" const keys = ['StreamTotalNotionalUnitOfMeasure', 'StreamTotalNotional', 'StreamNotionalUnitOfMeasure', 'StreamNotionalFrequencyUnit', 'StreamNotionalFrequencyPeriod', 'StreamNotional', 'StreamReceiveSide', 'StreamPaySide', 'StreamType'] - const v0: any[] = g0.getTypedTags(keys) + const v0: any[] | undefined = g0?.getTypedTags(keys) + expect(v0).toBeTruthy() + if (!v0) return expect(v0[0]).toEqual('MMBtu') expect(v0[1]).toEqual(310000.10) expect(v0[2]).toEqual('MMBtu') @@ -77,8 +79,10 @@ test('check instrument groups', () => { expect(v0[7]).toEqual(2) expect(v0[8]).toEqual(1) + if (!g1) return // TotNotlUOM="MMBtu" TotNotl="310000.10" NotlUOM="MMBtu" NotlUnit="D" NotlPeriod="1" Notl="10000.20" RcvSide="2" PaySide="1" Typ="0"> - const v1: any[] = g1.getTypedTags(keys) + const v1: any[] | undefined = g1.getTypedTags(keys) + expect(v1).toBeTruthy() expect(v1[0]).toEqual('MMBtu') expect(v1[1]).toEqual(310000.10) expect(v1[2]).toEqual('MMBtu') @@ -90,7 +94,8 @@ test('check instrument groups', () => { expect(v1[8]).toEqual(0) }) -/*{ +/* +{ "SenderCompID": "CME", "TargetCompID": "ATS_BROKER1", "SenderSubID": "STP", @@ -99,7 +104,7 @@ test('check instrument groups', () => { test('expect Hdr view to be on Batch', () => { const batch = toViews.batch - const o: IBatch = batch.toObject() + const o: IBatch = batch?.toObject() const hdr: IStandardHeader = o.StandardHeader expect(hdr).toBeTruthy() expect(hdr.SenderCompID).toEqual('CME') diff --git a/src/test/fixml/repo-full-fixml-msg.test.ts b/src/test/fixml/repo-full-fixml-msg.test.ts index f19fe08c..6f268940 100644 --- a/src/test/fixml/repo-full-fixml-msg.test.ts +++ b/src/test/fixml/repo-full-fixml-msg.test.ts @@ -15,7 +15,7 @@ let definitions: FixDefinitions let jsonHelper: JsonHelper let sessionDescription: ISessionDescription const root: string = path.join(__dirname, '../../../data/examples/FIXML') -let setup: Setup = null +let setup: Setup beforeAll(async () => { setup = new Setup('session/test-initiator.json', null) @@ -27,7 +27,7 @@ beforeAll(async () => { async function testEncodeDecode (asObj: ILooseObject, msgType: string): Promise { // encode to FIXML format from provided object. - return new Promise(async (resolve, reject) => { + return await new Promise(async (resolve, reject) => { const fe = new FixmlEncoder(new ElasticBuffer(), definitions) fe.encode(asObj, msgType) const fixml: string = fe.buffer.toString() @@ -53,7 +53,7 @@ async function testEncodeDecode (asObj: ILooseObject, msgType: string): Promise< test('MktDataFull settle fixml object', async () => { const msgType: string = 'MktDataFull' - const file: string = path.join(root,'cme/md/settle') + const file: string = path.join(root, 'cme/md/settle') const asObj: ILooseObject = jsonHelper.fromJson(`${file}/object.json`, msgType) const o: ILooseObject = await testEncodeDecode(asObj, msgType) expect(o).toEqual(asObj) @@ -61,7 +61,7 @@ test('MktDataFull settle fixml object', async () => { test('AllocInstrctn fixml object', async () => { const msgType: string = 'AllocInstrctn' - const file: string = path.join(root,'cme/alloc/Claiming Firm Requests Sub-allocation with Allocation Instructions/') + const file: string = path.join(root, 'cme/alloc/Claiming Firm Requests Sub-allocation with Allocation Instructions/') const asObj: ILooseObject = jsonHelper.fromJson(`${file}/object.json`, msgType) const o: ILooseObject = await testEncodeDecode(asObj, msgType) expect(o).toEqual(asObj) @@ -69,7 +69,7 @@ test('AllocInstrctn fixml object', async () => { test('AllocRpt fixml object', async () => { const msgType: string = 'AllocRpt' - const file: string = path.join(root,'cme/alloc/Clearing System Notifies Allocation to the Claiming Firm - Cross-Exchange/') + const file: string = path.join(root, 'cme/alloc/Clearing System Notifies Allocation to the Claiming Firm - Cross-Exchange/') const asObj: ILooseObject = jsonHelper.fromJson(`${file}/object.json`, msgType) const o: ILooseObject = await testEncodeDecode(asObj, msgType) expect(o).toEqual(asObj) @@ -77,7 +77,7 @@ test('AllocRpt fixml object', async () => { test('TrdCaptRpt fixml object', async () => { const msgType: string = 'TrdCaptRpt' - const file: string = path.join(root,'cme/tc/Delivery Fixed Commodity Swap') + const file: string = path.join(root, 'cme/tc/Delivery Fixed Commodity Swap') const asObj: ILooseObject = jsonHelper.fromJson(`${file}/object.json`, msgType) const o: ILooseObject = await testEncodeDecode(asObj, msgType) expect(o).toEqual(asObj) @@ -85,7 +85,7 @@ test('TrdCaptRpt fixml object', async () => { test('MktDataFull fixml object', async () => { const msgType: string = 'MktDataFull' - const file: string = path.join(root,'cme/md/futures') + const file: string = path.join(root, 'cme/md/futures') const asObj: ILooseObject = jsonHelper.fromJson(`${file}/object.json`, msgType) const o: ILooseObject = await testEncodeDecode(asObj, msgType) expect(o).toEqual(asObj) @@ -93,7 +93,7 @@ test('MktDataFull fixml object', async () => { test('UserReq logon fixml object', async () => { const msgType: string = 'UserReq' - const file: string = path.join(root,'cme/ur/logon') + const file: string = path.join(root, 'cme/ur/logon') const asObj: ILooseObject = jsonHelper.fromJson(`${file}/object.json`, msgType) const o: ILooseObject = await testEncodeDecode(asObj, msgType) expect(o).toEqual(asObj) @@ -101,7 +101,7 @@ test('UserReq logon fixml object', async () => { test('UserReq logoff fixml object', async () => { const msgType: string = 'UserReq' - const file: string = path.join(root,'cme/ur/logoff') + const file: string = path.join(root, 'cme/ur/logoff') const asObj: ILooseObject = jsonHelper.fromJson(`${file}/object.json`, msgType) const o: ILooseObject = await testEncodeDecode(asObj, msgType) expect(o).toEqual(asObj) @@ -109,7 +109,7 @@ test('UserReq logoff fixml object', async () => { test('TrdCaptRpt 2 fixml object', async () => { const msgType: string = 'TrdCaptRpt' - const file: string = path.join(root,'cme/tc/Initial Single Side Submission') + const file: string = path.join(root, 'cme/tc/Initial Single Side Submission') const asObj: ILooseObject = jsonHelper.fromJson(`${file}/object.json`, msgType) const o: ILooseObject = await testEncodeDecode(asObj, msgType) expect(o).toEqual(asObj) @@ -117,7 +117,7 @@ test('TrdCaptRpt 2 fixml object', async () => { test('TrdCaptRpt 3 fixml object', async () => { const msgType: string = 'TrdCaptRpt' - const file: string = path.join(root,'cme/tc/Accepted Unmatched') + const file: string = path.join(root, 'cme/tc/Accepted Unmatched') const asObj: ILooseObject = jsonHelper.fromJson(`${file}/object.json`, msgType) const o: ILooseObject = await testEncodeDecode(asObj, msgType) expect(o).toEqual(asObj) @@ -125,7 +125,7 @@ test('TrdCaptRpt 3 fixml object', async () => { test('TrdCaptRptReq fixml object', async () => { const msgType: string = 'TrdCaptRptReq' - const file: string = path.join(root,'cme/tc/Trading Firm Continued Subscription') + const file: string = path.join(root, 'cme/tc/Trading Firm Continued Subscription') const asObj: ILooseObject = jsonHelper.fromJson(`${file}/object.json`, msgType) const o: ILooseObject = await testEncodeDecode(asObj, msgType) expect(o).toEqual(asObj) @@ -133,7 +133,7 @@ test('TrdCaptRptReq fixml object', async () => { test('Order fixml object', async () => { const msgType: string = 'Order' - const file: string = path.join(root,'om/nso') + const file: string = path.join(root, 'om/nso') const asObj: ILooseObject = jsonHelper.fromJson(`${file}/object.json`, msgType) const o: ILooseObject = await testEncodeDecode(asObj, msgType) expect(o).toEqual(asObj) @@ -141,7 +141,7 @@ test('Order fixml object', async () => { test('ExecRpt fixml object', async () => { const msgType: string = 'ExecRpt' - const file: string = path.join(root,'om/er') + const file: string = path.join(root, 'om/er') const asObj: ILooseObject = jsonHelper.fromJson(`${file}/object.json`, msgType) const o: ILooseObject = await testEncodeDecode(asObj, msgType) expect(o).toEqual(asObj) diff --git a/src/transport/ascii/ascii-msg-transmitter.ts b/src/transport/ascii/ascii-msg-transmitter.ts index 29fc39d8..e908a281 100644 --- a/src/transport/ascii/ascii-msg-transmitter.ts +++ b/src/transport/ascii/ascii-msg-transmitter.ts @@ -14,17 +14,17 @@ export class AsciiMsgTransmitter extends MsgTransmitter { public msgSeqNum: number public time: Date - private readonly header: ContainedFieldSet - private readonly trailer: ContainedFieldSet + private readonly header: ContainedFieldSet | null + private readonly trailer: ContainedFieldSet | null constructor (@inject(DITokens.IJsFixConfig) public readonly config: IJsFixConfig) { super(config.sessionContainer.resolve(DITokens.TransmitBuffer), config.definitions, config.description) - this.msgSeqNum = (config.description.LastSentSeqNum || 0) + 1 // adding 1 as this the next sequence # to use. + this.msgSeqNum = (config.description.LastSentSeqNum ?? 0) + 1 // adding 1 as this the next sequence # to use. const buffer = this.buffer const tf: TimeFormatter = new TimeFormatter(buffer) this.encoder = new AsciiEncoder(buffer, config.definitions, tf, - config.delimiter || AsciiChars.Soh, - config.logDelimiter || AsciiChars.Pipe) + config.delimiter ?? AsciiChars.Soh, + config.logDelimiter ?? AsciiChars.Pipe) const components = config.definitions.component this.header = components.get('StandardHeader') this.trailer = components.get('StandardTrailer') @@ -43,7 +43,7 @@ export class AsciiMsgTransmitter extends MsgTransmitter { return checksum } - public encodeMessage (msgType: string, obj: ILooseObject): void { + public encodeMessage (msgType: string, obj: ILooseObject): any { const encoder: AsciiEncoder = this.encoder as AsciiEncoder const factory = this.config.factory let headerProps: Partial = {} @@ -54,29 +54,35 @@ export class AsciiMsgTransmitter extends MsgTransmitter { headerProps.OrigSendingTime = SendingTime // when first sent } + const msgSeqNum = this.msgSeqNum const sendingTime = this.time || new Date() - const hdr: ILooseObject = factory.header(msgType, this.msgSeqNum, sendingTime, headerProps) - + const hdr: ILooseObject | null = factory?.header(msgType, msgSeqNum, sendingTime, headerProps) ?? null // Only increment sequence number if this is not a duplicate message. + if (!hdr) return null if (!headerProps.PossDupFlag) { this.msgSeqNum++ } const buffer = this.buffer buffer.reset() - const msgDef: MessageDefinition = this.definitions.message.get(msgType) + const msgDef: MessageDefinition | null = this.definitions.message.get(msgType) if (!msgDef) { this.emit('error', new Error(`ascii transmitter cannot find definition for ${msgType}`)) return } - encoder.encode(hdr, this.header.name) + const headerName = this.header?.name ?? 'header' + const trailerName = this.trailer?.name ?? 'trailer' + encoder.encode(hdr, headerName) encoder.encode(bodyProps, msgDef.name) const lenPos = encoder.bodyLengthPos - const bodyLength: number = Math.max(4, this.config.description.BodyLengthChars || 7) + const bodyLength: number = Math.max(4, this.config.description.BodyLengthChars ?? 7) const len = buffer.getPos() - encoder.msgTypePos buffer.patchPaddedNumberAtPos(lenPos, len, bodyLength) - let checksum: number = this.checksum() - const trl: ILooseObject = factory.trailer(checksum) - encoder.encode(trl, this.trailer.name) + const checksum: number = this.checksum() + const trl: ILooseObject | null = factory?.trailer(checksum) ?? null + if (trl) { + encoder.encode(trl, trailerName) + } + return hdr } } diff --git a/src/transport/ascii/ascii-session-msg-factory.ts b/src/transport/ascii/ascii-session-msg-factory.ts index ea4bcc38..2f91b1ec 100644 --- a/src/transport/ascii/ascii-session-msg-factory.ts +++ b/src/transport/ascii/ascii-session-msg-factory.ts @@ -11,10 +11,9 @@ import { } from '../../types/FIX4.4/repo' export class AsciiSessionMsgFactory extends ASessionMsgFactory { - - constructor (readonly description: ISessionDescription, mutator: ObjectMutator = null) { + constructor (readonly description: ISessionDescription, mutator: ObjectMutator | null = null) { super(description, mutator) - this.isAscii = description.application.protocol === 'ascii' + this.isAscii = description.application?.protocol === 'ascii' } public logon (): ILooseObject { @@ -31,14 +30,14 @@ export class AsciiSessionMsgFactory extends ASessionMsgFactory { public logout (text: string): ILooseObject { const o: ILogout = { - Text: text + Text: text } as ILogout return this.mutate(o, MsgType.Logout) } public header (msgType: string, seqNum: number, time: Date, overrideData?: Partial): ILooseObject { const description = this.description - const bodyLength: number = Math.max(4, description.BodyLengthChars || 7) + const bodyLength: number = Math.max(4, description.BodyLengthChars ?? 7) const placeHolder = Math.pow(10, bodyLength - 1) + 1 const o: IStandardHeader = { BeginString: description.BeginString, @@ -56,7 +55,7 @@ export class AsciiSessionMsgFactory extends ASessionMsgFactory { } export class SessionMsgFactory extends AsciiSessionMsgFactory { - constructor (public readonly description: ISessionDescription, public mutator: ObjectMutator = null) { + constructor (public readonly description: ISessionDescription, public mutator: ObjectMutator | null = null) { super(description, mutator) } } diff --git a/src/transport/ascii/ascii-session.ts b/src/transport/ascii/ascii-session.ts index 00c4e150..faf750bf 100644 --- a/src/transport/ascii/ascii-session.ts +++ b/src/transport/ascii/ascii-session.ts @@ -9,9 +9,8 @@ import { IMsgApplication } from '../msg-application' import { SegmentType } from '../../buffer/segment/segment-type' export abstract class AsciiSession extends FixSession { - public heartbeat: boolean = true - protected store: IFixMsgStore = null + protected store: IFixMsgStore | null = null protected resender: FixMsgAsciiStoreResend protected constructor (public readonly config: IJsFixConfig) { @@ -23,7 +22,6 @@ export abstract class AsciiSession extends FixSession { } private checkSeqNo (msgType: string, view: MsgView): boolean { - switch (msgType) { case MsgType.SequenceReset: { return true @@ -74,22 +72,26 @@ export abstract class AsciiSession extends FixSession { private sendReject (msgType: string, seqNo: number, msg: string, reason: number): void { const factory = this.config.factory - const reject = factory.reject(msgType, seqNo, msg, reason) - this.sessionLogger.warning(`rejecting with ${JSON.stringify(reject)}`) - this.send(MsgType.Reject, reject) + const reject = factory?.reject(msgType, seqNo, msg, reason) + if (reject) { + this.sessionLogger.warning(`rejecting with ${JSON.stringify(reject)}`) + this.send(MsgType.Reject, reject) + } } - protected sendResendRequest (lastSeq: number, receivedSeq: number) { - const resend = this.config.factory.resendRequest(lastSeq + 1, 0) - this.sessionLogger.warning(`received seq ${receivedSeq}, but last known seq is ${lastSeq}. Sending resend request for all messages > ${lastSeq}`) - this.send(MsgType.ResendRequest, resend) + protected sendResendRequest (lastSeq: number, receivedSeq: number): void { + const resend = this.config.factory?.resendRequest(lastSeq + 1, 0) + if (resend) { + this.sessionLogger.warning(`received seq ${receivedSeq}, but last known seq is ${lastSeq}. Sending resend request for all messages > ${lastSeq}`) + this.send(MsgType.ResendRequest, resend) + } } private checkIntegrity (msgType: string, view: MsgView): boolean { const state = this.sessionState const seqNum = view.getTyped(MsgTag.MsgSeqNum) - const received: number = parseInt(view.getString(MsgTag.CheckSum), 10) + const received: number = parseInt(view.getString(MsgTag.CheckSum) ?? '', 10) const computed = view.checksum() if (received !== computed) { const msg: string = `msgType ${msgType} checksum failed. received = ${received} computed = ${computed}` @@ -110,7 +112,7 @@ export abstract class AsciiSession extends FixSession { return false } - const undefinedMsg: string = view.undefinedForMsg() + const undefinedMsg: string | null = view.undefinedForMsg() if (undefinedMsg) { const msg: string = `msgType ${msgType} ${undefinedMsg}` this.sendReject(msgType, seqNum, msg, SessionRejectReason.TagNotDefinedForThisMessageType) @@ -155,7 +157,7 @@ export abstract class AsciiSession extends FixSession { * Override to resend stored messages following a sequence reset. * @protected */ - protected onResendRequest (view: MsgView) { + protected onResendRequest (view: MsgView): void { // if no records are in store then send a gap fill for entire sequence this.setState(SessionState.HandleResendRequest) const [beginSeqNo, endSeqNo] = view.getTypedTags([MsgTag.BeginSeqNo, MsgTag.EndSeqNo]) @@ -164,7 +166,9 @@ export abstract class AsciiSession extends FixSession { const validRecords = records.filter(rec => rec.obj !== null) this.sessionLogger.info(`sending ${validRecords.length}`) validRecords.forEach(rec => { - this.send(rec.msgType, rec.obj) + if (rec.obj) { + this.send(rec.msgType, rec.obj) + } }) this.setState(SessionState.ActiveNormalSession) }).catch(e => { @@ -181,7 +185,6 @@ export abstract class AsciiSession extends FixSession { } protected onSessionMsg (msgType: string, view: MsgView): void { - const logger = this.sessionLogger switch (msgType) { @@ -202,8 +205,10 @@ export abstract class AsciiSession extends FixSession { } case MsgType.TestRequest: { - const req: string = view.getString(MsgTag.TestReqID) - this.sendHeartbeat(req) + const req: string | null = view.getString(MsgTag.TestReqID) + if (req) { + this.sendHeartbeat(req) + } break } @@ -235,7 +240,6 @@ export abstract class AsciiSession extends FixSession { } protected onMsg (msgType: string, view: MsgView): void { - if (!this.checkSeqNo(msgType, view)) { this.sessionLogger.info(`message '${msgType}' failed checkSeqNo.`) return @@ -272,13 +276,15 @@ export abstract class AsciiSession extends FixSession { } } - private startTimer (interval: number = 200) { + private startTimer (interval: number = 200): void { + const logger = this.sessionLogger + logger.info(`start heartbeat timer. interval = ${interval}`) this.timer = setInterval(() => { this.tick() }, interval) } - private peerLogon (view: MsgView) { + private peerLogon (view: MsgView): void { const logger = this.sessionLogger const [heartBtInt, peerCompId, userName, password] = view.getTypedTags([MsgTag.HeartBtInt, MsgTag.SenderCompID, MsgTag.Username, MsgTag.Password]) logger.info(`peerLogon Username = ${userName}, heartBtInt = ${heartBtInt}, peerCompId = ${peerCompId}, userName = ${userName}`) @@ -297,29 +303,34 @@ export abstract class AsciiSession extends FixSession { this.setState(SessionState.InitiationLogonReceived) } if (this.heartbeat) { - logger.debug(`start heartbeat timer.`) this.startTimer() } - logger.info(`system ready, inform app`) + logger.info('system ready, inform app') this.onReady(view) } - private sendTestRequest () { + private sendTestRequest (): void { const factory = this.config.factory this.setState(SessionState.AwaitingProcessingResponseToTestRequest) - this.send(MsgType.TestRequest, factory.testRequest()) + const tr = factory?.testRequest() + if (tr) { + this.send(MsgType.TestRequest, tr) + } } - private sendHeartbeat (testReqId: string) { + private sendHeartbeat (testReqId: string): void { const factory = this.config.factory - this.send(MsgType.Heartbeat, factory.heartbeat(testReqId)) + const hb = factory?.heartbeat(testReqId) + if (hb) { + this.send(MsgType.Heartbeat, hb) + } } private tick (): void { if (!this.transport) return const sessionState = this.sessionState const action: TickAction = sessionState.calcAction(new Date()) - const application: IMsgApplication = this.transport.config.description.application + const application: IMsgApplication | null = this.transport.config.description.application ?? null const logger = this.sessionLogger switch (action) { @@ -342,7 +353,7 @@ export abstract class AsciiSession extends FixSession { case TickAction.TerminateOnError: { logger.info(sessionState.toString()) - this.terminate(new Error(`${application.name}: peer not responding`)) + this.terminate(new Error(`${application?.name}: peer not responding`)) break } @@ -354,7 +365,7 @@ export abstract class AsciiSession extends FixSession { } default: - throw new Error(`unexpected action`) + throw new Error('unexpected action') } } } diff --git a/src/transport/duplex/http-duplex.ts b/src/transport/duplex/http-duplex.ts index 7948d369..c06575c0 100644 --- a/src/transport/duplex/http-duplex.ts +++ b/src/transport/duplex/http-duplex.ts @@ -1,6 +1,6 @@ import { FixDuplex } from './fix-duplex' import { Readable, Writable } from 'stream' -import * as rp from 'request-promise-native' +import axios, { isCancel, AxiosError } from 'axios' import { IHttpAdapter } from '../http/http-adapter' export class HttpDuplex extends FixDuplex { @@ -13,7 +13,7 @@ export class HttpDuplex extends FixDuplex { private static makeReadable (): Readable { const Readable = require('stream').Readable const reader = { - read: () => { + read: () => { // nothing } } @@ -28,13 +28,15 @@ export class HttpDuplex extends FixDuplex { try { const adapter = this.adapter const options = adapter.getOptions(data) - rp(options).then((message: any) => { - const body = adapter.endMessage(message) - forward.push(body) - done() - }).catch((err: Error) => { - receiver.emit('error', err) - }) + if (options) { + axios(options).then((message: any) => { + const body = adapter.endMessage(message) + forward.push(body) + done() + }).catch((err: Error) => { + receiver.emit('error', err) + }) + } } catch (e) { done(e) } diff --git a/src/transport/duplex/string-duplex.ts b/src/transport/duplex/string-duplex.ts index f56dc35f..f14ffb6b 100644 --- a/src/transport/duplex/string-duplex.ts +++ b/src/transport/duplex/string-duplex.ts @@ -2,7 +2,6 @@ import { FixDuplex } from './fix-duplex' import { Readable, Writable } from 'stream' export class StringDuplex extends FixDuplex { - constructor (public readonly text: string = '', public chunks: boolean = false) { super() this.readable = StringDuplex.makeReadable(text, chunks) @@ -13,7 +12,7 @@ export class StringDuplex extends FixDuplex { const Readable = require('stream').Readable let total: number = 0 const reader = { - read: (size: number) => { + read: (size: number) => { total += size if (text.length > 0 && total > text.length) { readable.push(null) @@ -47,17 +46,17 @@ export class StringDuplex extends FixDuplex { return receiver } - private static sendReaderChunks (text: string, readable: Readable) { + private static sendReaderChunks (text: string, readable: Readable): void { // simulate a set of chunks sent to parser let start = 0 let iteration = 0 let remaining = text.length while (remaining > 0) { iteration++ - let chunk = Math.min(remaining, iteration % 10) + const chunk = Math.min(remaining, iteration % 10) remaining -= chunk const end = start + chunk - let snippet = text.slice(start, end) + const snippet = text.slice(start, end) readable.push(snippet) start = end } diff --git a/src/transport/factory/msg-transport.ts b/src/transport/factory/msg-transport.ts index a0be61e2..9ab8b90f 100644 --- a/src/transport/factory/msg-transport.ts +++ b/src/transport/factory/msg-transport.ts @@ -12,12 +12,11 @@ export class MsgTransport { public readonly receiver: MsgParser constructor (@inject(DITokens.sessionId) public readonly id: number, - @inject(DITokens.IJsFixConfig) public readonly config: IJsFixConfig, - @inject(DITokens.FixDuplex) public readonly duplex: FixDuplex) { - + @inject(DITokens.IJsFixConfig) public readonly config: IJsFixConfig, + @inject(DITokens.FixDuplex) public readonly duplex: FixDuplex) { const delimiter = config.delimiter if (!delimiter) { - throw new Error(`no delimiter char given.`) + throw new Error('no delimiter char given.') } const sessionContainer = this.config.sessionContainer @@ -39,8 +38,8 @@ export class MsgTransport { this.duplex.end() } - public wait (): Promise { - return new Promise((resolve, reject) => { + public async wait (): Promise { + return await new Promise((resolve, reject) => { this.receiver.on('end', () => { resolve(this.id) }) diff --git a/src/transport/fix-acceptor.ts b/src/transport/fix-acceptor.ts index b0f2f826..778fde8a 100644 --- a/src/transport/fix-acceptor.ts +++ b/src/transport/fix-acceptor.ts @@ -5,9 +5,9 @@ import { IMsgApplication } from './msg-application' export abstract class FixAcceptor extends events.EventEmitter { public transports: INumericKeyed = {} - protected constructor (public readonly application: IMsgApplication) { + protected constructor (public readonly application: IMsgApplication | null) { super() } abstract listen (): void - abstract close (cb: Function): void + abstract close (cb?: Function): void } diff --git a/src/transport/fix-entity.ts b/src/transport/fix-entity.ts index b4113460..4849313d 100644 --- a/src/transport/fix-entity.ts +++ b/src/transport/fix-entity.ts @@ -3,7 +3,7 @@ import * as events from 'events' export abstract class FixEntity extends events.EventEmitter { abstract start (): Promise - constructor (public readonly config: IJsFixConfig) { + protected constructor (public readonly config: IJsFixConfig) { super() } } diff --git a/src/transport/fix-initiator.ts b/src/transport/fix-initiator.ts index f8faf77b..3386f576 100644 --- a/src/transport/fix-initiator.ts +++ b/src/transport/fix-initiator.ts @@ -2,7 +2,7 @@ import { MsgTransport } from './factory' import { IMsgApplication } from './msg-application' export abstract class FixInitiator { - protected constructor (public readonly application: IMsgApplication) { + protected constructor (public readonly application: IMsgApplication | null) { } public abstract connect (timeout: number): Promise diff --git a/src/transport/fixml/fixml-msg-transmitter.ts b/src/transport/fixml/fixml-msg-transmitter.ts index d99eabb6..5c296854 100644 --- a/src/transport/fixml/fixml-msg-transmitter.ts +++ b/src/transport/fixml/fixml-msg-transmitter.ts @@ -15,14 +15,15 @@ export class FixmlMsgTransmitter extends MsgTransmitter { this.encoder = config.sessionContainer.resolve(DITokens.MsgEncoder) } - public encodeMessage (msgType: string, obj: ILooseObject): void { - const adapter = this.config.description.application.http.adapter + public encodeMessage (msgType: string, obj: ILooseObject): any { + const adapter = this?.config?.description?.application?.http?.adapter if (adapter) { adapter.beginMessage(msgType) } const fe = this.encoder as FixmlEncoder const factory = this.config.factory - obj.StandardHeader = factory.header() + obj.StandardHeader = factory?.header() fe.encode(obj, msgType) + return obj.StandardHeader } } diff --git a/src/transport/fixml/fixml-session-msg-factory.ts b/src/transport/fixml/fixml-session-msg-factory.ts index a1fdbdee..489b6e3a 100644 --- a/src/transport/fixml/fixml-session-msg-factory.ts +++ b/src/transport/fixml/fixml-session-msg-factory.ts @@ -12,8 +12,7 @@ import { } from '../../types/FIXML50SP2' export class FixmlSessionMsgFactory extends ASessionMsgFactory { - - constructor (readonly description: ISessionDescription, mutator: ObjectMutator = null) { + constructor (readonly description: ISessionDescription, mutator: ObjectMutator | null = null) { super(description, mutator) } diff --git a/src/transport/fixml/fixml-session.ts b/src/transport/fixml/fixml-session.ts index bc44741a..6ee35c98 100644 --- a/src/transport/fixml/fixml-session.ts +++ b/src/transport/fixml/fixml-session.ts @@ -6,7 +6,6 @@ import { FixSession } from '../session/fix-session' import { SessionState } from '../session/session-state' export abstract class FixmlSession extends FixSession { - protected constructor (public readonly config: IJsFixConfig) { super(config) this.requestLogoutType = 'UserReq' @@ -15,7 +14,6 @@ export abstract class FixmlSession extends FixSession { } protected onMsg (msgType: string, view: MsgView): void { - switch (msgType) { case 'UserReq': case 'UserRsp': { @@ -66,16 +64,19 @@ export abstract class FixmlSession extends FixSession { } } - private peerLogon (view: MsgView) { + private peerLogon (view: MsgView): void { const logger = this.sessionLogger const state = this.sessionState state.state = SessionState.InitiationLogonReceived state.peerCompId = view.getTyped(MsgTag.SenderCompID) if (this.acceptor) { - const reqId: string = view.getString('UserReqID') - this.send('UserRsp', this.config.factory.logon(reqId, true)) + const reqId: string = view.getString('UserReqID') ?? 'req' + const o = this?.config?.factory?.logon(reqId, true) + if (o) { + this.send('UserRsp', o) + } } - logger.info(`system ready, inform app`) + logger.info('system ready, inform app') this.onReady(view) } } diff --git a/src/transport/http/html-options.ts b/src/transport/http/html-options.ts index f0005004..f7d6a614 100644 --- a/src/transport/http/html-options.ts +++ b/src/transport/http/html-options.ts @@ -1,8 +1,8 @@ export interface IHtmlOptions { - method: string, - uri: string, - json: boolean, - body: any, - headers: any, + method: string + url: string + json: boolean + data: any + headers: any resolveWithFullResponse: boolean } diff --git a/src/transport/http/html-route.ts b/src/transport/http/html-route.ts index 610b104c..6e3e49d8 100644 --- a/src/transport/http/html-route.ts +++ b/src/transport/http/html-route.ts @@ -1,6 +1,6 @@ import { IHtmlOptions } from './html-options' export interface IHtmlRoute { - name: string, + name: string value: IHtmlOptions } diff --git a/src/transport/http/http-acceptor-listener.ts b/src/transport/http/http-acceptor-listener.ts index 0036838d..0ca2c863 100644 --- a/src/transport/http/http-acceptor-listener.ts +++ b/src/transport/http/http-acceptor-listener.ts @@ -12,8 +12,9 @@ export class HttpAcceptorListener extends FixEntity { constructor (@inject(DITokens.IJsFixConfig) public readonly config: IJsFixConfig) { super(config) } - start (): Promise { - return new Promise(async (accept, reject) => { + + async start (): Promise { + return new Promise(async (resolve, reject) => { const logger = this.config.logFactory.logger('acceptor') const sessionContainer = this.config.sessionContainer if (!sessionContainer.isRegistered(DITokens.FixSession)) { @@ -24,15 +25,16 @@ export class HttpAcceptorListener extends FixEntity { acceptor.on('transport', (t: MsgTransport) => { logger.info(`creates new transport using DI token ${DITokens.FixSession}.`) const acceptorSession = sessionContainer.resolve(DITokens.FixSession) + this.emit('session', acceptorSession, t) acceptorSession.run(t).then(() => { logger.info('ends') acceptor.close(() => { logger.info('acceptor closed.') - accept(true) + resolve(true) }) }).catch((e: Error) => { logger.error(e) - logger.info(e.stack) + logger.info(e?.stack ?? '') reject(e) }) }) diff --git a/src/transport/http/http-acceptor.ts b/src/transport/http/http-acceptor.ts index ba5d5df7..9402ec25 100644 --- a/src/transport/http/http-acceptor.ts +++ b/src/transport/http/http-acceptor.ts @@ -14,16 +14,16 @@ import { DITokens } from '../../runtime/di-tokens' @injectable() export class HttpAcceptor extends FixAcceptor { - private app: express.Express = express() + private readonly app: express.Express = express() private server: http.Server private readonly logger: IJsFixLogger private readonly router: express.Router private nextId: number = 0 - private keys: Dictionary = new Dictionary() + private readonly keys: Dictionary = new Dictionary() constructor (@inject(DITokens.IJsFixConfig) public readonly config: IJsFixConfig) { - super(config.description.application) - this.logger = config.logFactory.logger(`${config.description.application.name}:HttpAcceptor`) + super(config?.description?.application ?? null) + this.logger = config.logFactory.logger(`${config?.description?.application?.name}:HttpAcceptor`) this.logger.info('creating http server') this.router = express.Router() this.router.use(bodyParser.json()) @@ -33,21 +33,21 @@ export class HttpAcceptor extends FixAcceptor { public listen (): void { const app = this.config.description.application - const port = app.http.port + const port = app?.http?.port ?? 0 const logger = this.logger logger.info(`start to listen ${port}`) this.server = this.app.listen(port, () => { - logger.info(`app listening at http://localhost:${port}${app.http.uri}`) + logger.info(`app listening at http://localhost:${port}${app?.http?.uri}`) }) - this.server.on('error', ((err: Error) => { + this.server.on('error', (err: Error) => { logger.error(err) this.emit('error', err) - })) + }) } public close (callback?: (err?: Error) => void): void { const app = this.config.description.application - const port = app.http.port + const port = app?.http?.port this.logger.info(`close listener on port ${port}`) this.server.close(callback) } @@ -69,36 +69,36 @@ export class HttpAcceptor extends FixAcceptor { this.logger.info(`transport ${tid} ends total transports = ${keys.length}`) } - private respond (duplex: FixDuplex, res: express.Response, token: string = null): Promise { - return new Promise((accept, reject) => { - res.setHeader('Content-Type', 'application/json') + private async respond (duplex: FixDuplex, response: express.Response, token: string | null = null): Promise { + return new Promise((resolve, reject) => { + response.setHeader('Content-Type', 'application/json') const timer = setTimeout(() => { - const businessReject = `` + const businessReject = '' const b = Buffer.from(businessReject, 'utf-8') duplex.writable.removeListener('data', transmit) - res.send(b) + response.send(b) reject(new Error('no response')) }, 5000) - const transmit = (d: Buffer) => { + const transmit = (d: Buffer): void => { this.logger.info('responding to request') clearTimeout(timer) if (token) { - res.setHeader('authorization', token) + response.setHeader('authorization', token) } duplex.writable.removeListener('data', transmit) - res.send(d) - accept(true) + response.send(d) + resolve(true) } duplex.writable.on('data', transmit) }) } - private async logon (req: express.Request, res: express.Response) { + private async logon (req: express.Request, res: express.Response): Promise { const body: IFixmlRequest = req.body const id = this.nextId++ - this.logger.info(JSON.stringify(body, null,4)) + this.logger.info(JSON.stringify(body, null, 4)) // check hand back session key const d = new StringDuplex() const transport = new MsgTransport(id, this.config, d) @@ -111,28 +111,30 @@ export class HttpAcceptor extends FixAcceptor { d.readable.push(body.fixml) } - private async logout (req: express.Request, res: express.Response) { + private async logout (req: express.Request, res: express.Response): Promise { const headers = req.headers const body: IFixmlRequest = req.body - const t: MsgTransport = this.keys.get(headers.authorization) + const t: MsgTransport | null = this.keys.get(headers?.authorization ?? '') if (t) { const token = req.headers.authorization - this.harvestTransport(token, t.id) - const d = t.duplex - this.respond(d, res, token).then(() => { - this.logger.info('responded to logout') - t.end() - }).catch((e: Error) => { - this.logger.error(e) - }) - d.readable.push(body.fixml) + if (token) { + this.harvestTransport(token, t.id) + const d = t.duplex + this.respond(d, res, token).then(() => { + this.logger.info('responded to logout') + t.end() + }).catch((e: Error) => { + this.logger.error(e) + }) + d.readable.push(body.fixml) + } } } private subscribe (): void { const router = this.router const app = this.config.description.application - const root = app.http.uri + const root = app?.http?.uri const authorise = `${root}authorise` const query = `${root}query` this.logger.info(`uri: authorise ${authorise}, query ${query}`) @@ -149,9 +151,9 @@ export class HttpAcceptor extends FixAcceptor { router.get(query, async (req: express.Request, res: express.Response) => { const headers = req.headers const body: IFixmlRequest = req.body - const t: MsgTransport = this.keys.get(headers.authorization) + const t: MsgTransport | null = this.keys.get(headers.authorization ?? '') ?? null if (!t) { - this.logger.info(`received request with no token`) + this.logger.info('received request with no token') res.send({ error: 'no key with query' }) diff --git a/src/transport/http/http-adapter.ts b/src/transport/http/http-adapter.ts index 9492e45b..6b40480c 100644 --- a/src/transport/http/http-adapter.ts +++ b/src/transport/http/http-adapter.ts @@ -1,7 +1,7 @@ import { IHtmlOptions } from './html-options' export interface IHttpAdapter { - getOptions (data: Buffer): IHtmlOptions - beginMessage (msgType: string): void - endMessage (m: any): Buffer + getOptions: (data: Buffer) => IHtmlOptions | null + beginMessage: (msgType: string) => void + endMessage: (m: any) => Buffer } diff --git a/src/transport/http/http-initiator.ts b/src/transport/http/http-initiator.ts index 5a04c939..30ca0f0f 100644 --- a/src/transport/http/http-initiator.ts +++ b/src/transport/http/http-initiator.ts @@ -14,16 +14,16 @@ export class HttpInitiator extends FixEntity { this.logger = config.logFactory.logger('initiator') } - start (): Promise { - return this.connect(this.config) + async start (): Promise { + return await this.connect(this.config) } -// the adapter will be provided on config - connect (config: IJsFixConfig): Promise { - return new Promise(async (accept, reject) => { - const adapter = config.description.application.http.adapter + // the adapter will be provided on config + async connect (config: IJsFixConfig): Promise { + return await new Promise(async (resolve, reject) => { + const adapter = config?.description?.application?.http?.adapter if (!adapter) { - reject('http initiator needs config.description.application.http.adapter') + reject(new Error('http initiator needs config.description.application.http.adapter')) } const sessionContainer = this.config.sessionContainer if (!sessionContainer.isRegistered(DITokens.FixSession)) { @@ -32,15 +32,21 @@ export class HttpInitiator extends FixEntity { this.logger.info(`create session with DI Token ${DITokens.FixSession}`) const initiatorSession = sessionContainer.resolve(DITokens.FixSession) this.logger.info('connecting ...') - const initiatorTransport: MsgTransport = new MsgTransport(0, config, new HttpDuplex(adapter)) - this.logger.info('... connected, run session') - initiatorSession.run(initiatorTransport).then(() => { - this.logger.info('ends') - accept(true) - }).catch((e: Error) => { - this.logger.error(e) - reject(e) - }) + const initiatorTransport: MsgTransport | null = adapter + ? new MsgTransport(0, config, new HttpDuplex(adapter)) + : null + if (!initiatorTransport) { + reject(new Error('no initiatorTransport for http')) + } else { + this.logger.info('... connected, run session') + initiatorSession.run(initiatorTransport).then(() => { + this.logger.info('ends') + resolve(true) + }).catch((e: Error) => { + this.logger.error(e) + reject(e) + }) + } }) } } diff --git a/src/transport/http/http-json-sample-adapter.ts b/src/transport/http/http-json-sample-adapter.ts index 7ca6dda3..93c92232 100644 --- a/src/transport/http/http-json-sample-adapter.ts +++ b/src/transport/http/http-json-sample-adapter.ts @@ -9,14 +9,14 @@ import { DITokens } from '../../runtime/di-tokens' @injectable() export class HttpJsonSampleAdapter implements IHttpAdapter { - private logger: IJsFixLogger - private queue: HttpTransaction[] = [] - private token: string = null - private routes: Dictionary = new Dictionary() + private readonly logger: IJsFixLogger + private readonly queue: HttpTransaction[] = [] + private token: string | null = null + private readonly routes: Dictionary = new Dictionary() constructor (@inject(DITokens.IJsFixConfig) public readonly config: IJsFixConfig) { this.logger = config.logFactory.logger('http.adapter') const routes = this.routes - const options = config.description.application.http.options + const options = config?.description?.application?.http?.options if (!options) { return } @@ -26,17 +26,18 @@ export class HttpJsonSampleAdapter implements IHttpAdapter { this.logger.info(`instance created routes ${routes.count()}`) } - public getOptions (data: Buffer): IHtmlOptions { + public getOptions (data: Buffer): IHtmlOptions | null { const q = this.queue if (q.length === 0) { return null } - const next: HttpTransaction = q.shift() + const next: HttpTransaction | null = q.shift() ?? null + if (next == null) return null const options = next.options - options.body = { + options.data = { fixml: data.toString() } - this.logger.info(`${next.msgType}: ${next.options.method} ${next.options.uri} ${data.length}`) + this.logger.info(`${next.msgType}: ${next.options.method} ${next.options.url} ${data.length}`) return options } @@ -47,19 +48,19 @@ export class HttpJsonSampleAdapter implements IHttpAdapter { this.token = headers.authorization this.logger.info(`receive token ${this.token}`) } - return m.body + return m.data } beginMessage (msgType: string): void { // build options based on type const routes = this.routes - const route = routes.get(msgType) || routes.get('default') + const route = routes.get(msgType) ?? routes.get('default') const options = { - method: route.value.method, - uri: route.value.uri, - json: route.value.json, - resolveWithFullResponse: route.value.resolveWithFullResponse, - headers: route.value.headers + method: route?.value.method, + url: route?.value.url, + json: route?.value.json, + resolveWithFullResponse: route?.value.resolveWithFullResponse, + headers: route?.value.headers } as IHtmlOptions const headers = options.headers if (headers) { diff --git a/src/transport/http/http-transaction.ts b/src/transport/http/http-transaction.ts index f9c1cc20..1139c4ef 100644 --- a/src/transport/http/http-transaction.ts +++ b/src/transport/http/http-transaction.ts @@ -2,6 +2,6 @@ import { IHtmlOptions } from './html-options' export class HttpTransaction { constructor (public readonly msgType: string, - public readonly options: IHtmlOptions) { + public readonly options: IHtmlOptions) { } } diff --git a/src/transport/http/http-transport-description.ts b/src/transport/http/http-transport-description.ts index ae9a4ea7..de08ec7b 100644 --- a/src/transport/http/http-transport-description.ts +++ b/src/transport/http/http-transport-description.ts @@ -3,7 +3,7 @@ import { IHttpAdapter } from './http-adapter' export interface IHttpTransportDescription { readonly port: number - readonly uri: string, + readonly uri: string readonly options: IHtmlRoute[] adapter: IHttpAdapter } diff --git a/src/transport/msg-application.ts b/src/transport/msg-application.ts index 08a8d7a4..50618500 100644 --- a/src/transport/msg-application.ts +++ b/src/transport/msg-application.ts @@ -3,11 +3,11 @@ import { IHttpTransportDescription } from './http/http-transport-description' export interface IMsgApplication { readonly name: string - readonly type: string, + readonly type: string readonly resilient: boolean readonly reconnectSeconds: number - readonly tcp?: ITcpTransportDescription, - readonly http?: IHttpTransportDescription, - readonly protocol: string, + readonly tcp?: ITcpTransportDescription + readonly http?: IHttpTransportDescription + readonly protocol: string readonly dictionary: string } diff --git a/src/transport/msg-transmitter.ts b/src/transport/msg-transmitter.ts index 512cc5fe..555889ff 100644 --- a/src/transport/msg-transmitter.ts +++ b/src/transport/msg-transmitter.ts @@ -11,8 +11,8 @@ export abstract class MsgTransmitter extends events.EventEmitter { public encoder: MsgEncoder protected constructor (public readonly buffer: ElasticBuffer, - public readonly definitions: FixDefinitions, - public readonly session: ISessionDescription) { + public readonly definitions: FixDefinitions, + public readonly session: ISessionDescription) { super() this.encodeStream = this.encoderStream() this.encodeStream.on('error', (e: Error) => { @@ -28,7 +28,7 @@ export abstract class MsgTransmitter extends events.EventEmitter { this.encodeStream.write(new MsgPayload(msgType, obj)) } - public abstract encodeMessage (msgType: string, obj: ILooseObject): void + public abstract encodeMessage (msgType: string, obj: ILooseObject): any // read fix messages from one side, encode buffers on other ready to pipe // to output stream, say a socket @@ -41,11 +41,11 @@ export abstract class MsgTransmitter extends events.EventEmitter { try { const msgType = payload.msgType transmitter.encoder.reset() - transmitter.encodeMessage(msgType, payload.obj) + const state = transmitter.encodeMessage(msgType, payload.obj) payload.encoded = transmitter.encoder.trim() this.push(payload.encoded) const encodedTxt = transmitter.buffer.toString() - transmitter.emit('encoded', msgType, encodedTxt) + transmitter.emit('encoded', msgType, encodedTxt, state) done() } catch (e) { done(e) diff --git a/src/transport/session/a-session-msg-factory.ts b/src/transport/session/a-session-msg-factory.ts index 70881f51..2a353c07 100644 --- a/src/transport/session/a-session-msg-factory.ts +++ b/src/transport/session/a-session-msg-factory.ts @@ -13,12 +13,11 @@ import { ITestRequest } from '../../types/FIX4.4/repo' -export interface ObjectMutator { (description: ISessionDescription, type: string, o: ILooseObject): ILooseObject -} +export type ObjectMutator = (description: ISessionDescription, type: string, o: ILooseObject) => ILooseObject export abstract class ASessionMsgFactory implements ISessionMsgFactory { public isAscii: boolean - constructor (public readonly description: ISessionDescription, public mutator: ObjectMutator = null) { + protected constructor (public readonly description: ISessionDescription, public mutator: ObjectMutator | null = null) { } public reject (msgType: string, seqNo: number, msg: string, reason: number): ILooseObject { diff --git a/src/transport/session/fix-session-state.ts b/src/transport/session/fix-session-state.ts index 9ff60491..396e41a9 100644 --- a/src/transport/session/fix-session-state.ts +++ b/src/transport/session/fix-session-state.ts @@ -3,14 +3,15 @@ import moment = require('moment') import { TickAction } from '../tick-action' import { IFixSessionStateArgs } from './fix-session-state-args' import { SessionState } from './session-state' +import { ILooseObject } from '../../collections/collection' export class FixSessionState { public nextTickAction: TickAction = TickAction.Nothing - public lastReceivedAt: Date = null - public LastSentAt: Date = null - public lastTestRequestAt: Date = null - public logoutSentAt: Date = null + public lastReceivedAt: Date | null = null + public LastSentAt: Date | null = null + public lastTestRequestAt: Date | null = null + public logoutSentAt: Date | null = null public now: Date = new Date() public compId: string = '' public peerCompId: string = '' @@ -24,8 +25,9 @@ export class FixSessionState { private secondsSinceLogoutSent: number = -1 private secondsSinceSent: number = -1 private secondsSinceReceive: number = -1 + public lastHeader: ILooseObject | null = null - public reset (resetSeqNo: boolean): void { + public reset (lastPeerMsgSeqNum: number = 0): void { this.lastReceivedAt = null this.LastSentAt = null this.lastTestRequestAt = null @@ -35,16 +37,17 @@ export class FixSessionState { this.peerHeartBeatSecs = 0 this.logoutSentAt = null this.nextTickAction = TickAction.Nothing - if (resetSeqNo) { - this.lastPeerMsgSeqNum = 0 - } + this.lastPeerMsgSeqNum = lastPeerMsgSeqNum + this.lastHeader = null } - public constructor ({ heartBeat, - state = SessionState.Idle, - waitLogoutConfirmSeconds = 5, - stopSeconds = 2, - lastPeerMsgSeqNum = 0 }: IFixSessionStateArgs) { + public constructor ({ + heartBeat, + state = SessionState.Idle, + waitLogoutConfirmSeconds = 5, + stopSeconds = 2, + lastPeerMsgSeqNum = 0 + }: IFixSessionStateArgs) { this.heartBeat = heartBeat this.state = state this.waitLogoutConfirmSeconds = waitLogoutConfirmSeconds @@ -52,15 +55,18 @@ export class FixSessionState { this.lastPeerMsgSeqNum = lastPeerMsgSeqNum } - private static dateAsString (d: Date) { + private static dateAsString (d: Date | null): string { if (!d) { return 'null' } return moment(d).format('HH:mm:ss.SSS') } - public toString (): string { + public lastSentSeqNum (): number { + return this?.lastHeader?.MsgSeqNum ?? 0 + } + public toString (): string { const buffer = new ElasticBuffer(1024) buffer.writeString(`compId = ${this.compId}, `) @@ -79,6 +85,7 @@ export class FixSessionState { buffer.writeString(`peerHeartBeatSecs = ${this.peerHeartBeatSecs}, `) buffer.writeString(`peerCompId = ${this.peerCompId}, `) buffer.writeString(`lastPeerMsgSeqNum = ${this.lastPeerMsgSeqNum}, `) + buffer.writeString(`LastSentSeqNum = ${this.lastSentSeqNum()}, `) buffer.writeString(`secondsSinceLogoutSent = ${this.secondsSinceLogoutSent}, `) buffer.writeString(`secondsSinceSent = ${this.secondsSinceSent}, `) buffer.writeString(`secondsSinceReceive = ${this.secondsSinceReceive}`) @@ -91,7 +98,6 @@ export class FixSessionState { this.calcState() switch (this.state) { - case SessionState.PeerLogonRejected: { if (this.secondsSinceSent >= this.stopSeconds) { this.nextTickAction = TickAction.Stop @@ -154,7 +160,7 @@ export class FixSessionState { const time = this.now.getTime() this.nextTickAction = TickAction.Nothing this.secondsSinceLogoutSent = this.logoutSentAt ? (time - this.logoutSentAt.getTime()) / 1000 : -1 - this.secondsSinceSent = (time - this.LastSentAt.getTime()) / 1000 - this.secondsSinceReceive = (time - this.lastReceivedAt.getTime()) / 1000 + this.secondsSinceSent = this.LastSentAt != null ? (time - this.LastSentAt.getTime()) / 1000 : 0 + this.secondsSinceReceive = this.lastReceivedAt != null ? (time - this.lastReceivedAt.getTime()) / 1000 : 0 } } diff --git a/src/transport/session/fix-session.ts b/src/transport/session/fix-session.ts index 7d27f0c8..899e06b2 100644 --- a/src/transport/session/fix-session.ts +++ b/src/transport/session/fix-session.ts @@ -11,8 +11,8 @@ import { SegmentType } from '../../buffer/segment/segment-type' export abstract class FixSession extends events.EventEmitter { public logReceivedMsgs: boolean = false - protected timer: NodeJS.Timer = null - protected transport: MsgTransport = null + protected timer: NodeJS.Timer | null = null + protected transport: MsgTransport | null = null public manageSession: boolean = true public checkMsgIntegrity: boolean = false protected readonly me: string @@ -27,37 +27,75 @@ export abstract class FixSession extends events.EventEmitter { protected constructor (public readonly config: IJsFixConfig) { super() const description = config.description - this.me = description.application.name + this.me = description?.application?.name ?? 'me' this.sessionState = new FixSessionState( - { heartBeat: config.description.HeartBtInt, - lastPeerMsgSeqNum: config.description.LastReceivedSeqNum}) + { + heartBeat: config.description.HeartBtInt, + lastPeerMsgSeqNum: config.description.LastReceivedSeqNum + }) this.sessionLogger = config.logFactory.logger(`${this.me}:FixSession`) - this.initiator = description.application.type === 'initiator' + this.initiator = description?.application?.type === 'initiator' this.acceptor = !this.initiator this.checkMsgIntegrity = this.acceptor this.sessionState.compId = description.SenderCompId } - public setState (state: SessionState) { - if (state === this.sessionState.state) return + stateStr (theState: SessionState): string { + return SessionState[theState] + } + + assignState (newState: SessionState): void { + const currentState = this.sessionState.state + const currentStateStr = this.stateStr(currentState) const logger = this.sessionLogger - const prevState = this.sessionState.state - const msg = `current state ${SessionState[prevState]} (${prevState}) moves to ${SessionState[state]} (${state})` + const msg = `current state ${currentStateStr} (${currentState}) moves to ${SessionState[newState]} (${newState})` logger.info(msg) - this.sessionState.state = state + this.sessionState.state = newState + } + + public setState (state: SessionState): void { + const logger = this.sessionLogger + const currentState = this.sessionState.state + const currentStateStr = this.stateStr(currentState) + if (state === currentState) return + switch (currentState) { + case SessionState.ConfirmingLogout: + case SessionState.Stopped: + if (state !== SessionState.NetworkConnectionEstablished) { + logger.info(`ignoring request to change state as now already in ${currentStateStr}`) + } else { + this.assignState(state) + } + break + + default: { + this.assignState(state) + } + } } public getState (): SessionState { return this.sessionState.state } - public sendLogon () { - this.send(this.requestLogonType, this.config.factory.logon()) + public lastSentSeqNum (): number { + return this.sessionState.lastSentSeqNum() + } + + public lastPeerSeqNum (): number { + return this.sessionState.lastPeerMsgSeqNum } - private waitPromise (): Promise { + public sendLogon (): void { + const lo = this.config.factory?.logon() + if (lo) { + this.send(this.requestLogonType, lo) + } + } + + private async waitPromise (): Promise { const logger = this.sessionLogger - return new Promise((accept, reject) => { + return await new Promise((resolve, reject) => { if (this.initiator) { logger.debug(`initiator sending logon state = ${this.stateString()}`) this.sendLogon() @@ -73,12 +111,12 @@ export abstract class FixSession extends events.EventEmitter { }) this.on('done', () => { - accept(this.transport.id) + resolve(this.transport?.id) }) }) } - public run (transport: MsgTransport): Promise { + public async run (transport: MsgTransport): Promise { const logger = this.sessionLogger if (this.transport) { logger.info(`reset from previous transport. state ${this.stateString()}`) @@ -86,18 +124,13 @@ export abstract class FixSession extends events.EventEmitter { } this.transport = transport this.subscribe() - return this.waitPromise() + return await this.waitPromise() } - protected expectedState (): boolean { + protected expectedEndState (): boolean { switch (this.sessionState.state) { - case SessionState.ActiveNormalSession: - case SessionState.ReceiveLogout: case SessionState.Stopped: case SessionState.ConfirmingLogout: - case SessionState.HandleResendRequest: - case SessionState.AwaitingProcessingResponseToTestRequest: - case SessionState.AwaitingProcessingResponseToResendRequest: return true default: @@ -105,65 +138,97 @@ export abstract class FixSession extends events.EventEmitter { } } - protected subscribe () { + protected rxOnEnd (): void { + const logger = this.sessionLogger + logger.info(`rx end received sessionState = [${this.sessionState.toString()}]`) + const expectedState = this.expectedEndState() + if (expectedState) { + logger.info(`rx receives end state = ${this.stateString()} - stop session`) + this.stop() + } else { + this.setState(SessionState.DetectBrokenNetworkConnection) + const e = new Error(`unexpected state - transport failed? = ${this.stateString()}`) + logger.info(`rx error ${e.message}`) + this.terminate(e) + } + } - const transport = this.transport + protected rxOnMsg (msgType: string, view: MsgView): void { const logger = this.sessionLogger - const rx = transport.receiver - const tx = transport.transmitter + if (this.logReceivedMsgs) { + const name = view.segment.type !== SegmentType.Unknown ? view?.segment?.set?.name : 'unknown' + logger.info(`${msgType}: ${name}`) + logger.info(`${view.toString()}`) + } + this.sessionState.lastReceivedAt = new Date() + if (this.manageSession) { + this.onMsg(msgType, view) + } else { + this.checkForwardMsg(msgType, view) + } + } - rx.on('msg', (msgType: string, view: MsgView) => { - if (this.logReceivedMsgs) { - const name = view.segment.type !== SegmentType.Unknown ? view.segment.set.name : 'unknown' - logger.info(`${msgType}: ${name}`) - logger.info(`${view.toString()}`) - } - this.sessionState.lastReceivedAt = new Date() - if (this.manageSession) { - this.onMsg(msgType, view) - } else { - this.checkForwardMsg(msgType, view) - } - }) + protected rxOnDone (): void { + const logger = this.sessionLogger + logger.info('rx done received') + this.done() + } - rx.on('error', (e: Error) => { - logger.warning(`rx error event: ${e.message} ${e.stack || ''}`) - this.terminate(e) - }) + protected rxOnError (e: Error): void { + const logger = this.sessionLogger + logger.warning(`rx error event: ${e.message} ${e.stack ?? ''}`) + this.terminate(e) + } - rx.on('done', () => { - logger.info('rx done received') - this.done() - }) + protected rxOnDecoded (msgType: string, data: ElasticBuffer, ptr: number): void { + const logger = this.sessionLogger + logger.debug(`rx: [${msgType}] ${ptr} bytes`) + this.onDecoded(msgType, data.toString(ptr)) + } - rx.on('end', () => { - logger.info(`rx end received sessionState = [${this.sessionState.toString()}]`) - const expectedState = this.expectedState() - if (expectedState) { - logger.info(`rx graceful end state = ${this.stateString()}`) - this.done() - } else { - const e = new Error(`unexpected state - transport failed? = ${this.stateString()}`) - logger.info(`rx error ${e.message}`) - this.terminate(e) - } - }) + protected txOnError (e: Error): void { + const logger = this.sessionLogger + logger.warning(`tx error event: ${e.message} ${e.stack ?? ''}`) + this.terminate(e) + } - rx.on('decoded', (msgType: string, data: ElasticBuffer, ptr: number) => { - logger.debug(`rx: [${msgType}] ${ptr} bytes`) - this.onDecoded(msgType, data.toString(ptr)) - }) + protected txOnEncoded (msgType: string, data: string, hdr: ILooseObject): void { + const logger = this.sessionLogger + this.sessionState.lastHeader = hdr + logger.debug(`tx: [${msgType}] ${data.length} bytes seqNo = ${this.lastSentSeqNum()}`) + this.onEncoded(msgType, data) + } - tx.on('error', (e: Error) => { - logger.warning(`tx error event: ${e.message} ${e.stack || ''}`) - this.terminate(e) - }) + protected unsubscribe (): void { + const logger = this.sessionLogger + logger.info(`unsubscribe sessionState = [${this.sessionState.toString()}]`) + const transport = this.transport + const rx = transport?.receiver + const tx = transport?.transmitter + + rx?.removeListener('msg', this.rxOnMsg) + rx?.removeListener('error', this.rxOnError) + rx?.removeListener('done', this.rxOnDone) + rx?.removeListener('end', this.rxOnEnd) + rx?.removeListener('decoded', this.rxOnDecoded) + tx?.removeListener('error', this.txOnError) + tx?.removeListener('encoded', this.txOnEncoded) + } - tx.on('encoded', (msgType: string, data: string) => { - logger.debug(`tx: [${msgType}] ${data.length} bytes`) - this.onEncoded(msgType, data) - }) + protected subscribe (): void { + const transport = this.transport + + const rx = transport?.receiver + const tx = transport?.transmitter + const inst = this + rx?.on('msg', (msgType: string, view: MsgView) => inst.rxOnMsg(msgType, view)) + rx?.on('error', (e: Error) => inst.rxOnError(e)) + rx?.on('done', () => inst.rxOnDone()) + rx?.on('end', () => inst.rxOnEnd()) + rx?.on('decoded', (msgType: string, data: ElasticBuffer, ptr: number) => inst.rxOnDecoded(msgType, data, ptr)) + tx?.on('error', (e: Error) => inst.txOnError(e)) + tx?.on('encoded', (msgType: string, data: string, hdr: ILooseObject) => inst.txOnEncoded(msgType, data, hdr)) } protected validStateApplicationMsg (): boolean { @@ -191,12 +256,18 @@ export abstract class FixSession extends events.EventEmitter { this.onApplicationMsg(msgType, view) } - protected terminate (error: Error): void { - if (this.sessionState.state === SessionState.Stopped) return - this.sessionLogger.error(error) + private stopTimer (): void { if (this.timer) { + this.sessionLogger.info('stopTimer') clearInterval(this.timer) + this.timer = null } + } + + protected terminate (error: Error): void { + if (this.sessionState.state === SessionState.Stopped) return + this.sessionLogger.error(error) + this.stopTimer() if (this.transport) { this.transport.end() } @@ -205,7 +276,7 @@ export abstract class FixSession extends events.EventEmitter { this.emit('error', error) } - protected peerLogout (view: MsgView) { + protected peerLogout (view: MsgView): void { const msg = view.getString(MsgTag.Text) const state = this.sessionState.state switch (state) { @@ -225,7 +296,7 @@ export abstract class FixSession extends events.EventEmitter { } } - protected send (msgType: string, obj: ILooseObject) { + protected send (msgType: string, obj: ILooseObject): void { const state = this.sessionState.state switch (state) { case SessionState.Stopped: { @@ -235,16 +306,19 @@ export abstract class FixSession extends events.EventEmitter { default: { this.sessionState.LastSentAt = new Date() - this.transport.transmitter.send(msgType, obj) + this.transport?.transmitter.send(msgType, obj) break } } } - protected sendLogout (msg: string) { + protected sendLogout (msg: string): void { const factory = this.config.factory this.sessionLogger.info(`sending logout with ${msg}`) - this.send(this.requestLogoutType, factory.logout(this.requestLogoutType, msg)) + const lo = factory?.logout(this.requestLogoutType, msg) + if (lo) { + this.send(this.requestLogoutType, lo) + } } protected sessionLogout (): void { @@ -267,7 +341,8 @@ export abstract class FixSession extends events.EventEmitter { } case SessionState.ConfirmingLogout: { - // this instance responds to logout + // this instance responds to log out + this.setState(SessionState.ConfirmingLogout) sessionState.logoutSentAt = new Date() const msg = `${this.me} confirming logout` this.sessionLogger.info(msg) @@ -291,7 +366,7 @@ export abstract class FixSession extends events.EventEmitter { } case SessionState.Stopped: - this.sessionLogger.info(`done. session is now stopped`) + this.sessionLogger.info('done. session is now stopped') break default: { @@ -302,25 +377,23 @@ export abstract class FixSession extends events.EventEmitter { this.sessionLogger.info(`done. check logout sequence state ${this.stateString()}`) } - public reset (): void { - if (this.timer) { - clearInterval(this.timer) - } + public reset (resetSeqNum?: number | null): void { + this.stopTimer() this.transport = null - const resetSeqNum = this.config.description.ResetSeqNumFlag || true - this.sessionState.reset(resetSeqNum) // from header def ... eventually + const resetFlag = this.config.description.ResetSeqNumFlag + const seqNum = resetFlag ? 0 : resetSeqNum ?? this.sessionState.lastPeerMsgSeqNum + this.sessionState.reset(seqNum) // from header def ... eventually this.setState(SessionState.NetworkConnectionEstablished) } - protected stop (error: Error = null): void { + protected stop (error: Error | null = null): void { if (this.sessionState.state === SessionState.Stopped) { return } - if (this.timer) { - clearInterval(this.timer) - } - this.sessionLogger.info(`stop: kill transport`) - this.transport.end() + this.stopTimer() + this.unsubscribe() + this.sessionLogger.info('stop: kill transport') + this.transport?.end() if (error) { this.sessionLogger.info(`stop: emit error ${error.message}`) this.emit('error', error) @@ -329,7 +402,7 @@ export abstract class FixSession extends events.EventEmitter { } this.setState(SessionState.Stopped) - this.onStopped(error) + this.onStopped(error ?? undefined) this.transport = null } diff --git a/src/transport/session/make-fix-session.ts b/src/transport/session/make-fix-session.ts index fc933191..d603d9f3 100644 --- a/src/transport/session/make-fix-session.ts +++ b/src/transport/session/make-fix-session.ts @@ -1,5 +1,4 @@ import { IJsFixConfig } from '../../config' import { FixSession } from './fix-session' -export interface MakeFixSession { (config: IJsFixConfig): FixSession -} +export type MakeFixSession = (config: IJsFixConfig) => FixSession diff --git a/src/transport/session/session-description.ts b/src/transport/session/session-description.ts index b24a6b78..13c26902 100644 --- a/src/transport/session/session-description.ts +++ b/src/transport/session/session-description.ts @@ -9,10 +9,10 @@ export interface ISessionDescription { readonly SenderCompId: string readonly TargetCompID: string readonly ResetSeqNumFlag: boolean - readonly LastSentSeqNum?: number + LastSentSeqNum?: number readonly LastReceivedSeqNum?: number readonly SenderSubID: string readonly TargetSubID: string readonly BeginString: string - readonly BodyLengthChars?: number, + readonly BodyLengthChars?: number } diff --git a/src/transport/session/session-msg-factory.ts b/src/transport/session/session-msg-factory.ts index 4a1b76b9..a5adad62 100644 --- a/src/transport/session/session-msg-factory.ts +++ b/src/transport/session/session-msg-factory.ts @@ -4,13 +4,13 @@ import { ISessionDescription } from './session-description' export interface ISessionMsgFactory { description: ISessionDescription - reject (msgType: string, seqNo: number, msg: string, reason: number): ILooseObject - logout (msgType: string, text: string): ILooseObject - logon (userRequestId?: string, isResponse?: boolean): ILooseObject - testRequest (reqId?: string): ILooseObject - resendRequest (from: number, to: number): ILooseObject - sequenceReset (newSeq: number, gapFill?: boolean): ILooseObject - heartbeat (testReqId: string): ILooseObject - header (msgType?: string, seqNum?: number, time?: Date, overrideData?: Partial): ILooseObject - trailer (checksum: number): ILooseObject + reject: (msgType: string, seqNo: number, msg: string, reason: number) => ILooseObject + logout: (msgType: string, text: string) => ILooseObject + logon: (userRequestId?: string, isResponse?: boolean) => ILooseObject + testRequest: (reqId?: string) => ILooseObject + resendRequest: (from: number, to: number) => ILooseObject + sequenceReset: (newSeq: number, gapFill?: boolean) => ILooseObject + heartbeat: (testReqId: string) => ILooseObject + header: (msgType?: string, seqNum?: number, time?: Date, overrideData?: Partial) => ILooseObject + trailer: (checksum: number) => ILooseObject } diff --git a/src/transport/tcp/recovering-tcp-initiator.ts b/src/transport/tcp/recovering-tcp-initiator.ts index f78ff143..dacfa533 100644 --- a/src/transport/tcp/recovering-tcp-initiator.ts +++ b/src/transport/tcp/recovering-tcp-initiator.ts @@ -11,38 +11,39 @@ import { IMsgApplication } from '../msg-application' import { FixEntity } from '../fix-entity' /* - create one application session instance - and recover a lost transport. Hence the application + create one application session instance - and recover a lost transport. Hence, the application will automatically re-connected and the "message recovery" policy enacted i.e. replay from last known sequence number or sequence reset. */ @injectable() export class RecoveringTcpInitiator extends FixEntity { - public tcp: ITcpTransportDescription + public tcp: ITcpTransportDescription | null public session: FixSession private readonly logger: IJsFixLogger - private application: IMsgApplication + private readonly application: IMsgApplication | null private initiator: TcpInitiator private transport: MsgTransport - private th: Timeout = null + private th: Timeout | null = null public recoveryAttemptSecs: number = 5 public backoffFailConnectSecs: number = 30 constructor (@inject(DITokens.IJsFixConfig) public readonly jsFixConfig: IJsFixConfig) { super(jsFixConfig) - this.application = this.jsFixConfig.description.application - this.logger = jsFixConfig.logFactory.logger(`${this.application.name}:RecoveringTcpInitiator`) + this.application = this.jsFixConfig.description.application ?? null + const name = this.application?.name ?? 'na' + this.logger = jsFixConfig.logFactory.logger(`${name}:RecoveringTcpInitiator`) if (!this.application) { - throw new Error(`no application in session description.`) + throw new Error('no application in session description.') } - this.tcp = this.application.tcp + this.tcp = this.application.tcp ?? null if (!this.tcp) { - throw new Error(`no tcp in session description need tcp { host: hostname, port: port }`) + throw new Error('no tcp in session description need tcp { host: hostname, port: port }') } this.createSession(jsFixConfig) } - private createSession (jsFixConfig: IJsFixConfig) { + private createSession (jsFixConfig: IJsFixConfig): void { this.logger.info(`creating an application session with DI token ${DITokens.FixSession}.`) this.session = jsFixConfig.sessionContainer.resolve(DITokens.FixSession) this.session.on('done', () => { @@ -60,12 +61,20 @@ export class RecoveringTcpInitiator extends FixEntity { return this.session.getState() } - private newTransport (transport: MsgTransport) { + public lastSentSeqNum (): number { + return this.session.lastSentSeqNum() + } + + private resetSeq (): boolean { + return this.jsFixConfig.description.ResetSeqNumFlag + } + + private newTransport (transport: MsgTransport): void { this.transport = transport this.emit('transport', transport) this.logger.info(`initiator connects id ${(transport.id)}`) const session = this.session - if (this.jsFixConfig.description.ResetSeqNumFlag) { + if (this.resetSeq()) { this.logger.info('reset sequence numbers') session.reset() } @@ -84,7 +93,7 @@ export class RecoveringTcpInitiator extends FixEntity { this.logger.info(`running session with transport ${transport.id} state = ${session.getState()}`) } - private clearTimer () { + private clearTimer (): void { if (this.th) { clearTimeout(this.th) this.th = null @@ -98,6 +107,13 @@ export class RecoveringTcpInitiator extends FixEntity { private recover (): void { this.session.setState(SessionState.DetectBrokenNetworkConnection) this.logger.info(`recover session transport - attempt in ${this.recoveryAttemptSecs} secs`) + if (!this.resetSeq()) { + const lastSentSeqNum = this.lastSentSeqNum() + if (lastSentSeqNum > 0) { + this.logger.info(`recover session set LastSentSeqNum ${lastSentSeqNum} for new transport`) + this.config.description.LastSentSeqNum = lastSentSeqNum + } + } this.th = setTimeout(() => { this.connect(60).then(t => { this.logger.info(`new transport ${t.id}`) @@ -105,22 +121,24 @@ export class RecoveringTcpInitiator extends FixEntity { this.logger.info(`failed to re-connect ${e.message} - backoff for ${this.backoffFailConnectSecs}`) this.th = setTimeout(() => { this.logger.info('returning to recover()') - this.recover() + setImmediate(() => { + this.recover() + }) }, this.backoffFailConnectSecs * 1000) }) - },this.recoveryAttemptSecs * 1000) + }, this.recoveryAttemptSecs * 1000) } - public start (): Promise { - return this.run() + public async start (): Promise { + return await this.run() } // for first connection - reject if no initial connection established within timeout // once connection established, will not resolve until session is ended - i.e. lost // connections are re-established using the same session instance. - public run (initialTimeout: number = 60): Promise { - return new Promise((resolve, reject) => { + public async run (initialTimeout: number = 60): Promise { + return await new Promise((resolve, reject) => { this.connect(initialTimeout).then(() => { this.on('end', () => { this.clearTimer() @@ -137,8 +155,8 @@ export class RecoveringTcpInitiator extends FixEntity { // return a promise for new transport - or reject if no connection within timeout - private connect (timeout: number): Promise { - return new Promise((resolve, reject) => { + private async connect (timeout: number): Promise { + return await new Promise((resolve, reject) => { this.logger.info(`connect: start initiator timeout ${timeout}`) this.session.setState(SessionState.InitiateConnection) this.initiator = new TcpInitiator(this.jsFixConfig) diff --git a/src/transport/tcp/tcp-acceptor-listener.ts b/src/transport/tcp/tcp-acceptor-listener.ts index 66ae4c45..b69467a7 100644 --- a/src/transport/tcp/tcp-acceptor-listener.ts +++ b/src/transport/tcp/tcp-acceptor-listener.ts @@ -13,8 +13,8 @@ export class TcpAcceptorListener extends FixEntity { super(config) } - start (): Promise { - return new Promise(async (accept, reject) => { + async start (): Promise { + return await new Promise(async (resolve, reject) => { const logger = this.config.logFactory.logger('acceptor') const sessionContainer = this.config.sessionContainer if (!sessionContainer.isRegistered(DITokens.FixSession)) { @@ -25,11 +25,12 @@ export class TcpAcceptorListener extends FixEntity { acceptor.on('transport', (t: MsgTransport) => { logger.info(`creates new transport using DI token ${DITokens.FixSession}.`) const acceptorSession = sessionContainer.resolve(DITokens.FixSession) + this.emit('session', acceptorSession, t) acceptorSession.run(t).then(() => { logger.info('ends') acceptor.close(() => { logger.info('acceptor closed.') - accept(true) + resolve(true) }) }).catch((e: Error) => { logger.info(`error in session - close listener ${e.message}`) diff --git a/src/transport/tcp/tcp-acceptor.ts b/src/transport/tcp/tcp-acceptor.ts index 0285d238..46f82dd3 100644 --- a/src/transport/tcp/tcp-acceptor.ts +++ b/src/transport/tcp/tcp-acceptor.ts @@ -11,21 +11,21 @@ import { DITokens } from '../../runtime/di-tokens' @injectable() export class TcpAcceptor extends FixAcceptor { private server: Server - private logger: IJsFixLogger + private readonly logger: IJsFixLogger private nextId: number = 0 constructor (@inject(DITokens.IJsFixConfig) public readonly config: IJsFixConfig) { - super(config.description.application) - this.logger = config.logFactory.logger(`${config.description.application.name}:TcpAcceptor`) - const tlsOptions: TlsOptions = this.tlsOptions() + super(config.description.application ?? null) + this.logger = config.logFactory.logger(`${config.description.application?.name}:TcpAcceptor`) + const tlsOptions: TlsOptions | null = this.tlsOptions() if (tlsOptions) { this.tlsServer() } else { this.unsecureServer() } - this.server.on('error', ((err: Error) => { + this.server.on('error', (err: Error) => { throw err - })) + }) } getId (): number { @@ -36,12 +36,13 @@ export class TcpAcceptor extends FixAcceptor { tlsServer (): void { try { const config: IJsFixConfig = this.config - const tcp = this.config.description.application.tcp - const tlsOptions: TlsOptions = TlsOptionsFactory.getTlsOptions(tcp.tls) - this.logger.info(`create tls server`) + const tcp = this.config.description.application?.tcp + const tlsOptions: TlsOptions | null = tcp?.tls ? TlsOptionsFactory.getTlsOptions(tcp.tls) : null + this.logger.info('create tls server') + if (!tlsOptions) return this.server = tlsCreateServer(tlsOptions, (tlsSocket: TLSSocket) => { - if (tcp.tls.enableTrace) { - this.logger.info(`enabling tls session trace`) + if (tcp?.tls?.enableTrace) { + this.logger.info('enabling tls session trace') tlsSocket.enableTrace() } if (tlsSocket.authorized) { @@ -50,7 +51,7 @@ export class TcpAcceptor extends FixAcceptor { this.logger.info(`tls creates session ${id} ${tlsSocket.authorized}`) this.onSocket(id, tlsSocket, config) } else { - this.logger.info(`no transport created on tls with no authorized connection`) + this.logger.info('no transport created on tls with no authorized connection') } }) } catch (e) { @@ -59,10 +60,10 @@ export class TcpAcceptor extends FixAcceptor { } } - unsecureServer () { + unsecureServer (): void { try { const config = this.config - this.logger.info(`create unsecured server`) + this.logger.info('create unsecured server') this.server = netCreateServer((socket: Socket) => { const id: number = this.getId() this.logger.info(`net creates session ${id}`) @@ -75,12 +76,12 @@ export class TcpAcceptor extends FixAcceptor { } } - tlsOptions (): TlsOptions { - const tcp = this.config.description.application.tcp - return TlsOptionsFactory.getTlsOptions(tcp.tls) + tlsOptions (): TlsOptions | null { + const tcp = this.config.description.application?.tcp + return tcp?.tls ? TlsOptionsFactory.getTlsOptions(tcp?.tls) : null } - private onSocket (id: number, socket: Socket, config: IJsFixConfig) { + private onSocket (id: number, socket: Socket, config: IJsFixConfig): void { const transport: MsgTransport = new MsgTransport(id, config, new TcpDuplex(socket)) this.saveTransport(id, transport) transport.receiver.on('end', () => { @@ -93,7 +94,7 @@ export class TcpAcceptor extends FixAcceptor { } public listen (): void { - const port = this.config.description.application.tcp.port + const port = this.config.description.application?.tcp?.port this.logger.info(`start to listen ${port}`) this.server.on('connection', () => { this.logger.info('insecure connection established') @@ -105,19 +106,19 @@ export class TcpAcceptor extends FixAcceptor { } public close (callback?: (err?: Error) => void): void { - const port = this.config.description.application.tcp.port + const port = this.config.description.application?.tcp?.port ?? -1 this.logger.info(`close listener on port ${port}`) this.server.close(callback) } - private saveTransport (tid: number, transport: MsgTransport) { + private saveTransport (tid: number, transport: MsgTransport): void { this.transports[tid] = transport const keys: string[] = Object.keys(this.transports) this.logger.info(`new transport id = ${tid} created total transports = ${keys.length}`) this.emit('transport', transport) } - private harvestTransport (tid: number) { + private harvestTransport (tid: number): void { delete this.transports[tid] const keys: string[] = Object.keys(this.transports) this.logger.info(`transport ${tid} ends total transports = ${keys.length}`) diff --git a/src/transport/tcp/tcp-initiator-connector.ts b/src/transport/tcp/tcp-initiator-connector.ts index 9ce74522..341bb52f 100644 --- a/src/transport/tcp/tcp-initiator-connector.ts +++ b/src/transport/tcp/tcp-initiator-connector.ts @@ -12,8 +12,10 @@ export class TcpInitiatorConnector extends FixEntity { constructor (@inject(DITokens.IJsFixConfig) public readonly config: IJsFixConfig) { super(config) } - start (reconnectTimeout: number = 0): Promise { - return new Promise(async (accept, reject) => { + + async start (reconnectTimeout: number = 0): Promise { + // eslint-disable-next-line no-async-promise-executor + return await new Promise(async (resolve, reject) => { const logger = this.config.logFactory.logger('initiator') const sessionContainer = this.config.sessionContainer if (!sessionContainer.isRegistered(DITokens.FixSession)) { @@ -27,7 +29,7 @@ export class TcpInitiatorConnector extends FixEntity { await this.connect(initiatorSession) logger.info('session has ended') connecting = false - accept(true) + resolve(true) } catch (e) { if (!reconnectTimeout) { connecting = false @@ -41,14 +43,18 @@ export class TcpInitiatorConnector extends FixEntity { }) } - delay (p: number): Promise { - return new Promise((accept) => { - if (!p) { - accept(true) + async delay (p: number): Promise { + return await new Promise((resolve, reject) => { + try { + if (!p) { + resolve(true) + } + setTimeout(() => { + resolve(true) + }, p) + } catch (e) { + reject(e) } - setTimeout(() => { - accept(true) - }, p) }) } diff --git a/src/transport/tcp/tcp-initiator.ts b/src/transport/tcp/tcp-initiator.ts index b5c68782..5807409d 100644 --- a/src/transport/tcp/tcp-initiator.ts +++ b/src/transport/tcp/tcp-initiator.ts @@ -23,21 +23,22 @@ export enum InitiatorState { @injectable() export class TcpInitiator extends FixInitiator { - public tcp: ITcpTransportDescription + public tcp: ITcpTransportDescription | null public state: InitiatorState = InitiatorState.Idle private readonly logger: IJsFixLogger private duplex: FixDuplex - private th: Timeout = null + private th: Timeout | null = null constructor (@inject(DITokens.IJsFixConfig) public readonly jsFixConfig: IJsFixConfig) { - super(jsFixConfig.description.application) - this.logger = jsFixConfig.logFactory.logger(`${this.application.name}:TcpInitiator`) + super(jsFixConfig.description.application ?? null) + const name = this.application?.name ?? 'initiator' + this.logger = jsFixConfig.logFactory.logger(`${name}:TcpInitiator`) if (!this.application) { - throw new Error(`no application in session description.`) + throw new Error('no application in session description.') } - this.tcp = this.application.tcp + this.tcp = this.application.tcp ?? null if (!this.tcp) { - throw new Error(`no tcp in session description need tcp { host: hostname, port: port }`) + throw new Error('no tcp in session description need tcp { host: hostname, port: port }') } } @@ -59,15 +60,15 @@ export class TcpInitiator extends FixInitiator { } } - public connect (timeoutSeconds: number): Promise { - return new Promise(async (resolve, reject) => { + public async connect (timeoutSeconds: number): Promise { + return await new Promise(async (resolve, reject) => { switch (this.state) { case InitiatorState.Idle: { this.state = InitiatorState.Connecting this.logger.info(`connecting with timeout ${timeoutSeconds}`) this.tryConnect() .then((t: MsgTransport) => resolve(t)) - .catch((e: Error) => { + .catch((_: Error) => { this.repeatConnect(timeoutSeconds) .then((t: MsgTransport) => resolve(t)) .catch((e: Error) => reject(e)) @@ -75,29 +76,36 @@ export class TcpInitiator extends FixInitiator { break } - default: - const e: Error = new Error(`connect not valid from non idle state`) + default: { + const e: Error = new Error('connect not valid from non idle state') this.logger.warning(`rejecting from state ${this.state}`) reject(e) + } } }) } - private unsecureDuplex (): Promise { + private async unsecureDuplex (): Promise { const tcp = this.tcp - return new Promise((resolve, reject) => { + return await new Promise((resolve, reject) => { try { - this.logger.info(`unsecureDuplex try to connect to endPoint`) - const socket = createConnection(tcp, () => { - try { - this.logger.info(`net.createConnection cb, resolving`) - const tcpDuplex = new TcpDuplex(socket) - resolve(tcpDuplex) - } catch (e) { - reject(e) - } - }) - socket.on('error', (err) => { + this.logger.info('unsecureDuplex try to connect to endPoint') + const socket = tcp + ? createConnection(tcp, () => { + try { + this.logger.info('net.createConnection cb, resolving') + if (socket) { + const tcpDuplex = new TcpDuplex(socket) + resolve(tcpDuplex) + } else { + reject(new Error('no socket in tcp initiator')) + } + } catch (e) { + reject(e) + } + }) + : null + socket?.on('error', (err) => { reject(err) }) } catch (e) { @@ -106,14 +114,15 @@ export class TcpInitiator extends FixInitiator { }) } - private tlsDuplex (): Promise < TcpDuplex > { - return new Promise((resolve, reject) => { - let tlsSocket: TLSSocket = null + private async tlsDuplex (): Promise { + return await new Promise((resolve, reject) => { + let tlsSocket: TLSSocket | null = null const tcp = this.tcp - const connectionOptions: ConnectionOptions = TlsOptionsFactory.getTlsConnectionOptions(tcp) + const connectionOptions: ConnectionOptions | null = tcp ? TlsOptionsFactory.getTlsConnectionOptions(tcp) : null if (connectionOptions) { try { tlsSocket = tlsConnect(connectionOptions, () => { + if (!tlsSocket) return null this.logger.info(`client connected ${tlsSocket.authorized ? 'authorized' : 'unauthorized'}`) if (!tlsSocket.authorized) { const error = tlsSocket.authorizationError @@ -123,11 +132,11 @@ export class TcpInitiator extends FixInitiator { } else { tlsSocket.setEncoding('utf8') const tlsDuplex = new TcpDuplex(tlsSocket) - if (tcp.tls.enableTrace) { - this.logger.info(`enabling tls session trace`) + if (tcp?.tls?.enableTrace) { + this.logger.info('enabling tls session trace') tlsSocket.enableTrace() } - this.logger.info(`tlsDuplex resolving`) + this.logger.info('tlsDuplex resolving') resolve(tlsDuplex) } }) @@ -141,12 +150,12 @@ export class TcpInitiator extends FixInitiator { }) } - private tryConnect (): Promise < MsgTransport > { - return new Promise((resolve, reject) => { + private async tryConnect (): Promise < MsgTransport > { + return await new Promise((resolve, reject) => { const tcp = this.tcp - const connectionOptions: ConnectionOptions = TlsOptionsFactory.getTlsConnectionOptions(tcp) + const connectionOptions: ConnectionOptions | null = tcp ? TlsOptionsFactory.getTlsConnectionOptions(tcp) : null const connector = connectionOptions ? this.tlsDuplex() : this.unsecureDuplex() - this.logger.info(`tryConnect ${tcp.host}:${tcp.port}`) + this.logger.info(`tryConnect ${tcp?.host}:${tcp?.port}`) connector.then(duplex => { this.duplex = duplex resolve(new MsgTransport(0, this.jsFixConfig, duplex)) @@ -156,20 +165,22 @@ export class TcpInitiator extends FixInitiator { }) } - public clearTimer () { - if (this .th) { + public clearTimer (): void { + if (this.th) { clearInterval(this.th) this.th = null } } - private repeatConnect (timeoutSeconds: number): Promise < MsgTransport > { - return new Promise(async (resolve, reject) => { + private async repeatConnect (timeoutSeconds: number): Promise < MsgTransport > { + return await new Promise(async (resolve, reject) => { const application = this.application const promisify = util.promisify const timeoutPromise = promisify(setTimeout) + const reconnectSeconds = application?.reconnectSeconds ?? 5 let retries = 0 let lastError: Error + const name = application?.name ?? 'initiator' this.th = setInterval(() => { ++retries this.tryConnect() @@ -177,15 +188,15 @@ export class TcpInitiator extends FixInitiator { this.state = InitiatorState.Connected this.clearTimer() resolve(t) - }).catch((e: Error) => { - lastError = e - this.logger.info(`${application.name}: retries ${retries} ${e.message}`) - }) - }, application.reconnectSeconds * 1000) + }).catch((e: Error) => { + lastError = e + this.logger.info(`${name}: retries ${retries} ${e.message}`) + }) + }, reconnectSeconds * 1000) timeoutPromise(timeoutSeconds * 1000).then(() => { this.clearTimer() this.state = InitiatorState.Stopped - const e = lastError ?? new Error(`${application.name}: timeout of ${timeoutSeconds} whilst connecting`) + const e = lastError ?? new Error(`${name}: timeout of ${timeoutSeconds} whilst connecting`) reject(e) }).catch(e => { reject(e) diff --git a/src/transport/tcp/tcp-transport-description.ts b/src/transport/tcp/tcp-transport-description.ts index ea9ccf0b..4fcc706e 100644 --- a/src/transport/tcp/tcp-transport-description.ts +++ b/src/transport/tcp/tcp-transport-description.ts @@ -2,6 +2,6 @@ import { ITlsOptions } from './tls-options' export interface ITcpTransportDescription { readonly port: number - readonly host: string, + readonly host: string readonly tls?: ITlsOptions } diff --git a/src/transport/tcp/tls-options-factory.ts b/src/transport/tcp/tls-options-factory.ts index 2634e2e2..7a766eaf 100644 --- a/src/transport/tcp/tls-options-factory.ts +++ b/src/transport/tcp/tls-options-factory.ts @@ -5,7 +5,7 @@ const path = require('path') const fs = require('fs') export class TlsOptionsFactory { - static read (filePath: string) { + static read (filePath: string): string { const root: string = path.join(__dirname, '../../../') const fullPath = path.join(root, filePath) return fs.readFileSync(fullPath, @@ -14,8 +14,8 @@ export class TlsOptionsFactory { }) } - static getTlsOptions (tls: ITlsOptions): TlsOptions { - let tlsOptions: TlsOptions = null + static getTlsOptions (tls: ITlsOptions): TlsOptions | null { + let tlsOptions: TlsOptions | null = null if (tls) { tlsOptions = { requestCert: tls.requestCert, @@ -24,7 +24,7 @@ export class TlsOptionsFactory { if (tls.key) { tlsOptions.key = TlsOptionsFactory.read(tls.key) - tlsOptions.cert = TlsOptionsFactory.read(tls.cert) + tlsOptions.cert = tls?.cert ? TlsOptionsFactory.read(tls?.cert) : undefined } if (tls.ca && tls.ca.length > 0) { @@ -34,15 +34,15 @@ export class TlsOptionsFactory { if (tls.nodeTlsServerOptions) { tlsOptions = { ...tlsOptions, - ...tls.nodeTlsServerOptions, + ...tls.nodeTlsServerOptions } } } return tlsOptions } - static getTlsConnectionOptions (tcp: ITcpTransportDescription): ConnectionOptions { - let connectionOptions: ConnectionOptions = null + static getTlsConnectionOptions (tcp: ITcpTransportDescription): ConnectionOptions | null { + let connectionOptions: ConnectionOptions | null = null const tls = tcp.tls if (tls) { connectionOptions = { @@ -50,8 +50,8 @@ export class TlsOptionsFactory { host: tcp.host } as ConnectionOptions if (tls.key) { - connectionOptions.key = TlsOptionsFactory.read(tcp.tls.key) - connectionOptions.cert = TlsOptionsFactory.read(tcp.tls.cert) + connectionOptions.key = TlsOptionsFactory.read(tcp.tls?.key ?? '') + connectionOptions.cert = tcp.tls?.cert ? TlsOptionsFactory.read(tcp?.tls?.cert) : undefined } if (tcp.tls.ca && tcp.tls.ca.length > 0) { connectionOptions.ca = tcp.tls.ca.map(i => TlsOptionsFactory.read(i)) @@ -65,7 +65,7 @@ export class TlsOptionsFactory { if (tls.nodeTlsConnectionOptions) { connectionOptions = { ...connectionOptions, - ...tls.nodeTlsConnectionOptions, + ...tls.nodeTlsConnectionOptions } } } diff --git a/src/transport/tcp/tls-options.ts b/src/transport/tcp/tls-options.ts index 35fa3d61..14c685ed 100644 --- a/src/transport/tcp/tls-options.ts +++ b/src/transport/tcp/tls-options.ts @@ -1,14 +1,14 @@ -import { ConnectionOptions, TlsOptions } from "tls" +import { ConnectionOptions, TlsOptions } from 'tls' export interface ITlsOptions { - readonly key?: string, - readonly cert?: string, - readonly ca?: string[], - readonly timeout?: number, - readonly sessionTimeout?: number, - readonly enableTrace?: boolean, - readonly requestCert?: boolean, - readonly rejectUnauthorized?: boolean, - readonly nodeTlsConnectionOptions?: ConnectionOptions, + readonly key?: string + readonly cert?: string + readonly ca?: string[] + readonly timeout?: number + readonly sessionTimeout?: number + readonly enableTrace?: boolean + readonly requestCert?: boolean + readonly rejectUnauthorized?: boolean + readonly nodeTlsConnectionOptions?: ConnectionOptions readonly nodeTlsServerOptions?: TlsOptions } diff --git a/src/util/json-helper.ts b/src/util/json-helper.ts index bb74cff7..5dc042ba 100644 --- a/src/util/json-helper.ts +++ b/src/util/json-helper.ts @@ -17,7 +17,7 @@ export class JsonHelper { constructor (public readonly definitions: FixDefinitions) { } - private static patchSimple (object: ILooseObject, field: ContainedSimpleField) { + private static patchSimple (object: ILooseObject, field: ContainedSimpleField): void { let name: string = field.definition.name let v: any = object[name] if (v == null) { @@ -35,11 +35,12 @@ export class JsonHelper { break } - case TagType.UtcTimestamp: + case TagType.UtcTimestamp: { const m = moment(v) const d = m.toDate() object[name] = d break + } case TagType.UtcDateOnly: { const m = moment(v) @@ -74,7 +75,7 @@ export class JsonHelper { public fromJson (fileName: string, msgType: string): ILooseObject { const msg: ILooseObject = require(fileName) - const def: MessageDefinition = this.definitions.message.get(msgType) + const def: MessageDefinition | null = this.definitions.message.get(msgType) if (!def) { return msg } @@ -103,14 +104,14 @@ export class JsonHelper { this.dispatcher.dispatchFields(set.fields, dispatcher) } - private patchComponent (object: ILooseObject, cf: ContainedComponentField) { + private patchComponent (object: ILooseObject, cf: ContainedComponentField): void { const c = object[cf.name] || object[cf.definition.name] if (c) { this.patchJsonFields(cf.definition, c) } } - private patchGroup (object: ILooseObject, gf: ContainedGroupField) { + private patchGroup (object: ILooseObject, gf: ContainedGroupField): void { const arr: ILooseObject[] = object[gf.definition.name] || object[gf.name] if (arr) { arr.forEach((o) => { diff --git a/src/util/message-generator.ts b/src/util/message-generator.ts index 9a5fd62b..6973cf51 100644 --- a/src/util/message-generator.ts +++ b/src/util/message-generator.ts @@ -11,7 +11,7 @@ export class MessageGenerator { constructor (public readonly words: string[], public readonly definitions: FixDefinitions) { } - public static getRandomEnum (field: SimpleFieldDefinition) { + public static getRandomEnum (field: SimpleFieldDefinition): any { const tagType: TagType = field.tagType const keys: string[] = field.enums.keys() const choice: string = keys[Math.floor(Math.random() * keys.length)] @@ -56,7 +56,7 @@ export class MessageGenerator { } density = Math.max(0.2, density) density = Math.min(1.0, density) - const def: MessageDefinition = this.definitions.message.get(msgType) + const def: MessageDefinition | null = this.definitions.message.get(msgType) if (!def) { throw new Error(`definitions do not contain type ${msgType}`) } @@ -68,7 +68,7 @@ export class MessageGenerator { return reducer.reduce(set, { simple: (a: ILooseObject, sf: ContainedSimpleField) => { const tag: number = sf.definition.tag - let include = tag === set.firstSimple.definition.tag || this.length > 0 || Math.random() <= density + const include = tag === set.firstSimple.definition.tag || this.length > 0 || Math.random() <= density if (include) { const val: any = sf.definition.isEnum() ? MessageGenerator.getRandomEnum(sf.definition) : this.createSimple(sf) if (val != null) { @@ -97,7 +97,6 @@ export class MessageGenerator { private createSimple (field: ContainedSimpleField): any { const tagType: TagType = field.definition.tagType switch (tagType) { - case TagType.String: { return this.nextString() } @@ -131,21 +130,21 @@ export class MessageGenerator { this.length = 0 const now: Date = new Date() return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() - , 0, 0, 0, 0)) + , 0, 0, 0, 0)) } case TagType.UtcTimeOnly: { this.length = 0 const s: Date = new Date() return new Date(Date.UTC(0, 0, 0, s.getUTCHours(), s.getUTCMinutes(), - s.getUTCSeconds(), s.getUTCMilliseconds())) + s.getUTCSeconds(), s.getUTCMilliseconds())) } case TagType.UtcTimestamp: { this.length = 0 const now: Date = new Date() return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), - now.getUTCHours(), now.getUTCMinutes(), now.getUTCSeconds())) + now.getUTCHours(), now.getUTCMinutes(), now.getUTCSeconds())) } case TagType.LocalDate: { diff --git a/tsconfig.json b/tsconfig.json index 7788f26f..6bd58656 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "rootDir": "src", "experimentalDecorators": true, "emitDecoratorMetadata": true, "target": "es6", @@ -11,6 +12,7 @@ "sourceMap": true, "inlineSources": true, "pretty": true, + "strictNullChecks": true, "outDir": "./dist" }, "compileOnSave": true, diff --git a/tslint.json b/tslint.json deleted file mode 100644 index 79670514..00000000 --- a/tslint.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "tslint-config-standard" -} \ No newline at end of file