From e0493623bfeb700b2413e4b87d964975845eac77 Mon Sep 17 00:00:00 2001 From: TimelordUK Date: Sun, 1 Jan 2023 00:22:53 +0000 Subject: [PATCH 01/19] switch to eslint using typescript rather than tslint - fix the many new errors picked up on mew linter --- .eslintrc | 29 + .eslintrc.js | 6 - .idea/inspectionProfiles/Project_Default.xml | 8 +- package-lock.json | 3142 ++++++++++++----- package.json | 24 +- src/buffer/ascii/ascii-chars.ts | 1 - src/buffer/ascii/ascii-encoder.ts | 34 +- src/buffer/ascii/ascii-parser-state.ts | 8 +- src/buffer/ascii/ascii-parser.ts | 29 +- src/buffer/ascii/ascii-segment-parser.ts | 31 +- src/buffer/ascii/ascii-view.ts | 36 +- src/buffer/ascii/itime-formatter.ts | 24 +- src/buffer/ascii/time-formatter.ts | 23 +- src/buffer/elastic-buffer.ts | 7 +- src/buffer/encode-proxy.ts | 11 +- src/buffer/encoder-state.ts | 10 +- src/buffer/fixml/fixml-encoder.ts | 20 +- src/buffer/fixml/fixml-parser.ts | 140 +- src/buffer/fixml/fixml-view.ts | 13 +- src/buffer/fixml/populated-attributes.ts | 2 +- src/buffer/msg-encoder.ts | 3 +- src/buffer/msg-view.ts | 124 +- src/buffer/segment/segment-description.ts | 26 +- src/buffer/segment/segment-summary.ts | 5 +- src/buffer/structure.ts | 12 +- src/buffer/time-format-template.ts | 2 +- src/collections/dictionary.ts | 9 +- src/config/empty-log-factory.ts | 1 + src/config/get-js-fx-logger.ts | 3 +- src/config/js-fix-config.ts | 12 +- src/config/js-fix-logger.ts | 23 +- src/config/js-fix-winston-log-factory.ts | 3 +- src/config/winston-logger.ts | 16 +- src/dict-parser.ts | 40 +- src/dictionary/compiler/compiler-settings.ts | 10 +- src/dictionary/compiler/compiler-type.ts | 2 +- src/dictionary/compiler/enum-compiler.ts | 30 +- src/dictionary/compiler/msg-compiler.ts | 32 +- src/dictionary/compiler/standard-snippet.ts | 55 +- .../contained/contained-component-field.ts | 9 +- .../contained/contained-field-dispatcher.ts | 6 +- .../contained/contained-field-set.ts | 42 +- src/dictionary/contained/contained-field.ts | 5 +- .../contained/contained-group-field.ts | 8 +- .../contained/contained-simple-field.ts | 10 +- src/dictionary/contained/field-dispatcher.ts | 6 +- src/dictionary/contained/fields-dispatch.ts | 1 - .../definition/component-field-definition.ts | 8 +- src/dictionary/definition/fix-definitions.ts | 16 +- .../definition/group-field-definition.ts | 10 +- .../definition/message-definition.ts | 8 +- .../definition/simple-field-definition.ts | 16 +- src/dictionary/dict-primitive.ts | 4 +- src/dictionary/field-enum.ts | 5 +- src/dictionary/fix-versions.ts | 20 +- .../fix-repository/repository-xml-parser.ts | 15 +- .../parser/fix-repository/repository.ts | 68 +- .../parser/fixml/components-parser.ts | 108 +- src/dictionary/parser/fixml/fields-parser.ts | 19 +- src/dictionary/parser/fixml/fix-xsd-parser.ts | 5 +- src/dictionary/parser/fixml/include-graph.ts | 47 +- src/dictionary/parser/fixml/xsd-parser.ts | 10 +- .../quickfix/field-definition-parser.ts | 8 +- .../parser/quickfix/field-set-parser.ts | 4 +- .../parser/quickfix/message-parser.ts | 15 +- src/dictionary/parser/quickfix/node-parser.ts | 38 +- .../parser/quickfix/parse-context.ts | 8 +- .../quickfix/quick-fix-xml-file-parser.ts | 28 +- src/dictionary/set-reduce.ts | 7 +- src/dictionary/type-dispatcher.ts | 6 +- src/jsfix-cmd.ts | 70 +- src/runtime/make-config.ts | 25 +- src/runtime/session-container.ts | 39 +- src/runtime/session-launcher.ts | 59 +- src/sample/http/oms/app.ts | 17 +- src/sample/http/oms/http-client.ts | 6 +- src/sample/http/oms/http-server.ts | 2 +- src/sample/http/oms/oms-factory.ts | 6 +- src/sample/tcp/qf-md/app.ts | 6 +- src/sample/tcp/qf-md/md-client.ts | 2 +- src/sample/tcp/qf-md/md-factory.ts | 5 +- src/sample/tcp/qf-md/md-server.ts | 4 +- src/sample/tcp/recovering-skeleton/app.ts | 3 +- .../recovering-skeleton/respawn-acceptor.ts | 6 +- .../recovering-skeleton/skeleton-client.ts | 4 +- .../recovering-skeleton/skeleton-server.ts | 8 +- src/sample/tcp/skeleton/app.ts | 2 +- src/sample/tcp/skeleton/skeleton-session.ts | 14 +- src/sample/tcp/tls-trade-capture/app.ts | 2 +- src/sample/tcp/trade-capture/app-launcher.ts | 8 +- .../tcp/trade-capture/trade-capture-client.ts | 12 +- .../tcp/trade-capture/trade-capture-server.ts | 15 +- src/sample/tcp/trade-capture/trade-factory.ts | 2 +- src/store/fix-msg-ascii-store-resend.ts | 11 +- src/store/fix-msg-memory-store.ts | 43 +- src/store/fix-msg-store-record.ts | 16 +- src/store/fix-msg-store-state.ts | 4 +- src/store/fix-msg-store.ts | 14 +- src/test/ascii/ascii-encoder.test.ts | 139 +- src/test/ascii/ascii-parser.test.ts | 43 +- src/test/ascii/ascii-segment.test.ts | 22 +- src/test/ascii/ascii-store-replay.test.ts | 8 +- src/test/ascii/ascii-tag-pos.test.ts | 12 +- src/test/ascii/execution-report.test.ts | 136 +- src/test/ascii/fix-log-replay.test.ts | 10 +- src/test/ascii/fix-repo-dict.test.ts | 304 +- src/test/ascii/includes.test.ts | 2 +- src/test/ascii/logon.test.ts | 12 +- src/test/ascii/memory-store.test.ts | 14 +- src/test/ascii/qf-full-msg.test.ts | 10 +- src/test/ascii/repo-full-ascii-msg.test.ts | 22 +- src/test/ascii/session-state.test.ts | 2 +- src/test/ascii/session.test.ts | 49 +- src/test/ascii/view-decode.test.ts | 67 +- src/test/encode-proxy.test.ts | 70 +- src/test/env/experiment.ts | 6 +- src/test/env/parsing-result.ts | 8 +- src/test/env/setup.ts | 13 +- src/test/env/skeleton-runner.ts | 37 +- src/test/env/test-recovery.ts | 18 +- src/test/env/to-views.ts | 6 +- src/test/fixml/fixml-alloc-parse.test.ts | 43 +- .../fixml/fixml-mkt-data-fut-parse.test.ts | 4 +- .../fixml/fixml-mkt-data-settle-parse.test.ts | 2 +- .../fixml/fixml-tc-bi-lateral-parse.test.ts | 33 +- src/test/fixml/repo-full-fixml-msg.test.ts | 28 +- src/transport/ascii/ascii-msg-transmitter.ts | 30 +- .../ascii/ascii-session-msg-factory.ts | 11 +- src/transport/ascii/ascii-session.ts | 69 +- src/transport/duplex/http-duplex.ts | 18 +- src/transport/duplex/string-duplex.ts | 9 +- src/transport/factory/msg-transport.ts | 11 +- src/transport/fix-acceptor.ts | 4 +- src/transport/fix-initiator.ts | 2 +- src/transport/fixml/fixml-msg-transmitter.ts | 4 +- .../fixml/fixml-session-msg-factory.ts | 3 +- src/transport/fixml/fixml-session.ts | 13 +- src/transport/http/html-options.ts | 10 +- src/transport/http/html-route.ts | 2 +- src/transport/http/http-acceptor-listener.ts | 9 +- src/transport/http/http-acceptor.ts | 62 +- src/transport/http/http-adapter.ts | 6 +- src/transport/http/http-initiator.ts | 38 +- .../http/http-json-sample-adapter.ts | 27 +- src/transport/http/http-transaction.ts | 2 +- .../http/http-transport-description.ts | 2 +- src/transport/msg-application.ts | 8 +- src/transport/msg-transmitter.ts | 4 +- .../session/a-session-msg-factory.ts | 5 +- src/transport/session/fix-session-state.ts | 28 +- src/transport/session/fix-session.ts | 83 +- src/transport/session/make-fix-session.ts | 3 +- src/transport/session/session-description.ts | 2 +- src/transport/session/session-msg-factory.ts | 18 +- src/transport/tcp/recovering-tcp-initiator.ts | 37 +- src/transport/tcp/tcp-acceptor-listener.ts | 6 +- src/transport/tcp/tcp-acceptor.ts | 45 +- src/transport/tcp/tcp-initiator-connector.ts | 16 +- src/transport/tcp/tcp-initiator.ts | 101 +- .../tcp/tcp-transport-description.ts | 2 +- src/transport/tcp/tls-options-factory.ts | 20 +- src/transport/tcp/tls-options.ts | 20 +- src/util/json-helper.ts | 11 +- src/util/message-generator.ts | 13 +- tsconfig.json | 2 + tslint.json | 3 - 166 files changed, 4186 insertions(+), 2664 deletions(-) create mode 100644 .eslintrc delete mode 100644 .eslintrc.js delete mode 100644 tslint.json diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 00000000..41f35a47 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,29 @@ +{ + "parser": "@typescript-eslint/parser", + "plugins": [ + "@typescript-eslint" + ], + + "rules": { + "@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/.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/package-lock.json b/package-lock.json index d7d8baa7..c538cf98 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "align-text": "^1.0.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", @@ -27,24 +27,28 @@ "yauzl": "^2.10.0" }, "devDependencies": { - "@types/express": "^4.17.14", + "@types/express": "^4.17.15", "@types/express-serve-static-core": "^4.17.31", - "@types/jest": "^29.1.2", - "@types/lodash": "^4.14.186", + "@types/jest": "^29.2.4", + "@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.35.1", + "eslint": "^8.22.0", + "eslint-config-standard-with-typescript": "^22.0.0", + "eslint-plugin-jquery": "^1.5.1", + "eslint-plugin-jsdoc": "^39.3.6", + "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 +507,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 +522,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 +605,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 +637,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 +649,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 +688,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 +746,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 +779,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 +796,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 +842,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 +882,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 +936,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 +999,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 +1014,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 +1029,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 +1070,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 +1198,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,13 +1288,13 @@ } }, "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": "*" } @@ -1304,15 +1344,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.4", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.2.4.tgz", + "integrity": "sha512-PipFB04k2qTRPePduVLTRiPzQfvMeLwUN3Z21hsAKaB/W9IIzgB2pizCL466ftJlcyZqnHoC9ZHpxLGl3fS86A==", "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 +1366,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 +1394,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 +1461,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 +1490,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": { @@ -1458,44 +1510,533 @@ "integrity": "sha512-Nz4MPhecOFArtm81gFQvQqdV7XYCrWKx5uUt6GNHredFHn1i2mtWqXTON7EPXMtNi1qjtjEM/VCHDhcHsAMLXQ==", "dev": true, "dependencies": { - "@types/yargs-parser": "*" + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "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", + "integrity": "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==", + "dev": true, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz", + "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", + "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/@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", - "dev": true - }, - "node_modules/@typescript-eslint/types": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz", - "integrity": "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==", + "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": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz", - "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==", + "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": "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": "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 +2048,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 +2111,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 +2156,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" @@ -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", @@ -1815,12 +2404,12 @@ "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", @@ -2125,6 +2714,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" } @@ -2391,6 +2981,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", @@ -2529,9 +3128,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 +3405,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" @@ -2873,9 +3473,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 +3639,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 +3657,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 +3682,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 +3744,24 @@ "eslint-plugin-react": "^7.28.0" } }, + "node_modules/eslint-config-standard-with-typescript": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard-with-typescript/-/eslint-config-standard-with-typescript-22.0.0.tgz", + "integrity": "sha512-VA36U7UlFpwULvkdnh6MQj5GAV2Q+tT68ALLAwJP0ZuNXU2m0wX07uxX4qyLRdHgSzH4QJ73CveKBuSOYvh7vQ==", + "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 +3990,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 +4081,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 +4310,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 +4339,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 +4378,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 +4410,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 +4474,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 +4595,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" @@ -3896,9 +4711,9 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "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,7 +4723,7 @@ "micromatch": "^4.0.4" }, "engines": { - "node": ">=8" + "node": ">=8.6.0" } }, "node_modules/fast-json-stable-stringify": { @@ -4165,12 +4980,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", @@ -4326,16 +5135,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 +5175,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", @@ -4818,6 +5633,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", @@ -5038,15 +5862,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 +5916,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 +5977,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 +6027,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 +6088,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 +6131,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 +6163,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 +6189,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 +6214,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 +6258,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 +6294,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 +6334,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 +6354,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 +6383,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 +6446,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 +6495,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 +6506,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 +6561,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 +6594,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 +6639,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 +6674,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 +6712,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", @@ -5912,6 +6746,15 @@ "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", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -6319,13 +7162,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 +7296,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 +7370,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", @@ -7171,9 +8022,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", @@ -7371,9 +8222,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", @@ -7692,6 +8543,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,6 +8704,28 @@ "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", @@ -8382,6 +9256,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 +9279,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" } @@ -8560,9 +9370,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", @@ -8656,12 +9466,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", @@ -8858,9 +9662,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 +9673,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 +10049,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 +10126,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 +10161,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 +10187,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 +10229,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 +10255,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 +10272,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 +10303,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 +10382,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 +10424,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 +10479,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 +10585,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,13 +10675,13 @@ } }, "@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": "*" } @@ -9904,15 +10731,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.4", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.2.4.tgz", + "integrity": "sha512-PipFB04k2qTRPePduVLTRiPzQfvMeLwUN3Z21hsAKaB/W9IIzgB2pizCL466ftJlcyZqnHoC9ZHpxLGl3fS86A==", "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 +10753,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 +10780,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 +10846,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 +10875,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": { @@ -10059,12 +10898,241 @@ "@types/yargs-parser": "*" } }, - "@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", - "dev": true - }, + "@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "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 +11174,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 +11283,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": { @@ -10203,9 +11360,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", @@ -10315,12 +11472,12 @@ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" }, "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", @@ -10538,7 +11695,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", @@ -10744,6 +11902,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", @@ -10842,9 +12006,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 +12210,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": { @@ -11101,9 +12266,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 +12393,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 +12411,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 +12436,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 +12467,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 +12487,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" @@ -11335,6 +12514,15 @@ "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": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -11349,6 +12537,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 +12592,16 @@ "dev": true, "requires": {} }, + "eslint-config-standard-with-typescript": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard-with-typescript/-/eslint-config-standard-with-typescript-22.0.0.tgz", + "integrity": "sha512-VA36U7UlFpwULvkdnh6MQj5GAV2Q+tT68ALLAwJP0ZuNXU2m0wX07uxX4qyLRdHgSzH4QJ73CveKBuSOYvh7vQ==", + "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 +12784,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 +12847,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 +12976,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 +13059,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": { @@ -11846,9 +13148,9 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "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", @@ -12058,12 +13360,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", @@ -12174,16 +13470,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 +13498,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", @@ -12508,6 +13810,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", @@ -12670,15 +13978,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 +14011,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 +14059,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 +14091,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 +14133,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 +14166,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 +14191,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 +14211,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 +14224,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 +14265,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 +14294,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 +14318,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 +14347,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 +14407,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 +14449,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 +14460,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 +14502,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 +14528,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 +14560,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 +14588,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 +14616,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", @@ -13329,6 +14643,12 @@ "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", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -13647,13 +14967,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 +15062,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 +15112,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", @@ -14269,9 +15597,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", @@ -14419,9 +15747,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", @@ -14638,7 +15966,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,6 +16107,28 @@ "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", @@ -15147,6 +16498,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 +16515,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" } @@ -15288,9 +16582,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": { @@ -15349,12 +16643,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", @@ -15506,9 +16794,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 +16805,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..50bf86b4 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "align-text": "^1.0.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", @@ -83,23 +83,27 @@ "yauzl": "^2.10.0" }, "devDependencies": { - "@types/express": "^4.17.14", + "@types/express": "^4.17.15", "@types/express-serve-static-core": "^4.17.31", - "@types/jest": "^29.1.2", - "@types/lodash": "^4.14.186", + "@types/jest": "^29.2.4", + "@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.35.1", + "eslint": "^8.22.0", + "eslint-config-standard-with-typescript": "^22.0.0", + "eslint-plugin-jquery": "^1.5.1", + "eslint-plugin-jsdoc": "^39.3.6", + "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..3a6f4a41 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) { @@ -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..73686ae4 100644 --- a/src/buffer/msg-view.ts +++ b/src/buffer/msg-view.ts @@ -3,8 +3,10 @@ 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 { + ContainedGroupField, ContainedComponentField, ContainedField, + ContainedFieldSet, ContainedSimpleField +} from '../dictionary/contained' import { SetReduce } from '../dictionary' import { ILooseObject } from '../collections/collection' import { ElasticBuffer } from './elastic-buffer' @@ -16,7 +18,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 +28,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 +69,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 +83,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 +94,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 +124,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 +136,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 +158,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 +171,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 +182,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 +205,30 @@ 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) + } + const reduced: MsgView | null = parts.reduce(reducer, this) + return reduced } public abstract checksum (): number @@ -225,17 +237,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 { + if (this.segment.set == null) return 0 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') { + 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 +263,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 +301,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 +310,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 +387,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 +398,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 +423,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/dictionary.ts b/src/collections/dictionary.ts index 116a1b1f..7f26e682 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: { [id: string]: T } = {} 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..3a2875c3 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) } 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/jsfix-cmd.ts b/src/jsfix-cmd.ts index 6db5dfbf..455e875b 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] + 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)) } @@ -453,7 +453,7 @@ export class JsfixCmd { 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) => { @@ -472,8 +472,8 @@ export class JsfixCmd { 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) + console.log(`[${msgType}]: repeats = ${repeats}, fields = ${v?.structure?.tags.nextTagPos}, length = ${contents.length} chars, elapsed ms ${elapsed}, ${(elapsed / repeats) * 1000} micros per msg`) + resolve(true) } }) }) @@ -521,8 +521,8 @@ 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.') 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..00616f58 100644 --- a/src/runtime/session-launcher.ts +++ b/src/runtime/session-launcher.ts @@ -11,20 +11,20 @@ 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, _) => { + private async empty (): Promise { + return await new Promise((resolve, reject) => { setImmediate(() => { this.logger.info('resolving an empty promise') resolve(null) @@ -32,35 +32,35 @@ export abstract class SessionLauncher { }) } - 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() + return await entity.start() } else { - return this.empty() + return await this.empty() } } - 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() + return await entity.start() } else { - return this.empty() + return await this.empty() } } - 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 +68,7 @@ export abstract class SessionLauncher { }) } - public exec () { + public exec (): void { this.run().then(() => { console.log('finished.') }).catch(e => { @@ -88,9 +88,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 +113,26 @@ 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 () { + private async setup (): Promise { this.sessionContainer.registerGlobal(this.loggerFactory) - const server = this.acceptorConfig ? this.makeServer() : this.empty() - const client = this.initiatorConfig ? this.makeClient() : this.empty() + const server = this.acceptorConfig ? await this.makeServer() : await this.empty() + const client = this.initiatorConfig ? await this.makeClient() : await this.empty() 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..309a6bb2 100644 --- a/src/sample/http/oms/app.ts +++ b/src/sample/http/oms/app.ts @@ -18,17 +18,20 @@ 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 { + 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 await initiator.start() + } } } diff --git a/src/sample/http/oms/http-client.ts b/src/sample/http/oms/http-client.ts index 8c03fc73..888c3d41 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.warning(`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..9a0614fc 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}`) } 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..16f823da 100644 --- a/src/sample/tcp/recovering-skeleton/app.ts +++ b/src/sample/tcp/recovering-skeleton/app.ts @@ -11,14 +11,13 @@ import { SkeletonServer } from './skeleton-server' import { FixSession, FixEntity } from '../../../transport' class AppLauncher extends SessionLauncher { - public constructor () { super( 'data/session/test-initiator.json', 'data/session/test-acceptor.json') } - protected override registerApplication (sessionContainer: DependencyContainer) { + 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 diff --git a/src/sample/tcp/recovering-skeleton/respawn-acceptor.ts b/src/sample/tcp/recovering-skeleton/respawn-acceptor.ts index a90ebe80..37adea36 100644 --- a/src/sample/tcp/recovering-skeleton/respawn-acceptor.ts +++ b/src/sample/tcp/recovering-skeleton/respawn-acceptor.ts @@ -15,12 +15,12 @@ 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() } 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 { 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..fa5e4dc6 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/) @@ -519,14 +531,16 @@ 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 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') 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') }) @@ -534,14 +548,16 @@ 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') + 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') 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 +569,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..364bd722 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) @@ -644,7 +643,7 @@ test('instrumentLeg structure', () => { }) 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,7 +655,7 @@ test('LegSecAltIDGrp [0] structure', () => { expect(legSecAltIDGrp[index].endPosition).toEqual(406) expect(legSecAltIDGrp[index].endTag).toEqual(606) - const noLegSecurityAltID: SegmentDescription[] = structure.layout.NoLegSecurityAltID + const noLegSecurityAltID: SegmentDescription[] = structure?.layout.NoLegSecurityAltID expect(noLegSecurityAltID).toBeTruthy() expect(Array.isArray(noLegSecurityAltID)).toEqual(true) expect(noLegSecurityAltID.length).toEqual(3) @@ -672,7 +671,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,7 +683,7 @@ test('LegSecAltIDGrp [1] structure', () => { expect(legSecAltIDGrp[index].endPosition).toEqual(475) expect(legSecAltIDGrp[index].endTag).toEqual(606) - const noLegSecurityAltID: SegmentDescription[] = structure.layout.NoLegSecurityAltID + const noLegSecurityAltID: SegmentDescription[] = structure?.layout.NoLegSecurityAltID expect(noLegSecurityAltID).toBeTruthy() expect(Array.isArray(noLegSecurityAltID)).toEqual(true) expect(noLegSecurityAltID.length).toEqual(3) @@ -700,7 +699,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,7 +711,7 @@ test('LegSecAltIDGrp [2] structure', () => { expect(legSecAltIDGrp[index].endPosition).toEqual(564) expect(legSecAltIDGrp[index].endTag).toEqual(606) - const noLegSecurityAltID: SegmentDescription[] = structure.layout.NoLegSecurityAltID + const noLegSecurityAltID: SegmentDescription[] = structure?.layout.NoLegSecurityAltID expect(noLegSecurityAltID).toBeTruthy() expect(Array.isArray(noLegSecurityAltID)).toEqual(true) expect(noLegSecurityAltID.length).toEqual(3) @@ -754,9 +753,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 +775,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 +832,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..da6d4841 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 (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..8e0be956 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 } } @@ -199,7 +206,9 @@ test('client logon reject missing 108', async () => { 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 changed = logonMsg + .replace('35=A', '35=ZZ') + .replace('34=1', `34=${at.msgSeqNum + 1}`) await runSkeletons(2, changed) const cviews = experiment.client.views const sviews = experiment.server.views @@ -217,7 +226,9 @@ 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 @@ -235,12 +246,14 @@ 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}`) + const changed = heartbeat + .replace('49=init-comp', '49=init-not!') + .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.length).toEqual(3) + expect(sviews.length).toEqual(3) expect(cviews[0].segment.name).toEqual('Logon') expect(cviews[1].segment.name).toEqual('Reject') expect(sviews[0].segment.name).toEqual('Logon') 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..da06ba5b 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) }) 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..10b42c0d 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,8 +82,7 @@ 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) diff --git a/src/test/env/skeleton-runner.ts b/src/test/env/skeleton-runner.ts index cf16da91..04f3c1d9 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 @@ -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..f133291d 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') @@ -55,28 +55,32 @@ export class AsciiMsgTransmitter extends MsgTransmitter { } const sendingTime = this.time || new Date() - const hdr: ILooseObject = factory.header(msgType, this.msgSeqNum, sendingTime, headerProps) - + const hdr: ILooseObject | null = factory?.header(msgType, this.msgSeqNum, sendingTime, headerProps) ?? null // Only increment sequence number if this is not a duplicate message. + if (!hdr) return 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) + } } } 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..d71958d9 100644 --- a/src/transport/ascii/ascii-session.ts +++ b/src/transport/ascii/ascii-session.ts @@ -7,11 +7,11 @@ import { SessionState } from '../tcp' import { TickAction } from '../tick-action' import { IMsgApplication } from '../msg-application' import { SegmentType } from '../../buffer/segment/segment-type' +import { factory } from 'mathjs' 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 +23,6 @@ export abstract class AsciiSession extends FixSession { } private checkSeqNo (msgType: string, view: MsgView): boolean { - switch (msgType) { case MsgType.SequenceReset: { return true @@ -74,22 +73,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 +113,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 +158,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 +167,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 +186,6 @@ export abstract class AsciiSession extends FixSession { } protected onSessionMsg (msgType: string, view: MsgView): void { - const logger = this.sessionLogger switch (msgType) { @@ -202,8 +206,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 +241,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 +277,13 @@ export abstract class AsciiSession extends FixSession { } } - private startTimer (interval: number = 200) { + private startTimer (interval: number = 200): void { 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 +302,35 @@ export abstract class AsciiSession extends FixSession { this.setState(SessionState.InitiationLogonReceived) } if (this.heartbeat) { - logger.debug(`start heartbeat timer.`) + 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..39c8f27b 100644 --- a/src/transport/duplex/http-duplex.ts +++ b/src/transport/duplex/http-duplex.ts @@ -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) { + rp(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-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..09904816 100644 --- a/src/transport/fixml/fixml-msg-transmitter.ts +++ b/src/transport/fixml/fixml-msg-transmitter.ts @@ -16,13 +16,13 @@ export class FixmlMsgTransmitter extends MsgTransmitter { } public encodeMessage (msgType: string, obj: ILooseObject): void { - const adapter = this.config.description.application.http.adapter + 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) } } 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..88065263 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 + uri: string + json: boolean + body: 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..cc41ef85 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 await new Promise(async (resolve, reject) => { const logger = this.config.logFactory.logger('acceptor') const sessionContainer = this.config.sessionContainer if (!sessionContainer.isRegistered(DITokens.FixSession)) { @@ -28,11 +29,11 @@ export class HttpAcceptorListener extends FixEntity { 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..64e7372b 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,18 +69,18 @@ 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) => { + private async respond (duplex: FixDuplex, res: express.Response, token: string | null = null): Promise { + return await new Promise((resolve, reject) => { res.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) 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) { @@ -88,17 +88,17 @@ export class HttpAcceptor extends FixAcceptor { } duplex.writable.removeListener('data', transmit) res.send(d) - accept(true) + 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..bba6c992 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,12 +26,13 @@ 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 = { fixml: data.toString() @@ -53,13 +54,13 @@ export class HttpJsonSampleAdapter implements IHttpAdapter { 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, + uri: route?.value.uri, + 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..1363aaa1 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) => { 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..5d36f596 100644 --- a/src/transport/session/fix-session-state.ts +++ b/src/transport/session/fix-session-state.ts @@ -7,10 +7,10 @@ import { SessionState } from './session-state' 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 = '' @@ -40,11 +40,13 @@ export class FixSessionState { } } - 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,7 +54,7 @@ export class FixSessionState { this.lastPeerMsgSeqNum = lastPeerMsgSeqNum } - private static dateAsString (d: Date) { + private static dateAsString (d: Date | null): string { if (!d) { return 'null' } @@ -60,7 +62,6 @@ export class FixSessionState { } public toString (): string { - const buffer = new ElasticBuffer(1024) buffer.writeString(`compId = ${this.compId}, `) @@ -91,7 +92,6 @@ export class FixSessionState { this.calcState() switch (this.state) { - case SessionState.PeerLogonRejected: { if (this.secondsSinceSent >= this.stopSeconds) { this.nextTickAction = TickAction.Stop @@ -154,7 +154,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..f81d37e8 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,18 +27,20 @@ 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) { + public setState (state: SessionState): void { if (state === this.sessionState.state) return const logger = this.sessionLogger const prevState = this.sessionState.state @@ -51,13 +53,16 @@ export abstract class FixSession extends events.EventEmitter { return this.sessionState.state } - public sendLogon () { - this.send(this.requestLogonType, this.config.factory.logon()) + public sendLogon (): void { + const lo = this.config.factory?.logon() + if (lo) { + this.send(this.requestLogonType, lo) + } } - private waitPromise (): Promise { + 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 +78,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,7 +91,7 @@ export abstract class FixSession extends events.EventEmitter { } this.transport = transport this.subscribe() - return this.waitPromise() + return await this.waitPromise() } protected expectedState (): boolean { @@ -105,17 +110,16 @@ export abstract class FixSession extends events.EventEmitter { } } - protected subscribe () { - + protected subscribe (): void { const transport = this.transport const logger = this.sessionLogger - const rx = transport.receiver - const tx = transport.transmitter + const rx = transport?.receiver + const tx = transport?.transmitter - rx.on('msg', (msgType: string, view: MsgView) => { + rx?.on('msg', (msgType: string, view: MsgView) => { if (this.logReceivedMsgs) { - const name = view.segment.type !== SegmentType.Unknown ? view.segment.set.name : 'unknown' + const name = view.segment.type !== SegmentType.Unknown ? view?.segment?.set?.name : 'unknown' logger.info(`${msgType}: ${name}`) logger.info(`${view.toString()}`) } @@ -127,17 +131,17 @@ export abstract class FixSession extends events.EventEmitter { } }) - rx.on('error', (e: Error) => { - logger.warning(`rx error event: ${e.message} ${e.stack || ''}`) + rx?.on('error', (e: Error) => { + logger.warning(`rx error event: ${e.message} ${e.stack ?? ''}`) this.terminate(e) }) - rx.on('done', () => { + rx?.on('done', () => { logger.info('rx done received') this.done() }) - rx.on('end', () => { + rx?.on('end', () => { logger.info(`rx end received sessionState = [${this.sessionState.toString()}]`) const expectedState = this.expectedState() if (expectedState) { @@ -150,17 +154,17 @@ export abstract class FixSession extends events.EventEmitter { } }) - rx.on('decoded', (msgType: string, data: ElasticBuffer, ptr: number) => { + rx?.on('decoded', (msgType: string, data: ElasticBuffer, ptr: number) => { logger.debug(`rx: [${msgType}] ${ptr} bytes`) this.onDecoded(msgType, data.toString(ptr)) }) - tx.on('error', (e: Error) => { - logger.warning(`tx error event: ${e.message} ${e.stack || ''}`) + tx?.on('error', (e: Error) => { + logger.warning(`tx error event: ${e.message} ${e.stack ?? ''}`) this.terminate(e) }) - tx.on('encoded', (msgType: string, data: string) => { + tx?.on('encoded', (msgType: string, data: string) => { logger.debug(`tx: [${msgType}] ${data.length} bytes`) this.onEncoded(msgType, data) }) @@ -205,7 +209,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 +229,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 +239,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 { @@ -291,7 +298,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: { @@ -312,15 +319,15 @@ export abstract class FixSession extends events.EventEmitter { 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.sessionLogger.info('stop: kill transport') + this.transport?.end() if (error) { this.sessionLogger.info(`stop: emit error ${error.message}`) this.emit('error', error) @@ -329,7 +336,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..615a9b82 100644 --- a/src/transport/session/session-description.ts +++ b/src/transport/session/session-description.ts @@ -14,5 +14,5 @@ export interface ISessionDescription { 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..094c2bd4 100644 --- a/src/transport/tcp/recovering-tcp-initiator.ts +++ b/src/transport/tcp/recovering-tcp-initiator.ts @@ -18,31 +18,32 @@ import { FixEntity } from '../fix-entity' @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,7 +61,7 @@ export class RecoveringTcpInitiator extends FixEntity { return this.session.getState() } - private newTransport (transport: MsgTransport) { + private newTransport (transport: MsgTransport): void { this.transport = transport this.emit('transport', transport) this.logger.info(`initiator connects id ${(transport.id)}`) @@ -84,7 +85,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 @@ -108,19 +109,19 @@ export class RecoveringTcpInitiator extends FixEntity { 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 +138,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..a40e977c 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)) { @@ -29,7 +29,7 @@ export class TcpAcceptorListener extends FixEntity { 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..18a12ea6 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,13 +43,13 @@ export class TcpInitiatorConnector extends FixEntity { }) } - delay (p: number): Promise { - return new Promise((accept) => { + async delay (p: number): Promise { + return await new Promise((resolve, reject) => { if (!p) { - accept(true) + resolve(true) } setTimeout(() => { - accept(true) + resolve(true) }, p) }) } diff --git a/src/transport/tcp/tcp-initiator.ts b/src/transport/tcp/tcp-initiator.ts index b5c68782..2b0ae0ad 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,8 +60,8 @@ 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 @@ -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 From 2632dfcc69f8bebaa1e6c613ac1ef8db9292266e Mon Sep 17 00:00:00 2001 From: TimelordUK Date: Sun, 1 Jan 2023 00:44:43 +0000 Subject: [PATCH 02/19] switch to eslint using typescript rather than tslint - fix the many new errors picked up on mew linter --- .eslintrc | 1 + src/runtime/session-launcher.ts | 34 +++++++++++++++++++++++---------- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/.eslintrc b/.eslintrc index 41f35a47..339bc5af 100644 --- a/.eslintrc +++ b/.eslintrc @@ -5,6 +5,7 @@ ], "rules": { + "@typescript-eslint/return-await": "off", "@typescript-eslint/no-this-alias": "off", "@typescript-eslint/no-var-requires": "off", "@typescript-eslint/no-unused-vars": "off", diff --git a/src/runtime/session-launcher.ts b/src/runtime/session-launcher.ts index 00616f58..b6380279 100644 --- a/src/runtime/session-launcher.ts +++ b/src/runtime/session-launcher.ts @@ -25,28 +25,32 @@ export abstract class SessionLauncher { private async empty (): Promise { return await new Promise((resolve, reject) => { - setImmediate(() => { - this.logger.info('resolving an empty promise') - resolve(null) - }) + try { + setImmediate(() => { + this.logger.info('resolving an empty promise') + resolve(null) + }) + } catch (e) { + reject(e) + } }) } protected async getAcceptor (sessionContainer: DependencyContainer): Promise { if (sessionContainer.isRegistered(DITokens.FixEntity)) { const entity = sessionContainer.resolve(DITokens.FixEntity) - return await entity.start() + return entity.start() } else { - return await this.empty() + return this.empty() } } protected async getInitiator (sessionContainer: DependencyContainer): Promise { if (sessionContainer.isRegistered(DITokens.FixEntity)) { const entity = sessionContainer.resolve(DITokens.FixEntity) - return await entity.start() + return entity.start() } else { - return await this.empty() + return this.empty() } } @@ -124,10 +128,20 @@ export abstract class SessionLauncher { return await this.getAcceptor(sessionContainer) } + 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.acceptorConfig ? await this.makeServer() : await this.empty() - const client = this.initiatorConfig ? await this.makeClient() : await this.empty() + const server = this.serverOrEmpty() + const client = this.clientOrEmpty() this.logger.info('launching ....') return await Promise.all([server, client]) } From 5b8fc682d08667832f9ea317b5015001dd429978 Mon Sep 17 00:00:00 2001 From: TimelordUK Date: Sun, 1 Jan 2023 12:33:26 +0000 Subject: [PATCH 03/19] switch to eslint using typescript rather than tslint - fix the many new errors picked up on mew linter --- src/buffer/msg-view.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/buffer/msg-view.ts b/src/buffer/msg-view.ts index 73686ae4..1d4d2a67 100644 --- a/src/buffer/msg-view.ts +++ b/src/buffer/msg-view.ts @@ -242,9 +242,9 @@ export abstract class MsgView { protected abstract toTyped (field: SimpleFieldDefinition): any protected resolveTag (tagOrName: number | string): number { - if (this.segment.set == null) return 0 let tag: number 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) { From 85d2ad55f412ce749159d7514884c85e64f88b9b Mon Sep 17 00:00:00 2001 From: TimelordUK Date: Sun, 1 Jan 2023 12:39:44 +0000 Subject: [PATCH 04/19] switch to eslint using typescript rather than tslint - fix the many new errors picked up on mew linter --- src/test/ascii/session.test.ts | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/test/ascii/session.test.ts b/src/test/ascii/session.test.ts index 8e0be956..f50c2e3b 100644 --- a/src/test/ascii/session.test.ts +++ b/src/test/ascii/session.test.ts @@ -204,11 +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 @@ -218,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) @@ -232,11 +238,7 @@ test('heartbeat invalid tag', async () => { 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) @@ -249,14 +251,9 @@ test('heartbeat invalid sender comp', async () => { const changed = heartbeat .replace('49=init-comp', '49=init-not!') .replace('34=1', `34=${at.msgSeqNum + 1}`) - await runSkeletons(2, changed) const cviews = experiment.client.views const sviews = experiment.server.views - expect(cviews.length).toEqual(3) - expect(sviews.length).toEqual(3) - 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) From 3713c4f8b79c9e75d7d98ce55970c523a2897495 Mon Sep 17 00:00:00 2001 From: TimelordUK Date: Sun, 1 Jan 2023 13:05:59 +0000 Subject: [PATCH 05/19] switch to eslint using typescript rather than tslint - fix the many new errors picked up on mew linter --- src/jsfix-cmd.ts | 2 +- src/test/ascii/ascii-encoder.test.ts | 19 +++++----- src/test/ascii/execution-report.test.ts | 39 ++++++++------------ src/test/ascii/qf-full-msg.test.ts | 2 +- src/test/env/experiment.ts | 2 +- src/test/env/setup.ts | 2 +- src/test/env/skeleton-runner.ts | 2 +- src/transport/ascii/ascii-session.ts | 1 - src/transport/fix-entity.ts | 2 +- src/transport/session/fix-session.ts | 2 +- src/transport/tcp/tcp-initiator-connector.ts | 14 ++++--- src/transport/tcp/tcp-initiator.ts | 2 +- 12 files changed, 41 insertions(+), 48 deletions(-) diff --git a/src/jsfix-cmd.ts b/src/jsfix-cmd.ts index 455e875b..6f9ccbc6 100644 --- a/src/jsfix-cmd.ts +++ b/src/jsfix-cmd.ts @@ -386,7 +386,7 @@ export class JsfixCmd { if (!stats[msgType]) { i = 1 } else { - i = stats[msgType] + 1 + i = stats[msgType] as number + 1 } stats[msgType] = i break diff --git a/src/test/ascii/ascii-encoder.test.ts b/src/test/ascii/ascii-encoder.test.ts index fa5e4dc6..91056616 100644 --- a/src/test/ascii/ascii-encoder.test.ts +++ b/src/test/ascii/ascii-encoder.test.ts @@ -527,9 +527,7 @@ 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 @@ -537,6 +535,13 @@ test('encode custom header 1 - expect DeliverToCompID DepA', async () => { expect(fix).toBeTruthy() const res: ParsingResult = await setup.client.parseText(fix) 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) @@ -547,13 +552,7 @@ test('encode custom header 1 - expect DeliverToCompID DepA', async () => { 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') - 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 { res, tag } = await getNewOrderSingle(o1) expect(tag).toEqual('DepC') expect(res.event).toEqual('msg') expect(res.msgType).toEqual(MsgType.NewOrderSingle) diff --git a/src/test/ascii/execution-report.test.ts b/src/test/ascii/execution-report.test.ts index 364bd722..d7cbc4ac 100644 --- a/src/test/ascii/execution-report.test.ts +++ b/src/test/ascii/execution-report.test.ts @@ -642,6 +642,18 @@ 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 expect(legSecAltIDGrp).toBeTruthy() @@ -655,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) @@ -683,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) @@ -711,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) diff --git a/src/test/ascii/qf-full-msg.test.ts b/src/test/ascii/qf-full-msg.test.ts index da6d4841..8630c455 100644 --- a/src/test/ascii/qf-full-msg.test.ts +++ b/src/test/ascii/qf-full-msg.test.ts @@ -26,7 +26,7 @@ beforeAll(async () => { async function testEncodeDecode (msgType: string, msg: ILooseObject): Promise { // encode to FIX format from provided object. - return await new Promise(async (resolve, reject) => { + 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) diff --git a/src/test/env/experiment.ts b/src/test/env/experiment.ts index da06ba5b..2d815daf 100644 --- a/src/test/env/experiment.ts +++ b/src/test/env/experiment.ts @@ -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/setup.ts b/src/test/env/setup.ts index 10b42c0d..fecb925f 100644 --- a/src/test/env/setup.ts +++ b/src/test/env/setup.ts @@ -89,7 +89,7 @@ export class Setup { } } - 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 04f3c1d9..98e50bbe 100644 --- a/src/test/env/skeleton-runner.ts +++ b/src/test/env/skeleton-runner.ts @@ -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) diff --git a/src/transport/ascii/ascii-session.ts b/src/transport/ascii/ascii-session.ts index d71958d9..07d6afde 100644 --- a/src/transport/ascii/ascii-session.ts +++ b/src/transport/ascii/ascii-session.ts @@ -7,7 +7,6 @@ import { SessionState } from '../tcp' import { TickAction } from '../tick-action' import { IMsgApplication } from '../msg-application' import { SegmentType } from '../../buffer/segment/segment-type' -import { factory } from 'mathjs' export abstract class AsciiSession extends FixSession { public heartbeat: boolean = true 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/session/fix-session.ts b/src/transport/session/fix-session.ts index f81d37e8..b06d19ba 100644 --- a/src/transport/session/fix-session.ts +++ b/src/transport/session/fix-session.ts @@ -274,7 +274,7 @@ export abstract class FixSession extends events.EventEmitter { } case SessionState.ConfirmingLogout: { - // this instance responds to logout + // this instance responds to log out sessionState.logoutSentAt = new Date() const msg = `${this.me} confirming logout` this.sessionLogger.info(msg) diff --git a/src/transport/tcp/tcp-initiator-connector.ts b/src/transport/tcp/tcp-initiator-connector.ts index 18a12ea6..341bb52f 100644 --- a/src/transport/tcp/tcp-initiator-connector.ts +++ b/src/transport/tcp/tcp-initiator-connector.ts @@ -45,12 +45,16 @@ export class TcpInitiatorConnector extends FixEntity { async delay (p: number): Promise { return await new Promise((resolve, reject) => { - if (!p) { - resolve(true) + try { + if (!p) { + resolve(true) + } + setTimeout(() => { + resolve(true) + }, p) + } catch (e) { + reject(e) } - setTimeout(() => { - resolve(true) - }, p) }) } diff --git a/src/transport/tcp/tcp-initiator.ts b/src/transport/tcp/tcp-initiator.ts index 2b0ae0ad..5807409d 100644 --- a/src/transport/tcp/tcp-initiator.ts +++ b/src/transport/tcp/tcp-initiator.ts @@ -68,7 +68,7 @@ export class TcpInitiator extends FixInitiator { 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)) From fab4f2a54460ae25cf49a689273d0ad9fec82c43 Mon Sep 17 00:00:00 2001 From: TimelordUK Date: Sun, 1 Jan 2023 14:10:37 +0000 Subject: [PATCH 06/19] switch to eslint using typescript rather than tslint - fix the many new errors picked up on mew linter add coverage --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 50bf86b4..59552b8e 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ }, "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", From 004dcd19b0f31b1f14d2c2f31f1aee4bcfc063dc Mon Sep 17 00:00:00 2001 From: TimelordUK Date: Sun, 1 Jan 2023 14:43:23 +0000 Subject: [PATCH 07/19] switch to eslint using typescript rather than tslint - fix the many new errors picked up on mew linter add coverage fix npm run --- .gitignore | 2 + .../FIX.4.4/quickfix/heartbeat/fix.txt | 1 + package.json | 66 +++++++++---------- src/sample/http/oms/http-client.ts | 2 +- src/sample/http/oms/http-server.ts | 6 ++ 5 files changed, 43 insertions(+), 34 deletions(-) create mode 100644 data/examples/FIX.4.4/quickfix/heartbeat/fix.txt diff --git a/.gitignore b/.gitignore index 83b95961..df42b529 100644 --- a/.gitignore +++ b/.gitignore @@ -82,3 +82,5 @@ 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 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/package.json b/package.json index 59552b8e..39183915 100644 --- a/package.json +++ b/package.json @@ -27,40 +27,40 @@ "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", + "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", diff --git a/src/sample/http/oms/http-client.ts b/src/sample/http/oms/http-client.ts index 888c3d41..5f39eb11 100644 --- a/src/sample/http/oms/http-client.ts +++ b/src/sample/http/oms/http-client.ts @@ -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 9a0614fc..3b991fa5 100644 --- a/src/sample/http/oms/http-server.ts +++ b/src/sample/http/oms/http-server.ts @@ -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 } } } From 9b1120c145f93c1f21be6eec691751c3452e3642 Mon Sep 17 00:00:00 2001 From: TimelordUK Date: Sun, 1 Jan 2023 14:54:08 +0000 Subject: [PATCH 08/19] add new benchmark heartbeat --- .../FIX.4.4/quickfix/heartbeat/object.json | 16 ++++++++++++++++ .../FIX.4.4/quickfix/heartbeat/tokens.txt | 5 +++++ package.json | 1 + 3 files changed, 22 insertions(+) create mode 100644 data/examples/FIX.4.4/quickfix/heartbeat/object.json create mode 100644 data/examples/FIX.4.4/quickfix/heartbeat/tokens.txt 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/package.json b/package.json index 39183915..6e2b6ee6 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "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", From 0e15678f469b137ae18337a58aac08ab7fda013e Mon Sep 17 00:00:00 2001 From: TimelordUK Date: Sun, 1 Jan 2023 15:37:07 +0000 Subject: [PATCH 09/19] add new benchmark heartbeat --- src/jsfix-cmd.ts | 76 +++++++++++++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 26 deletions(-) diff --git a/src/jsfix-cmd.ts b/src/jsfix-cmd.ts index 6f9ccbc6..1e716228 100644 --- a/src/jsfix-cmd.ts +++ b/src/jsfix-cmd.ts @@ -448,6 +448,44 @@ 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') @@ -455,28 +493,14 @@ export class JsfixCmd { } 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`) - resolve(true) - } + this.promisedRead(fix) + .then(contents => { + this.benchParse(contents, repeats, true) + .then((a: any) => resolve(a)) + .catch(e => reject(e)) + }).catch(e => { + reject(e) }) - }) }) } @@ -522,22 +546,22 @@ function showHelp (): void { 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') + ' --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') From cf1aa756ea0149ac6d9b59f5419a7f421f66abc1 Mon Sep 17 00:00:00 2001 From: TimelordUK Date: Sun, 1 Jan 2023 16:51:25 +0000 Subject: [PATCH 10/19] add packages --- package-lock.json | 48 +++++++++++++++++++++++------------------------ package.json | 12 ++++++------ 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/package-lock.json b/package-lock.json index c538cf98..604ac93d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,8 +28,8 @@ }, "devDependencies": { "@types/express": "^4.17.15", - "@types/express-serve-static-core": "^4.17.31", - "@types/jest": "^29.2.4", + "@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", @@ -38,11 +38,11 @@ "@types/sax": "^1.2.4", "@types/uuid": "^9.0.0", "@types/winston": "^2.4.4", - "@typescript-eslint/eslint-plugin-tslint": "^5.35.1", - "eslint": "^8.22.0", - "eslint-config-standard-with-typescript": "^22.0.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.3.6", + "eslint-plugin-jsdoc": "^39.6.4", "eslint-plugin-node": "^11.1.0", "jest": "^29.3.1", "madge": "^5.0.1", @@ -1300,9 +1300,9 @@ } }, "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": "*", @@ -1344,9 +1344,9 @@ } }, "node_modules/@types/jest": { - "version": "29.2.4", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.2.4.tgz", - "integrity": "sha512-PipFB04k2qTRPePduVLTRiPzQfvMeLwUN3Z21hsAKaB/W9IIzgB2pizCL466ftJlcyZqnHoC9ZHpxLGl3fS86A==", + "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", @@ -3745,9 +3745,9 @@ } }, "node_modules/eslint-config-standard-with-typescript": { - "version": "22.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard-with-typescript/-/eslint-config-standard-with-typescript-22.0.0.tgz", - "integrity": "sha512-VA36U7UlFpwULvkdnh6MQj5GAV2Q+tT68ALLAwJP0ZuNXU2m0wX07uxX4qyLRdHgSzH4QJ73CveKBuSOYvh7vQ==", + "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", @@ -10687,9 +10687,9 @@ } }, "@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": "*", @@ -10731,9 +10731,9 @@ } }, "@types/jest": { - "version": "29.2.4", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.2.4.tgz", - "integrity": "sha512-PipFB04k2qTRPePduVLTRiPzQfvMeLwUN3Z21hsAKaB/W9IIzgB2pizCL466ftJlcyZqnHoC9ZHpxLGl3fS86A==", + "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", @@ -12593,9 +12593,9 @@ "requires": {} }, "eslint-config-standard-with-typescript": { - "version": "22.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard-with-typescript/-/eslint-config-standard-with-typescript-22.0.0.tgz", - "integrity": "sha512-VA36U7UlFpwULvkdnh6MQj5GAV2Q+tT68ALLAwJP0ZuNXU2m0wX07uxX4qyLRdHgSzH4QJ73CveKBuSOYvh7vQ==", + "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", diff --git a/package.json b/package.json index 6e2b6ee6..0a3d57d4 100644 --- a/package.json +++ b/package.json @@ -85,8 +85,8 @@ }, "devDependencies": { "@types/express": "^4.17.15", - "@types/express-serve-static-core": "^4.17.31", - "@types/jest": "^29.2.4", + "@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", @@ -95,11 +95,11 @@ "@types/sax": "^1.2.4", "@types/uuid": "^9.0.0", "@types/winston": "^2.4.4", - "@typescript-eslint/eslint-plugin-tslint": "^5.35.1", - "eslint": "^8.22.0", - "eslint-config-standard-with-typescript": "^22.0.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.3.6", + "eslint-plugin-jsdoc": "^39.6.4", "eslint-plugin-node": "^11.1.0", "jest": "^29.3.1", "madge": "^5.0.1", From 3722552f3cb24b5214c36ce2a46948f5efcaada8 Mon Sep 17 00:00:00 2001 From: TimelordUK Date: Sun, 1 Jan 2023 18:07:05 +0000 Subject: [PATCH 11/19] add packages --- src/sample/tcp/recovering-skeleton/app.ts | 39 ++++++++++++++--------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/src/sample/tcp/recovering-skeleton/app.ts b/src/sample/tcp/recovering-skeleton/app.ts index 16f823da..25a8afcb 100644 --- a/src/sample/tcp/recovering-skeleton/app.ts +++ b/src/sample/tcp/recovering-skeleton/app.ts @@ -17,6 +17,27 @@ class AppLauncher extends SessionLauncher { 'data/session/test-acceptor.json') } + private asInitiator (sessionContainer: DependencyContainer): void { + sessionContainer.register(DITokens.FixSession, { + useClass: SkeletonClient + }) + sessionContainer.register(DITokens.FixEntity, { + useClass: RecoveringTcpInitiator + }) + } + + 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 @@ -24,23 +45,11 @@ class AppLauncher extends SessionLauncher { 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 }) From 17041cd2e9ae0d19803ba3cab00ceac14c43952f Mon Sep 17 00:00:00 2001 From: TimelordUK Date: Sun, 1 Jan 2023 20:04:01 +0000 Subject: [PATCH 12/19] improve loss of comms where we do not want to send logout on broken transport --- src/transport/session/fix-session.ts | 165 +++++++++++++++++---------- 1 file changed, 106 insertions(+), 59 deletions(-) diff --git a/src/transport/session/fix-session.ts b/src/transport/session/fix-session.ts index b06d19ba..cf9daa2d 100644 --- a/src/transport/session/fix-session.ts +++ b/src/transport/session/fix-session.ts @@ -41,12 +41,28 @@ export abstract class FixSession extends events.EventEmitter { } public setState (state: SessionState): void { - if (state === this.sessionState.state) return const logger = this.sessionLogger const prevState = this.sessionState.state - const msg = `current state ${SessionState[prevState]} (${prevState}) moves to ${SessionState[state]} (${state})` - logger.info(msg) - this.sessionState.state = state + + if (state === prevState) return + switch (prevState) { + case SessionState.ConfirmingLogout: + case SessionState.Stopped: + if (state !== SessionState.NetworkConnectionEstablished) { + logger.info(`ignoring request to change state as now already in ${SessionState[prevState]} `) + } else { + const msg = `current state ${SessionState[prevState]} (${prevState}) moves to ${SessionState[state]} (${state})` + logger.info(msg) + this.sessionState.state = state + } + break + + default: { + const msg = `current state ${SessionState[prevState]} (${prevState}) moves to ${SessionState[state]} (${state})` + logger.info(msg) + this.sessionState.state = state + } + } } public getState (): SessionState { @@ -94,15 +110,10 @@ export abstract class FixSession extends events.EventEmitter { 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: @@ -110,64 +121,96 @@ export abstract class FixSession extends events.EventEmitter { } } - protected subscribe (): void { - const transport = this.transport + 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 rx = transport?.receiver - const tx = transport?.transmitter + protected rxOnMsg (msgType: string, view: MsgView): void { + const logger = this.sessionLogger - 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) - } - }) + 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('error', (e: Error) => { - logger.warning(`rx error event: ${e.message} ${e.stack ?? ''}`) - this.terminate(e) - }) + protected rxOnDone (): void { + const logger = this.sessionLogger + logger.info('rx done received') + this.done() + } - rx?.on('done', () => { - logger.info('rx done received') - this.done() - }) + protected rxOnError (e: Error): void { + const logger = this.sessionLogger + logger.warning(`rx error event: ${e.message} ${e.stack ?? ''}`) + this.terminate(e) + } - 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 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('decoded', (msgType: string, data: ElasticBuffer, ptr: number) => { - logger.debug(`rx: [${msgType}] ${ptr} bytes`) - this.onDecoded(msgType, data.toString(ptr)) - }) + protected txOnError (e: Error): void { + const logger = this.sessionLogger + logger.warning(`tx error event: ${e.message} ${e.stack ?? ''}`) + this.terminate(e) + } - tx?.on('error', (e: Error) => { - logger.warning(`tx error event: ${e.message} ${e.stack ?? ''}`) - this.terminate(e) - }) + protected txOnEncoded (msgType: string, data: string): void { + const logger = this.sessionLogger + logger.debug(`tx: [${msgType}] ${data.length} bytes`) + this.onEncoded(msgType, data) + } - tx?.on('encoded', (msgType: string, data: string) => { - logger.debug(`tx: [${msgType}] ${data.length} bytes`) - this.onEncoded(msgType, data) - }) + 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) + } + + protected subscribe (): void { + const transport = this.transport + + const rx = transport?.receiver + const tx = transport?.transmitter + + rx?.on('msg', (msgType: string, view: MsgView) => this.rxOnMsg(msgType, view)) + rx?.on('error', (e: Error) => this.rxOnError(e)) + rx?.on('done', () => this.rxOnDone()) + rx?.on('end', () => this.rxOnEnd()) + rx?.on('decoded', (msgType: string, data: ElasticBuffer, ptr: number) => this.rxOnDecoded(msgType, data, ptr)) + tx?.on('error', (e: Error) => this.txOnError(e)) + tx?.on('encoded', (msgType: string, data: string) => this.txOnEncoded(msgType, data)) } protected validStateApplicationMsg (): boolean { @@ -214,6 +257,7 @@ export abstract class FixSession extends events.EventEmitter { const state = this.sessionState.state switch (state) { case SessionState.WaitingLogoutConfirm: { + this.unsubscribe() this.sessionLogger.info(`peer confirms logout Text = '${msg}'`) this.stop() break @@ -275,10 +319,12 @@ export abstract class FixSession extends events.EventEmitter { case SessionState.ConfirmingLogout: { // this instance responds to log out + this.setState(SessionState.ConfirmingLogout) sessionState.logoutSentAt = new Date() const msg = `${this.me} confirming logout` this.sessionLogger.info(msg) this.sendLogout(msg) + this.unsubscribe() break } @@ -326,6 +372,7 @@ export abstract class FixSession extends events.EventEmitter { if (this.timer) { clearInterval(this.timer) } + this.unsubscribe() this.sessionLogger.info('stop: kill transport') this.transport?.end() if (error) { From 950a6c0f4bde161e6e63e8879d2a16d21f24d9bd Mon Sep 17 00:00:00 2001 From: TimelordUK Date: Mon, 2 Jan 2023 15:26:05 +0000 Subject: [PATCH 13/19] improve recovery when not resetting seq numbers by not sending logout on broken socket to break seq num and change the recovering skeleton demo to work with no reset of seq numbers to show this now works --- src/sample/tcp/recovering-skeleton/app.ts | 16 +++- .../recovering-skeleton/respawn-acceptor.ts | 64 ++++++++++++++- src/transport/ascii/ascii-msg-transmitter.ts | 8 +- src/transport/ascii/ascii-session.ts | 3 +- src/transport/fixml/fixml-msg-transmitter.ts | 3 +- src/transport/msg-transmitter.ts | 6 +- src/transport/session/fix-session-state.ts | 14 +++- src/transport/session/fix-session.ts | 81 ++++++++++++------- src/transport/session/session-description.ts | 2 +- src/transport/tcp/recovering-tcp-initiator.ts | 21 ++++- src/transport/tcp/tcp-acceptor-listener.ts | 1 + 11 files changed, 172 insertions(+), 47 deletions(-) diff --git a/src/sample/tcp/recovering-skeleton/app.ts b/src/sample/tcp/recovering-skeleton/app.ts index 25a8afcb..7a370dd7 100644 --- a/src/sample/tcp/recovering-skeleton/app.ts +++ b/src/sample/tcp/recovering-skeleton/app.ts @@ -8,13 +8,23 @@ 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' + +const root: string = '../../../../' + +function makeConfig (config: string): ISessionDescription { + const o = require(path.join(root, config)) + o.ResetSeqNumFlag = false + console.log(JSON.stringify(o, null, 4)) + 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 { diff --git a/src/sample/tcp/recovering-skeleton/respawn-acceptor.ts b/src/sample/tcp/recovering-skeleton/respawn-acceptor.ts index 37adea36..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) @@ -19,13 +24,70 @@ export class RespawnAcceptor extends FixEntity { 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 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/transport/ascii/ascii-msg-transmitter.ts b/src/transport/ascii/ascii-msg-transmitter.ts index f133291d..e908a281 100644 --- a/src/transport/ascii/ascii-msg-transmitter.ts +++ b/src/transport/ascii/ascii-msg-transmitter.ts @@ -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,10 +54,11 @@ export class AsciiMsgTransmitter extends MsgTransmitter { headerProps.OrigSendingTime = SendingTime // when first sent } + const msgSeqNum = this.msgSeqNum const sendingTime = this.time || new Date() - const hdr: ILooseObject | null = factory?.header(msgType, this.msgSeqNum, sendingTime, headerProps) ?? null + 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 + if (!hdr) return null if (!headerProps.PossDupFlag) { this.msgSeqNum++ } @@ -82,5 +83,6 @@ export class AsciiMsgTransmitter extends MsgTransmitter { if (trl) { encoder.encode(trl, trailerName) } + return hdr } } diff --git a/src/transport/ascii/ascii-session.ts b/src/transport/ascii/ascii-session.ts index 07d6afde..faf750bf 100644 --- a/src/transport/ascii/ascii-session.ts +++ b/src/transport/ascii/ascii-session.ts @@ -277,6 +277,8 @@ export abstract class AsciiSession extends FixSession { } private startTimer (interval: number = 200): void { + const logger = this.sessionLogger + logger.info(`start heartbeat timer. interval = ${interval}`) this.timer = setInterval(() => { this.tick() }, interval) @@ -301,7 +303,6 @@ 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') diff --git a/src/transport/fixml/fixml-msg-transmitter.ts b/src/transport/fixml/fixml-msg-transmitter.ts index 09904816..5c296854 100644 --- a/src/transport/fixml/fixml-msg-transmitter.ts +++ b/src/transport/fixml/fixml-msg-transmitter.ts @@ -15,7 +15,7 @@ export class FixmlMsgTransmitter extends MsgTransmitter { this.encoder = config.sessionContainer.resolve(DITokens.MsgEncoder) } - public encodeMessage (msgType: string, obj: ILooseObject): void { + public encodeMessage (msgType: string, obj: ILooseObject): any { const adapter = this?.config?.description?.application?.http?.adapter if (adapter) { adapter.beginMessage(msgType) @@ -24,5 +24,6 @@ export class FixmlMsgTransmitter extends MsgTransmitter { const factory = this.config.factory obj.StandardHeader = factory?.header() fe.encode(obj, msgType) + return obj.StandardHeader } } diff --git a/src/transport/msg-transmitter.ts b/src/transport/msg-transmitter.ts index 1363aaa1..555889ff 100644 --- a/src/transport/msg-transmitter.ts +++ b/src/transport/msg-transmitter.ts @@ -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/fix-session-state.ts b/src/transport/session/fix-session-state.ts index 5d36f596..396e41a9 100644 --- a/src/transport/session/fix-session-state.ts +++ b/src/transport/session/fix-session-state.ts @@ -3,6 +3,7 @@ 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 @@ -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,9 +37,8 @@ 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 ({ @@ -61,6 +62,10 @@ export class FixSessionState { return moment(d).format('HH:mm:ss.SSS') } + public lastSentSeqNum (): number { + return this?.lastHeader?.MsgSeqNum ?? 0 + } + public toString (): string { const buffer = new ElasticBuffer(1024) @@ -80,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}`) diff --git a/src/transport/session/fix-session.ts b/src/transport/session/fix-session.ts index cf9daa2d..eb35492d 100644 --- a/src/transport/session/fix-session.ts +++ b/src/transport/session/fix-session.ts @@ -8,6 +8,8 @@ import { ILooseObject } from '../../collections/collection' import * as events from 'events' import { SessionState } from './session-state' import { SegmentType } from '../../buffer/segment/segment-type' +import { MsgTransmitter } from '../msg-transmitter' +import { boolean } from 'mathjs' export abstract class FixSession extends events.EventEmitter { public logReceivedMsgs: boolean = false @@ -40,27 +42,36 @@ export abstract class FixSession extends events.EventEmitter { this.sessionState.compId = description.SenderCompId } - public setState (state: SessionState): void { + 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 ${currentStateStr} (${currentState}) moves to ${SessionState[newState]} (${newState})` + logger.info(msg) + this.sessionState.state = newState + } - if (state === prevState) return - switch (prevState) { + 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 ${SessionState[prevState]} `) + logger.info(`ignoring request to change state as now already in ${currentStateStr}`) } else { - const msg = `current state ${SessionState[prevState]} (${prevState}) moves to ${SessionState[state]} (${state})` - logger.info(msg) - this.sessionState.state = state + this.assignState(state) } break default: { - const msg = `current state ${SessionState[prevState]} (${prevState}) moves to ${SessionState[state]} (${state})` - logger.info(msg) - this.sessionState.state = state + this.assignState(state) } } } @@ -69,6 +80,14 @@ export abstract class FixSession extends events.EventEmitter { return this.sessionState.state } + public lastSentSeqNum (): number { + return this.sessionState.lastSentSeqNum() + } + + public lastPeerSeqNum (): number { + return this.sessionState.lastPeerMsgSeqNum + } + public sendLogon (): void { const lo = this.config.factory?.logon() if (lo) { @@ -176,12 +195,17 @@ export abstract class FixSession extends events.EventEmitter { this.terminate(e) } - protected txOnEncoded (msgType: string, data: string): void { + protected txOnEncoded (msgType: string, data: string, hdr: ILooseObject): void { const logger = this.sessionLogger - logger.debug(`tx: [${msgType}] ${data.length} bytes`) + this.sessionState.lastHeader = hdr + logger.debug(`tx: [${msgType}] ${data.length} bytes seqNo = ${this.lastSentSeqNum()}`) this.onEncoded(msgType, data) } + public getTransport (): MsgTransport | null { + return this.transport + } + protected unsubscribe (): void { const logger = this.sessionLogger logger.info(`unsubscribe sessionState = [${this.sessionState.toString()}]`) @@ -210,7 +234,7 @@ export abstract class FixSession extends events.EventEmitter { rx?.on('end', () => this.rxOnEnd()) rx?.on('decoded', (msgType: string, data: ElasticBuffer, ptr: number) => this.rxOnDecoded(msgType, data, ptr)) tx?.on('error', (e: Error) => this.txOnError(e)) - tx?.on('encoded', (msgType: string, data: string) => this.txOnEncoded(msgType, data)) + tx?.on('encoded', (msgType: string, data: string, hdr: ILooseObject) => this.txOnEncoded(msgType, data, hdr)) } protected validStateApplicationMsg (): boolean { @@ -238,12 +262,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() } @@ -257,7 +287,6 @@ export abstract class FixSession extends events.EventEmitter { const state = this.sessionState.state switch (state) { case SessionState.WaitingLogoutConfirm: { - this.unsubscribe() this.sessionLogger.info(`peer confirms logout Text = '${msg}'`) this.stop() break @@ -324,7 +353,6 @@ export abstract class FixSession extends events.EventEmitter { const msg = `${this.me} confirming logout` this.sessionLogger.info(msg) this.sendLogout(msg) - this.unsubscribe() break } @@ -355,13 +383,12 @@ 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) } @@ -369,9 +396,7 @@ export abstract class FixSession extends events.EventEmitter { if (this.sessionState.state === SessionState.Stopped) { return } - if (this.timer) { - clearInterval(this.timer) - } + this.stopTimer() this.unsubscribe() this.sessionLogger.info('stop: kill transport') this.transport?.end() diff --git a/src/transport/session/session-description.ts b/src/transport/session/session-description.ts index 615a9b82..13c26902 100644 --- a/src/transport/session/session-description.ts +++ b/src/transport/session/session-description.ts @@ -9,7 +9,7 @@ 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 diff --git a/src/transport/tcp/recovering-tcp-initiator.ts b/src/transport/tcp/recovering-tcp-initiator.ts index 094c2bd4..440dc222 100644 --- a/src/transport/tcp/recovering-tcp-initiator.ts +++ b/src/transport/tcp/recovering-tcp-initiator.ts @@ -61,12 +61,20 @@ export class RecoveringTcpInitiator extends FixEntity { return this.session.getState() } + 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() } @@ -99,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}`) @@ -106,7 +121,9 @@ 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) diff --git a/src/transport/tcp/tcp-acceptor-listener.ts b/src/transport/tcp/tcp-acceptor-listener.ts index a40e977c..b69467a7 100644 --- a/src/transport/tcp/tcp-acceptor-listener.ts +++ b/src/transport/tcp/tcp-acceptor-listener.ts @@ -25,6 +25,7 @@ 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(() => { From 9eec3899dff3b19d33434cd9001693890f655068 Mon Sep 17 00:00:00 2001 From: TimelordUK Date: Mon, 2 Jan 2023 18:11:48 +0000 Subject: [PATCH 14/19] replace legacy request promises with axios --- data/session/test-http-initiator.json | 4 +- package-lock.json | 723 +++--------------- package.json | 3 +- src/sample/http/oms/app.ts | 16 +- src/transport/duplex/http-duplex.ts | 4 +- src/transport/http/html-options.ts | 4 +- src/transport/http/http-acceptor-listener.ts | 2 +- src/transport/http/http-acceptor.ts | 12 +- .../http/http-json-sample-adapter.ts | 8 +- src/transport/session/fix-session.ts | 16 +- 10 files changed, 133 insertions(+), 659 deletions(-) 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 604ac93d..3853b662 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "MIT", "dependencies": { "align-text": "^1.0.2", + "axios": "^1.2.2", "express": "^4.18.2", "lodash": "^4.17.21", "mathjs": "^11.5.0", @@ -17,8 +18,6 @@ "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", @@ -2180,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", @@ -2358,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", @@ -2390,19 +2374,16 @@ "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.3.1", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.3.1.tgz", @@ -2536,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", @@ -2797,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", @@ -3074,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", @@ -3093,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", @@ -3452,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", @@ -4692,23 +4635,11 @@ } ] }, - "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.12", @@ -4729,7 +4660,8 @@ "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", @@ -4901,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": { @@ -5088,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", @@ -5193,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", @@ -5302,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", @@ -5723,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", @@ -5776,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", @@ -6741,11 +6644,6 @@ "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", @@ -6779,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", @@ -6795,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", @@ -6817,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", @@ -7462,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", @@ -7800,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", @@ -8104,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", @@ -8263,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", @@ -8498,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", @@ -8732,30 +8504,6 @@ "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", @@ -8858,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", @@ -9141,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", @@ -9300,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", @@ -9441,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" } @@ -9488,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", @@ -11299,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", @@ -11432,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", @@ -11461,15 +11149,15 @@ "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.3.1", @@ -11565,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", @@ -11750,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", @@ -11964,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", @@ -11980,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", @@ -12245,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", @@ -13132,20 +12785,11 @@ } } }, - "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.12", @@ -13163,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", @@ -13310,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", @@ -13432,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", @@ -13513,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", @@ -13588,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", @@ -13870,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", @@ -13908,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", @@ -14638,11 +14251,6 @@ "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", @@ -14667,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", @@ -14683,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", @@ -14699,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", @@ -15188,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", @@ -15432,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", @@ -15662,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", @@ -15773,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", @@ -15931,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", @@ -16135,22 +15648,6 @@ "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", @@ -16206,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", @@ -16421,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", @@ -16533,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", @@ -16624,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" } @@ -16659,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", diff --git a/package.json b/package.json index 0a3d57d4..54e71d09 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "license": "MIT", "dependencies": { "align-text": "^1.0.2", + "axios": "^1.2.2", "express": "^4.18.2", "lodash": "^4.17.21", "mathjs": "^11.5.0", @@ -74,8 +75,6 @@ "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", diff --git a/src/sample/http/oms/app.ts b/src/sample/http/oms/app.ts index 309a6bb2..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 () { @@ -24,13 +25,26 @@ class AppLauncher extends SessionLauncher { } as EngineFactory } + 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) const http = config?.description?.application?.http if (http) { http.adapter = sessionContainer.resolve(DITokens.IHttpAdapter) const initiator = sessionContainer.resolve(DITokens.FixEntity) - return await initiator.start() + return this.waitFor(1000).then(async () => { + return initiator.start() + }) } } } diff --git a/src/transport/duplex/http-duplex.ts b/src/transport/duplex/http-duplex.ts index 39c8f27b..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 { @@ -29,7 +29,7 @@ export class HttpDuplex extends FixDuplex { const adapter = this.adapter const options = adapter.getOptions(data) if (options) { - rp(options).then((message: any) => { + axios(options).then((message: any) => { const body = adapter.endMessage(message) forward.push(body) done() diff --git a/src/transport/http/html-options.ts b/src/transport/http/html-options.ts index 88065263..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 + url: string json: boolean - body: any + data: any headers: any resolveWithFullResponse: boolean } diff --git a/src/transport/http/http-acceptor-listener.ts b/src/transport/http/http-acceptor-listener.ts index cc41ef85..362bfbf7 100644 --- a/src/transport/http/http-acceptor-listener.ts +++ b/src/transport/http/http-acceptor-listener.ts @@ -14,7 +14,7 @@ export class HttpAcceptorListener extends FixEntity { } async start (): Promise { - return await new Promise(async (resolve, reject) => { + return new Promise(async (resolve, reject) => { const logger = this.config.logFactory.logger('acceptor') const sessionContainer = this.config.sessionContainer if (!sessionContainer.isRegistered(DITokens.FixSession)) { diff --git a/src/transport/http/http-acceptor.ts b/src/transport/http/http-acceptor.ts index 64e7372b..9402ec25 100644 --- a/src/transport/http/http-acceptor.ts +++ b/src/transport/http/http-acceptor.ts @@ -69,14 +69,14 @@ export class HttpAcceptor extends FixAcceptor { this.logger.info(`transport ${tid} ends total transports = ${keys.length}`) } - private async respond (duplex: FixDuplex, res: express.Response, token: string | null = null): Promise { - return await new Promise((resolve, 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 b = Buffer.from(businessReject, 'utf-8') duplex.writable.removeListener('data', transmit) - res.send(b) + response.send(b) reject(new Error('no response')) }, 5000) @@ -84,10 +84,10 @@ export class HttpAcceptor extends FixAcceptor { 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) + response.send(d) resolve(true) } diff --git a/src/transport/http/http-json-sample-adapter.ts b/src/transport/http/http-json-sample-adapter.ts index bba6c992..93c92232 100644 --- a/src/transport/http/http-json-sample-adapter.ts +++ b/src/transport/http/http-json-sample-adapter.ts @@ -34,10 +34,10 @@ export class HttpJsonSampleAdapter implements IHttpAdapter { 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 } @@ -48,7 +48,7 @@ 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 { @@ -57,7 +57,7 @@ export class HttpJsonSampleAdapter implements IHttpAdapter { const route = routes.get(msgType) ?? routes.get('default') const options = { method: route?.value.method, - uri: route?.value.uri, + url: route?.value.url, json: route?.value.json, resolveWithFullResponse: route?.value.resolveWithFullResponse, headers: route?.value.headers diff --git a/src/transport/session/fix-session.ts b/src/transport/session/fix-session.ts index eb35492d..8ae9c62b 100644 --- a/src/transport/session/fix-session.ts +++ b/src/transport/session/fix-session.ts @@ -227,14 +227,14 @@ export abstract class FixSession extends events.EventEmitter { const rx = transport?.receiver const tx = transport?.transmitter - - rx?.on('msg', (msgType: string, view: MsgView) => this.rxOnMsg(msgType, view)) - rx?.on('error', (e: Error) => this.rxOnError(e)) - rx?.on('done', () => this.rxOnDone()) - rx?.on('end', () => this.rxOnEnd()) - rx?.on('decoded', (msgType: string, data: ElasticBuffer, ptr: number) => this.rxOnDecoded(msgType, data, ptr)) - tx?.on('error', (e: Error) => this.txOnError(e)) - tx?.on('encoded', (msgType: string, data: string, hdr: ILooseObject) => this.txOnEncoded(msgType, data, hdr)) + 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 { From 6aa228775a466cf8413a18b27b177b5505d5c8b6 Mon Sep 17 00:00:00 2001 From: TimelordUK Date: Mon, 2 Jan 2023 19:22:47 +0000 Subject: [PATCH 15/19] linting --- src/buffer/elastic-buffer.ts | 4 ++-- src/buffer/msg-view.ts | 10 ++++++---- src/collections/collection.ts | 8 ++------ src/collections/dictionary.ts | 2 +- .../definition/simple-field-definition.ts | 2 +- src/dictionary/version-util.ts | 16 ++++++++-------- 6 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/buffer/elastic-buffer.ts b/src/buffer/elastic-buffer.ts index 3a6f4a41..d09b6ab3 100644 --- a/src/buffer/elastic-buffer.ts +++ b/src/buffer/elastic-buffer.ts @@ -140,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 { @@ -250,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 { diff --git a/src/buffer/msg-view.ts b/src/buffer/msg-view.ts index 1d4d2a67..a30414b2 100644 --- a/src/buffer/msg-view.ts +++ b/src/buffer/msg-view.ts @@ -4,8 +4,11 @@ import { Structure } from './structure' import { Dictionary } from '../collections' import { Tags } from './tag/tags' import { - ContainedGroupField, ContainedComponentField, ContainedField, - ContainedFieldSet, ContainedSimpleField + ContainedComponentField, + ContainedField, + ContainedFieldSet, + ContainedGroupField, + ContainedSimpleField } from '../dictionary/contained' import { SetReduce } from '../dictionary' import { ILooseObject } from '../collections/collection' @@ -227,8 +230,7 @@ export abstract class MsgView { } return null } - const reduced: MsgView | null = parts.reduce(reducer, this) - return reduced + return parts.reduce(reducer, this) } public abstract checksum (): number 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 7f26e682..32415dfc 100644 --- a/src/collections/dictionary.ts +++ b/src/collections/dictionary.ts @@ -1,7 +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) diff --git a/src/dictionary/definition/simple-field-definition.ts b/src/dictionary/definition/simple-field-definition.ts index 3a2875c3..5d193f6c 100644 --- a/src/dictionary/definition/simple-field-definition.ts +++ b/src/dictionary/definition/simple-field-definition.ts @@ -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/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 From bfbd4f4593c694db6008d06e3507fb22d5bca534 Mon Sep 17 00:00:00 2001 From: TimelordUK Date: Mon, 2 Jan 2023 19:28:55 +0000 Subject: [PATCH 16/19] linting --- src/transport/session/fix-session.ts | 6 ------ src/transport/tcp/recovering-tcp-initiator.ts | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/transport/session/fix-session.ts b/src/transport/session/fix-session.ts index 8ae9c62b..899e06b2 100644 --- a/src/transport/session/fix-session.ts +++ b/src/transport/session/fix-session.ts @@ -8,8 +8,6 @@ import { ILooseObject } from '../../collections/collection' import * as events from 'events' import { SessionState } from './session-state' import { SegmentType } from '../../buffer/segment/segment-type' -import { MsgTransmitter } from '../msg-transmitter' -import { boolean } from 'mathjs' export abstract class FixSession extends events.EventEmitter { public logReceivedMsgs: boolean = false @@ -202,10 +200,6 @@ export abstract class FixSession extends events.EventEmitter { this.onEncoded(msgType, data) } - public getTransport (): MsgTransport | null { - return this.transport - } - protected unsubscribe (): void { const logger = this.sessionLogger logger.info(`unsubscribe sessionState = [${this.sessionState.toString()}]`) diff --git a/src/transport/tcp/recovering-tcp-initiator.ts b/src/transport/tcp/recovering-tcp-initiator.ts index 440dc222..dacfa533 100644 --- a/src/transport/tcp/recovering-tcp-initiator.ts +++ b/src/transport/tcp/recovering-tcp-initiator.ts @@ -11,7 +11,7 @@ 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. */ From a32983f2f3905412e6a5f566a50032e2a81d2708 Mon Sep 17 00:00:00 2001 From: TimelordUK Date: Tue, 3 Jan 2023 13:36:47 +0000 Subject: [PATCH 17/19] linting --- .gitignore | 1 + src/sample/tcp/recovering-skeleton/app.ts | 1 - src/transport/http/http-acceptor-listener.ts | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index df42b529..100c36b0 100644 --- a/.gitignore +++ b/.gitignore @@ -84,3 +84,4 @@ 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/src/sample/tcp/recovering-skeleton/app.ts b/src/sample/tcp/recovering-skeleton/app.ts index 7a370dd7..c452c677 100644 --- a/src/sample/tcp/recovering-skeleton/app.ts +++ b/src/sample/tcp/recovering-skeleton/app.ts @@ -16,7 +16,6 @@ const root: string = '../../../../' function makeConfig (config: string): ISessionDescription { const o = require(path.join(root, config)) o.ResetSeqNumFlag = false - console.log(JSON.stringify(o, null, 4)) return o as ISessionDescription } diff --git a/src/transport/http/http-acceptor-listener.ts b/src/transport/http/http-acceptor-listener.ts index 362bfbf7..0ca2c863 100644 --- a/src/transport/http/http-acceptor-listener.ts +++ b/src/transport/http/http-acceptor-listener.ts @@ -25,6 +25,7 @@ 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(() => { From 705c486ccbf9c2340f428842c751aef7aa098525 Mon Sep 17 00:00:00 2001 From: TimelordUK Date: Tue, 3 Jan 2023 13:37:05 +0000 Subject: [PATCH 18/19] 2.3.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3853b662..56cb5068 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "jspurefix", - "version": "2.2.1", + "version": "2.3.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "jspurefix", - "version": "2.2.1", + "version": "2.3.0", "license": "MIT", "dependencies": { "align-text": "^1.0.2", diff --git a/package.json b/package.json index 54e71d09..8119b303 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jspurefix", - "version": "2.2.1", + "version": "2.3.0", "description": "pure node js fix engine", "keywords": [ "typescript", From c7b8ea21c17a11b52d44aaf4474405f6b7e9049b Mon Sep 17 00:00:00 2001 From: TimelordUK Date: Tue, 3 Jan 2023 14:14:07 +0000 Subject: [PATCH 19/19] 3.0.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 56cb5068..6dd618f6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "jspurefix", - "version": "2.3.0", + "version": "3.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "jspurefix", - "version": "2.3.0", + "version": "3.0.0", "license": "MIT", "dependencies": { "align-text": "^1.0.2", diff --git a/package.json b/package.json index 8119b303..ec66a0b8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jspurefix", - "version": "2.3.0", + "version": "3.0.0", "description": "pure node js fix engine", "keywords": [ "typescript",