diff --git a/package-lock.json b/package-lock.json index 969a09fe..720a7ea3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -63,6 +63,7 @@ "@storybook/addon-links": "^6.2.9", "@storybook/addon-svelte-csf": "^2.0.11", "@storybook/svelte": "^6.2.9", + "@total-typescript/ts-reset": "^0.5.1", "@types/jest": "^24.9.1", "@types/koa-router": "^7.4.4", "@types/string-similarity": "^4.0.0", @@ -14891,6 +14892,12 @@ "node": ">= 6" } }, + "node_modules/@total-typescript/ts-reset": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@total-typescript/ts-reset/-/ts-reset-0.5.1.tgz", + "integrity": "sha512-AqlrT8YA1o7Ff5wPfMOL0pvL+1X+sw60NN6CcOCqs658emD6RfiXhF7Gu9QcfKBH7ELY2nInLhKSCWVoNL70MQ==", + "dev": true + }, "node_modules/@trysound/sax": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", @@ -24632,34 +24639,26 @@ }, "node_modules/fsevents/node_modules/abbrev": { "version": "1.1.1", - "dev": true, "inBundle": true, - "license": "ISC", - "optional": true + "license": "ISC" }, "node_modules/fsevents/node_modules/ansi-regex": { "version": "2.1.1", - "dev": true, "inBundle": true, "license": "MIT", - "optional": true, "engines": { "node": ">=0.10.0" } }, "node_modules/fsevents/node_modules/aproba": { "version": "1.2.0", - "dev": true, "inBundle": true, - "license": "ISC", - "optional": true + "license": "ISC" }, "node_modules/fsevents/node_modules/are-we-there-yet": { "version": "1.1.5", - "dev": true, "inBundle": true, "license": "ISC", - "optional": true, "dependencies": { "delegates": "^1.0.0", "readable-stream": "^2.0.6" @@ -24667,17 +24666,13 @@ }, "node_modules/fsevents/node_modules/balanced-match": { "version": "1.0.0", - "dev": true, "inBundle": true, - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/fsevents/node_modules/brace-expansion": { "version": "1.1.11", - "dev": true, "inBundle": true, "license": "MIT", - "optional": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -24685,75 +24680,57 @@ }, "node_modules/fsevents/node_modules/chownr": { "version": "1.1.4", - "dev": true, "inBundle": true, - "license": "ISC", - "optional": true + "license": "ISC" }, "node_modules/fsevents/node_modules/code-point-at": { "version": "1.1.0", - "dev": true, "inBundle": true, "license": "MIT", - "optional": true, "engines": { "node": ">=0.10.0" } }, "node_modules/fsevents/node_modules/concat-map": { "version": "0.0.1", - "dev": true, "inBundle": true, - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/fsevents/node_modules/console-control-strings": { "version": "1.1.0", - "dev": true, "inBundle": true, - "license": "ISC", - "optional": true + "license": "ISC" }, "node_modules/fsevents/node_modules/core-util-is": { "version": "1.0.2", - "dev": true, "inBundle": true, - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/fsevents/node_modules/debug": { "version": "3.2.6", - "dev": true, "inBundle": true, "license": "MIT", - "optional": true, "dependencies": { "ms": "^2.1.1" } }, "node_modules/fsevents/node_modules/deep-extend": { "version": "0.6.0", - "dev": true, "inBundle": true, "license": "MIT", - "optional": true, "engines": { "node": ">=4.0.0" } }, "node_modules/fsevents/node_modules/delegates": { "version": "1.0.0", - "dev": true, "inBundle": true, - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/fsevents/node_modules/detect-libc": { "version": "1.0.3", - "dev": true, "inBundle": true, "license": "Apache-2.0", - "optional": true, "bin": { "detect-libc": "bin/detect-libc.js" }, @@ -24763,27 +24740,21 @@ }, "node_modules/fsevents/node_modules/fs-minipass": { "version": "1.2.7", - "dev": true, "inBundle": true, "license": "ISC", - "optional": true, "dependencies": { "minipass": "^2.6.0" } }, "node_modules/fsevents/node_modules/fs.realpath": { "version": "1.0.0", - "dev": true, "inBundle": true, - "license": "ISC", - "optional": true + "license": "ISC" }, "node_modules/fsevents/node_modules/gauge": { "version": "2.7.4", - "dev": true, "inBundle": true, "license": "ISC", - "optional": true, "dependencies": { "aproba": "^1.0.3", "console-control-strings": "^1.0.0", @@ -24797,10 +24768,8 @@ }, "node_modules/fsevents/node_modules/glob": { "version": "7.1.6", - "dev": true, "inBundle": true, "license": "ISC", - "optional": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -24818,17 +24787,13 @@ }, "node_modules/fsevents/node_modules/has-unicode": { "version": "2.0.1", - "dev": true, "inBundle": true, - "license": "ISC", - "optional": true + "license": "ISC" }, "node_modules/fsevents/node_modules/iconv-lite": { "version": "0.4.24", - "dev": true, "inBundle": true, "license": "MIT", - "optional": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -24838,20 +24803,16 @@ }, "node_modules/fsevents/node_modules/ignore-walk": { "version": "3.0.3", - "dev": true, "inBundle": true, "license": "ISC", - "optional": true, "dependencies": { "minimatch": "^3.0.4" } }, "node_modules/fsevents/node_modules/inflight": { "version": "1.0.6", - "dev": true, "inBundle": true, "license": "ISC", - "optional": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -24859,27 +24820,21 @@ }, "node_modules/fsevents/node_modules/inherits": { "version": "2.0.4", - "dev": true, "inBundle": true, - "license": "ISC", - "optional": true + "license": "ISC" }, "node_modules/fsevents/node_modules/ini": { "version": "1.3.5", - "dev": true, "inBundle": true, "license": "ISC", - "optional": true, "engines": { "node": "*" } }, "node_modules/fsevents/node_modules/is-fullwidth-code-point": { "version": "1.0.0", - "dev": true, "inBundle": true, "license": "MIT", - "optional": true, "dependencies": { "number-is-nan": "^1.0.0" }, @@ -24889,17 +24844,13 @@ }, "node_modules/fsevents/node_modules/isarray": { "version": "1.0.0", - "dev": true, "inBundle": true, - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/fsevents/node_modules/minimatch": { "version": "3.0.4", - "dev": true, "inBundle": true, "license": "ISC", - "optional": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -24909,17 +24860,13 @@ }, "node_modules/fsevents/node_modules/minimist": { "version": "1.2.5", - "dev": true, "inBundle": true, - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/fsevents/node_modules/minipass": { "version": "2.9.0", - "dev": true, "inBundle": true, "license": "ISC", - "optional": true, "dependencies": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -24927,10 +24874,8 @@ }, "node_modules/fsevents/node_modules/minizlib": { "version": "1.3.3", - "dev": true, "inBundle": true, "license": "MIT", - "optional": true, "dependencies": { "minipass": "^2.9.0" } @@ -24938,10 +24883,8 @@ "node_modules/fsevents/node_modules/mkdirp": { "version": "0.5.3", "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", - "dev": true, "inBundle": true, "license": "MIT", - "optional": true, "dependencies": { "minimist": "^1.2.5" }, @@ -24951,17 +24894,13 @@ }, "node_modules/fsevents/node_modules/ms": { "version": "2.1.2", - "dev": true, "inBundle": true, - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/fsevents/node_modules/needle": { "version": "2.3.3", - "dev": true, "inBundle": true, "license": "MIT", - "optional": true, "dependencies": { "debug": "^3.2.6", "iconv-lite": "^0.4.4", @@ -24976,10 +24915,8 @@ }, "node_modules/fsevents/node_modules/node-pre-gyp": { "version": "0.14.0", - "dev": true, "inBundle": true, "license": "BSD-3-Clause", - "optional": true, "dependencies": { "detect-libc": "^1.0.2", "mkdirp": "^0.5.1", @@ -24998,10 +24935,8 @@ }, "node_modules/fsevents/node_modules/nopt": { "version": "4.0.3", - "dev": true, "inBundle": true, "license": "ISC", - "optional": true, "dependencies": { "abbrev": "1", "osenv": "^0.1.4" @@ -25012,27 +24947,21 @@ }, "node_modules/fsevents/node_modules/npm-bundled": { "version": "1.1.1", - "dev": true, "inBundle": true, "license": "ISC", - "optional": true, "dependencies": { "npm-normalize-package-bin": "^1.0.1" } }, "node_modules/fsevents/node_modules/npm-normalize-package-bin": { "version": "1.0.1", - "dev": true, "inBundle": true, - "license": "ISC", - "optional": true + "license": "ISC" }, "node_modules/fsevents/node_modules/npm-packlist": { "version": "1.4.8", - "dev": true, "inBundle": true, "license": "ISC", - "optional": true, "dependencies": { "ignore-walk": "^3.0.1", "npm-bundled": "^1.0.1", @@ -25041,10 +24970,8 @@ }, "node_modules/fsevents/node_modules/npmlog": { "version": "4.1.2", - "dev": true, "inBundle": true, "license": "ISC", - "optional": true, "dependencies": { "are-we-there-yet": "~1.1.2", "console-control-strings": "~1.1.0", @@ -25054,60 +24981,48 @@ }, "node_modules/fsevents/node_modules/number-is-nan": { "version": "1.0.1", - "dev": true, "inBundle": true, "license": "MIT", - "optional": true, "engines": { "node": ">=0.10.0" } }, "node_modules/fsevents/node_modules/object-assign": { "version": "4.1.1", - "dev": true, "inBundle": true, "license": "MIT", - "optional": true, "engines": { "node": ">=0.10.0" } }, "node_modules/fsevents/node_modules/once": { "version": "1.4.0", - "dev": true, "inBundle": true, "license": "ISC", - "optional": true, "dependencies": { "wrappy": "1" } }, "node_modules/fsevents/node_modules/os-homedir": { "version": "1.0.2", - "dev": true, "inBundle": true, "license": "MIT", - "optional": true, "engines": { "node": ">=0.10.0" } }, "node_modules/fsevents/node_modules/os-tmpdir": { "version": "1.0.2", - "dev": true, "inBundle": true, "license": "MIT", - "optional": true, "engines": { "node": ">=0.10.0" } }, "node_modules/fsevents/node_modules/osenv": { "version": "0.1.5", - "dev": true, "inBundle": true, "license": "ISC", - "optional": true, "dependencies": { "os-homedir": "^1.0.0", "os-tmpdir": "^1.0.0" @@ -25115,27 +25030,21 @@ }, "node_modules/fsevents/node_modules/path-is-absolute": { "version": "1.0.1", - "dev": true, "inBundle": true, "license": "MIT", - "optional": true, "engines": { "node": ">=0.10.0" } }, "node_modules/fsevents/node_modules/process-nextick-args": { "version": "2.0.1", - "dev": true, "inBundle": true, - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/fsevents/node_modules/rc": { "version": "1.2.8", - "dev": true, "inBundle": true, "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "optional": true, "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -25148,10 +25057,8 @@ }, "node_modules/fsevents/node_modules/readable-stream": { "version": "2.3.7", - "dev": true, "inBundle": true, "license": "MIT", - "optional": true, "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -25164,10 +25071,8 @@ }, "node_modules/fsevents/node_modules/rimraf": { "version": "2.7.1", - "dev": true, "inBundle": true, "license": "ISC", - "optional": true, "dependencies": { "glob": "^7.1.3" }, @@ -25177,65 +25082,49 @@ }, "node_modules/fsevents/node_modules/safe-buffer": { "version": "5.1.2", - "dev": true, "inBundle": true, - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/fsevents/node_modules/safer-buffer": { "version": "2.1.2", - "dev": true, "inBundle": true, - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/fsevents/node_modules/sax": { "version": "1.2.4", - "dev": true, "inBundle": true, - "license": "ISC", - "optional": true + "license": "ISC" }, "node_modules/fsevents/node_modules/semver": { "version": "5.7.1", - "dev": true, "inBundle": true, "license": "ISC", - "optional": true, "bin": { "semver": "bin/semver" } }, "node_modules/fsevents/node_modules/set-blocking": { "version": "2.0.0", - "dev": true, "inBundle": true, - "license": "ISC", - "optional": true + "license": "ISC" }, "node_modules/fsevents/node_modules/signal-exit": { "version": "3.0.2", - "dev": true, "inBundle": true, - "license": "ISC", - "optional": true + "license": "ISC" }, "node_modules/fsevents/node_modules/string_decoder": { "version": "1.1.1", - "dev": true, "inBundle": true, "license": "MIT", - "optional": true, "dependencies": { "safe-buffer": "~5.1.0" } }, "node_modules/fsevents/node_modules/string-width": { "version": "1.0.2", - "dev": true, "inBundle": true, "license": "MIT", - "optional": true, "dependencies": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -25247,10 +25136,8 @@ }, "node_modules/fsevents/node_modules/strip-ansi": { "version": "3.0.1", - "dev": true, "inBundle": true, "license": "MIT", - "optional": true, "dependencies": { "ansi-regex": "^2.0.0" }, @@ -25260,20 +25147,16 @@ }, "node_modules/fsevents/node_modules/strip-json-comments": { "version": "2.0.1", - "dev": true, "inBundle": true, "license": "MIT", - "optional": true, "engines": { "node": ">=0.10.0" } }, "node_modules/fsevents/node_modules/tar": { "version": "4.4.13", - "dev": true, "inBundle": true, "license": "ISC", - "optional": true, "dependencies": { "chownr": "^1.1.1", "fs-minipass": "^1.2.5", @@ -25289,34 +25172,26 @@ }, "node_modules/fsevents/node_modules/util-deprecate": { "version": "1.0.2", - "dev": true, "inBundle": true, - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/fsevents/node_modules/wide-align": { "version": "1.1.3", - "dev": true, "inBundle": true, "license": "ISC", - "optional": true, "dependencies": { "string-width": "^1.0.2 || 2" } }, "node_modules/fsevents/node_modules/wrappy": { "version": "1.0.2", - "dev": true, "inBundle": true, - "license": "ISC", - "optional": true + "license": "ISC" }, "node_modules/fsevents/node_modules/yallist": { "version": "3.1.1", - "dev": true, "inBundle": true, - "license": "ISC", - "optional": true + "license": "ISC" }, "node_modules/ftp": { "version": "0.3.10", @@ -52500,6 +52375,12 @@ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==" }, + "@total-typescript/ts-reset": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@total-typescript/ts-reset/-/ts-reset-0.5.1.tgz", + "integrity": "sha512-AqlrT8YA1o7Ff5wPfMOL0pvL+1X+sw60NN6CcOCqs658emD6RfiXhF7Gu9QcfKBH7ELY2nInLhKSCWVoNL70MQ==", + "dev": true + }, "@trysound/sax": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", @@ -60297,27 +60178,19 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "ansi-regex": { "version": "2.1.1", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "aproba": { "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "are-we-there-yet": { "version": "1.1.5", "bundled": true, - "dev": true, - "optional": true, "requires": { "delegates": "^1.0.0", "readable-stream": "^2.0.6" @@ -60325,15 +60198,11 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, - "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -60341,81 +60210,57 @@ }, "chownr": { "version": "1.1.4", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "code-point-at": { "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "concat-map": { "version": "0.0.1", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "core-util-is": { "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "debug": { "version": "3.2.6", "bundled": true, - "dev": true, - "optional": true, "requires": { "ms": "^2.1.1" } }, "deep-extend": { "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "delegates": { "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "detect-libc": { "version": "1.0.3", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "fs-minipass": { "version": "1.2.7", "bundled": true, - "dev": true, - "optional": true, "requires": { "minipass": "^2.6.0" } }, "fs.realpath": { "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "gauge": { "version": "2.7.4", "bundled": true, - "dev": true, - "optional": true, "requires": { "aproba": "^1.0.3", "console-control-strings": "^1.0.0", @@ -60430,8 +60275,6 @@ "glob": { "version": "7.1.6", "bundled": true, - "dev": true, - "optional": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -60443,15 +60286,11 @@ }, "has-unicode": { "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "iconv-lite": { "version": "0.4.24", "bundled": true, - "dev": true, - "optional": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" } @@ -60459,8 +60298,6 @@ "ignore-walk": { "version": "3.0.3", "bundled": true, - "dev": true, - "optional": true, "requires": { "minimatch": "^3.0.4" } @@ -60468,8 +60305,6 @@ "inflight": { "version": "1.0.6", "bundled": true, - "dev": true, - "optional": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -60477,51 +60312,37 @@ }, "inherits": { "version": "2.0.4", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "ini": { "version": "1.3.5", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } }, "isarray": { "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "minimatch": { "version": "3.0.4", "bundled": true, - "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "1.2.5", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "minipass": { "version": "2.9.0", "bundled": true, - "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -60530,8 +60351,6 @@ "minizlib": { "version": "1.3.3", "bundled": true, - "dev": true, - "optional": true, "requires": { "minipass": "^2.9.0" } @@ -60539,23 +60358,17 @@ "mkdirp": { "version": "0.5.3", "bundled": true, - "dev": true, - "optional": true, "requires": { "minimist": "^1.2.5" } }, "ms": { "version": "2.1.2", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "needle": { "version": "2.3.3", "bundled": true, - "dev": true, - "optional": true, "requires": { "debug": "^3.2.6", "iconv-lite": "^0.4.4", @@ -60565,8 +60378,6 @@ "node-pre-gyp": { "version": "0.14.0", "bundled": true, - "dev": true, - "optional": true, "requires": { "detect-libc": "^1.0.2", "mkdirp": "^0.5.1", @@ -60583,8 +60394,6 @@ "nopt": { "version": "4.0.3", "bundled": true, - "dev": true, - "optional": true, "requires": { "abbrev": "1", "osenv": "^0.1.4" @@ -60593,23 +60402,17 @@ "npm-bundled": { "version": "1.1.1", "bundled": true, - "dev": true, - "optional": true, "requires": { "npm-normalize-package-bin": "^1.0.1" } }, "npm-normalize-package-bin": { "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "npm-packlist": { "version": "1.4.8", "bundled": true, - "dev": true, - "optional": true, "requires": { "ignore-walk": "^3.0.1", "npm-bundled": "^1.0.1", @@ -60619,8 +60422,6 @@ "npmlog": { "version": "4.1.2", "bundled": true, - "dev": true, - "optional": true, "requires": { "are-we-there-yet": "~1.1.2", "console-control-strings": "~1.1.0", @@ -60630,42 +60431,30 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "object-assign": { "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "once": { "version": "1.4.0", "bundled": true, - "dev": true, - "optional": true, "requires": { "wrappy": "1" } }, "os-homedir": { "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "os-tmpdir": { "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "osenv": { "version": "0.1.5", "bundled": true, - "dev": true, - "optional": true, "requires": { "os-homedir": "^1.0.0", "os-tmpdir": "^1.0.0" @@ -60673,21 +60462,15 @@ }, "path-is-absolute": { "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "process-nextick-args": { "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "rc": { "version": "1.2.8", "bundled": true, - "dev": true, - "optional": true, "requires": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -60698,8 +60481,6 @@ "readable-stream": { "version": "2.3.7", "bundled": true, - "dev": true, - "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -60713,53 +60494,37 @@ "rimraf": { "version": "2.7.1", "bundled": true, - "dev": true, - "optional": true, "requires": { "glob": "^7.1.3" } }, "safe-buffer": { "version": "5.1.2", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "safer-buffer": { "version": "2.1.2", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "sax": { "version": "1.2.4", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "semver": { "version": "5.7.1", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "set-blocking": { "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "signal-exit": { "version": "3.0.2", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "string_decoder": { "version": "1.1.1", "bundled": true, - "dev": true, - "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -60767,8 +60532,6 @@ "string-width": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -60778,23 +60541,17 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, - "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } }, "strip-json-comments": { "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "tar": { "version": "4.4.13", "bundled": true, - "dev": true, - "optional": true, "requires": { "chownr": "^1.1.1", "fs-minipass": "^1.2.5", @@ -60807,30 +60564,22 @@ }, "util-deprecate": { "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "wide-align": { "version": "1.1.3", "bundled": true, - "dev": true, - "optional": true, "requires": { "string-width": "^1.0.2 || 2" } }, "wrappy": { "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true + "bundled": true }, "yallist": { "version": "3.1.1", - "bundled": true, - "dev": true, - "optional": true + "bundled": true } } }, diff --git a/package.json b/package.json index 193c055e..99157993 100644 --- a/package.json +++ b/package.json @@ -100,6 +100,7 @@ "@storybook/addon-links": "^6.2.9", "@storybook/addon-svelte-csf": "^2.0.11", "@storybook/svelte": "^6.2.9", + "@total-typescript/ts-reset": "^0.5.1", "@types/jest": "^24.9.1", "@types/koa-router": "^7.4.4", "@types/string-similarity": "^4.0.0", diff --git a/src/database/models/userArticleLink.js b/src/database/models/userArticleLink.js index 300cc126..b8a916d6 100644 --- a/src/database/models/userArticleLink.js +++ b/src/database/models/userArticleLink.js @@ -33,7 +33,7 @@ class UserArticleLink extends Base { * * @param {string} userId * @param {string} articleId - * @param {object} data + * @param {object} [data] * @returns {Promise} */ static async createOrUpdateByUserIdAndArticleId(userId, articleId, data) { diff --git a/src/graphql/cofacts-api.graphql b/src/graphql/cofacts-api.graphql index 02410368..d896b13a 100644 --- a/src/graphql/cofacts-api.graphql +++ b/src/graphql/cofacts-api.graphql @@ -18,6 +18,7 @@ type Query { """ GetUser(id: String, slug: String): User GetCategory(id: String): Category + GetYdoc(id: String!): Ydoc ListArticles( filter: ListArticleFilter orderBy: [ListArticleOrderBy] @@ -176,13 +177,13 @@ type Query { type Article implements Node { id: ID! text: String - createdAt: String + createdAt: String! updatedAt: String status: ReplyRequestStatusEnum! references: [ArticleReference] """Number of normal article replies""" - replyCount: Int + replyCount: Int! """ Connections between this article and replies. Sorted by the logic described in https://github.com/cofacts/rumors-line-bot/issues/78. @@ -207,7 +208,7 @@ type Article implements Node { """Only list the articleReplies created by the currently logged in user""" selfOnly: Boolean - ): [ArticleReply] + ): [ArticleReply!]! """ Automated reply from AI before human fact checkers compose an fact check @@ -224,10 +225,10 @@ type Article implements Node { """Returns only article categories with the specified statuses""" statuses: [ArticleCategoryStatusEnum!] = [NORMAL] - ): [ArticleCategory] + ): [ArticleCategory!]! """Number of normal article categories""" - categoryCount: Int + categoryCount: Int! replyRequests( """Returns only article replies with the specified statuses""" statuses: [ReplyRequestStatusEnum!] = [NORMAL] @@ -258,7 +259,7 @@ type Article implements Node { Specify a cursor, returns results before this cursor. cannot be used with "after". """ before: String - ): ArticleConnection + ): ArticleConnection! """Hyperlinks in article text""" hyperlinks: [Hyperlink] @@ -293,8 +294,8 @@ enum ReplyRequestStatusEnum { } type ArticleReference { - createdAt: String - type: ArticleReferenceTypeEnum + createdAt: String! + type: ArticleReferenceTypeEnum! permalink: String } @@ -311,33 +312,33 @@ enum ArticleReferenceTypeEnum { """The linkage between an Article and a Reply""" type ArticleReply { - replyId: String + replyId: String! reply: Reply """Cached reply type value stored in ArticleReply""" replyType: ReplyTypeEnum - articleId: String + articleId: String! article: Article """The user who conencted this reply and this article.""" user: User userId: String! appId: String! - canUpdateStatus: Boolean - feedbackCount: Int - positiveFeedbackCount: Int - negativeFeedbackCount: Int + canUpdateStatus: Boolean! + feedbackCount: Int! + positiveFeedbackCount: Int! + negativeFeedbackCount: Int! feedbacks( """Returns only aricle reply feedbacks with the specified statuses""" statuses: [ArticleReplyFeedbackStatusEnum!] = [NORMAL] - ): [ArticleReplyFeedback] + ): [ArticleReplyFeedback!]! """ The feedback of current user. null when not logged in or not voted yet. """ ownVote: FeedbackVote - status: ArticleReplyStatusEnum - createdAt: String + status: ArticleReplyStatusEnum! + createdAt: String! updatedAt: String } @@ -346,9 +347,9 @@ type Reply implements Node { """The user submitted this reply version""" user: User - createdAt: String + createdAt: String! text: String - type: ReplyTypeEnum + type: ReplyTypeEnum! reference: String articleReplies( """Deprecated. Please use statuses instead.""" @@ -356,7 +357,7 @@ type Reply implements Node { """Returns only article replies with the specified statuses""" statuses: [ArticleReplyStatusEnum!] = [NORMAL] - ): [ArticleReply] + ): [ArticleReply!]! """ Hyperlinks in reply text or reference. May be empty array if no URLs are included. `null` when hyperlinks are still fetching. @@ -379,7 +380,7 @@ type Reply implements Node { Specify a cursor, returns results before this cursor. cannot be used with "after". """ before: String - ): ReplyConnection + ): ReplyConnection! } type User implements Node { @@ -979,6 +980,21 @@ type Cooccurrence implements Node { updatedAt: String! } +type Ydoc { + """Binary that stores as base64 encoded string""" + data: String + + """Ydoc snapshots which are used to restore to specific version""" + versions: [YdocVersion] +} + +type YdocVersion { + createdAt: String + + """Binary that stores as base64 encoded string""" + snapshot: String +} + input ListArticleFilter { """Show only articles created by a specific app.""" appId: String @@ -1557,9 +1573,7 @@ type Mutation { articleType: ArticleTypeEnum! reference: ArticleReferenceInput! - """ - The reason why the user want to submit this article. Mandatory for 1st sender - """ + """The reason why the user want to submit this article""" reason: String ): MutationResult diff --git a/src/types/chatbotState.ts b/src/types/chatbotState.ts index 82578ca5..2aabc40f 100644 --- a/src/types/chatbotState.ts +++ b/src/types/chatbotState.ts @@ -55,6 +55,9 @@ export type Context = { /** User selected article in DB */ selectedArticleId?: string; selectedArticleText?: string; + + /** FIXME: Probably not required now */ + selectedReplyId?: string; }; export type ChatbotStateHandlerParams = { diff --git a/src/types/reset.d.ts b/src/types/reset.d.ts new file mode 100644 index 00000000..12bd3edc --- /dev/null +++ b/src/types/reset.d.ts @@ -0,0 +1 @@ +import '@total-typescript/ts-reset'; diff --git a/src/webhook/handlers/__tests__/askingArticleSource.test.js b/src/webhook/handlers/__tests__/askingArticleSource.test.js index 2a1b1fc8..3f099a1f 100644 --- a/src/webhook/handlers/__tests__/askingArticleSource.test.js +++ b/src/webhook/handlers/__tests__/askingArticleSource.test.js @@ -56,7 +56,6 @@ it('returns instructions if user did not forward the whole message', async () => "type": "span", }, ], - "text": "Instructions", "type": "text", "wrap": true, }, diff --git a/src/webhook/handlers/askingArticleSource.js b/src/webhook/handlers/askingArticleSource.ts similarity index 94% rename from src/webhook/handlers/askingArticleSource.js rename to src/webhook/handlers/askingArticleSource.ts index 6448d9b8..70dd2df5 100644 --- a/src/webhook/handlers/askingArticleSource.js +++ b/src/webhook/handlers/askingArticleSource.ts @@ -12,9 +12,11 @@ import { } from './utils'; import { TUTORIAL_STEPS } from './tutorial'; +import { ChatbotStateHandler } from 'src/types/chatbotState'; -export default async function askingArticleSource(params) { - let { data, state, event, userId, replies } = params; +const askingArticleSource: ChatbotStateHandler = async (params) => { + const { data, state, event, userId } = params; + let { replies } = params; const visitor = ga(userId, state, data.searchedText); @@ -25,7 +27,7 @@ export default async function askingArticleSource(params) { case POSTBACK_NO: replies = [ createTextMessage({ - text: t`Instructions`, + altText: t`Instructions`, contents: [ { type: 'span', @@ -171,4 +173,6 @@ export default async function askingArticleSource(params) { visitor.send(); return { data, event, userId, replies }; -} +}; + +export default askingArticleSource; diff --git a/src/webhook/handlers/askingArticleSubmissionConsent.js b/src/webhook/handlers/askingArticleSubmissionConsent.ts similarity index 70% rename from src/webhook/handlers/askingArticleSubmissionConsent.js rename to src/webhook/handlers/askingArticleSubmissionConsent.ts index 339d069f..95248075 100644 --- a/src/webhook/handlers/askingArticleSubmissionConsent.js +++ b/src/webhook/handlers/askingArticleSubmissionConsent.ts @@ -1,4 +1,7 @@ import { t } from 'ttag'; +import { Message } from '@line/bot-sdk'; + +import { ChatbotStateHandler } from 'src/types/chatbotState'; import ga from 'src/lib/ga'; import gql from 'src/lib/gql'; import { getArticleURL } from 'src/lib/sharedUtils'; @@ -15,9 +18,21 @@ import { } from './utils'; import UserSettings from 'src/database/models/userSettings'; import UserArticleLink from 'src/database/models/userArticleLink'; +import { + ArticleTypeEnum, + SubmitMediaArticleUnderConsentMutation, + SubmitMediaArticleUnderConsentMutationVariables, + SubmitTextArticleUnderConsentMutation, + SubmitTextArticleUnderConsentMutationVariables, +} from 'typegen/graphql'; + +function uppercase(s: T) { + return s.toUpperCase() as Uppercase; +} -export default async function askingArticleSubmissionConsent(params) { - let { data, state, event, userId, replies } = params; +const askingArticleSubmissionConsent: ChatbotStateHandler = async (params) => { + const { data, event, userId } = params; + let { state, replies } = params; const visitor = ga(userId, state, data.searchedText); @@ -41,22 +56,43 @@ export default async function askingArticleSubmissionConsent(params) { let article; if (isTextArticle) { const result = await gql` - mutation ($text: String!) { + mutation SubmitTextArticleUnderConsent($text: String!) { CreateArticle(text: $text, reference: { type: LINE }) { id } } - `({ text: data.searchedText }, { userId }); + `< + SubmitTextArticleUnderConsentMutation, + SubmitTextArticleUnderConsentMutationVariables + >({ text: data.searchedText ?? '' }, { userId }); article = result.data.CreateArticle; } else { + /* istanbul ignore if */ if (!data.messageId) { // Should not be here throw new Error('No message ID found, cannot submit message.'); } + const articleType: ArticleTypeEnum = (() => { + switch (data.messageType) { + case 'image': + case 'audio': + case 'video': + return uppercase(data.messageType); + default: + throw new Error( + `[askingArticleSubmissionConsent] unsupported message type ${data.messageType}` + ); + } + })(); + const proxyUrl = getLineContentProxyURL(data.messageId); + const result = await gql` - mutation ($mediaUrl: String!, $articleType: ArticleTypeEnum!) { + mutation SubmitMediaArticleUnderConsent( + $mediaUrl: String! + $articleType: ArticleTypeEnum! + ) { CreateMediaArticle( mediaUrl: $mediaUrl articleType: $articleType @@ -65,13 +101,20 @@ export default async function askingArticleSubmissionConsent(params) { id } } - `( - { mediaUrl: proxyUrl, articleType: data.messageType.toUpperCase() }, - { userId } - ); + `< + SubmitMediaArticleUnderConsentMutation, + SubmitMediaArticleUnderConsentMutationVariables + >({ mediaUrl: proxyUrl, articleType }, { userId }); article = result.data.CreateMediaArticle; } + /* istanbul ignore if */ + if (!article?.id) { + throw new Error( + '[askingArticleSubmissionConsent] article is not created successfully' + ); + } + await UserArticleLink.createOrUpdateByUserIdAndArticleId( userId, article.id @@ -86,7 +129,7 @@ export default async function askingArticleSubmissionConsent(params) { userId ); - let maybeAIReplies = [ + let maybeAIReplies: Message[] = [ createTextMessage({ text: t`In the meantime, you can:`, }), @@ -150,7 +193,7 @@ export default async function askingArticleSubmissionConsent(params) { !allowNewReplyUpdate && createNotificationSettingsBubble(), createArticleShareBubble(articleUrl), - ].filter((m) => m), + ].filter(Boolean), }, }, ]; @@ -160,4 +203,6 @@ export default async function askingArticleSubmissionConsent(params) { visitor.send(); return { data, event, userId, replies }; -} +}; + +export default askingArticleSubmissionConsent; diff --git a/src/webhook/handlers/choosingArticle.js b/src/webhook/handlers/choosingArticle.ts similarity index 65% rename from src/webhook/handlers/choosingArticle.js rename to src/webhook/handlers/choosingArticle.ts index 9d3754f4..ef909246 100644 --- a/src/webhook/handlers/choosingArticle.js +++ b/src/webhook/handlers/choosingArticle.ts @@ -17,21 +17,36 @@ import { } from './utils'; import ga from 'src/lib/ga'; import UserSettings from 'src/database/models/userSettings'; +import { + GetArticleInChoosingArticleQuery, + GetArticleInChoosingArticleQueryVariables, + ReplyTypeEnum, + SubmitReplyRequestWithoutReasonMutation, + SubmitReplyRequestWithoutReasonMutationVariables, +} from 'typegen/graphql'; import UserArticleLink from '../../database/models/userArticleLink'; -import choosingReply from '../handlers/choosingReply'; +import choosingReply from './choosingReply'; +import { ChatbotStateHandler } from 'src/types/chatbotState'; +import { FlexBubble, Message } from '@line/bot-sdk'; /** * 第2句 (template message):按照時間排序「不在查證範圍」之外的回應,每則回應第一行是 * 「⭕ 含有真實訊息」或「❌ 含有不實訊息」之類的 (含 emoticon),然後是回應文字。如果 * 還有空間,才放「不在查證範圍」的回應。最後一句的最後一格顯示「看其他回應」,連到網站。 */ -function reorderArticleReplies(articleReplies) { +function reorderArticleReplies( + articleReplies: NonNullable< + GetArticleInChoosingArticleQuery['GetArticle'] + >['articleReplies'] +) { const replies = []; const notArticleReplies = []; - for (let articleReply of articleReplies) { - if (articleReply.reply.type !== 'NOT_ARTICLE') { + for (const articleReply of articleReplies ?? []) { + const reply = articleReply?.reply; + if (!reply) continue; // Make Typescript happy + if (reply.type !== 'NOT_ARTICLE') { replies.push(articleReply); } else { notArticleReplies.push(articleReply); @@ -42,8 +57,9 @@ function reorderArticleReplies(articleReplies) { // https://developers.line.biz/en/reference/messaging-api/#template-messages -export default async function choosingArticle(params) { - let { data, state, event, userId, replies } = params; +const choosingArticle: ChatbotStateHandler = async (params) => { + const { data, state, event, userId } = params; + let { replies } = params; if (event.type !== 'postback' && event.type !== 'server_choose') { throw new ManipulationError(t`Please choose from provided options.`); @@ -113,7 +129,7 @@ export default async function choosingArticle(params) { const { data: { GetArticle }, } = await gql` - query ($id: String!) { + query GetArticleInChoosingArticle($id: String!) { GetArticle(id: $id) { text replyCount @@ -129,7 +145,10 @@ export default async function choosingArticle(params) { } } } - `({ + `< + GetArticleInChoosingArticleQuery, + GetArticleInChoosingArticleQueryVariables + >({ id: selectedArticleId, }); @@ -138,7 +157,7 @@ export default async function choosingArticle(params) { } // Store it so that other handlers can use - data.selectedArticleText = GetArticle.text; + data.selectedArticleText = GetArticle.text ?? ''; const visitor = ga(userId, state, data.selectedArticleText); @@ -154,28 +173,34 @@ export default async function choosingArticle(params) { visitor.send(); // choose reply for user - event = { - type: 'server_choose', - input: articleReplies[0].reply.id, - }; - return await choosingReply({ data, state: 'CHOOSING_REPLY', - event, + event: { + type: 'server_choose', + input: articleReplies[0].reply?.id ?? '', + }, userId, replies: [], }); } if (articleReplies.length !== 0) { - const countOfType = {}; + const countOfType: Record = { + RUMOR: 0, + NOT_RUMOR: 0, + NOT_ARTICLE: 0, + OPINIONATED: 0, + }; articleReplies.forEach((ar) => { + /* istanbul ignore if */ + if (!ar.reply?.type) return; + // Track which Reply is searched. And set tracking event as non-interactionHit. visitor.event({ ec: 'Reply', ea: 'Search', el: ar.reply.id, ni: true }); const type = ar.reply.type; - countOfType[type] = (countOfType[type] || 0) + 1; + countOfType[type] += 1; }); const summary = @@ -201,93 +226,103 @@ export default async function choosingArticle(params) { const replyOptions = articleReplies .slice(0, 10) - .map(({ reply, positiveFeedbackCount, negativeFeedbackCount }) => { - const typeWords = createTypeWords(reply.type).toLowerCase(); - const displayTextWhenChosen = ellipsis(reply.text, 25); - - return { - type: 'bubble', - direction: 'ltr', - header: { - type: 'box', - layout: 'horizontal', - spacing: 'md', - paddingBottom: 'none', - contents: [ - { - type: 'text', - text: '💬', - flex: 0, - }, - { - type: 'text', - text: t`Someone thinks it ${typeWords}`, - gravity: 'center', - size: 'sm', - weight: 'bold', - wrap: true, - color: '#AAAAAA', - }, - ], - }, - body: { - type: 'box', - layout: 'vertical', - contents: [ - { - type: 'text', - text: ellipsis(reply.text, 300, '...'), // 50KB for entire Flex carousel - align: 'start', - wrap: true, - margin: 'md', - maxLines: 10, - }, - { - type: 'filler', - }, - { - type: 'separator', - margin: 'md', - }, - { - type: 'box', - layout: 'horizontal', - contents: [ - { - type: 'text', - text: createFeedbackWords( - positiveFeedbackCount, - negativeFeedbackCount - ), - size: 'xs', - wrap: true, - }, - ], - margin: 'md', - spacing: 'none', - }, - ], - }, - footer: { - type: 'box', - layout: 'vertical', - contents: [ - { - type: 'button', - action: createPostbackAction( - `👀 ${t`Take a look`}`, - reply.id, - t`I choose “${displayTextWhenChosen}”`, - data.sessionId, - 'CHOOSING_REPLY' - ), - style: 'primary', - color: '#ffb600', - }, - ], - }, - }; - }); + .map( + ({ + reply, + positiveFeedbackCount, + negativeFeedbackCount, + }): FlexBubble | undefined => { + /* istanbul ignore if */ + if (!reply) return; + + const typeWords = createTypeWords(reply.type).toLowerCase(); + const displayTextWhenChosen = ellipsis(reply.text ?? '', 25); + + return { + type: 'bubble', + direction: 'ltr', + header: { + type: 'box', + layout: 'horizontal', + spacing: 'md', + paddingBottom: 'none', + contents: [ + { + type: 'text', + text: '💬', + flex: 0, + }, + { + type: 'text', + text: t`Someone thinks it ${typeWords}`, + gravity: 'center', + size: 'sm', + weight: 'bold', + wrap: true, + color: '#AAAAAA', + }, + ], + }, + body: { + type: 'box', + layout: 'vertical', + contents: [ + { + type: 'text', + text: ellipsis(reply.text ?? '', 300, '...'), // 50KB for entire Flex carousel + align: 'start', + wrap: true, + margin: 'md', + maxLines: 10, + }, + { + type: 'filler', + }, + { + type: 'separator', + margin: 'md', + }, + { + type: 'box', + layout: 'horizontal', + contents: [ + { + type: 'text', + text: createFeedbackWords( + positiveFeedbackCount, + negativeFeedbackCount + ), + size: 'xs', + wrap: true, + }, + ], + margin: 'md', + spacing: 'none', + }, + ], + }, + footer: { + type: 'box', + layout: 'vertical', + contents: [ + { + type: 'button', + action: createPostbackAction( + `👀 ${t`Take a look`}`, + reply.id, + t`I choose “${displayTextWhenChosen}”`, + data.sessionId, + 'CHOOSING_REPLY' + ), + style: 'primary', + color: '#ffb600', + }, + ], + }, + }; + } + ) + .filter(Boolean); replies = [ { @@ -330,7 +365,7 @@ export default async function choosingArticle(params) { ); const isTextArticle = GetArticle.articleType === 'TEXT'; - let maybeAIReplies = [ + let maybeAIReplies: Message[] = [ createTextMessage({ text: t`In the meantime, you can:`, }), @@ -400,7 +435,7 @@ Don’t trust the message just yet!`, createNotificationSettingsBubble(), createArticleShareBubble(articleUrl), - ].filter((m) => m), + ].filter(Boolean), }, }, ]; @@ -412,10 +447,15 @@ Don’t trust the message just yet!`, replyRequestCount } } - `({ id: selectedArticleId }, { userId }); + `< + SubmitReplyRequestWithoutReasonMutation, + SubmitReplyRequestWithoutReasonMutationVariables + >({ id: selectedArticleId }, { userId }); } visitor.send(); return { data, event, userId, replies }; -} +}; + +export default choosingArticle; diff --git a/src/webhook/handlers/choosingReply.js b/src/webhook/handlers/choosingReply.ts similarity index 77% rename from src/webhook/handlers/choosingReply.js rename to src/webhook/handlers/choosingReply.ts index 4f395336..6b40330f 100644 --- a/src/webhook/handlers/choosingReply.js +++ b/src/webhook/handlers/choosingReply.ts @@ -9,13 +9,23 @@ import { import { getArticleURL, createTypeWords } from 'src/lib/sharedUtils'; import ga from 'src/lib/ga'; import UserSettings from 'src/database/models/userSettings'; +import { FlexBubble } from '@line/bot-sdk'; +import { ChatbotStateHandler } from 'src/types/chatbotState'; +import { + GetReplyRelatedDataQuery, + GetReplyRelatedDataQueryVariables, + ReplyTypeEnum, +} from 'typegen/graphql'; /** * @param {string} articleId - Article ID of the article-reply to feedback * @param {string} replyId - Reply ID of the article-reply to feedback * @returns {object} Flex message bubble object that asks the user if reply is helpful */ -function createAskReplyFeedbackBubble(articleId, replyId) { +function createAskReplyFeedbackBubble( + articleId: string, + replyId: string +): FlexBubble { return { type: 'bubble', body: { @@ -61,12 +71,16 @@ function createAskReplyFeedbackBubble(articleId, replyId) { } /** - * @param {string} articleId - article ID to share - * @param {string} fullArticleText - article text - * @param {ReplyTypeEnum} replyTypeEnumValue - reply's type enum + * @param articleId - article ID to share + * @param fullArticleText - article text + * @param replyTypeEnumValue - reply's type enum * @returns Flex message bubble object that asks user to share */ -function createShareBubble(articleId, fullArticleText, replyTypeEnumValue) { +function createShareBubble( + articleId: string, + fullArticleText: string, + replyTypeEnumValue: ReplyTypeEnum +): FlexBubble { const articleUrl = getArticleURL(articleId); const articleText = ellipsis(fullArticleText, 15); const replyType = createTypeWords(replyTypeEnumValue).toLowerCase(); @@ -116,8 +130,9 @@ function createShareBubble(articleId, fullArticleText, replyTypeEnumValue) { }; } -export default async function choosingReply(params) { - let { data, state, event, userId, replies } = params; +const choosingReply: ChatbotStateHandler = async (params) => { + const { data, state, event, userId } = params; + let { replies } = params; if (event.type !== 'postback' && event.type !== 'server_choose') { throw new ManipulationError(t`Please choose from provided options.`); @@ -137,9 +152,18 @@ export default async function choosingReply(params) { replyCount } } - `({ id: selectedReplyId, articleId: data.selectedArticleId }); + `({ + id: selectedReplyId, + articleId: data.selectedArticleId ?? '', + }); - if (errors) { + /* istanbul ignore if */ + if ( + errors || + getReplyData.GetReply === null || + getReplyData.GetArticle === null + ) { + console.error('[GetReplyRelatedData]', errors); throw new ManipulationError( t`We have problem retrieving message and reply data, please forward the message again` ); @@ -151,14 +175,17 @@ export default async function choosingReply(params) { ); replies = [ - ...createReplyMessages(GetReply, GetArticle, data.selectedArticleId), + ...createReplyMessages(GetReply, GetArticle, data.selectedArticleId ?? ''), { type: 'flex', altText: t`Is the reply helpful?`, contents: { type: 'carousel', contents: [ - createAskReplyFeedbackBubble(data.selectedArticleId, selectedReplyId), + createAskReplyFeedbackBubble( + data.selectedArticleId ?? '', + selectedReplyId + ), // Ask user to turn on notification if the user did not turn it on process.env.NOTIFY_METHOD && @@ -166,11 +193,11 @@ export default async function choosingReply(params) { createNotificationSettingsBubble(), createShareBubble( - data.selectedArticleId, - data.selectedArticleText, + data.selectedArticleId ?? '', + data.selectedArticleText ?? '', GetReply.type ), - ].filter((m) => m), + ].filter(Boolean), }, }, ]; @@ -183,4 +210,6 @@ export default async function choosingReply(params) { visitor.send(); return { data, event, userId, replies }; -} +}; + +export default choosingReply; diff --git a/src/webhook/handlers/utils.ts b/src/webhook/handlers/utils.ts index fdbfd456..ea13c8da 100644 --- a/src/webhook/handlers/utils.ts +++ b/src/webhook/handlers/utils.ts @@ -6,6 +6,8 @@ import type { EventBase, TextMessage, FlexMessage, + FlexText, + FlexComponent, } from '@line/bot-sdk'; import { t, msgid, ngettext } from 'ttag'; import GraphemeSplitter from 'grapheme-splitter'; @@ -542,7 +544,10 @@ const AI_REPLY_IMAGE_VERSION = '20230405'; * @param userId * @returns AI reply object, or null of AI cannot return reply. */ -export async function createAIReply(articleId: string, userId: string) { +export async function createAIReply( + articleId: string, + userId: string +): Promise { const text = ( await gql` mutation CreateAIReply($articleId: String!) { @@ -686,34 +691,59 @@ export function createCommentBubble(articleId: string): FlexBubble { }; } +/** + * Omit<> breaks FlexText's discriminated union, thus we Omit<> separately and then union back + */ +type FlexTextWithoutType = + | Omit< + FlexText & { + /* Discriminator */ text?: never; + contents: FlexSpan[]; + /* Must be supplied in this case */ altText: string; + }, + 'type' + > + | Omit; + /** * Creates a single flex bubble message that acts identical to text message, but cannot be copied * nor forwarded by the user. * * This prevents user to "share" Cofacts chatbot's text to Cofacts chatbot itself. * - * @param {Object} textProps - https://developers.line.biz/en/reference/messaging-api/#f-text. + * @param textProps - https://developers.line.biz/en/reference/messaging-api/#f-text. * type & wrap is specified by default. - * @returns {Object} A single flex bubble message + * @returns A single flex bubble message */ -export function createTextMessage( - textProps: Omit -): FlexMessage { +export function createTextMessage(textProps: FlexTextWithoutType): FlexMessage { + const altText = 'altText' in textProps ? textProps.altText : textProps.text; + + const content: FlexComponent = { + type: 'text', + wrap: true, + // Exclude altText from FlexComponent content + ...(() => { + if (!('altText' in textProps)) { + return textProps; + } + const { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + altText, + ...other + } = textProps; + return other; + })(), + }; + return { type: 'flex', - altText: textProps.text, + altText, contents: { type: 'bubble', body: { type: 'box', layout: 'vertical', - contents: [ - { - type: 'text', - wrap: true, - ...textProps, - }, - ], + contents: [content], }, }, }; diff --git a/typegen/gql.ts b/typegen/gql.ts index 50b7fe6e..6461e361 100644 --- a/typegen/gql.ts +++ b/typegen/gql.ts @@ -13,6 +13,11 @@ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/ * Therefore it is highly recommended to use the babel or swc plugin for production. */ const documents = { + "\n mutation SubmitTextArticleUnderConsent($text: String!) {\n CreateArticle(text: $text, reference: { type: LINE }) {\n id\n }\n }\n ": types.SubmitTextArticleUnderConsentDocument, + "\n mutation SubmitMediaArticleUnderConsent(\n $mediaUrl: String!\n $articleType: ArticleTypeEnum!\n ) {\n CreateMediaArticle(\n mediaUrl: $mediaUrl\n articleType: $articleType\n reference: { type: LINE }\n ) {\n id\n }\n }\n ": types.SubmitMediaArticleUnderConsentDocument, + "\n query GetArticleInChoosingArticle($id: String!) {\n GetArticle(id: $id) {\n text\n replyCount\n articleType\n articleReplies(status: NORMAL) {\n reply {\n id\n type\n text\n }\n positiveFeedbackCount\n negativeFeedbackCount\n }\n }\n }\n ": types.GetArticleInChoosingArticleDocument, + "\n mutation SubmitReplyRequestWithoutReason($id: String!) {\n CreateOrUpdateReplyRequest(articleId: $id) {\n replyRequestCount\n }\n }\n ": types.SubmitReplyRequestWithoutReasonDocument, + "\n query GetReplyRelatedData($id: String!, $articleId: String!) {\n GetReply(id: $id) {\n type\n text\n reference\n createdAt\n }\n GetArticle(id: $articleId) {\n replyCount\n }\n }\n ": types.GetReplyRelatedDataDocument, "\n query ListArticlesInInitState($text: String!) {\n ListArticles(\n filter: { moreLikeThis: { like: $text } }\n orderBy: [{ _score: DESC }]\n first: 4\n ) {\n edges {\n node {\n text\n id\n articleType\n }\n highlight {\n text\n hyperlinks {\n title\n summary\n }\n }\n }\n }\n }\n ": types.ListArticlesInInitStateDocument, "\n query ListArticlesInProcessMedia($mediaUrl: String!) {\n ListArticles(\n filter: {\n mediaUrl: $mediaUrl\n articleTypes: [TEXT, IMAGE, AUDIO, VIDEO]\n transcript: { shouldCreate: true }\n }\n orderBy: [{ _score: DESC }]\n first: 9\n ) {\n edges {\n score\n mediaSimilarity\n node {\n id\n articleType\n attachmentUrl(variant: THUMBNAIL)\n }\n highlight {\n text\n hyperlinks {\n title\n summary\n }\n }\n }\n }\n }\n ": types.ListArticlesInProcessMediaDocument, "fragment CreateReferenceWordsReply on Reply {\n reference\n type\n}\n\nfragment CreateReplyMessagesReply on Reply {\n text\n ...CreateReferenceWordsReply\n}\n\nfragment CreateReplyMessagesArticle on Article {\n replyCount\n}\n\nfragment CreateHighlightContentsHighlight on Highlights {\n text\n hyperlinks {\n title\n summary\n }\n}": types.CreateReferenceWordsReplyFragmentDoc, @@ -33,6 +38,26 @@ const documents = { */ export function graphql(source: string): unknown; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "\n mutation SubmitTextArticleUnderConsent($text: String!) {\n CreateArticle(text: $text, reference: { type: LINE }) {\n id\n }\n }\n "): (typeof documents)["\n mutation SubmitTextArticleUnderConsent($text: String!) {\n CreateArticle(text: $text, reference: { type: LINE }) {\n id\n }\n }\n "]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "\n mutation SubmitMediaArticleUnderConsent(\n $mediaUrl: String!\n $articleType: ArticleTypeEnum!\n ) {\n CreateMediaArticle(\n mediaUrl: $mediaUrl\n articleType: $articleType\n reference: { type: LINE }\n ) {\n id\n }\n }\n "): (typeof documents)["\n mutation SubmitMediaArticleUnderConsent(\n $mediaUrl: String!\n $articleType: ArticleTypeEnum!\n ) {\n CreateMediaArticle(\n mediaUrl: $mediaUrl\n articleType: $articleType\n reference: { type: LINE }\n ) {\n id\n }\n }\n "]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "\n query GetArticleInChoosingArticle($id: String!) {\n GetArticle(id: $id) {\n text\n replyCount\n articleType\n articleReplies(status: NORMAL) {\n reply {\n id\n type\n text\n }\n positiveFeedbackCount\n negativeFeedbackCount\n }\n }\n }\n "): (typeof documents)["\n query GetArticleInChoosingArticle($id: String!) {\n GetArticle(id: $id) {\n text\n replyCount\n articleType\n articleReplies(status: NORMAL) {\n reply {\n id\n type\n text\n }\n positiveFeedbackCount\n negativeFeedbackCount\n }\n }\n }\n "]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "\n mutation SubmitReplyRequestWithoutReason($id: String!) {\n CreateOrUpdateReplyRequest(articleId: $id) {\n replyRequestCount\n }\n }\n "): (typeof documents)["\n mutation SubmitReplyRequestWithoutReason($id: String!) {\n CreateOrUpdateReplyRequest(articleId: $id) {\n replyRequestCount\n }\n }\n "]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "\n query GetReplyRelatedData($id: String!, $articleId: String!) {\n GetReply(id: $id) {\n type\n text\n reference\n createdAt\n }\n GetArticle(id: $articleId) {\n replyCount\n }\n }\n "): (typeof documents)["\n query GetReplyRelatedData($id: String!, $articleId: String!) {\n GetReply(id: $id) {\n type\n text\n reference\n createdAt\n }\n GetArticle(id: $articleId) {\n replyCount\n }\n }\n "]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ diff --git a/typegen/graphql.ts b/typegen/graphql.ts index cbdb253c..15f67948 100644 --- a/typegen/graphql.ts +++ b/typegen/graphql.ts @@ -155,9 +155,9 @@ export type Article = Node & { aiReplies: Array; /** Automated transcript */ aiTranscripts: Array; - articleCategories: Maybe>>; + articleCategories: Array; /** Connections between this article and replies. Sorted by the logic described in https://github.com/cofacts/rumors-line-bot/issues/78. */ - articleReplies: Maybe>>; + articleReplies: Array; /** Message event type */ articleType: ArticleTypeEnum; /** Attachment hash to search or identify files */ @@ -165,17 +165,17 @@ export type Article = Node & { /** Attachment URL for this article. */ attachmentUrl: Maybe; /** Number of normal article categories */ - categoryCount: Maybe; + categoryCount: Scalars['Int']; cooccurrences: Maybe>; - createdAt: Maybe; + createdAt: Scalars['String']; /** Hyperlinks in article text */ hyperlinks: Maybe>>; id: Scalars['ID']; lastRequestedAt: Maybe; references: Maybe>>; - relatedArticles: Maybe; + relatedArticles: ArticleConnection; /** Number of normal article replies */ - replyCount: Maybe; + replyCount: Scalars['Int']; replyRequestCount: Maybe; replyRequests: Maybe>>; /** If the current user has requested for reply for this article. Null if not logged in. */ @@ -315,9 +315,9 @@ export type ArticleConnectionPageInfo = PageInfo & { }; export type ArticleReference = { - createdAt: Maybe; + createdAt: Scalars['String']; permalink: Maybe; - type: Maybe; + type: ArticleReferenceTypeEnum; }; export type ArticleReferenceInput = { @@ -336,20 +336,20 @@ export type ArticleReferenceTypeEnum = export type ArticleReply = { appId: Scalars['String']; article: Maybe
; - articleId: Maybe; - canUpdateStatus: Maybe; - createdAt: Maybe; - feedbackCount: Maybe; - feedbacks: Maybe>>; - negativeFeedbackCount: Maybe; + articleId: Scalars['String']; + canUpdateStatus: Scalars['Boolean']; + createdAt: Scalars['String']; + feedbackCount: Scalars['Int']; + feedbacks: Array; + negativeFeedbackCount: Scalars['Int']; /** The feedback of current user. null when not logged in or not voted yet. */ ownVote: Maybe; - positiveFeedbackCount: Maybe; + positiveFeedbackCount: Scalars['Int']; reply: Maybe; - replyId: Maybe; + replyId: Scalars['String']; /** Cached reply type value stored in ArticleReply */ replyType: Maybe; - status: Maybe; + status: ArticleReplyStatusEnum; updatedAt: Maybe; /** The user who conencted this reply and this article. */ user: Maybe; @@ -1024,6 +1024,7 @@ export type Query = { * Note that some fields like email is not visible to other users. */ GetUser: Maybe; + GetYdoc: Maybe; ListAIResponses: AiResponseConnection; ListAnalytics: AnalyticsConnection; ListArticleReplyFeedbacks: Maybe; @@ -1058,6 +1059,11 @@ export type QueryGetUserArgs = { }; +export type QueryGetYdocArgs = { + id: Scalars['String']; +}; + + export type QueryListAiResponsesArgs = { after: InputMaybe; before: InputMaybe; @@ -1162,16 +1168,16 @@ export type RelatedArticleOrderBy = { }; export type Reply = Node & { - articleReplies: Maybe>>; - createdAt: Maybe; + articleReplies: Array; + createdAt: Scalars['String']; /** Hyperlinks in reply text or reference. May be empty array if no URLs are included. `null` when hyperlinks are still fetching. */ hyperlinks: Maybe>>; id: Scalars['ID']; reference: Maybe; /** Replies that has similar text or references of this current reply */ - similarReplies: Maybe; + similarReplies: ReplyConnection; text: Maybe; - type: Maybe; + type: ReplyTypeEnum; /** The user submitted this reply version */ user: Maybe; }; @@ -1360,6 +1366,56 @@ export type ValidationResult = { success: Scalars['Boolean']; }; +export type Ydoc = { + /** Binary that stores as base64 encoded string */ + data: Maybe; + /** Ydoc snapshots which are used to restore to specific version */ + versions: Maybe>>; +}; + +export type YdocVersion = { + createdAt: Maybe; + /** Binary that stores as base64 encoded string */ + snapshot: Maybe; +}; + +export type SubmitTextArticleUnderConsentMutationVariables = Exact<{ + text: Scalars['String']; +}>; + + +export type SubmitTextArticleUnderConsentMutation = { CreateArticle: { id: string | null } | null }; + +export type SubmitMediaArticleUnderConsentMutationVariables = Exact<{ + mediaUrl: Scalars['String']; + articleType: ArticleTypeEnum; +}>; + + +export type SubmitMediaArticleUnderConsentMutation = { CreateMediaArticle: { id: string | null } | null }; + +export type GetArticleInChoosingArticleQueryVariables = Exact<{ + id: Scalars['String']; +}>; + + +export type GetArticleInChoosingArticleQuery = { GetArticle: { text: string | null, replyCount: number, articleType: ArticleTypeEnum, articleReplies: Array<{ positiveFeedbackCount: number, negativeFeedbackCount: number, reply: { id: string, type: ReplyTypeEnum, text: string | null } | null }> } | null }; + +export type SubmitReplyRequestWithoutReasonMutationVariables = Exact<{ + id: Scalars['String']; +}>; + + +export type SubmitReplyRequestWithoutReasonMutation = { CreateOrUpdateReplyRequest: { replyRequestCount: number | null } | null }; + +export type GetReplyRelatedDataQueryVariables = Exact<{ + id: Scalars['String']; + articleId: Scalars['String']; +}>; + + +export type GetReplyRelatedDataQuery = { GetReply: { type: ReplyTypeEnum, text: string | null, reference: string | null, createdAt: string } | null, GetArticle: { replyCount: number } | null }; + export type ListArticlesInInitStateQueryVariables = Exact<{ text: Scalars['String']; }>; @@ -1374,11 +1430,11 @@ export type ListArticlesInProcessMediaQueryVariables = Exact<{ export type ListArticlesInProcessMediaQuery = { ListArticles: { edges: Array<{ score: number | null, mediaSimilarity: number, node: { id: string, articleType: ArticleTypeEnum, attachmentUrl: string | null }, highlight: { text: string | null, hyperlinks: Array<{ title: string | null, summary: string | null } | null> | null } | null }> } | null }; -export type CreateReferenceWordsReplyFragment = { reference: string | null, type: ReplyTypeEnum | null }; +export type CreateReferenceWordsReplyFragment = { reference: string | null, type: ReplyTypeEnum }; -export type CreateReplyMessagesReplyFragment = { text: string | null, reference: string | null, type: ReplyTypeEnum | null }; +export type CreateReplyMessagesReplyFragment = { text: string | null, reference: string | null, type: ReplyTypeEnum }; -export type CreateReplyMessagesArticleFragment = { replyCount: number | null }; +export type CreateReplyMessagesArticleFragment = { replyCount: number }; export type CreateHighlightContentsHighlightFragment = { text: string | null, hyperlinks: Array<{ title: string | null, summary: string | null } | null> | null }; @@ -1393,6 +1449,11 @@ export const CreateReferenceWordsReplyFragmentDoc = {"kind":"Document","definiti export const CreateReplyMessagesReplyFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CreateReplyMessagesReply"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Reply"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"text"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"CreateReferenceWordsReply"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CreateReferenceWordsReply"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Reply"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reference"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}}]} as unknown as DocumentNode; export const CreateReplyMessagesArticleFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CreateReplyMessagesArticle"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Article"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"replyCount"}}]}}]} as unknown as DocumentNode; export const CreateHighlightContentsHighlightFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CreateHighlightContentsHighlight"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Highlights"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"text"}},{"kind":"Field","name":{"kind":"Name","value":"hyperlinks"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"summary"}}]}}]}}]} as unknown as DocumentNode; +export const SubmitTextArticleUnderConsentDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SubmitTextArticleUnderConsent"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"text"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"CreateArticle"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"text"},"value":{"kind":"Variable","name":{"kind":"Name","value":"text"}}},{"kind":"Argument","name":{"kind":"Name","value":"reference"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"type"},"value":{"kind":"EnumValue","value":"LINE"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode; +export const SubmitMediaArticleUnderConsentDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SubmitMediaArticleUnderConsent"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"mediaUrl"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"articleType"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ArticleTypeEnum"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"CreateMediaArticle"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"mediaUrl"},"value":{"kind":"Variable","name":{"kind":"Name","value":"mediaUrl"}}},{"kind":"Argument","name":{"kind":"Name","value":"articleType"},"value":{"kind":"Variable","name":{"kind":"Name","value":"articleType"}}},{"kind":"Argument","name":{"kind":"Name","value":"reference"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"type"},"value":{"kind":"EnumValue","value":"LINE"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode; +export const GetArticleInChoosingArticleDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetArticleInChoosingArticle"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"GetArticle"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"text"}},{"kind":"Field","name":{"kind":"Name","value":"replyCount"}},{"kind":"Field","name":{"kind":"Name","value":"articleType"}},{"kind":"Field","name":{"kind":"Name","value":"articleReplies"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"status"},"value":{"kind":"EnumValue","value":"NORMAL"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reply"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"text"}}]}},{"kind":"Field","name":{"kind":"Name","value":"positiveFeedbackCount"}},{"kind":"Field","name":{"kind":"Name","value":"negativeFeedbackCount"}}]}}]}}]}}]} as unknown as DocumentNode; +export const SubmitReplyRequestWithoutReasonDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SubmitReplyRequestWithoutReason"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"CreateOrUpdateReplyRequest"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"articleId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"replyRequestCount"}}]}}]}}]} as unknown as DocumentNode; +export const GetReplyRelatedDataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetReplyRelatedData"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"articleId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"GetReply"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"text"}},{"kind":"Field","name":{"kind":"Name","value":"reference"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}},{"kind":"Field","name":{"kind":"Name","value":"GetArticle"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"articleId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"replyCount"}}]}}]}}]} as unknown as DocumentNode; export const ListArticlesInInitStateDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ListArticlesInInitState"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"text"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ListArticles"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"moreLikeThis"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"like"},"value":{"kind":"Variable","name":{"kind":"Name","value":"text"}}}]}}]}},{"kind":"Argument","name":{"kind":"Name","value":"orderBy"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"_score"},"value":{"kind":"EnumValue","value":"DESC"}}]}]}},{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"4"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"text"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"articleType"}}]}},{"kind":"Field","name":{"kind":"Name","value":"highlight"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"text"}},{"kind":"Field","name":{"kind":"Name","value":"hyperlinks"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"summary"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const ListArticlesInProcessMediaDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ListArticlesInProcessMedia"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"mediaUrl"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ListArticles"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"mediaUrl"},"value":{"kind":"Variable","name":{"kind":"Name","value":"mediaUrl"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"articleTypes"},"value":{"kind":"ListValue","values":[{"kind":"EnumValue","value":"TEXT"},{"kind":"EnumValue","value":"IMAGE"},{"kind":"EnumValue","value":"AUDIO"},{"kind":"EnumValue","value":"VIDEO"}]}},{"kind":"ObjectField","name":{"kind":"Name","value":"transcript"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"shouldCreate"},"value":{"kind":"BooleanValue","value":true}}]}}]}},{"kind":"Argument","name":{"kind":"Name","value":"orderBy"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"_score"},"value":{"kind":"EnumValue","value":"DESC"}}]}]}},{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"9"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"score"}},{"kind":"Field","name":{"kind":"Name","value":"mediaSimilarity"}},{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"articleType"}},{"kind":"Field","name":{"kind":"Name","value":"attachmentUrl"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"variant"},"value":{"kind":"EnumValue","value":"THUMBNAIL"}}]}]}},{"kind":"Field","name":{"kind":"Name","value":"highlight"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"text"}},{"kind":"Field","name":{"kind":"Name","value":"hyperlinks"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"summary"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const CreateAiReplyDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateAIReply"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"articleId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"CreateAIReply"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"articleId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"articleId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"text"}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file