From f37a49e398711386fe9e120454aedc2bdd2122c3 Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Wed, 11 May 2022 01:45:02 +0400 Subject: [PATCH] refactor: strict typescript also: - save history simultaneously with question data - update squiggle - minor refactorings --- package-lock.json | 734 ++++-------------- package.json | 4 +- src/backend/flow/doEverything.ts | 9 +- src/backend/flow/history/updateHistory.ts | 11 - src/backend/flow/jobs.ts | 17 +- .../flow/rebuildNetliftySiteWithNewData.ts | 15 - src/backend/index.ts | 15 +- src/backend/platforms/_example.ts | 2 +- src/backend/platforms/betfair.ts | 27 +- src/backend/platforms/fantasyscotus.ts | 6 +- src/backend/platforms/foretold.ts | 20 +- src/backend/platforms/goodjudgment.ts | 48 +- src/backend/platforms/goodjudgmentopen.ts | 21 +- src/backend/platforms/guesstimate.ts | 7 +- src/backend/platforms/index.ts | 33 +- src/backend/platforms/infer.ts | 49 +- src/backend/platforms/kalshi.ts | 7 +- src/backend/platforms/manifold.ts | 8 +- src/backend/platforms/metaculus.ts | 33 +- src/backend/platforms/polymarket.ts | 14 +- src/backend/platforms/predictit.ts | 33 +- src/backend/platforms/rootclaim.ts | 4 +- src/backend/platforms/smarkets.ts | 161 ++-- src/backend/platforms/wildeford.ts | 26 +- src/backend/platforms/xrisk.ts | 4 +- src/backend/utils/algolia.ts | 2 +- .../pullForecastsToCSVForRating.ts | 51 -- .../pullMetaculusForecastsToCSVForRating.ts | 48 -- .../misc/process-forecasts-into-elicit.ts | 2 +- .../utils/misc/process-forecasts-template.ts | 2 +- src/backend/utils/sleep.ts | 3 + src/common/types.ts | 22 + src/pages/api/squiggle.ts | 5 +- .../components/HistoryChart/utils.ts | 2 +- .../questions/components/QuestionOptions.tsx | 3 +- src/web/questions/utils.ts | 17 - tsconfig.json | 2 +- 37 files changed, 453 insertions(+), 1014 deletions(-) delete mode 100644 src/backend/flow/history/updateHistory.ts delete mode 100644 src/backend/flow/rebuildNetliftySiteWithNewData.ts delete mode 100644 src/backend/utils/evaluations/pullForecastsToCSVForRating.ts delete mode 100644 src/backend/utils/evaluations/pullMetaculusForecastsToCSVForRating.ts create mode 100644 src/backend/utils/sleep.ts create mode 100644 src/common/types.ts diff --git a/package-lock.json b/package-lock.json index 4560da0..b0f9dcf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,15 +14,18 @@ "@pothos/plugin-prisma": "^3.4.0", "@pothos/plugin-relay": "^3.10.0", "@prisma/client": "^3.11.1", + "@quri/squiggle-lang": "^0.2.8", "@tailwindcss/forms": "^0.4.0", "@tailwindcss/typography": "^0.5.1", "@types/chroma-js": "^2.1.3", "@types/dom-to-image": "^2.6.4", + "@types/google-spreadsheet": "^3.2.1", "@types/jsdom": "^16.2.14", "@types/nprogress": "^0.2.0", "@types/react": "^17.0.39", "@types/react-copy-to-clipboard": "^5.0.2", "@types/textversionjs": "^1.1.1", + "@types/tunnel": "^0.0.3", "airtable": "^0.11.1", "algoliasearch": "^4.10.3", "autoprefixer": "^10.1.0", @@ -69,7 +72,6 @@ "react-safe": "^1.3.0", "react-select": "^5.2.2", "remark-gfm": "^3.0.1", - "squiggle-experimental": "^0.1.9", "tabletojson": "^2.0.4", "tailwindcss": "^3.0.22", "textversionjs": "^1.1.3", @@ -1044,10 +1046,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel%2fruntime/-/runtime-7.17.7.tgz", - "integrity": "sha512-L6rvG9GDxaLgFjg41K+5Yv9OMrU98sWe+Ykmc6FDJW/+vYZMhdOMKkISgzptMaERHvS2Y2lw9MDRm2gHhlQQoA==", - "license": "MIT", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.9.tgz", + "integrity": "sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==", "dependencies": { "regenerator-runtime": "^0.13.4" }, @@ -1496,12 +1497,6 @@ "react-dom": ">=16.8.0" } }, - "node_modules/@glennsl/bs-json": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@glennsl%2fbs-json/-/bs-json-5.0.4.tgz", - "integrity": "sha512-Th9DetZjRlMZrb74kgGJ44oWcoFyOTE884WlSuXft0Cd+J09vHRxiB7eVyK7Gthb4cSevsBBJDHYAbGGL25wPw==", - "license": "(LGPL-3.0 OR MPL-2.0)" - }, "node_modules/@graphql-codegen/add": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@graphql-codegen/add/-/add-3.1.1.tgz", @@ -2706,36 +2701,22 @@ "cross-spawn": "7.0.3" } }, + "node_modules/@quri/squiggle-lang": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@quri/squiggle-lang/-/squiggle-lang-0.2.8.tgz", + "integrity": "sha512-uK+oXE0chYtEW3la0qIxg6ueXRm34jvkL930RWhazVWC0S4aU2lzPJQ9Oz1h+8qIZuQRs89ZqfeO5r5IcjUTNQ==", + "dependencies": { + "jstat": "^1.9.5", + "mathjs": "^10.5.0", + "pdfast": "^0.2.0", + "rescript": "^9.1.4" + } + }, "node_modules/@repeaterjs/repeater": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@repeaterjs/repeater/-/repeater-3.0.4.tgz", "integrity": "sha512-AW8PKd6iX3vAZ0vA43nOUOnbq/X5ihgU+mSXXqunMkeQADGiqw/PY0JNeYtD5sr0PAy51YPgAPbDoeapv9r8WA==" }, - "node_modules/@rescript/react": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/@rescript%2freact/-/react-0.10.3.tgz", - "integrity": "sha512-Lf9rzrR3bQPKJjOK3PBRa/B3xrJ7CqQ1HYr9VHPVxJidarIJJFZBhj0Dg1uZURX+Wg/xiP0PHFxXmdj2bK8Vxw==", - "license": "MIT", - "peer": true, - "peerDependencies": { - "react": ">=16.8.1", - "react-dom": ">=16.8.1" - } - }, - "node_modules/@rescriptbr/reform": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/@rescriptbr%2freform/-/reform-11.0.2.tgz", - "integrity": "sha512-4C4iZQ5oQKhG1hLc0fJM2Dn47zQYegDGV7Qgp18nQX/3nVNg4JrlP3Fx9eD8NvqUmd3V5ew710Y+TU5FNS8n6A==", - "license": "MIT", - "dependencies": { - "reschema": "^2.0.3", - "rescript-react-update": "^3.0.1" - }, - "peerDependencies": { - "@rescript/react": "^0.10.1", - "rescript": "^9.1.1" - } - }, "node_modules/@samverschueren/stream-to-observable": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.1.tgz", @@ -3198,6 +3179,11 @@ "resolved": "https://registry.npmjs.org/@types/dom-to-image/-/dom-to-image-2.6.4.tgz", "integrity": "sha512-UddUdGF1qulrSDulkz3K2Ypq527MR6ixlgAzqLbxSiQ0icx0XDlIV+h4+edmjq/1dqn0KgN0xGSe1kI9t+vGuw==" }, + "node_modules/@types/google-spreadsheet": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/google-spreadsheet/-/google-spreadsheet-3.2.1.tgz", + "integrity": "sha512-k9fMPyRJuWFoRiD4ZU2xTJTBykeXIh+ittw59UHGvNVkyu0b82DorkEJe36LBlaLkqD2qPBAunVz6JZRMFvWIA==" + }, "node_modules/@types/hast": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/@types%2fhast/-/hast-2.3.4.tgz", @@ -3370,6 +3356,14 @@ "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.1.tgz", "integrity": "sha512-Y0K95ThC3esLEYD6ZuqNek29lNX2EM1qxV8y2FTLUB0ff5wWrk7az+mLrnNFUnaXcgKye22+sFBRXOgpPILZNg==" }, + "node_modules/@types/tunnel": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.3.tgz", + "integrity": "sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/unist": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types%2funist/-/unist-2.0.6.tgz", @@ -3555,6 +3549,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -3744,75 +3739,6 @@ "follow-redirects": "^1.14.7" } }, - "node_modules/babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "license": "MIT", - "dependencies": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" - } - }, - "node_modules/babel-code-frame/node_modules/ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-code-frame/node_modules/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "license": "MIT", - "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-code-frame/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/babel-code-frame/node_modules/js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "license": "MIT" - }, - "node_modules/babel-code-frame/node_modules/supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/babel-messages": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", - "license": "MIT", - "dependencies": { - "babel-runtime": "^6.22.0" - } - }, "node_modules/babel-plugin-dynamic-import-node": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", @@ -3855,28 +3781,6 @@ "integrity": "sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ==", "dev": true }, - "node_modules/babel-plugin-transform-es2015-modules-commonjs": { - "version": "6.26.2", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", - "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", - "license": "MIT", - "dependencies": { - "babel-plugin-transform-strict-mode": "^6.24.1", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-types": "^6.26.0" - } - }, - "node_modules/babel-plugin-transform-strict-mode": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", - "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", - "license": "MIT", - "dependencies": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, "node_modules/babel-preset-fbjs": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/babel-preset-fbjs/-/babel-preset-fbjs-3.4.0.tgz", @@ -3915,114 +3819,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "license": "MIT", - "dependencies": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - } - }, - "node_modules/babel-runtime/node_modules/core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", - "deprecated": "core-js@<3 is no longer maintained and not recommended for usage due to the number of issues. Please, upgrade your dependencies to the actual version of core-js@3.", - "hasInstallScript": true, - "license": "MIT" - }, - "node_modules/babel-runtime/node_modules/regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", - "license": "MIT" - }, - "node_modules/babel-template": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", - "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", - "license": "MIT", - "dependencies": { - "babel-runtime": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "lodash": "^4.17.4" - } - }, - "node_modules/babel-traverse": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", - "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", - "license": "MIT", - "dependencies": { - "babel-code-frame": "^6.26.0", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "debug": "^2.6.8", - "globals": "^9.18.0", - "invariant": "^2.2.2", - "lodash": "^4.17.4" - } - }, - "node_modules/babel-traverse/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/babel-traverse/node_modules/globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-traverse/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "license": "MIT" - }, - "node_modules/babel-types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", - "license": "MIT", - "dependencies": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" - } - }, - "node_modules/babel-types/node_modules/to-fast-properties": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "license": "MIT", - "bin": { - "babylon": "bin/babylon.js" - } - }, "node_modules/backo2": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", @@ -4793,12 +4589,15 @@ } }, "node_modules/complex.js": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.0.11.tgz", - "integrity": "sha512-6IArJLApNtdg1P1dFtn3dnyzoZBEF0MwMnrfF1exSBRpZYoy4yieMkpZhQDC0uwctw48vii0CFVyHfpgZ/DfGw==", - "license": "MIT OR GPL-2.0", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.1.1.tgz", + "integrity": "sha512-8njCHOTtFFLtegk6zQo0kkVX1rngygb/KQI6z1qZxlFI3scluC+LVTCFbrkWjBv4vvLlbQ9t88IPMC6k95VTTg==", "engines": { "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/infusion" } }, "node_modules/concat-map": { @@ -5347,10 +5146,9 @@ } }, "node_modules/decimal.js": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.0.tgz", - "integrity": "sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw==", - "license": "MIT" + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", + "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==" }, "node_modules/decode-named-character-reference": { "version": "1.0.1", @@ -5797,8 +5595,7 @@ "node_modules/escape-latex": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz", - "integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==", - "license": "MIT" + "integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==" }, "node_modules/escape-string-regexp": { "version": "4.0.0", @@ -6718,6 +6515,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^2.0.0" @@ -7109,6 +6907,7 @@ "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, "license": "MIT", "dependencies": { "loose-envify": "^1.0.0" @@ -7503,8 +7302,7 @@ "node_modules/javascript-natural-sort": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", - "integrity": "sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k=", - "license": "MIT" + "integrity": "sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k=" }, "node_modules/js-tokens": { "version": "4.0.0", @@ -7580,11 +7378,6 @@ "node": ">=0.4.0" } }, - "node_modules/jsdom/node_modules/decimal.js": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", - "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==" - }, "node_modules/jsdom/node_modules/form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", @@ -7819,6 +7612,11 @@ "html2canvas": "^1.0.0-rc.5" } }, + "node_modules/jstat": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/jstat/-/jstat-1.9.5.tgz", + "integrity": "sha512-cWnp4vObF5GmB2XsIEzxI/1ZTcYlcfNqxQ/9Fp5KFUa0Jf/4tO0ZkGVnqoEHDisJvYgvn5n3eWZbd2xTVJJPUQ==" + }, "node_modules/jwa": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", @@ -8553,34 +8351,25 @@ } }, "node_modules/mathjs": { - "version": "5.10.3", - "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-5.10.3.tgz", - "integrity": "sha512-ySjg30BC3dYjQm73ILZtwcWzFJde0VU6otkXW/57IjjuYRa3Qaf0Kb8pydEuBZYtqW2OxreAtsricrAmOj3jIw==", - "license": "Apache-2.0", + "version": "10.5.2", + "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-10.5.2.tgz", + "integrity": "sha512-yLl2yA597A+07+xsOgy1+pq/m4ZokQceWQbB9OsIkK2guCpA2Px9k11zul93eJAA0WUFZiMK6VcruY9ZBbSKjA==", "dependencies": { - "complex.js": "2.0.11", - "decimal.js": "10.2.0", - "escape-latex": "1.2.0", - "fraction.js": "4.0.12", - "javascript-natural-sort": "0.7.1", - "seed-random": "2.2.0", - "tiny-emitter": "2.1.0", - "typed-function": "1.1.0" + "@babel/runtime": "^7.17.9", + "complex.js": "^2.1.1", + "decimal.js": "^10.3.1", + "escape-latex": "^1.2.0", + "fraction.js": "^4.2.0", + "javascript-natural-sort": "^0.7.1", + "seedrandom": "^3.0.5", + "tiny-emitter": "^2.1.0", + "typed-function": "^2.1.0" }, "bin": { "mathjs": "bin/cli.js" }, "engines": { - "node": ">= 6" - } - }, - "node_modules/mathjs/node_modules/fraction.js": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.0.12.tgz", - "integrity": "sha512-8Z1K0VTG4hzYY7kA/1sj4/r1/RWLBD3xwReT/RCrUCbzPszjNQCCsy3ktkU/eaEqX3MYa4pY37a52eiBlPMlhA==", - "license": "MIT OR GPL-2.0", - "engines": { - "node": "*" + "node": ">= 12" } }, "node_modules/mdast-util-definitions": { @@ -36501,8 +36290,7 @@ "node_modules/pdfast": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/pdfast/-/pdfast-0.2.0.tgz", - "integrity": "sha1-jLxVbhvyUiF3eHwN4uDUNzuohck=", - "license": "MIT" + "integrity": "sha1-jLxVbhvyUiF3eHwN4uDUNzuohck=" }, "node_modules/performance-now": { "version": "2.1.0", @@ -37388,12 +37176,6 @@ "performance-now": "^2.1.0" } }, - "node_modules/rationale": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/rationale/-/rationale-0.2.0.tgz", - "integrity": "sha512-Pd8w5Inv1JhTfRyx03zs486CEAn6UKXvvOtxVRLsewngsBSffo3MQwUKYS75L/8vPt98wmf7iaZROx362/f7Bw==", - "license": "MIT" - }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -37810,19 +37592,11 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, - "node_modules/reschema": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/reschema/-/reschema-2.2.0.tgz", - "integrity": "sha512-vkFNYm2GEqrmoK+n1xomn/EXgltvCfqmowMxXkr2IBa6Zw0JItunzOap73PaS9DUGak0h6xHoIOciGcq+LC9cg==", - "license": "MIT" - }, "node_modules/rescript": { "version": "9.1.4", "resolved": "https://registry.npmjs.org/rescript/-/rescript-9.1.4.tgz", "integrity": "sha512-aXANK4IqecJzdnDpJUsU6pxMViCR5ogAxzuqS0mOr8TloMnzAjJFu63fjD6LCkWrKAhlMkFFzQvVQYaAaVkFXw==", "hasInstallScript": true, - "license": "SEE LICENSE IN LICENSE", - "peer": true, "bin": { "bsc": "bsc", "bsrefmt": "bsrefmt", @@ -37830,17 +37604,6 @@ "rescript": "rescript" } }, - "node_modules/rescript-react-update": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rescript-react-update/-/rescript-react-update-3.0.2.tgz", - "integrity": "sha512-oOOaeWma6XEhfZwlB/hB6u+UfYtuHjWS4RpoBeE8NbPX60GYLx5BLoVjEqLKoqXFPJv/2ZgghOKBn2LZo0VaJg==", - "license": "MIT", - "peerDependencies": { - "@rescript/react": "^0.10.1", - "react": "^16.8.0 || ^17.0.0", - "react-dom": "^16.8.0 || ^17.0.0" - } - }, "node_modules/resolve": { "version": "1.22.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", @@ -38013,11 +37776,10 @@ "integrity": "sha512-MuCAyrGZcTLfQoH2XoBlQ8C6bzwN88XT/0slOGz0pn8+gIP85BOAfYa44ZXQUTOwRwPU0QvgU+V+OSajl/59Xg==", "dev": true }, - "node_modules/seed-random": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/seed-random/-/seed-random-2.2.0.tgz", - "integrity": "sha1-KpsZ4lCoFwmSMaW5mk2vgLf77VQ=", - "license": "MIT" + "node_modules/seedrandom": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", + "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" }, "node_modules/selfsigned": { "version": "2.0.0", @@ -38335,27 +38097,6 @@ "tslib": "^2.0.3" } }, - "node_modules/squiggle-experimental": { - "version": "0.1.9", - "resolved": "https://registry.npmjs.org/squiggle-experimental/-/squiggle-experimental-0.1.9.tgz", - "integrity": "sha512-ObEKX+2/chpeRY51u27o41oggYRzHmrdslHLOQkqsiM+hslQAgHeulS5xE+md09DMTAgkUOZFtil3Sb3Vxty2Q==", - "license": "MIT", - "dependencies": { - "@glennsl/bs-json": "^5.0.2", - "@rescriptbr/reform": "^11.0.1", - "babel-plugin-transform-es2015-modules-commonjs": "^6.26.2", - "lodash": "4.17.15", - "mathjs": "5.10.3", - "pdfast": "^0.2.0", - "rationale": "0.2.0" - } - }, - "node_modules/squiggle-experimental/node_modules/lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "license": "MIT" - }, "node_modules/stable": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", @@ -38429,6 +38170,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^2.0.0" @@ -38772,8 +38514,7 @@ "node_modules/tiny-emitter": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", - "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==", - "license": "MIT" + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" }, "node_modules/tiny-glob": { "version": "0.2.9", @@ -39018,11 +38759,11 @@ } }, "node_modules/typed-function": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-1.1.0.tgz", - "integrity": "sha512-TuQzwiT4DDg19beHam3E66oRXhyqlyfgjHB/5fcvsRXbfmWPJfto9B4a0TBdTrQAPGlGmXh/k7iUI+WsObgORA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-2.1.0.tgz", + "integrity": "sha512-bctQIOqx2iVbWGDGPWwIm18QScpu2XRmkC19D8rQGFsjKSgteq/o1hTZvIG/wuDq8fanpBDrLkLq+aEN/6y5XQ==", "engines": { - "node": ">= 6" + "node": ">= 10" } }, "node_modules/typescript": { @@ -40947,9 +40688,9 @@ } }, "@babel/runtime": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel%2fruntime/-/runtime-7.17.7.tgz", - "integrity": "sha512-L6rvG9GDxaLgFjg41K+5Yv9OMrU98sWe+Ykmc6FDJW/+vYZMhdOMKkISgzptMaERHvS2Y2lw9MDRm2gHhlQQoA==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.9.tgz", + "integrity": "sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -41255,11 +40996,6 @@ "use-isomorphic-layout-effect": "^1.1.1" } }, - "@glennsl/bs-json": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@glennsl%2fbs-json/-/bs-json-5.0.4.tgz", - "integrity": "sha512-Th9DetZjRlMZrb74kgGJ44oWcoFyOTE884WlSuXft0Cd+J09vHRxiB7eVyK7Gthb4cSevsBBJDHYAbGGL25wPw==" - }, "@graphql-codegen/add": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@graphql-codegen/add/-/add-3.1.1.tgz", @@ -42154,27 +41890,22 @@ "cross-spawn": "7.0.3" } }, + "@quri/squiggle-lang": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@quri/squiggle-lang/-/squiggle-lang-0.2.8.tgz", + "integrity": "sha512-uK+oXE0chYtEW3la0qIxg6ueXRm34jvkL930RWhazVWC0S4aU2lzPJQ9Oz1h+8qIZuQRs89ZqfeO5r5IcjUTNQ==", + "requires": { + "jstat": "^1.9.5", + "mathjs": "^10.5.0", + "pdfast": "^0.2.0", + "rescript": "^9.1.4" + } + }, "@repeaterjs/repeater": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@repeaterjs/repeater/-/repeater-3.0.4.tgz", "integrity": "sha512-AW8PKd6iX3vAZ0vA43nOUOnbq/X5ihgU+mSXXqunMkeQADGiqw/PY0JNeYtD5sr0PAy51YPgAPbDoeapv9r8WA==" }, - "@rescript/react": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/@rescript%2freact/-/react-0.10.3.tgz", - "integrity": "sha512-Lf9rzrR3bQPKJjOK3PBRa/B3xrJ7CqQ1HYr9VHPVxJidarIJJFZBhj0Dg1uZURX+Wg/xiP0PHFxXmdj2bK8Vxw==", - "peer": true, - "requires": {} - }, - "@rescriptbr/reform": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/@rescriptbr%2freform/-/reform-11.0.2.tgz", - "integrity": "sha512-4C4iZQ5oQKhG1hLc0fJM2Dn47zQYegDGV7Qgp18nQX/3nVNg4JrlP3Fx9eD8NvqUmd3V5ew710Y+TU5FNS8n6A==", - "requires": { - "reschema": "^2.0.3", - "rescript-react-update": "^3.0.1" - } - }, "@samverschueren/stream-to-observable": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.1.tgz", @@ -42442,6 +42173,11 @@ "resolved": "https://registry.npmjs.org/@types/dom-to-image/-/dom-to-image-2.6.4.tgz", "integrity": "sha512-UddUdGF1qulrSDulkz3K2Ypq527MR6ixlgAzqLbxSiQ0icx0XDlIV+h4+edmjq/1dqn0KgN0xGSe1kI9t+vGuw==" }, + "@types/google-spreadsheet": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/google-spreadsheet/-/google-spreadsheet-3.2.1.tgz", + "integrity": "sha512-k9fMPyRJuWFoRiD4ZU2xTJTBykeXIh+ittw59UHGvNVkyu0b82DorkEJe36LBlaLkqD2qPBAunVz6JZRMFvWIA==" + }, "@types/hast": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/@types%2fhast/-/hast-2.3.4.tgz", @@ -42603,6 +42339,14 @@ "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.1.tgz", "integrity": "sha512-Y0K95ThC3esLEYD6ZuqNek29lNX2EM1qxV8y2FTLUB0ff5wWrk7az+mLrnNFUnaXcgKye22+sFBRXOgpPILZNg==" }, + "@types/tunnel": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.3.tgz", + "integrity": "sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA==", + "requires": { + "@types/node": "*" + } + }, "@types/unist": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types%2funist/-/unist-2.0.6.tgz", @@ -42749,7 +42493,8 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true }, "ansi-styles": { "version": "4.3.0", @@ -42861,58 +42606,6 @@ "follow-redirects": "^1.14.7" } }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - } - } - }, - "babel-messages": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", - "requires": { - "babel-runtime": "^6.22.0" - } - }, "babel-plugin-dynamic-import-node": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", @@ -42952,26 +42645,6 @@ "integrity": "sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ==", "dev": true }, - "babel-plugin-transform-es2015-modules-commonjs": { - "version": "6.26.2", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", - "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", - "requires": { - "babel-plugin-transform-strict-mode": "^6.24.1", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-types": "^6.26.0" - } - }, - "babel-plugin-transform-strict-mode": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", - "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, "babel-preset-fbjs": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/babel-preset-fbjs/-/babel-preset-fbjs-3.4.0.tgz", @@ -43007,98 +42680,6 @@ "babel-plugin-syntax-trailing-function-commas": "^7.0.0-beta.0" } }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - }, - "dependencies": { - "core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" - }, - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" - } - } - }, - "babel-template": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", - "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", - "requires": { - "babel-runtime": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "lodash": "^4.17.4" - } - }, - "babel-traverse": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", - "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", - "requires": { - "babel-code-frame": "^6.26.0", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "debug": "^2.6.8", - "globals": "^9.18.0", - "invariant": "^2.2.2", - "lodash": "^4.17.4" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==" - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "babel-types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", - "requires": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" - }, - "dependencies": { - "to-fast-properties": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=" - } - } - }, - "babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" - }, "backo2": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", @@ -43642,9 +43223,9 @@ "dev": true }, "complex.js": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.0.11.tgz", - "integrity": "sha512-6IArJLApNtdg1P1dFtn3dnyzoZBEF0MwMnrfF1exSBRpZYoy4yieMkpZhQDC0uwctw48vii0CFVyHfpgZ/DfGw==" + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.1.1.tgz", + "integrity": "sha512-8njCHOTtFFLtegk6zQo0kkVX1rngygb/KQI6z1qZxlFI3scluC+LVTCFbrkWjBv4vvLlbQ9t88IPMC6k95VTTg==" }, "concat-map": { "version": "0.0.1", @@ -44073,9 +43654,9 @@ "dev": true }, "decimal.js": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.0.tgz", - "integrity": "sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw==" + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", + "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==" }, "decode-named-character-reference": { "version": "1.0.1", @@ -45059,6 +44640,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, "requires": { "ansi-regex": "^2.0.0" } @@ -45334,6 +44916,7 @@ "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, "requires": { "loose-envify": "^1.0.0" } @@ -45663,11 +45246,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==" }, - "decimal.js": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", - "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==" - }, "form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", @@ -45850,6 +45428,11 @@ "html2canvas": "^1.0.0-rc.5" } }, + "jstat": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/jstat/-/jstat-1.9.5.tgz", + "integrity": "sha512-cWnp4vObF5GmB2XsIEzxI/1ZTcYlcfNqxQ/9Fp5KFUa0Jf/4tO0ZkGVnqoEHDisJvYgvn5n3eWZbd2xTVJJPUQ==" + }, "jwa": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", @@ -46430,25 +46013,19 @@ "integrity": "sha512-y8j3a5/DkJCmS5x4dMCQL+OR0+2EAq3DOtio1COSHsmW2BGXnNCK3v12hJt1LrUz5iZH5g0LmuYOjDdI+czghA==" }, "mathjs": { - "version": "5.10.3", - "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-5.10.3.tgz", - "integrity": "sha512-ySjg30BC3dYjQm73ILZtwcWzFJde0VU6otkXW/57IjjuYRa3Qaf0Kb8pydEuBZYtqW2OxreAtsricrAmOj3jIw==", + "version": "10.5.2", + "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-10.5.2.tgz", + "integrity": "sha512-yLl2yA597A+07+xsOgy1+pq/m4ZokQceWQbB9OsIkK2guCpA2Px9k11zul93eJAA0WUFZiMK6VcruY9ZBbSKjA==", "requires": { - "complex.js": "2.0.11", - "decimal.js": "10.2.0", - "escape-latex": "1.2.0", - "fraction.js": "4.0.12", - "javascript-natural-sort": "0.7.1", - "seed-random": "2.2.0", - "tiny-emitter": "2.1.0", - "typed-function": "1.1.0" - }, - "dependencies": { - "fraction.js": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.0.12.tgz", - "integrity": "sha512-8Z1K0VTG4hzYY7kA/1sj4/r1/RWLBD3xwReT/RCrUCbzPszjNQCCsy3ktkU/eaEqX3MYa4pY37a52eiBlPMlhA==" - } + "@babel/runtime": "^7.17.9", + "complex.js": "^2.1.1", + "decimal.js": "^10.3.1", + "escape-latex": "^1.2.0", + "fraction.js": "^4.2.0", + "javascript-natural-sort": "^0.7.1", + "seedrandom": "^3.0.5", + "tiny-emitter": "^2.1.0", + "typed-function": "^2.1.0" } }, "mdast-util-definitions": { @@ -68043,11 +67620,6 @@ "performance-now": "^2.1.0" } }, - "rationale": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/rationale/-/rationale-0.2.0.tgz", - "integrity": "sha512-Pd8w5Inv1JhTfRyx03zs486CEAn6UKXvvOtxVRLsewngsBSffo3MQwUKYS75L/8vPt98wmf7iaZROx362/f7Bw==" - }, "rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -68359,22 +67931,10 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, - "reschema": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/reschema/-/reschema-2.2.0.tgz", - "integrity": "sha512-vkFNYm2GEqrmoK+n1xomn/EXgltvCfqmowMxXkr2IBa6Zw0JItunzOap73PaS9DUGak0h6xHoIOciGcq+LC9cg==" - }, "rescript": { "version": "9.1.4", "resolved": "https://registry.npmjs.org/rescript/-/rescript-9.1.4.tgz", - "integrity": "sha512-aXANK4IqecJzdnDpJUsU6pxMViCR5ogAxzuqS0mOr8TloMnzAjJFu63fjD6LCkWrKAhlMkFFzQvVQYaAaVkFXw==", - "peer": true - }, - "rescript-react-update": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rescript-react-update/-/rescript-react-update-3.0.2.tgz", - "integrity": "sha512-oOOaeWma6XEhfZwlB/hB6u+UfYtuHjWS4RpoBeE8NbPX60GYLx5BLoVjEqLKoqXFPJv/2ZgghOKBn2LZo0VaJg==", - "requires": {} + "integrity": "sha512-aXANK4IqecJzdnDpJUsU6pxMViCR5ogAxzuqS0mOr8TloMnzAjJFu63fjD6LCkWrKAhlMkFFzQvVQYaAaVkFXw==" }, "resolve": { "version": "1.22.0", @@ -68484,10 +68044,10 @@ "integrity": "sha512-MuCAyrGZcTLfQoH2XoBlQ8C6bzwN88XT/0slOGz0pn8+gIP85BOAfYa44ZXQUTOwRwPU0QvgU+V+OSajl/59Xg==", "dev": true }, - "seed-random": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/seed-random/-/seed-random-2.2.0.tgz", - "integrity": "sha1-KpsZ4lCoFwmSMaW5mk2vgLf77VQ=" + "seedrandom": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", + "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" }, "selfsigned": { "version": "2.0.0", @@ -68704,27 +68264,6 @@ "tslib": "^2.0.3" } }, - "squiggle-experimental": { - "version": "0.1.9", - "resolved": "https://registry.npmjs.org/squiggle-experimental/-/squiggle-experimental-0.1.9.tgz", - "integrity": "sha512-ObEKX+2/chpeRY51u27o41oggYRzHmrdslHLOQkqsiM+hslQAgHeulS5xE+md09DMTAgkUOZFtil3Sb3Vxty2Q==", - "requires": { - "@glennsl/bs-json": "^5.0.2", - "@rescriptbr/reform": "^11.0.1", - "babel-plugin-transform-es2015-modules-commonjs": "^6.26.2", - "lodash": "4.17.15", - "mathjs": "5.10.3", - "pdfast": "^0.2.0", - "rationale": "0.2.0" - }, - "dependencies": { - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" - } - } - }, "stable": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", @@ -68786,6 +68325,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, "requires": { "ansi-regex": "^2.0.0" } @@ -69195,9 +68735,9 @@ "integrity": "sha512-EUV9jo4sffrwlg8s0zDhP0T2WD3pru5Xi0+HTE3zTUmBaZNhfkite9PdSJwdXLwPVW0jnAHT56pZHIOYckPEiw==" }, "typed-function": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-1.1.0.tgz", - "integrity": "sha512-TuQzwiT4DDg19beHam3E66oRXhyqlyfgjHB/5fcvsRXbfmWPJfto9B4a0TBdTrQAPGlGmXh/k7iUI+WsObgORA==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-2.1.0.tgz", + "integrity": "sha512-bctQIOqx2iVbWGDGPWwIm18QScpu2XRmkC19D8rQGFsjKSgteq/o1hTZvIG/wuDq8fanpBDrLkLq+aEN/6y5XQ==" }, "typescript": { "version": "4.6.2", diff --git a/package.json b/package.json index 2eecce6..f141b9e 100644 --- a/package.json +++ b/package.json @@ -33,15 +33,18 @@ "@pothos/plugin-prisma": "^3.4.0", "@pothos/plugin-relay": "^3.10.0", "@prisma/client": "^3.11.1", + "@quri/squiggle-lang": "^0.2.8", "@tailwindcss/forms": "^0.4.0", "@tailwindcss/typography": "^0.5.1", "@types/chroma-js": "^2.1.3", "@types/dom-to-image": "^2.6.4", + "@types/google-spreadsheet": "^3.2.1", "@types/jsdom": "^16.2.14", "@types/nprogress": "^0.2.0", "@types/react": "^17.0.39", "@types/react-copy-to-clipboard": "^5.0.2", "@types/textversionjs": "^1.1.1", + "@types/tunnel": "^0.0.3", "airtable": "^0.11.1", "algoliasearch": "^4.10.3", "autoprefixer": "^10.1.0", @@ -88,7 +91,6 @@ "react-safe": "^1.3.0", "react-select": "^5.2.2", "remark-gfm": "^3.0.1", - "squiggle-experimental": "^0.1.9", "tabletojson": "^2.0.4", "tailwindcss": "^3.0.22", "textversionjs": "^1.1.3", diff --git a/src/backend/flow/doEverything.ts b/src/backend/flow/doEverything.ts index c6e0bab..64835d0 100644 --- a/src/backend/flow/doEverything.ts +++ b/src/backend/flow/doEverything.ts @@ -3,14 +3,7 @@ import { executeJobByName } from "./jobs"; /* Do everything */ export async function doEverything() { - let jobNames = [ - ...platforms.map((platform) => platform.name), - "merge", - "algolia", - "history", - "netlify", - ]; - // Removed Good Judgment from the fetcher, doing it using cron instead because cloudflare blocks the utility on heroku. + let jobNames = [...platforms.map((platform) => platform.name), "algolia"]; console.log(""); console.log(""); diff --git a/src/backend/flow/history/updateHistory.ts b/src/backend/flow/history/updateHistory.ts deleted file mode 100644 index 8b56de3..0000000 --- a/src/backend/flow/history/updateHistory.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { prisma } from "../../database/prisma"; - -export async function updateHistory() { - const questions = await prisma.question.findMany({}); - await prisma.history.createMany({ - data: questions.map((q) => ({ - ...q, - idref: q.id, - })), - }); -} diff --git a/src/backend/flow/jobs.ts b/src/backend/flow/jobs.ts index 01a5a81..099e3c6 100644 --- a/src/backend/flow/jobs.ts +++ b/src/backend/flow/jobs.ts @@ -1,9 +1,8 @@ import { doEverything } from "../flow/doEverything"; -import { updateHistory } from "../flow/history/updateHistory"; -import { rebuildNetlifySiteWithNewData } from "../flow/rebuildNetliftySiteWithNewData"; import { rebuildFrontpage } from "../frontpage"; import { platforms, processPlatform } from "../platforms"; import { rebuildAlgoliaDatabase } from "../utils/algolia"; +import { sleep } from "../utils/sleep"; interface Job { name: string; @@ -23,16 +22,6 @@ export const jobs: Job[] = [ message: 'Rebuild algolia database ("index")', run: rebuildAlgoliaDatabase, }, - { - name: "history", - message: "Update history", - run: updateHistory, - }, - { - name: "netlify", - message: `Rebuild netlify site with new data`, - run: rebuildNetlifySiteWithNewData, - }, { name: "frontpage", message: "Rebuild frontpage", @@ -46,10 +35,6 @@ export const jobs: Job[] = [ }, ]; -function sleep(ms: number) { - return new Promise((resolve) => setTimeout(resolve, ms)); -} - async function tryCatchTryAgain(fun: () => Promise) { try { console.log("Initial try"); diff --git a/src/backend/flow/rebuildNetliftySiteWithNewData.ts b/src/backend/flow/rebuildNetliftySiteWithNewData.ts deleted file mode 100644 index 72ab28c..0000000 --- a/src/backend/flow/rebuildNetliftySiteWithNewData.ts +++ /dev/null @@ -1,15 +0,0 @@ -import axios from "axios"; - -import { applyIfSecretExists } from "../utils/getSecrets"; - -async function rebuildNetlifySiteWithNewData_inner(cookie: string) { - let payload = {}; - let response = await axios.post(cookie, payload); - let data = response.data; - console.log(data); -} - -export async function rebuildNetlifySiteWithNewData() { - const cookie = process.env.REBUIDNETLIFYHOOKURL || ""; - await applyIfSecretExists(cookie, rebuildNetlifySiteWithNewData_inner); -} diff --git a/src/backend/index.ts b/src/backend/index.ts index 2d9edfa..bb4234d 100644 --- a/src/backend/index.ts +++ b/src/backend/index.ts @@ -2,11 +2,10 @@ import "dotenv/config"; import readline from "readline"; -import util from "util"; import { executeJobByName, jobs } from "./flow/jobs"; -let generateWhatToDoMessage = () => { +const generateWhatToDoMessage = () => { const color = "\x1b[36m"; const resetColor = "\x1b[0m"; let completeMessages = [ @@ -23,10 +22,10 @@ let generateWhatToDoMessage = () => { return completeMessages; }; -let whattodoMessage = generateWhatToDoMessage(); +const whattodoMessage = generateWhatToDoMessage(); /* BODY */ -let commandLineUtility = async () => { +const commandLineUtility = async () => { const pickOption = async () => { if (process.argv.length === 3) { return process.argv[2]; // e.g., npm run cli polymarket @@ -37,9 +36,15 @@ let commandLineUtility = async () => { output: process.stdout, }); - const question = util.promisify(rl.question).bind(rl); + const question = (query: string) => { + return new Promise((resolve: (s: string) => void) => { + rl.question(query, resolve); + }); + }; + const answer = await question(whattodoMessage); rl.close(); + return answer; }; diff --git a/src/backend/platforms/_example.ts b/src/backend/platforms/_example.ts index e41113c..336bbc9 100644 --- a/src/backend/platforms/_example.ts +++ b/src/backend/platforms/_example.ts @@ -21,7 +21,7 @@ async function fetchData() { return response; } -async function processPredictions(predictions) { +async function processPredictions(predictions: any[]) { let results = await predictions.map((prediction) => { const id = `${platformName}-${prediction.id}`; const probability = prediction.probability; diff --git a/src/backend/platforms/betfair.ts b/src/backend/platforms/betfair.ts index aa950d8..d9495a1 100644 --- a/src/backend/platforms/betfair.ts +++ b/src/backend/platforms/betfair.ts @@ -27,7 +27,7 @@ const arraysEqual = (a: string[], b: string[]) => { return true; }; -const mergeRunners = (runnerCatalog, runnerBook) => { +const mergeRunners = (runnerCatalog: any, runnerBook: any) => { let keys = Object.keys(runnerCatalog); let result = []; for (let key of keys) { @@ -45,16 +45,13 @@ async function fetchPredictions() { const response = await axios({ url: endpoint, method: "GET", - headers: { - "Content-Type": "text/html", - }, httpsAgent: agent, }).then((response) => response.data); return response; } -async function whipIntoShape(data) { +async function whipIntoShape(data: any) { let catalogues = data.market_catalogues; let books = data.market_books; let keys1 = Object.keys(catalogues).sort(); @@ -78,7 +75,7 @@ async function whipIntoShape(data) { return results; } -async function processPredictions(data) { +async function processPredictions(data: any) { let predictions = await whipIntoShape(data); // console.log(JSON.stringify(predictions, null, 4)) let results: FetchedQuestion[] = predictions.map((prediction) => { @@ -87,13 +84,17 @@ async function processPredictions(data) { } */ let id = `${platformName}-${prediction.marketId}`; let normalizationFactor = prediction.options - .filter((option) => option.status == "ACTIVE" && option.totalMatched > 0) - .map((option) => option.lastPriceTraded) - .map((x) => 1 / x) - .reduce((a, b) => a + b, 0); + .filter( + (option: any) => option.status == "ACTIVE" && option.totalMatched > 0 + ) + .map((option: any) => option.lastPriceTraded) + .map((x: any) => 1 / x) + .reduce((a: any, b: any) => a + b, 0); let options = prediction.options - .filter((option) => option.status == "ACTIVE" && option.totalMatched > 0) - .map((option) => ({ + .filter( + (option: any) => option.status == "ACTIVE" && option.totalMatched > 0 + ) + .map((option: any) => ({ name: option.runnerName, probability: option.lastPriceTraded != 0 @@ -142,7 +143,7 @@ export const betfair: Platform = { color: "#3d674a", async fetcher() { const data = await fetchPredictions(); - const results = await processPredictions(data); // somehow needed + const results = await processPredictions(data); return results; }, calculateStars(data) { diff --git a/src/backend/platforms/fantasyscotus.ts b/src/backend/platforms/fantasyscotus.ts index 7527599..8181afe 100644 --- a/src/backend/platforms/fantasyscotus.ts +++ b/src/backend/platforms/fantasyscotus.ts @@ -29,7 +29,7 @@ async function fetchData() { return response; } -async function getPredictionsData(caseUrl) { +async function getPredictionsData(caseUrl: string) { let newCaseUrl = `https://fantasyscotus.net/user-predictions${caseUrl}?filterscount=0&groupscount=0&sortdatafield=username&sortorder=asc&pagenum=0&pagesize=20&recordstartindex=0&recordendindex=20&_=${unixtime}`; //console.log(newCaseUrl) let predictions = await axios({ @@ -49,7 +49,7 @@ async function getPredictionsData(caseUrl) { }).then((res) => res.data); let predictionsAffirm = predictions.filter( - (prediction) => prediction.percent_affirm > 50 + (prediction: any) => prediction.percent_affirm > 50 ); //console.log(predictions) //console.log(predictionsAffirm.length/predictions.length) @@ -61,7 +61,7 @@ async function getPredictionsData(caseUrl) { }; } -async function processData(data) { +async function processData(data: any) { let events = data.object_list; let historicalPercentageCorrect = data.stats.pcnt_correct; let historicalProbabilityCorrect = diff --git a/src/backend/platforms/foretold.ts b/src/backend/platforms/foretold.ts index 0673fae..55692e3 100644 --- a/src/backend/platforms/foretold.ts +++ b/src/backend/platforms/foretold.ts @@ -18,8 +18,10 @@ let highQualityCommunities = [ ]; /* Support functions */ -async function fetchAllCommunityQuestions(communityId) { - let response = await axios({ +async function fetchAllCommunityQuestions(communityId: string) { + // TODO - fetch foretold graphql schema to type the result properly? + // (should be doable with graphql-code-generator, why not) + const response = await axios({ url: graphQLendpoint, method: "POST", headers: { "Content-Type": "application/json" }, @@ -30,10 +32,10 @@ async function fetchAllCommunityQuestions(communityId) { channelId: "${communityId}", states: OPEN, first: 500 - ){ + ) { total - edges{ - node{ + edges { + node { id name valueType @@ -52,8 +54,8 @@ async function fetchAllCommunityQuestions(communityId) { }) .then((res) => res.data) .then((res) => res.data.measurables.edges); - //console.log(response) - return response; + + return response as any[]; } export const foretold: Platform = { @@ -67,11 +69,11 @@ export const foretold: Platform = { questions = questions.map((question) => question.node); questions = questions.filter((question) => question.previousAggregate); // Questions without any predictions questions.forEach((question) => { - let id = `${platformName}-${question.id}`; + const id = `${platformName}-${question.id}`; let options: FetchedQuestion["options"] = []; if (question.valueType == "PERCENTAGE") { - let probability = question.previousAggregate.value.percentage; + const probability = question.previousAggregate.value.percentage; options = [ { name: "Yes", diff --git a/src/backend/platforms/goodjudgment.ts b/src/backend/platforms/goodjudgment.ts index ab487bf..be78ebe 100644 --- a/src/backend/platforms/goodjudgment.ts +++ b/src/backend/platforms/goodjudgment.ts @@ -1,7 +1,6 @@ /* Imports */ import axios from "axios"; import { Tabletojson } from "tabletojson"; -import tunnel from "tunnel"; import { average } from "../../utils"; import { hash } from "../utils/hash"; @@ -18,7 +17,7 @@ export const goodjudgment: Platform = { color: "#7d4f1b", async fetcher() { // Proxy fuckery - let proxy; + // let proxy; /* * try { proxy = await axios @@ -29,19 +28,19 @@ export const goodjudgment: Platform = { console.log("Proxy generation failed; using backup proxy instead"); // hard-coded backup proxy */ - proxy = { - ip: process.env.BACKUP_PROXY_IP, - port: process.env.BACKUP_PROXY_PORT, - }; - // } - let agent = tunnel.httpsOverHttp({ - proxy: { - host: proxy.ip, - port: proxy.port, - }, - }); + // proxy = { + // ip: process.env.BACKUP_PROXY_IP, + // port: process.env.BACKUP_PROXY_PORT, + // }; + // // } + // let agent = tunnel.httpsOverHttp({ + // proxy: { + // host: proxy.ip, + // port: proxy.port, + // }, + // }); - let content = await axios + const content = await axios .request({ url: "https://goodjudgment.io/superforecasts/", method: "get", @@ -58,17 +57,16 @@ export const goodjudgment: Platform = { let jsonTable = Tabletojson.convert(content, { stripHtmlFromCells: false }); jsonTable.shift(); // deletes first element jsonTable.pop(); // deletes last element - // console.log(jsonTable) + for (let table of jsonTable) { - // console.log(table) let title = table[0]["0"].split("\t\t\t").splice(3)[0]; if (title != undefined) { title = title.replaceAll("", ""); - let id = `${platformName}-${hash(title)}`; - let description = table - .filter((row) => row["0"].includes("BACKGROUND:")) - .map((row) => row["0"]) - .map((text) => + const id = `${platformName}-${hash(title)}`; + const description = table + .filter((row: any) => row["0"].includes("BACKGROUND:")) + .map((row: any) => row["0"]) + .map((text: any) => text .split("BACKGROUND:")[1] .split("Examples of Superforecaster")[0] @@ -80,16 +78,16 @@ export const goodjudgment: Platform = { .replaceAll(" ", "") .replaceAll("
", "") )[0]; - let options = table - .filter((row) => "4" in row) - .map((row) => ({ + const options = table + .filter((row: any) => "4" in row) + .map((row: any) => ({ name: row["2"] .split('')[1] .replace("", ""), probability: Number(row["3"].split("%")[0]) / 100, type: "PROBABILITY", })); - let analysis = table.filter((row) => + let analysis = table.filter((row: any) => row[0] ? row[0].toLowerCase().includes("commentary") : false ); // "Examples of Superforecaster Commentary" / Analysis diff --git a/src/backend/platforms/goodjudgmentopen.ts b/src/backend/platforms/goodjudgmentopen.ts index 05c7a8d..a22fbea 100644 --- a/src/backend/platforms/goodjudgmentopen.ts +++ b/src/backend/platforms/goodjudgmentopen.ts @@ -4,6 +4,7 @@ import { Tabletojson } from "tabletojson"; import { average } from "../../utils"; import { applyIfSecretExists } from "../utils/getSecrets"; +import { sleep } from "../utils/sleep"; import toMarkdown from "../utils/toMarkdown"; import { FetchedQuestion, Platform } from "./"; @@ -23,11 +24,10 @@ const id = () => 0; /* Support functions */ async function fetchPage(page: number, cookie: string) { - let response = await axios({ + const response: string = await axios({ url: htmlEndPoint + page, method: "GET", headers: { - "Content-Type": "text/html", Cookie: cookie, }, }).then((res) => res.data); @@ -36,11 +36,10 @@ async function fetchPage(page: number, cookie: string) { } async function fetchStats(questionUrl: string, cookie: string) { - let response = await axios({ + let response: string = await axios({ url: questionUrl + "/stats", method: "GET", headers: { - "Content-Type": "text/html", Cookie: cookie, Referer: questionUrl, }, @@ -74,7 +73,7 @@ async function fetchStats(questionUrl: string, cookie: string) { let optionsHtmlElement = ""; let tablesAsJson = Tabletojson.convert(optionsHtmlElement); let firstTable = tablesAsJson[0]; - options = firstTable.map((element) => ({ + options = firstTable.map((element: any) => ({ name: element["0"], probability: Number(element["1"].replace("%", "")) / 100, type: "PROBABILITY", @@ -133,7 +132,7 @@ function isSignedIn(html: string) { return isSignedInBool; } -function reachedEnd(html) { +function reachedEnd(html: string) { let reachedEndBool = html.includes("No questions match your filter"); if (reachedEndBool) { //console.log(html) @@ -142,10 +141,6 @@ function reachedEnd(html) { return reachedEndBool; } -function sleep(ms: number) { - return new Promise((resolve) => setTimeout(resolve, ms)); -} - /* Body */ async function goodjudgmentopen_inner(cookie: string) { @@ -176,7 +171,11 @@ async function goodjudgmentopen_inner(cookie: string) { } } let questionNumRegex = new RegExp("questions/([0-9]+)"); - let questionNum = url.match(questionNumRegex)[1]; //.split("questions/")[1].split("-")[0]; + const questionNumMatch = url.match(questionNumRegex); + if (!questionNumMatch) { + throw new Error(`Couldn't find question num in ${url}`); + } + let questionNum = questionNumMatch[1]; let id = `${platformName}-${questionNum}`; let question = { id: id, diff --git a/src/backend/platforms/guesstimate.ts b/src/backend/platforms/guesstimate.ts index b517215..432a5fd 100644 --- a/src/backend/platforms/guesstimate.ts +++ b/src/backend/platforms/guesstimate.ts @@ -2,8 +2,8 @@ import axios from "axios"; import { Question } from "@prisma/client"; -import { AlgoliaQuestion } from "../../backend/utils/algolia"; import { prisma } from "../database/prisma"; +import { AlgoliaQuestion } from "../utils/algolia"; import { FetchedQuestion, Platform, prepareQuestion } from "./"; /* Definitions */ @@ -12,7 +12,7 @@ const searchEndpoint = const apiEndpoint = "https://guesstimate.herokuapp.com"; -const modelToQuestion = (model: any): Question => { +const modelToQuestion = (model: any): ReturnType => { const { description } = model; // const description = model.description // ? model.description.replace(/\n/g, " ").replace(/ /g, " ") @@ -77,12 +77,11 @@ async function search(query: string): Promise { const fetchQuestion = async (id: number): Promise => { const response = await axios({ url: `${apiEndpoint}/spaces/${id}` }); let q = modelToQuestion(response.data); - q = await prisma.question.upsert({ + return await prisma.question.upsert({ where: { id: q.id }, create: q, update: q, }); - return q; }; export const guesstimate: Platform & { diff --git a/src/backend/platforms/index.ts b/src/backend/platforms/index.ts index d9127ed..87c6139 100644 --- a/src/backend/platforms/index.ts +++ b/src/backend/platforms/index.ts @@ -1,5 +1,6 @@ import { Question } from "@prisma/client"; +import { QuestionOption } from "../../common/types"; import { prisma } from "../database/prisma"; import { betfair } from "./betfair"; import { fantasyscotus } from "./fantasyscotus"; @@ -45,11 +46,7 @@ export type FetchedQuestion = Omit< > & { timestamp?: Date; extra?: object; // required in DB but annoying to return empty; also this is slightly stricter than Prisma's JsonValue - options: { - name?: string; - probability?: number; - type: "PROBABILITY"; - }[]; // stronger type than Prisma's JsonValue + options: QuestionOption[]; // stronger type than Prisma's JsonValue qualityindicators: Omit; // slightly stronger type than Prisma's JsonValue }; @@ -92,10 +89,23 @@ export const platforms: Platform[] = [ xrisk, ]; +// Typing notes: +// There's a difference between prisma's Question type (type returned from `find` and `findMany`) and its input types due to JsonValue vs InputJsonValue mismatch. +// On the other hand, we can't use Prisma.QuestionUpdateInput or Prisma.QuestionCreateManyInput either, because we use this question in guesstimate's code for preparing questions from guesstimate models... +// So here we build a new type which should be ok to use both in place of prisma's Question type and as an input to its update or create methods. +type PreparedQuestion = Omit< + Question, + "extra" | "qualityindicators" | "options" +> & { + extra: NonNullable; + qualityindicators: NonNullable; + options: NonNullable; +}; + export const prepareQuestion = ( q: FetchedQuestion, platform: Platform -): Question => { +): PreparedQuestion => { return { extra: {}, timestamp: new Date(), @@ -131,8 +141,8 @@ export const processPlatform = async (platform: Platform) => { const fetchedIdsSet = new Set(fetchedIds); const oldIdsSet = new Set(oldIds); - const createdQuestions: Question[] = []; - const updatedQuestions: Question[] = []; + const createdQuestions: PreparedQuestion[] = []; + const updatedQuestions: PreparedQuestion[] = []; const deletedIds = oldIds.filter((id) => !fetchedIdsSet.has(id)); for (const q of fetchedQuestions.map((q) => prepareQuestion(q, platform))) { @@ -163,6 +173,13 @@ export const processPlatform = async (platform: Platform) => { }, }); + await prisma.history.createMany({ + data: [...createdQuestions, ...updatedQuestions].map((q) => ({ + ...q, + idref: q.id, + })), + }); + console.log( `Done, ${deletedIds.length} deleted, ${updatedQuestions.length} updated, ${createdQuestions.length} created` ); diff --git a/src/backend/platforms/infer.ts b/src/backend/platforms/infer.ts index b004917..9d22048 100644 --- a/src/backend/platforms/infer.ts +++ b/src/backend/platforms/infer.ts @@ -1,9 +1,11 @@ /* Imports */ import axios from "axios"; +import { FullQuestionOption } from "../../common/types"; import { average } from "../../utils"; import { applyIfSecretExists } from "../utils/getSecrets"; import { measureTime } from "../utils/measureTime"; +import { sleep } from "../utils/sleep"; import toMarkdown from "../utils/toMarkdown"; import { FetchedQuestion, Platform } from "./"; @@ -16,20 +18,20 @@ const SLEEP_TIME_EXTRA = 2000; /* Support functions */ -function cleanDescription(text) { +function cleanDescription(text: string) { let md = toMarkdown(text); let result = md.replaceAll("---", "-").replaceAll(" ", " "); return result; } -async function fetchPage(page, cookie) { +async function fetchPage(page: number, cookie: string) { console.log(`Page #${page}`); if (page == 1) { cookie = cookie.split(";")[0]; // Interesting that it otherwise doesn't work :( } let urlEndpoint = `${htmlEndPoint}/?page=${page}`; console.log(urlEndpoint); - let response = await axios({ + const response: string = await axios({ url: urlEndpoint, method: "GET", headers: { @@ -41,8 +43,8 @@ async function fetchPage(page, cookie) { return response; } -async function fetchStats(questionUrl, cookie) { - let response = await axios({ +async function fetchStats(questionUrl: string, cookie: string) { + let response: string = await axios({ url: questionUrl + "/stats", method: "GET", headers: { @@ -56,7 +58,7 @@ async function fetchStats(questionUrl, cookie) { throw Error("Not logged in"); } // Init - let options = []; + let options: FullQuestionOption[] = []; // Parse the embedded json let htmlElements = response.split("\n"); @@ -81,7 +83,7 @@ async function fetchStats(questionUrl, cookie) { questionType.includes("Forecast::Question") || !questionType.includes("Forecast::MultiTimePeriodQuestion") ) { - options = firstEmbeddedJson.question.answers.map((answer) => ({ + options = firstEmbeddedJson.question.answers.map((answer: any) => ({ name: answer.name, probability: answer.normalized_probability, type: "PROBABILITY", @@ -91,12 +93,11 @@ async function fetchStats(questionUrl, cookie) { options[0].probability > 1 ? 1 - options[0].probability / 100 : 1 - options[0].probability; - let optionNo = { + options.push({ name: "No", probability: probabilityNo, type: "PROBABILITY", - }; - options.push(optionNo); + }); } } let result = { @@ -112,7 +113,7 @@ async function fetchStats(questionUrl, cookie) { return result; } -function isSignedIn(html) { +function isSignedIn(html: string) { let isSignedInBool = !( html.includes("You need to sign in or sign up before continuing") || html.includes("Sign up") @@ -124,7 +125,7 @@ function isSignedIn(html) { return isSignedInBool; } -function reachedEnd(html) { +function reachedEnd(html: string) { let reachedEndBool = html.includes("No questions match your filter"); if (reachedEndBool) { //console.log(html) @@ -133,10 +134,6 @@ function reachedEnd(html) { return reachedEndBool; } -function sleep(ms) { - return new Promise((resolve) => setTimeout(resolve, ms)); -} - /* Body */ async function infer_inner(cookie: string) { @@ -169,14 +166,18 @@ async function infer_inner(cookie: string) { await sleep(Math.random() * SLEEP_TIME_RANDOM + SLEEP_TIME_EXTRA); // don't be as noticeable try { - let moreinfo = await fetchStats(url, cookie); - let questionNumRegex = new RegExp("questions/([0-9]+)"); - let questionNum = url.match(questionNumRegex)[1]; //.split("questions/")[1].split("-")[0]; - let id = `${platformName}-${questionNum}`; + const moreinfo = await fetchStats(url, cookie); + const questionNumRegex = new RegExp("questions/([0-9]+)"); + const questionNumMatch = url.match(questionNumRegex); + if (!questionNumMatch) { + throw new Error(`Couldn't find question num in ${url}`); + } + let questionNum = questionNumMatch[1]; + const id = `${platformName}-${questionNum}`; let question: FetchedQuestion = { - id: id, - title: title, - url: url, + id, + title, + url, ...moreinfo, }; console.log(JSON.stringify(question, null, 4)); @@ -231,7 +232,7 @@ export const infer: Platform = { color: "#223900", async fetcher() { let cookie = process.env.INFER_COOKIE; - return await applyIfSecretExists(cookie, infer_inner); + return (await applyIfSecretExists(cookie, infer_inner)) || null; }, calculateStars(data) { let nuno = () => 2; diff --git a/src/backend/platforms/kalshi.ts b/src/backend/platforms/kalshi.ts index 665f0e6..7b76686 100644 --- a/src/backend/platforms/kalshi.ts +++ b/src/backend/platforms/kalshi.ts @@ -6,18 +6,17 @@ import { FetchedQuestion, Platform } from "./"; /* Definitions */ const platformName = "kalshi"; -let jsonEndpoint = "https://trading-api.kalshi.com/v1/cached/markets/"; //"https://subgraph-matic.poly.market/subgraphs/name/TokenUnion/polymarket"//"https://subgraph-backup.poly.market/subgraphs/name/TokenUnion/polymarket"//'https://subgraph-matic.poly.market/subgraphs/name/TokenUnion/polymarket3' +let jsonEndpoint = "https://trading-api.kalshi.com/v1/cached/markets/"; async function fetchAllMarkets() { - // for info which the polymarket graphql API let response = await axios .get(jsonEndpoint) .then((response) => response.data.markets); - // console.log(response) + return response; } -async function processMarkets(markets) { +async function processMarkets(markets: any[]) { let dateNow = new Date().toISOString(); // console.log(markets) markets = markets.filter((market) => market.close_date > dateNow); diff --git a/src/backend/platforms/manifold.ts b/src/backend/platforms/manifold.ts index b735470..6a035a4 100644 --- a/src/backend/platforms/manifold.ts +++ b/src/backend/platforms/manifold.ts @@ -6,7 +6,7 @@ import { FetchedQuestion, Platform } from "./"; /* Definitions */ const platformName = "manifold"; -let endpoint = "https://manifold.markets/api/v0/markets"; +const endpoint = "https://manifold.markets/api/v0/markets"; // See https://manifoldmarkets.notion.site/Manifold-Markets-API-5e7d0aef4dcf452bb04b319e178fabc5 /* Support functions */ @@ -43,8 +43,8 @@ function showStatistics(results: FetchedQuestion[]) { ); } -async function processPredictions(predictions) { - let results: FetchedQuestion[] = await predictions.map((prediction) => { +function processPredictions(predictions: any[]): FetchedQuestion[] { + let results: FetchedQuestion[] = predictions.map((prediction) => { let id = `${platformName}-${prediction.id}`; // oops, doesn't match platform name let probability = prediction.probability; let options: FetchedQuestion["options"] = [ @@ -90,7 +90,7 @@ export const manifold: Platform = { color: "#793466", async fetcher() { let data = await fetchData(); - let results = await processPredictions(data); // somehow needed + let results = processPredictions(data); // somehow needed showStatistics(results); return results; }, diff --git a/src/backend/platforms/metaculus.ts b/src/backend/platforms/metaculus.ts index 0327ca6..fc14057 100644 --- a/src/backend/platforms/metaculus.ts +++ b/src/backend/platforms/metaculus.ts @@ -2,17 +2,18 @@ import axios from "axios"; import { average } from "../../utils"; +import { sleep } from "../utils/sleep"; import toMarkdown from "../utils/toMarkdown"; import { FetchedQuestion, Platform } from "./"; /* Definitions */ const platformName = "metaculus"; -let jsonEndPoint = "https://www.metaculus.com/api2/questions/?page="; let now = new Date().toISOString(); let DEBUG_MODE = "off"; let SLEEP_TIME = 5000; + /* Support functions */ -async function fetchMetaculusQuestions(next) { +async function fetchMetaculusQuestions(next: string) { // Numbers about a given address: how many, how much, at what price, etc. let response; let data; @@ -24,15 +25,17 @@ async function fetchMetaculusQuestions(next) { }); data = response.data; } catch (error) { - console.log(`Error in async function fetchMetaculusQuestions(next)`); - if (!!error.response.headers["retry-after"]) { - let timeout = error.response.headers["retry-after"]; - console.log(`Timeout: ${timeout}`); - await sleep(Number(timeout) * 1000 + SLEEP_TIME); - } else { - await sleep(SLEEP_TIME); - } + console.log(`Error in async function fetchMetaculusQuestions(next)`); console.log(error); + if (axios.isAxiosError(error)) { + if (error.response?.headers["retry-after"]) { + const timeout = error.response.headers["retry-after"]; + console.log(`Timeout: ${timeout}`); + await sleep(Number(timeout) * 1000 + SLEEP_TIME); + } else { + await sleep(SLEEP_TIME); + } + } } finally { try { response = await axios({ @@ -50,11 +53,7 @@ async function fetchMetaculusQuestions(next) { return data; } -function sleep(ms: number) { - return new Promise((resolve) => setTimeout(resolve, ms)); -} - -async function fetchMetaculusQuestionDescription(slug) { +async function fetchMetaculusQuestionDescription(slug: string) { try { let response = await axios({ method: "get", @@ -67,11 +66,12 @@ async function fetchMetaculusQuestionDescription(slug) { `We encountered some error when attempting to fetch a metaculus page. Trying again` ); if ( + axios.isAxiosError(error) && typeof error.response != "undefined" && typeof error.response.headers != "undefined" && typeof error.response.headers["retry-after"] != "undefined" ) { - let timeout = error.response.headers["retry-after"]; + const timeout = error.response.headers["retry-after"]; console.log(`Timeout: ${timeout}`); await sleep(Number(timeout) * 1000 + SLEEP_TIME); } else { @@ -190,6 +190,7 @@ export const metaculus: Platform = { return all_questions; }, + calculateStars(data) { const { numforecasts } = data.qualityindicators; let nuno = () => diff --git a/src/backend/platforms/polymarket.ts b/src/backend/platforms/polymarket.ts index 395b60e..b263050 100644 --- a/src/backend/platforms/polymarket.ts +++ b/src/backend/platforms/polymarket.ts @@ -6,8 +6,8 @@ import { FetchedQuestion, Platform } from "./"; /* Definitions */ const platformName = "polymarket"; -let graphQLendpoint = - "https://api.thegraph.com/subgraphs/name/polymarket/matic-markets-5"; // "https://api.thegraph.com/subgraphs/name/polymarket/matic-markets-4"// "https://api.thegraph.com/subgraphs/name/tokenunion/polymarket-matic"//"https://subgraph-matic.poly.market/subgraphs/name/TokenUnion/polymarket"//"https://subgraph-backup.poly.market/subgraphs/name/TokenUnion/polymarket"//'https://subgraph-matic.poly.market/subgraphs/name/TokenUnion/polymarket3' +const graphQLendpoint = + "https://api.thegraph.com/subgraphs/name/polymarket/matic-markets-5"; let units = 10 ** 6; async function fetchAllContractInfo() { @@ -18,11 +18,11 @@ async function fetchAllContractInfo() { // "https://strapi-matic.poly.market/markets?active=true&_sort=volume:desc&_limit=-1" to get all markets, including closed ones ) .then((query) => query.data); - response = response.filter((res) => res.closed != true); + response = response.filter((res: any) => res.closed != true); return response; } -async function fetchIndividualContractData(marketMakerAddress) { +async function fetchIndividualContractData(marketMakerAddress: string) { let daysSinceEra = Math.round(Date.now() / (1000 * 24 * 60 * 60)) - 7; // last week let response = await axios({ url: graphQLendpoint, @@ -59,7 +59,7 @@ async function fetchIndividualContractData(marketMakerAddress) { }) .then((res) => res.data) .then((res) => res.data.fixedProductMarketMakers); - // console.log(response) + return response; } @@ -93,7 +93,7 @@ export const polymarket: Platform = { // let isbinary = Number(moreMarketInfo.conditions[0].outcomeSlotCount) == 2 // let percentage = Number(moreMarketInfo.outcomeTokenPrices[0]) * 100 // let percentageFormatted = isbinary ? (percentage.toFixed(0) + "%") : "none" - let options = []; + let options: FetchedQuestion["options"] = []; for (let outcome in moreMarketInfo.outcomeTokenPrices) { options.push({ name: String(marketInfo.outcomes[outcome]), @@ -107,7 +107,7 @@ export const polymarket: Platform = { title: marketInfo.question, url: "https://polymarket.com/market/" + marketInfo.slug, description: marketInfo.description, - options: options, + options, qualityindicators: { numforecasts: numforecasts.toFixed(0), liquidity: liquidity.toFixed(2), diff --git a/src/backend/platforms/predictit.ts b/src/backend/platforms/predictit.ts index 550a622..6b94956 100644 --- a/src/backend/platforms/predictit.ts +++ b/src/backend/platforms/predictit.ts @@ -1,24 +1,25 @@ import axios from "axios"; import { average } from "../../utils"; +import { sleep } from "../utils/sleep"; import toMarkdown from "../utils/toMarkdown"; import { FetchedQuestion, Platform } from "./"; const platformName = "predictit"; /* Support functions */ -async function fetchmarkets() { - let response = await axios({ +async function fetchmarkets(): Promise { + const response = await axios({ method: "get", url: "https://www.predictit.org/api/marketdata/all/", }); - let openMarkets = response.data.markets.filter( - (market) => market.status == "Open" + const openMarkets = response.data.markets.filter( + (market: any) => market.status == "Open" ); return openMarkets; } -async function fetchmarketrules(market_id) { +async function fetchmarketrules(market_id: string | number) { let response = await axios({ method: "get", url: "https://www.predictit.org/api/Market/" + market_id, @@ -34,10 +35,6 @@ async function fetchmarketvolumes() { return response.data; } -function sleep(ms: number) { - return new Promise((resolve) => setTimeout(resolve, ms)); -} - /* Body */ export const predictit: Platform = { name: platformName, @@ -65,13 +62,15 @@ export const predictit: Platform = { let shares_volume = market["TotalSharesTraded"]; // let percentageFormatted = isbinary ? Number(Number(market.contracts[0].lastTradePrice) * 100).toFixed(0) + "%" : "none" - let options = market.contracts.map((contract) => ({ - name: contract.name, - probability: contract.lastTradePrice, - type: "PROBABILITY", - })); + let options: FetchedQuestion["options"] = (market.contracts as any[]).map( + (contract) => ({ + name: String(contract.name), + probability: Number(contract.lastTradePrice), + type: "PROBABILITY", + }) + ); let totalValue = options - .map((element) => Number(element.probability)) + .map((element: any) => Number(element.probability)) .reduce((a, b) => a + b, 0); if (options.length != 1 && totalValue > 1) { @@ -81,7 +80,7 @@ export const predictit: Platform = { })); } else if (options.length == 1) { let option = options[0]; - let probability = option["probability"]; + let probability = option.probability; options = [ { name: "Yes", @@ -90,7 +89,7 @@ export const predictit: Platform = { }, { name: "No", - probability: 1 - probability, + probability: 1 - (probability || 0), type: "PROBABILITY", }, ]; diff --git a/src/backend/platforms/rootclaim.ts b/src/backend/platforms/rootclaim.ts index 374e38e..4d58d4a 100644 --- a/src/backend/platforms/rootclaim.ts +++ b/src/backend/platforms/rootclaim.ts @@ -55,7 +55,7 @@ export const rootclaim: Platform = { for (const claim of claims) { const id = `${platformName}-${claim.slug.toLowerCase()}`; - let options = []; + let options: FetchedQuestion["options"] = []; for (let scenario of claim.scenarios) { options.push({ name: toMarkdown(scenario.name || scenario.text) @@ -76,7 +76,7 @@ export const rootclaim: Platform = { title: toMarkdown(claim.question).replace("\n", ""), url, description: toMarkdown(description).replace("'", "'"), - options: options, + options, qualityindicators: { numforecasts: 1, }, diff --git a/src/backend/platforms/smarkets.ts b/src/backend/platforms/smarkets.ts index 52b6059..1d3fa19 100644 --- a/src/backend/platforms/smarkets.ts +++ b/src/backend/platforms/smarkets.ts @@ -1,5 +1,6 @@ import axios from "axios"; +import { QuestionOption } from "../../common/types"; import { average } from "../../utils"; import { FetchedQuestion, Platform } from "./"; @@ -7,57 +8,51 @@ import { FetchedQuestion, Platform } from "./"; const platformName = "smarkets"; let htmlEndPointEntrance = "https://api.smarkets.com/v3/events/"; let VERBOSE = false; -let empty = () => 0; /* Support functions */ -async function fetchEvents(url) { - let response = await axios({ +async function fetchEvents(url: string) { + const response = await axios({ url: htmlEndPointEntrance + url, method: "GET", - headers: { - "Content-Type": "text/html", - }, }).then((res) => res.data); - VERBOSE ? console.log(response) : empty(); + VERBOSE && console.log(response); return response; } -async function fetchMarkets(eventid) { - let response = await axios({ +async function fetchMarkets(eventid: string) { + const response = await axios({ url: `https://api.smarkets.com/v3/events/${eventid}/markets/`, method: "GET", - headers: { - "Content-Type": "text/json", - }, }) .then((res) => res.data) .then((res) => res.markets); return response; } -async function fetchContracts(marketid) { - let response = await axios({ +async function fetchContracts(marketid: string) { + const response = await axios({ url: `https://api.smarkets.com/v3/markets/${marketid}/contracts/`, method: "GET", - headers: { - "Content-Type": "text/html", - }, }).then((res) => res.data); - VERBOSE ? console.log(response) : empty(); - return response; + VERBOSE && console.log(response); + + if (!(response.contracts instanceof Array)) { + throw new Error("Invalid response while fetching contracts"); + } + return response.contracts as any[]; } -async function fetchPrices(marketid) { - let response = await axios({ +async function fetchPrices(marketid: string) { + const response = await axios({ url: `https://api.smarkets.com/v3/markets/${marketid}/last_executed_prices/`, method: "GET", - headers: { - "Content-Type": "text/html", - }, }).then((res) => res.data); - VERBOSE ? console.log(response) : empty(); - return response; + VERBOSE && console.log(response); + if (!response.last_executed_prices) { + throw new Error("Invalid response while fetching prices"); + } + return response.last_executed_prices; } export const smarkets: Platform = { @@ -70,77 +65,91 @@ export const smarkets: Platform = { let events = []; while (htmlPath) { - let data = await fetchEvents(htmlPath); + const data = await fetchEvents(htmlPath); events.push(...data.events); htmlPath = data.pagination.next_page; } - VERBOSE ? console.log(events) : empty(); + VERBOSE && console.log(events); + let markets = []; - for (let event of events) { - VERBOSE ? console.log(Date.now()) : empty(); - VERBOSE ? console.log(event.name) : empty(); + for (const event of events) { + VERBOSE && console.log(Date.now()); + VERBOSE && console.log(event.name); + let eventMarkets = await fetchMarkets(event.id); - eventMarkets = eventMarkets.map((market) => ({ + eventMarkets = eventMarkets.map((market: any) => ({ ...market, + // smarkets doesn't have separate urls for different markets in a single event + // we could use anchors (e.g. https://smarkets.com/event/886716/politics/uk/uk-party-leaders/next-conservative-leader#contract-collapse-9815728-control), but it's unclear if they aren't going to change slug: event.full_slug, })); - VERBOSE ? console.log("Markets fetched") : empty(); - VERBOSE ? console.log(event.id) : empty(); - VERBOSE ? console.log(eventMarkets) : empty(); + VERBOSE && console.log("Markets fetched"); + VERBOSE && console.log(event.id); + VERBOSE && console.log(eventMarkets); markets.push(...eventMarkets); - //let lastPrices = await fetchPrices(market.id) } - VERBOSE ? console.log(markets) : empty(); + VERBOSE && console.log(markets); let results = []; for (let market of markets) { - VERBOSE ? console.log("================") : empty(); - VERBOSE ? console.log("Market: ", market) : empty(); - let id = `${platformName}-${market.id}`; - let name = market.name; + VERBOSE && console.log("================"); + VERBOSE && console.log("Market: ", market); let contracts = await fetchContracts(market.id); - VERBOSE ? console.log("Contracts: ", contracts) : empty(); + VERBOSE && console.log("Contracts: ", contracts); let prices = await fetchPrices(market.id); - VERBOSE - ? console.log("Prices: ", prices["last_executed_prices"][market.id]) - : empty(); + VERBOSE && console.log("Prices: ", prices[market.id]); - let optionsObj = {}; - for (let contract of contracts["contracts"]) { - optionsObj[contract.id] = { name: contract.name }; - } - for (let price of prices["last_executed_prices"][market.id]) { + let optionsObj: { + [k: string]: QuestionOption; + } = {}; + + const contractIdToName = Object.fromEntries( + contracts.map((c) => [c.id as string, c.name as string]) + ); + + for (const price of prices[market.id]) { + const contractName = contractIdToName[price.contract_id]; + if (!contractName) { + console.warn( + `Couldn't find contract ${price.contract_id} in contracts data, skipping` + ); + continue; + } optionsObj[price.contract_id] = { - ...optionsObj[price.contract_id], + name: contractName, probability: price.last_executed_price ? Number(price.last_executed_price) - : null, + : undefined, type: "PROBABILITY", }; } - let options: any[] = Object.values(optionsObj); + let options: QuestionOption[] = Object.values(optionsObj); // monkey patch the case where there are only two options and only one has traded. if ( options.length == 2 && - options.map((option) => option.probability).includes(null) + options.map((option) => option.probability).includes(undefined) ) { - let nonNullPrice = + const nonNullPrice = options[0].probability == null ? options[1].probability : options[0].probability; - options = options.map((option) => { - let probability = option.probability; - return { - ...option, - probability: probability == null ? 100 - nonNullPrice : probability, - // yes, 100, because prices are not yet normalized. - }; - }); + + if (nonNullPrice != null) { + options = options.map((option) => { + let probability = option.probability; + return { + ...option, + probability: + probability == null ? 100 - nonNullPrice : probability, + // yes, 100, because prices are not yet normalized. + }; + }); + } } // Normalize normally - let totalValue = options + const totalValue = options .map((element) => Number(element.probability)) .reduce((a, b) => a + b, 0); @@ -148,30 +157,32 @@ export const smarkets: Platform = { ...element, probability: Number(element.probability) / totalValue, })); - VERBOSE ? console.log(options) : empty(); + VERBOSE && console.log(options); /* - if(contracts["contracts"].length == 2){ + if(contracts.length == 2){ isBinary = true - percentage = ( Number(prices["last_executed_prices"][market.id][0].last_executed_price) + (100 - Number(prices["last_executed_prices"][market.id][1].last_executed_price)) ) / 2 + percentage = ( Number(prices[market.id][0].last_executed_price) + (100 - Number(prices[market.id][1].last_executed_price)) ) / 2 percentage = Math.round(percentage)+"%" - let contractName = contracts["contracts"][0].name - name = name+ (contractName=="Yes"?'':` (${contracts["contracts"][0].name})`) + let contractName = contracts[0].name + name = name+ (contractName=="Yes"?'':` (${contracts[0].name})`) } */ - let result: FetchedQuestion = { - id: id, - title: name, + const id = `${platformName}-${market.id}`; + const title = market.name; + const result: FetchedQuestion = { + id, + title, url: "https://smarkets.com/event/" + market.event_id + market.slug, description: market.description, - options: options, + options, timestamp: new Date(), qualityindicators: {}, }; - VERBOSE ? console.log(result) : empty(); + VERBOSE && console.log(result); results.push(result); } - VERBOSE ? console.log(results) : empty(); + VERBOSE && console.log(results); return results; }, calculateStars(data) { diff --git a/src/backend/platforms/wildeford.ts b/src/backend/platforms/wildeford.ts index 5dee63d..532858d 100644 --- a/src/backend/platforms/wildeford.ts +++ b/src/backend/platforms/wildeford.ts @@ -13,7 +13,7 @@ const endpoint = `https://docs.google.com/spreadsheets/d/${SHEET_ID}/edit#gid=0` // https://docs.google.com/spreadsheets/d/1xcgYF7Q0D95TPHLLSgwhWBHFrWZUGJn7yTyAhDR4vi0/edit#gid=0 /* Support functions */ -const formatRow = (row) => { +const formatRow = (row: string[]) => { let colNames = [ "Prediction Date", "Prediction", @@ -23,15 +23,15 @@ const formatRow = (row) => { "Prediction Right?", "Brier Score", "Notes", - ]; - let result = {}; - row.forEach((col, i) => { + ] as const; + let result: Partial<{ [k in typeof colNames[number]]: string }> = {}; + row.forEach((col: string, i) => { result[colNames[i]] = col; }); - return result; + return result as Required; }; -async function fetchGoogleDoc(google_api_key) { +async function fetchGoogleDoc(google_api_key: string) { // https://gist.github.com/micalevisk/9bc831bd4b3e5a3f62b9810330129c59 let results = []; const doc = new GoogleSpreadsheet(SHEET_ID); @@ -41,7 +41,7 @@ async function fetchGoogleDoc(google_api_key) { console.log(">>", doc.title); const sheet = doc.sheetsByIndex[0]; - const rows = await sheet.getRows({ offset: 0 }); + const rows = await sheet.getRows(); console.log("# " + rows[0]._sheet.headerValues.join(",")); let isEnd = false; @@ -68,7 +68,9 @@ async function fetchGoogleDoc(google_api_key) { return results; } -async function processPredictions(predictions) { +async function processPredictions( + predictions: Awaited> +) { let currentPredictions = predictions.filter( (prediction) => prediction["Actual"] == "Unknown" ); @@ -101,8 +103,8 @@ async function processPredictions(predictions) { }); results = results.reverse(); - let uniqueTitles = []; - let uniqueResults = []; + let uniqueTitles: string[] = []; + let uniqueResults: FetchedQuestion[] = []; results.forEach((result) => { if (!uniqueTitles.includes(result.title)) uniqueResults.push(result); uniqueTitles.push(result.title); @@ -110,7 +112,7 @@ async function processPredictions(predictions) { return uniqueResults; } -export async function wildeford_inner(google_api_key) { +export async function wildeford_inner(google_api_key: string) { let predictions = await fetchGoogleDoc(google_api_key); return await processPredictions(predictions); } @@ -121,7 +123,7 @@ export const wildeford: Platform = { color: "#984158", async fetcher() { const GOOGLE_API_KEY = process.env.GOOGLE_API_KEY; // See: https://developers.google.com/sheets/api/guides/authorizing#APIKey - return await applyIfSecretExists(GOOGLE_API_KEY, wildeford_inner); + return (await applyIfSecretExists(GOOGLE_API_KEY, wildeford_inner)) || null; }, calculateStars(data) { let nuno = () => 3; diff --git a/src/backend/platforms/xrisk.ts b/src/backend/platforms/xrisk.ts index d5ab360..0b2e02b 100644 --- a/src/backend/platforms/xrisk.ts +++ b/src/backend/platforms/xrisk.ts @@ -14,8 +14,8 @@ export const xrisk: Platform = { let fileRaw = fs.readFileSync("./input/xrisk-questions.json", { encoding: "utf-8", }); - let results = JSON.parse(fileRaw); - results = results.map((item) => { + let parsedData = JSON.parse(fileRaw); + const results = parsedData.map((item: any) => { item.extra = item.moreoriginsdata; delete item.moreoriginsdata; return { diff --git a/src/backend/utils/algolia.ts b/src/backend/utils/algolia.ts index e50c2f6..40efeb7 100644 --- a/src/backend/utils/algolia.ts +++ b/src/backend/utils/algolia.ts @@ -44,7 +44,7 @@ export async function rebuildAlgoliaDatabase() { }) ); - if (index.exists()) { + if (await index.exists()) { console.log("Index exists"); await index.replaceAllObjects(records, { safe: true }); console.log( diff --git a/src/backend/utils/evaluations/pullForecastsToCSVForRating.ts b/src/backend/utils/evaluations/pullForecastsToCSVForRating.ts deleted file mode 100644 index c02315e..0000000 --- a/src/backend/utils/evaluations/pullForecastsToCSVForRating.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* Imports */ -import fs from "fs"; - -import { prisma } from "../../database/prisma"; - -/* Definitions */ - -/* Utilities */ - -/* Support functions */ -const getQualityIndicators = (question) => - Object.entries(question.qualityindicators) - .map((entry) => `${entry[0]}: ${entry[1]}`) - .join("; "); - -/* Body */ - -const main = async () => { - let highQualityPlatforms = [ - "CSET-foretell", - "Foretold", - "Good Judgment Open", - "Metaculus", - "PredictIt", - "Rootclaim", - ]; - const json = await prisma.question.findMany({}); - console.log(json.length); - //let uniquePlatforms = [...new Set(json.map(forecast => forecast.platform))] - //console.log(uniquePlatforms) - - const questionsFromGoodPlatforms = json.filter((question) => - highQualityPlatforms.includes(question.platform) - ); - const tsv = - "index\ttitle\turl\tqualityindicators\n" + - questionsFromGoodPlatforms - .map((question, index) => { - let row = `${index}\t${question.title}\t${ - question.url - }\t${getQualityIndicators(question)}`; - console.log(row); - return row; - }) - .join("\n"); - //console.log(tsv) - - // let string = JSON.stringify(json, null, 2) - fs.writeFileSync("metaforecasts.tsv", tsv); -}; -main(); diff --git a/src/backend/utils/evaluations/pullMetaculusForecastsToCSVForRating.ts b/src/backend/utils/evaluations/pullMetaculusForecastsToCSVForRating.ts deleted file mode 100644 index cbc048d..0000000 --- a/src/backend/utils/evaluations/pullMetaculusForecastsToCSVForRating.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* Imports */ -import fs from "fs"; - -import { shuffleArray } from "../../../utils"; -import { prisma } from "../../database/prisma"; - -/* Definitions */ - -/* Utilities */ - -/* Support functions */ -let getQualityIndicators = (question) => - Object.entries(question.qualityindicators) - .map((entry) => `${entry[0]}: ${entry[1]}`) - .join("; "); - -/* Body */ - -let main = async () => { - let highQualityPlatforms = ["Metaculus"]; // ['CSET-foretell', 'Foretold', 'Good Judgment Open', 'Metaculus', 'PredictIt', 'Rootclaim'] - let json = await prisma.question.findMany({}); - console.log(json.length); - //let uniquePlatforms = [...new Set(json.map(question => question.platform))] - //console.log(uniquePlatforms) - - let questionsFromGoodPlatforms = json.filter((question) => - highQualityPlatforms.includes(question.platform) - ); - let questionsFromGoodPlatformsShuffled = shuffleArray( - questionsFromGoodPlatforms - ); - let tsv = - "index\ttitle\turl\tqualityindicators\n" + - questionsFromGoodPlatforms - .map((question, index) => { - let row = `${index}\t${question.title}\t${ - question.url - }\t${getQualityIndicators(question)}`; - console.log(row); - return row; - }) - .join("\n"); - //console.log(tsv) - - // let string = JSON.stringify(json, null, 2) - fs.writeFileSync("metaforecasts_metaculus_v2.tsv", tsv); -}; -main(); diff --git a/src/backend/utils/misc/process-forecasts-into-elicit.ts b/src/backend/utils/misc/process-forecasts-into-elicit.ts index 8cf28b7..737d76e 100644 --- a/src/backend/utils/misc/process-forecasts-into-elicit.ts +++ b/src/backend/utils/misc/process-forecasts-into-elicit.ts @@ -11,7 +11,7 @@ let locationData = "./data/"; // let rawdata = fs.readFileSync("./data/merged-questions.json") // run from topmost folder, not from src async function main() { const data = await prisma.question.findMany({}); - const processDescription = (description) => { + const processDescription = (description: string | null | undefined) => { if (description == null || description == undefined || description == "") { return ""; } else { diff --git a/src/backend/utils/misc/process-forecasts-template.ts b/src/backend/utils/misc/process-forecasts-template.ts index 98323b4..04a8ea8 100644 --- a/src/backend/utils/misc/process-forecasts-template.ts +++ b/src/backend/utils/misc/process-forecasts-template.ts @@ -10,7 +10,7 @@ let rawdata = fs.readFileSync("../data/merged-questions.json", { }); let data = JSON.parse(rawdata); -let results = []; +let results: any[] = []; for (let datum of data) { // do something } diff --git a/src/backend/utils/sleep.ts b/src/backend/utils/sleep.ts new file mode 100644 index 0000000..0d7f188 --- /dev/null +++ b/src/backend/utils/sleep.ts @@ -0,0 +1,3 @@ +export function sleep(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} diff --git a/src/common/types.ts b/src/common/types.ts new file mode 100644 index 0000000..9756fb2 --- /dev/null +++ b/src/common/types.ts @@ -0,0 +1,22 @@ +import { QuestionFragment } from "../web/fragments.generated"; + +// this type is good both for backend (e.g. FetchedQuestion["options"]) and for graphql shapes +export type QuestionOption = { + name?: string; + probability?: number; + type: "PROBABILITY"; +}; + +export type FullQuestionOption = Exclude< + QuestionOption, + "name" | "probability" +> & { + name: NonNullable; + probability: NonNullable; +}; + +export const isFullQuestionOption = ( + option: QuestionOption | QuestionFragment["options"][0] +): option is FullQuestionOption => { + return option.name != null && option.probability != null; +}; diff --git a/src/pages/api/squiggle.ts b/src/pages/api/squiggle.ts index 76539a5..549e3fa 100644 --- a/src/pages/api/squiggle.ts +++ b/src/pages/api/squiggle.ts @@ -1,5 +1,6 @@ import { NextApiRequest, NextApiResponse } from "next/types"; -import { runMePlease } from "squiggle-experimental/dist/index.js"; + +import { run } from "@quri/squiggle-lang"; export default async function handler( req: NextApiRequest, @@ -24,6 +25,6 @@ $ curl -X POST -H "Content-Type: application/json" -d '{"model": "1 to 4"}' }); } else { console.log(body.model); - res.status(200).send(runMePlease(body.model)); + res.status(200).send(run(body.model)); } } diff --git a/src/web/questions/components/HistoryChart/utils.ts b/src/web/questions/components/HistoryChart/utils.ts index 3072809..5b1e920 100644 --- a/src/web/questions/components/HistoryChart/utils.ts +++ b/src/web/questions/components/HistoryChart/utils.ts @@ -1,8 +1,8 @@ import { addDays, startOfDay, startOfToday, startOfTomorrow } from "date-fns"; +import { isFullQuestionOption } from "../../../../common/types"; import { QuestionWithHistoryFragment } from "../../../fragments.generated"; import { isQuestionBinary } from "../../../utils"; -import { isFullQuestionOption } from "../../utils"; export type ChartSeries = { x: Date; y: number; name: string }[]; diff --git a/src/web/questions/components/QuestionOptions.tsx b/src/web/questions/components/QuestionOptions.tsx index c227f10..b6e77cb 100644 --- a/src/web/questions/components/QuestionOptions.tsx +++ b/src/web/questions/components/QuestionOptions.tsx @@ -1,6 +1,7 @@ +import { FullQuestionOption, isFullQuestionOption } from "../../../common/types"; import { QuestionFragment } from "../../fragments.generated"; import { isQuestionBinary } from "../../utils"; -import { formatProbability, FullQuestionOption, isFullQuestionOption } from "../utils"; +import { formatProbability } from "../utils"; const textColor = (probability: number) => { if (probability < 0.03) { diff --git a/src/web/questions/utils.ts b/src/web/questions/utils.ts index 36fa46b..f7f4f1c 100644 --- a/src/web/questions/utils.ts +++ b/src/web/questions/utils.ts @@ -8,20 +8,3 @@ export const formatProbability = (probability: number) => { : percentage.toFixed(0) + "%"; return percentageCapped; }; - -import { QuestionFragment } from "../fragments.generated"; - -export type QuestionOption = QuestionFragment["options"][0]; -export type FullQuestionOption = Exclude< - QuestionOption, - "name" | "probability" -> & { - name: NonNullable; - probability: NonNullable; -}; - -export const isFullQuestionOption = ( - option: QuestionOption -): option is FullQuestionOption => { - return option.name != null && option.probability != null; -}; diff --git a/tsconfig.json b/tsconfig.json index 246f41d..a38329d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,7 +12,7 @@ "dom.iterable", "esnext" ], - "strict": false, + "strict": true, "noEmit": true, "incremental": true, "moduleResolution": "node",