From c80e2ea2ef7f7e5186b71c754d10e07d5fd4d893 Mon Sep 17 00:00:00 2001 From: zk Date: Thu, 19 Mar 2026 20:20:11 +0000 Subject: [PATCH] feat: start actionising on the west coast --- Justfile-apps | 2 +- Justfile-db | 13 +- Justfile-gen | 6 +- bun.lock | 657 +- db/migrations/20260301154256_genesis.sql | 21 +- dev/bootstrap.sh | 6 - package.json | 13 +- tsconfig.json | 3 +- typescript-vendor/tap/.gitattributes | 11 - typescript-vendor/tap/.gitignore | 27 - typescript-vendor/tap/CHANGELOG.md | 2 - typescript-vendor/tap/CONTRIBUTING.md | 13 - typescript-vendor/tap/LICENSE | 15 - typescript-vendor/tap/README.md | 158 - typescript-vendor/tap/bin/jack.js | 837 --- typescript-vendor/tap/bin/jsx.js | 5 - typescript-vendor/tap/bin/run.js | 875 --- typescript-vendor/tap/coverage-map.js | 22 - typescript-vendor/tap/example/lib/math.js | 1 - .../tap/example/long-slow-many.js | 37 - .../tap/example/mocha-example.js | 89 - typescript-vendor/tap/lib/cb-promise.js | 9 - typescript-vendor/tap/lib/mocha.js | 162 - typescript-vendor/tap/lib/repl.js | 242 - typescript-vendor/tap/lib/synonyms.js | 80 - typescript-vendor/tap/lib/tap.js | 36 - typescript-vendor/tap/lib/tap.mjs | 31 - typescript-vendor/tap/lib/watch.js | 180 - typescript-vendor/tap/netlify.toml | 4 - typescript-vendor/tap/package-lock.json | 6245 ----------------- typescript-vendor/tap/package.json | 122 - typescript-vendor/tap/postpublish.sh | 7 - typescript-vendor/tap/scripts/snap.sh | 51 - typescript-vendor/tap/scripts/unit.sh | 20 - typescript-vendor/tap/settings.js | 62 - .../tap/tap-snapshots/test/repl.js.test.cjs | 187 - .../tap-snapshots/test/run/basic.js.test.cjs | 85 - .../test/run/before-after.js.test.cjs | 211 - .../tap-snapshots/test/run/cat.js.test.cjs | 23 - .../test/run/coverage.js.test.cjs | 217 - .../test/run/dump-config.js.test.cjs | 435 -- .../tap-snapshots/test/run/env.js.test.cjs | 21 - .../test/run/executables.js.test.cjs | 18 - .../tap-snapshots/test/run/files.js.test.cjs | 38 - .../tap-snapshots/test/run/flow.js.test.cjs | 32 - .../test/run/invalid-option.js.test.cjs | 14 - .../tap-snapshots/test/run/jsx.js.test.cjs | 19 - .../test/run/libtap-settings.js.test.cjs | 34 - .../test/run/nocolor-env.js.test.cjs | 19 - .../test/run/nonparallel.js.test.cjs | 31 - .../test/run/output-file.js.test.cjs | 176 - .../test/run/parallel.js.test.cjs | 79 - .../test/run/reporters.js.test.cjs | 31 - .../test/run/save-file.js.test.cjs | 197 - .../tap-snapshots/test/run/stdin.js.test.cjs | 28 - .../test/run/test-regex.js.test.cjs | 25 - .../tap/tap-snapshots/test/run/ts.js.test.cjs | 164 - .../test/run/watermarks.js.test.cjs | 114 - .../test/settings/default.js.test.cjs | 32 - .../test/settings/long-stack.js.test.cjs | 26 - .../tap-snapshots/test/synonyms.js.test.cjs | 270 - .../tap/tap-snapshots/test/test.mjs.test.cjs | 64 - .../tap/tap-snapshots/test/watch.js.test.cjs | 67 - typescript-vendor/tap/test/cb-promise.js | 13 - typescript-vendor/tap/test/clean-stacks.js | 76 - typescript-vendor/tap/test/coverage-map.js | 10 - typescript-vendor/tap/test/mocha.js | 132 - .../tap/test/regression-many-asserts-epipe.js | 9 - .../tap/test/regression-pipe-backup.js | 10 - typescript-vendor/tap/test/repl.js | 242 - typescript-vendor/tap/test/run/bad-rcfile.js | 16 - typescript-vendor/tap/test/run/basic.js | 143 - .../tap/test/run/before-after.js | 84 - typescript-vendor/tap/test/run/cat.js | 19 - typescript-vendor/tap/test/run/changed.js | 145 - typescript-vendor/tap/test/run/comments.js | 24 - typescript-vendor/tap/test/run/coverage.js | 151 - typescript-vendor/tap/test/run/dump-config.js | 115 - typescript-vendor/tap/test/run/env.js | 21 - .../tap/test/run/epipe-stdout.js | 22 - typescript-vendor/tap/test/run/executables.js | 30 - typescript-vendor/tap/test/run/files.js | 41 - typescript-vendor/tap/test/run/flow.js | 45 - typescript-vendor/tap/test/run/index.js | 117 - .../tap/test/run/invalid-option.js | 10 - typescript-vendor/tap/test/run/jsx.js | 31 - .../tap/test/run/libtap-settings.js | 132 - typescript-vendor/tap/test/run/nocolor-env.js | 25 - typescript-vendor/tap/test/run/nonparallel.js | 29 - typescript-vendor/tap/test/run/output-file.js | 91 - typescript-vendor/tap/test/run/parallel.js | 77 - .../tap/test/run/rcfile-extensions.js | 27 - typescript-vendor/tap/test/run/reporters.js | 70 - typescript-vendor/tap/test/run/save-file.js | 89 - .../tap/test/run/setup-tap-env.js | 24 - typescript-vendor/tap/test/run/stdin.js | 55 - typescript-vendor/tap/test/run/test-regex.js | 36 - typescript-vendor/tap/test/run/ts.js | 131 - typescript-vendor/tap/test/run/watermarks.js | 128 - .../tap/test/settings/clean-for-snapshot.js | 20 - .../tap/test/settings/default.js | 10 - .../tap/test/settings/long-stack.js | 12 - .../tap/test/settings/overrides.js | 51 - typescript-vendor/tap/test/synonyms.js | 5 - typescript-vendor/tap/test/tap.js | 57 - typescript-vendor/tap/test/test-esm.js | 22 - typescript-vendor/tap/test/test.mjs | 25 - typescript-vendor/tap/test/versions.js | 15 - typescript-vendor/tap/test/watch.js | 249 - typescript/api-parity/package.json | 2 +- typescript/api-parity/tests/activity.test.ts | 2 +- typescript/api-parity/tests/config.test.ts | 2 +- typescript/api-parity/tests/games.test.ts | 2 +- typescript/api-parity/tests/imports.test.ts | 2 +- typescript/api-parity/tests/scores.test.ts | 2 +- typescript/api-parity/tests/search.test.ts | 2 +- typescript/api-parity/tests/sessions.test.ts | 2 +- typescript/api-parity/tests/status.test.ts | 2 +- typescript/api-parity/tests/users.test.ts | 2 +- typescript/bliss/eslint.config.mjs | 9 + typescript/bliss/package.json | 2 +- typescript/bliss/src/actions.ts | 8 +- typescript/bliss/src/index.ts | 2 +- typescript/bot/src/config.ts | 127 +- typescript/bot/src/main.ts | 8 +- typescript/bot/src/server/middleware.ts | 4 +- typescript/bot/src/server/server.ts | 8 +- typescript/bot/src/slash-commands/commands.ts | 4 +- .../bot/src/slash-commands/commands/faq.ts | 6 +- .../src/slash-commands/commands/letmein.ts | 6 +- .../bot/src/slash-commands/commands/sync.ts | 4 +- typescript/bot/src/slash-commands/register.ts | 9 +- typescript/bot/src/test-utils/db.ts | 2 +- typescript/bot/src/utils/api-requests.ts | 4 +- typescript/bot/src/utils/embeds.ts | 6 +- typescript/bot/src/utils/fetch-tachi.ts | 4 +- typescript/bot/src/utils/misc.ts | 10 +- .../bot/src/webhook-handlers/class-update.ts | 6 +- .../bot/src/webhook-handlers/goal-achieved.ts | 4 +- .../src/webhook-handlers/quest-achieved.ts | 4 +- typescript/client/vite.config.ts | 9 +- typescript/common/package.json | 4 +- typescript/common/src/index.ts | 1 + typescript/common/src/lib/zod-schemas.ts | 4 + typescript/db-cli/src/index.ts | 10 +- typescript/db/src/generated/index.ts | 1 - typescript/db/src/generated/public/Account.ts | 10 +- .../db/src/generated/public/PrivApiClient.ts | 6 + .../db/src/generated/public/PublicSchema.ts | 3 - typescript/eslint-config/index.js | 3 + typescript/github-bot/.gitignore | 3 +- typescript/github-bot/tsconfig.tsbuildinfo | 1 - typescript/server/.env | 9 + typescript/server/.gitignore | 1 - typescript/server/README.md | 6 - typescript/server/buf.gen.yaml | 10 + typescript/server/example/.env | 6 - typescript/server/example/conf.json5 | 98 - typescript/server/nodemon.json | 6 - typescript/server/package.json | 15 +- .../src/actions/install-builtin-client.ts | 67 + .../{arcaea.test.ts => arcaea.oldtest.ts} | 6 +- .../{bms-pms.test.ts => bms-pms.oldtest.ts} | 8 +- .../{chunithm.test.ts => chunithm.oldtest.ts} | 10 +- .../games/{ddr.test.ts => ddr.oldtest.ts} | 0 .../src/game-implementations/games/ddr.ts | 4 +- .../{gitadora.test.ts => gitadora.oldtest.ts} | 6 +- .../games/{iidx.test.ts => iidx.oldtest.ts} | 6 +- .../{jubeat.test.ts => jubeat.oldtest.ts} | 10 +- .../src/game-implementations/games/jubeat.ts | 8 +- .../{maimai.test.ts => maimai.oldtest.ts} | 6 +- .../{maimaidx.test.ts => maimaidx.oldtest.ts} | 6 +- .../{museca.test.ts => museca.oldtest.ts} | 6 +- .../{ongeki.test.ts => ongeki.oldtest.ts} | 26 +- .../games/{popn.test.ts => popn.oldtest.ts} | 6 +- .../games/{sdvx.test.ts => sdvx.oldtest.ts} | 10 +- .../games/{usc.test.ts => usc.oldtest.ts} | 6 +- .../games/{wacca.test.ts => wacca.oldtest.ts} | 6 +- .../game-implementations/utils/pb-merge.ts | 4 +- .../utils/profile-calc.ts | 6 +- typescript/server/src/lib/actions/actions.ts | 30 +- .../server/src/lib/activity/activity.ts | 8 +- .../lib/builtin-clients/builtin-clients.ts | 90 +- .../lib/cdn/{cdn.test.ts => cdn.oldtest.ts} | 0 .../server/src/lib/db-formats/api-client.ts | 64 + .../src/lib/db-formats/user-settings.ts | 29 + typescript/server/src/lib/db-formats/user.ts | 67 + ...est.ts => create-game-settings.oldtest.ts} | 4 +- .../lib/game-settings/create-game-settings.ts | 6 +- .../src/lib/game-specific/iidx-playlists.ts | 4 +- .../{imports.test.ts => imports.oldtest.ts} | 14 +- typescript/server/src/lib/imports/imports.ts | 6 +- .../{invites.test.ts => invites.oldtest.ts} | 0 .../src/lib/jobs/backsync-bms-pms-data.ts | 6 +- .../server/src/lib/jobs/bms-table-sync.ts | 17 +- .../server/src/lib/jobs/ugs-snapshot.ts | 20 +- .../server/src/lib/jobs/update-bpi-data.ts | 4 +- .../server/src/lib/jobs/update-dp-tiers.ts | 6 +- .../server/src/lib/jobs/update-sp12-data.ts | 6 +- .../notifications/notification-wrappers.ts | 8 +- ...tions.test.ts => notifications.oldtest.ts} | 8 +- .../src/lib/notifications/notifications.ts | 6 +- .../src/lib/orphan-queue/orphan-queue.ts | 22 +- typescript/server/src/lib/rivals/rivals.ts | 20 +- ...erts.test.ts => string-asserts.oldtest.ts} | 0 .../goals/{goals.test.ts => goals.oldtest.ts} | 41 +- .../lib/score-import/framework/goals/goals.ts | 8 +- .../framework/import-locks/lock.ts | 12 +- .../{orphans.test.ts => orphans.oldtest.ts} | 30 +- .../score-import/framework/orphans/orphans.ts | 18 +- ...b-doc.test.ts => create-pb-doc.oldtest.ts} | 20 +- .../framework/pb/create-pb-doc.ts | 8 +- ...ess-pbs.test.ts => process-pbs.oldtest.ts} | 14 +- .../score-import/framework/pb/process-pbs.ts | 4 +- .../{classes.test.ts => classes.oldtest.ts} | 0 .../profile-calculated-data/classes.ts | 4 +- .../{quests.test.ts => quests.oldtest.ts} | 0 .../score-import/framework/quests/quests.ts | 8 +- ...import.test.ts => score-import.oldtest.ts} | 8 +- ...score.test.ts => hydrate-score.oldtest.ts} | 0 ...-score.test.ts => insert-score.oldtest.ts} | 10 +- .../framework/score-importing/insert-score.ts | 4 +- .../{score-id.test.ts => score-id.oldtest.ts} | 0 .../framework/score-importing/score-id.ts | 4 +- ....test.ts => score-import-error.oldtest.ts} | 0 .../score-importing/score-import-main.ts | 8 +- ...ing.test.ts => score-importing.oldtest.ts} | 0 .../score-importing/score-importing.ts | 8 +- ...core.test.ts => validate-score.oldtest.ts} | 0 ...ion.test.ts => name-generation.oldtest.ts} | 0 .../{sessions.test.ts => sessions.oldtest.ts} | 78 +- .../framework/sessions/sessions.ts | 10 +- .../status-tracking/import-status-tracking.ts | 8 +- ...s.test.ts => update-ugpt-stats.oldtest.ts} | 32 +- .../framework/ugpt-stats/update-ugpt-stats.ts | 10 +- ...converter.test.ts => converter.oldtest.ts} | 0 .../api/myt-chunithm/converter.ts | 38 +- .../import-types/api/myt-chunithm/parser.ts | 63 +- .../import-types/api/myt-chunithm/types.ts | 2 +- ...converter.test.ts => converter.oldtest.ts} | 0 .../api/myt-maimaidx/converter.ts | 26 +- .../import-types/api/myt-maimaidx/parser.ts | 63 +- .../import-types/api/myt-maimaidx/types.ts | 2 +- ...converter.test.ts => converter.oldtest.ts} | 0 .../import-types/api/myt-ongeki/converter.ts | 26 +- .../import-types/api/myt-ongeki/parser.ts | 63 +- .../import-types/api/myt-ongeki/types.ts | 2 +- .../api/myt-wacca/class-handler.ts | 40 +- ...converter.test.ts => converter.oldtest.ts} | 0 .../import-types/api/myt-wacca/converter.ts | 23 +- .../import-types/api/myt-wacca/parser.ts | 82 +- .../import-types/api/myt-wacca/types.ts | 2 +- ...converter.test.ts => converter.oldtest.ts} | 0 ...converter.test.ts => converter.oldtest.ts} | 0 .../import-types/common/api-cg/parsers.ts | 4 +- ...converter.test.ts => converter.oldtest.ts} | 0 ...converter.test.ts => converter.oldtest.ts} | 0 ...ndler.test.ts => class-handler.oldtest.ts} | 0 ...converter.test.ts => converter.oldtest.ts} | 4 +- .../{parser.test.ts => parser.oldtest.ts} | 0 .../{reauth.test.ts => reauth.oldtest.ts} | 16 +- .../import-types/common/api-kai/reauth.ts | 4 +- ...ndler.test.ts => class-handler.oldtest.ts} | 0 ...converter.test.ts => converter.oldtest.ts} | 4 +- .../{parser.test.ts => parser.oldtest.ts} | 0 ...se-api.test.ts => traverse-api.oldtest.ts} | 0 .../common/api-myt/traverse-api.ts | 160 +- ...converter.test.ts => converter.oldtest.ts} | 4 +- .../common/batch-manual/converter.ts | 30 +- .../{parser.test.ts => parser.oldtest.ts} | 0 ...converter.test.ts => converter.oldtest.ts} | 0 .../{parser.test.ts => parser.oldtest.ts} | 0 ...converter.test.ts => converter.oldtest.ts} | 0 .../{parser.test.ts => parser.oldtest.ts} | 0 ...converter.test.ts => converter.oldtest.ts} | 0 .../{parser.test.ts => parser.oldtest.ts} | 0 ...converter.test.ts => converter.oldtest.ts} | 0 .../{parser.test.ts => parser.oldtest.ts} | 0 ...converter.test.ts => converter.oldtest.ts} | 6 +- .../{parser.test.ts => parser.oldtest.ts} | 0 ...ndler.test.ts => class-handler.oldtest.ts} | 0 ...converter.test.ts => converter.oldtest.ts} | 0 .../{parser.test.ts => parser.oldtest.ts} | 0 ...converter.test.ts => converter.oldtest.ts} | 10 +- .../import-types/ir/fervidex/converter.ts | 4 +- .../{parser.test.ts => parser.oldtest.ts} | 0 ...converter.test.ts => converter.oldtest.ts} | 0 .../{parser.test.ts => parser.oldtest.ts} | 0 ...converter.test.ts => converter.oldtest.ts} | 0 .../{parser.test.ts => parser.oldtest.ts} | 0 ...converter.test.ts => converter.oldtest.ts} | 0 .../{parser.test.ts => parser.oldtest.ts} | 0 ...converter.test.ts => converter.oldtest.ts} | 4 +- .../import-types/ir/usc/converter.ts | 4 +- .../usc/{parser.test.ts => parser.oldtest.ts} | 0 ...cores.test.ts => delete-scores.oldtest.ts} | 42 +- .../src/lib/score-mutation/delete-scores.ts | 48 +- .../lib/score-mutation/update-isprimary.ts | 8 +- ...-score.test.ts => update-score.oldtest.ts} | 14 +- .../src/lib/score-mutation/update-score.ts | 20 +- .../{search.test.ts => search.oldtest.ts} | 6 +- typescript/server/src/lib/search/search.ts | 88 +- ...evaluator.test.ts => evaluator.oldtest.ts} | 5 +- .../server/src/lib/showcase/evaluator.ts | 8 +- .../server/src/lib/showcase/get-related.ts | 8 +- ...get-stats.test.ts => get-stats.oldtest.ts} | 14 +- .../server/src/lib/showcase/get-stats.ts | 4 +- .../{showcase.test.ts => showcase.oldtest.ts} | 8 +- .../server/src/lib/showcase/showcase.ts | 4 +- .../server/src/lib/targets/goal-utils.ts | 10 +- .../{goals.test.ts => goals.oldtest.ts} | 56 +- typescript/server/src/lib/targets/goals.ts | 101 +- typescript/server/src/lib/targets/quests.ts | 30 +- .../server/src/lib/webhooks/webhooks.ts | 4 +- typescript/server/src/main.ts | 22 +- .../proto/generated/cards/cards_connect.ts | 25 + .../proto/generated/cards/cards_grpc_pb.d.ts | 36 - .../proto/generated/cards/cards_grpc_pb.js | 43 - .../src/proto/generated/cards/cards_pb.d.ts | 89 - .../src/proto/generated/cards/cards_pb.js | 620 -- .../src/proto/generated/cards/cards_pb.ts | 101 + .../generated/chunithm/common_grpc_pb.d.ts | 1 - .../generated/chunithm/common_grpc_pb.js | 1 - .../proto/generated/chunithm/common_pb.d.ts | 67 - .../src/proto/generated/chunithm/common_pb.js | 105 - .../src/proto/generated/chunithm/common_pb.ts | 269 + .../generated/chunithm/playlog_grpc_pb.d.ts | 1 - .../generated/chunithm/playlog_grpc_pb.js | 1 - .../proto/generated/chunithm/playlog_pb.d.ts | 117 - .../proto/generated/chunithm/playlog_pb.js | 683 -- .../proto/generated/chunithm/playlog_pb.ts | 137 + .../proto/generated/chunithm/user_connect.ts | 25 + .../generated/chunithm/user_grpc_pb.d.ts | 36 - .../proto/generated/chunithm/user_grpc_pb.js | 44 - .../src/proto/generated/chunithm/user_pb.d.ts | 78 - .../src/proto/generated/chunithm/user_pb.js | 497 -- .../src/proto/generated/chunithm/user_pb.ts | 89 + .../generated/maimai/common_grpc_pb.d.ts | 1 - .../proto/generated/maimai/common_grpc_pb.js | 1 - .../src/proto/generated/maimai/common_pb.d.ts | 66 - .../src/proto/generated/maimai/common_pb.js | 104 - .../src/proto/generated/maimai/common_pb.ts | 260 + .../generated/maimai/playlog_grpc_pb.d.ts | 1 - .../proto/generated/maimai/playlog_grpc_pb.js | 1 - .../proto/generated/maimai/playlog_pb.d.ts | 129 - .../src/proto/generated/maimai/playlog_pb.js | 777 -- .../src/proto/generated/maimai/playlog_pb.ts | 150 + .../proto/generated/maimai/user_connect.ts | 25 + .../proto/generated/maimai/user_grpc_pb.d.ts | 36 - .../proto/generated/maimai/user_grpc_pb.js | 44 - .../src/proto/generated/maimai/user_pb.d.ts | 78 - .../src/proto/generated/maimai/user_pb.js | 497 -- .../src/proto/generated/maimai/user_pb.ts | 89 + .../generated/ongeki/common_grpc_pb.d.ts | 1 - .../proto/generated/ongeki/common_grpc_pb.js | 1 - .../src/proto/generated/ongeki/common_pb.d.ts | 84 - .../src/proto/generated/ongeki/common_pb.js | 126 - .../src/proto/generated/ongeki/common_pb.ts | 326 + .../generated/ongeki/playlog_grpc_pb.d.ts | 1 - .../proto/generated/ongeki/playlog_grpc_pb.js | 1 - .../proto/generated/ongeki/playlog_pb.d.ts | 309 - .../src/proto/generated/ongeki/playlog_pb.js | 1833 ----- .../src/proto/generated/ongeki/playlog_pb.ts | 312 + .../proto/generated/ongeki/user_connect.ts | 25 + .../proto/generated/ongeki/user_grpc_pb.d.ts | 36 - .../proto/generated/ongeki/user_grpc_pb.js | 44 - .../src/proto/generated/ongeki/user_pb.d.ts | 78 - .../src/proto/generated/ongeki/user_pb.js | 497 -- .../src/proto/generated/ongeki/user_pb.ts | 89 + .../proto/generated/wacca/common_grpc_pb.d.ts | 1 - .../proto/generated/wacca/common_grpc_pb.js | 1 - .../src/proto/generated/wacca/common_pb.d.ts | 125 - .../src/proto/generated/wacca/common_pb.js | 544 -- .../src/proto/generated/wacca/common_pb.ts | 285 + .../src/proto/generated/wacca/user_connect.ts | 36 + .../proto/generated/wacca/user_grpc_pb.d.ts | 53 - .../src/proto/generated/wacca/user_grpc_pb.js | 78 - .../src/proto/generated/wacca/user_pb.d.ts | 306 - .../src/proto/generated/wacca/user_pb.js | 2051 ------ .../src/proto/generated/wacca/user_pb.ts | 326 + .../src/scripts/invites/create-invite-code.ts | 4 +- .../server/src/scripts/load-seeds-mongo.ts | 4 +- .../server/src/scripts/make-user-admin.ts | 4 +- .../server/src/scripts/migrate-to-postgres.ts | 20 +- .../scripts/state-sync/dedupe-score-ids.ts | 6 +- .../state-sync/recalc-game-profiles.ts | 6 +- .../scripts/state-sync/rederive-all-scores.ts | 4 +- .../{auth.test.ts => auth.oldtest.ts} | 12 +- .../server/src/server/middleware/auth.ts | 6 +- ...e.test.ts => prudence-validate.oldtest.ts} | 0 ...ng.test.ts => sanitise-qstring.oldtest.ts} | 0 .../src/server/middleware/update-last-seen.ts | 4 +- .../admin/router.oldtest.ts} | 14 +- .../server/router/api/v1/admin/router.test.ts | 132 - .../src/server/router/api/v1/admin/router.ts | 30 +- .../auth.test.ts => v1/auth/auth.oldtest.ts} | 8 +- .../server/router/api/v1/auth/auth.test.ts | 448 +- .../src/server/router/api/v1/auth/auth.ts | 142 +- .../auth/router.oldtest.ts} | 22 +- .../server/router/api/v1/auth/router.test.ts | 378 - .../src/server/router/api/v1/auth/router.ts | 222 +- ...ddleware.test.ts => middleware.oldtest.ts} | 0 .../router/api/v1/clients/middleware.ts | 4 +- .../clients/router.oldtest.ts} | 36 +- .../router/api/v1/clients/router.test.ts | 468 -- .../server/router/api/v1/clients/router.ts | 16 +- .../{router.test.ts => router.oldtest.ts} | 0 .../bms/router.oldtest.ts} | 6 +- .../@gameSpecificRoutes/bms/router.test.ts | 88 - .../games/@gameSpecificRoutes/bms/router.ts | 6 +- .../_playtype/charts/_chartID/middleware.ts | 4 +- .../charts/_chartID/router.oldtest.ts} | 8 +- .../_playtype/charts/_chartID/router.test.ts | 98 - .../_game/_playtype/charts/_chartID/router.ts | 39 +- .../{router.test.ts => router.oldtest.ts} | 10 +- .../v1/games/_game/_playtype/charts/router.ts | 8 +- .../_game/_playtype/folders/middleware.ts | 8 +- .../_playtype/folders/router.oldtest.ts} | 6 +- .../_game/_playtype/folders/router.test.ts | 80 - .../games/_game/_playtype/folders/router.ts | 4 +- .../{router.test.ts => router.oldtest.ts} | 10 +- .../api/v1/games/_game/_playtype/router.ts | 14 +- .../_playtype/songs/_songID/middleware.ts | 4 +- .../{router.test.ts => router.oldtest.ts} | 0 .../_game/_playtype/songs/_songID/router.ts | 4 +- .../_game/_playtype/tables/middleware.ts | 8 +- .../_game/_playtype/tables/router.oldtest.ts} | 4 +- .../_game/_playtype/tables/router.test.ts | 51 - .../v1/games/_game/_playtype/tables/router.ts | 4 +- .../targets/goals/router.oldtest.ts} | 10 +- .../_playtype/targets/goals/router.test.ts | 94 - .../_game/_playtype/targets/goals/router.ts | 6 +- .../{router.test.ts => router.oldtest.ts} | 8 +- .../_playtype/targets/questlines/router.ts | 6 +- .../targets/quests/router.oldtest.ts} | 16 +- .../_playtype/targets/quests/router.test.ts | 124 - .../_game/_playtype/targets/quests/router.ts | 10 +- .../_playtype/targets/router.oldtest.ts} | 10 +- .../_game/_playtype/targets/router.test.ts | 102 - .../{router.test.ts => router.oldtest.ts} | 0 .../{router.test.ts => router.oldtest.ts} | 0 .../import/router.oldtest.ts} | 24 +- .../router/api/v1/import/router.test.ts | 393 -- .../router/api/v1/imports/middleware.ts | 4 +- .../{router.test.ts => router.oldtest.ts} | 18 +- .../server/router/api/v1/imports/router.ts | 14 +- .../oauth/router.oldtest.ts} | 6 +- .../server/router/api/v1/oauth/router.test.ts | 99 - .../src/server/router/api/v1/oauth/router.ts | 12 +- .../v1/{router.test.ts => router.oldtest.ts} | 0 .../api/v1/scores/_scoreID/middleware.ts | 4 +- .../scores/_scoreID/router.oldtest.ts} | 26 +- .../api/v1/scores/_scoreID/router.test.ts | 292 - .../router/api/v1/scores/_scoreID/router.ts | 10 +- .../{router.test.ts => router.oldtest.ts} | 10 +- .../api/v1/sessions/_sessionID/middleware.ts | 4 +- .../sessions/_sessionID/router.oldtest.ts} | 12 +- .../api/v1/sessions/_sessionID/router.test.ts | 204 - .../api/v1/sessions/_sessionID/router.ts | 16 +- .../{router.test.ts => router.oldtest.ts} | 0 .../_userID/api-tokens/router.oldtest.ts} | 18 +- .../users/_userID/api-tokens/router.test.ts | 289 - .../api/v1/users/_userID/api-tokens/router.ts | 14 +- .../users/_userID/banner/router.oldtest.ts} | 6 +- .../v1/users/_userID/banner/router.test.ts | 88 - .../api/v1/users/_userID/banner/router.ts | 9 +- .../api/v1/users/_userID/following/router.ts | 12 +- .../games/@gameSpecificRoutes/bms/router.ts | 6 +- .../games/@gameSpecificRoutes/iidx/router.ts | 6 +- .../{router.test.ts => router.oldtest.ts} | 22 +- .../_playtype/folders/_folderID/router.ts | 6 +- .../games/_game/_playtype/folders/router.ts | 4 +- .../games/_game/_playtype/middleware.ts | 4 +- .../_game/_playtype/pbs/router.oldtest.ts} | 14 +- .../games/_game/_playtype/pbs/router.test.ts | 297 - .../games/_game/_playtype/pbs/router.ts | 20 +- .../_game/_playtype/rivals/router.oldtest.ts} | 24 +- .../_game/_playtype/rivals/router.test.ts | 190 - .../games/_game/_playtype/rivals/router.ts | 4 +- .../{router.test.ts => router.oldtest.ts} | 34 +- .../_userID/games/_game/_playtype/router.ts | 30 +- .../_game/_playtype/scores/router.oldtest.ts} | 14 +- .../_game/_playtype/scores/router.test.ts | 239 - .../games/_game/_playtype/scores/router.ts | 12 +- .../_playtype/sessions/router.oldtest.ts} | 18 +- .../_game/_playtype/sessions/router.test.ts | 244 - .../games/_game/_playtype/sessions/router.ts | 12 +- .../{router.test.ts => router.oldtest.ts} | 34 +- .../games/_game/_playtype/settings/router.ts | 12 +- .../{router.test.ts => router.oldtest.ts} | 32 +- .../games/_game/_playtype/showcase/router.ts | 16 +- .../_game/_playtype/tables/router.oldtest.ts} | 6 +- .../_game/_playtype/tables/router.test.ts | 47 - .../{router.test.ts => router.oldtest.ts} | 52 +- .../_game/_playtype/targets/goals/router.ts | 12 +- .../targets/quests/router.oldtest.ts} | 42 +- .../_playtype/targets/quests/router.test.ts | 248 - .../_game/_playtype/targets/quests/router.ts | 16 +- .../_playtype/targets/router.oldtest.ts} | 18 +- .../_game/_playtype/targets/router.test.ts | 163 - .../games/_game/_playtype/targets/router.ts | 22 +- .../{router.test.ts => router.oldtest.ts} | 0 .../api/v1/users/_userID/imports/router.ts | 6 +- .../_userID/integrations/cg/_cgType/router.ts | 8 +- .../integrations/fervidex/router.oldtest.ts} | 19 +- .../integrations/fervidex/router.test.ts | 135 - .../_userID/integrations/fervidex/router.ts | 8 +- ...ddleware.test.ts => middleware.oldtest.ts} | 0 .../kai/_kaiType/router.oldtest.ts} | 6 +- .../integrations/kai/_kaiType/router.test.ts | 65 - .../integrations/kai/_kaiType/router.ts | 4 +- .../kshook-sv6c/router.oldtest.ts} | 12 +- .../integrations/kshook-sv6c/router.test.ts | 111 - .../integrations/kshook-sv6c/router.ts | 8 +- .../users/_userID/integrations/myt/router.ts | 8 +- .../{router.test.ts => router.oldtest.ts} | 20 +- .../api/v1/users/_userID/invites/router.ts | 18 +- .../_userID/notifications/router.oldtest.ts} | 18 +- .../_userID/notifications/router.test.ts | 197 - .../v1/users/_userID/notifications/router.ts | 8 +- .../pfp/{router.test.ts => router.oldtest.ts} | 6 +- .../router/api/v1/users/_userID/pfp/router.ts | 9 +- .../{router.test.ts => router.oldtest.ts} | 38 +- .../router/api/v1/users/_userID/router.ts | 67 +- .../api/v1/users/_userID/sessions/router.ts | 4 +- .../{router.test.ts => router.oldtest.ts} | 18 +- .../api/v1/users/_userID/settings/router.ts | 6 +- .../users/router.oldtest.ts} | 6 +- .../server/router/api/v1/users/router.test.ts | 77 - .../src/server/router/api/v1/users/router.ts | 33 +- .../router/api/v1postgres/activity/router.ts | 47 - .../router/api/v1postgres/admin/router.ts | 422 -- .../server/router/api/v1postgres/auth/auth.ts | 193 - .../router/api/v1postgres/auth/router.ts | 575 -- .../api/v1postgres/clients/middleware.test.ts | 124 - .../api/v1postgres/clients/middleware.ts | 68 - .../router/api/v1postgres/clients/router.ts | 356 - .../api/v1postgres/config/router.test.ts | 31 - .../router/api/v1postgres/config/router.ts | 47 - .../games/@gameSpecificRoutes/bms/router.ts | 158 - .../games/@gameSpecificRoutes/iidx/router.ts | 74 - .../games/@gameSpecificRoutes/router.ts | 14 - .../_playtype/charts/_chartID/middleware.ts | 28 - .../_game/_playtype/charts/_chartID/router.ts | 182 - .../_game/_playtype/charts/router.test.ts | 220 - .../games/_game/_playtype/charts/router.ts | 164 - .../_game/_playtype/folders/middleware.ts | 21 - .../games/_game/_playtype/folders/router.ts | 71 - .../games/_game/_playtype/middleware.ts | 38 - .../games/_game/_playtype/router.test.ts | 165 - .../games/_game/_playtype/router.ts | 285 - .../_playtype/songs/_songID/middleware.ts | 31 - .../_playtype/songs/_songID/router.test.ts | 41 - .../_game/_playtype/songs/_songID/router.ts | 35 - .../_game/_playtype/tables/middleware.ts | 22 - .../games/_game/_playtype/tables/router.ts | 72 - .../_game/_playtype/targets/goals/router.ts | 180 - .../targets/questlines/router.test.ts | 96 - .../_playtype/targets/questlines/router.ts | 75 - .../_game/_playtype/targets/quests/router.ts | 99 - .../games/_game/_playtype/targets/router.ts | 74 - .../api/v1postgres/games/_game/middleware.ts | 28 - .../api/v1postgres/games/_game/router.test.ts | 23 - .../api/v1postgres/games/_game/router.ts | 29 - .../api/v1postgres/games/router.test.ts | 27 - .../router/api/v1postgres/games/router.ts | 36 - .../router/api/v1postgres/import/router.ts | 186 - .../api/v1postgres/imports/middleware.ts | 49 - .../api/v1postgres/imports/router.test.ts | 166 - .../router/api/v1postgres/imports/router.ts | 368 - .../router/api/v1postgres/oauth/router.ts | 128 - .../router/api/v1postgres/router.test.ts | 39 - .../server/router/api/v1postgres/router.ts | 56 - .../v1postgres/scores/_scoreID/middleware.ts | 49 - .../api/v1postgres/scores/_scoreID/router.ts | 170 - .../router/api/v1postgres/scores/router.ts | 16 - .../api/v1postgres/search/router.test.ts | 53 - .../router/api/v1postgres/search/router.ts | 123 - .../router/api/v1postgres/seeds/router.ts | 317 - .../sessions/_sessionID/middleware.ts | 37 - .../v1postgres/sessions/_sessionID/router.ts | 365 - .../router/api/v1postgres/sessions/router.ts | 16 - .../api/v1postgres/status/router.test.ts | 84 - .../router/api/v1postgres/status/router.ts | 70 - .../users/_userID/api-tokens/router.ts | 181 - .../v1postgres/users/_userID/banner/router.ts | 134 - .../users/_userID/following/router.ts | 196 - .../games/@gameSpecificRoutes/bms/router.ts | 168 - .../games/@gameSpecificRoutes/iidx/router.ts | 223 - .../@gameSpecificRoutes/jubeat/router.ts | 35 - .../games/@gameSpecificRoutes/router.ts | 17 - .../folders/_folderID/router.test.ts | 204 - .../_playtype/folders/_folderID/router.ts | 211 - .../games/_game/_playtype/folders/router.ts | 87 - .../games/_game/_playtype/middleware.ts | 59 - .../games/_game/_playtype/pbs/router.ts | 338 - .../games/_game/_playtype/rivals/router.ts | 191 - .../games/_game/_playtype/router.test.ts | 284 - .../_userID/games/_game/_playtype/router.ts | 460 -- .../games/_game/_playtype/scores/router.ts | 151 - .../games/_game/_playtype/sessions/router.ts | 248 - .../_game/_playtype/settings/router.test.ts | 440 -- .../games/_game/_playtype/settings/router.ts | 183 - .../_game/_playtype/showcase/router.test.ts | 430 -- .../games/_game/_playtype/showcase/router.ts | 339 - .../games/_game/_playtype/tables/router.ts | 36 - .../_playtype/targets/goals/router.test.ts | 708 -- .../_game/_playtype/targets/goals/router.ts | 302 - .../_game/_playtype/targets/quests/router.ts | 245 - .../games/_game/_playtype/targets/router.ts | 217 - .../users/_userID/imports/router.test.ts | 4 - .../users/_userID/imports/router.ts | 104 - .../_userID/integrations/cg/_cgType/router.ts | 132 - .../_userID/integrations/fervidex/router.ts | 92 - .../kai/_kaiType/middleware.test.ts | 33 - .../integrations/kai/_kaiType/middleware.ts | 20 - .../integrations/kai/_kaiType/router.ts | 220 - .../integrations/kshook-sv6c/router.ts | 68 - .../users/_userID/integrations/myt/router.ts | 99 - .../users/_userID/integrations/router.ts | 17 - .../users/_userID/invites/router.test.ts | 163 - .../users/_userID/invites/router.ts | 149 - .../v1postgres/users/_userID/middleware.ts | 118 - .../users/_userID/notifications/router.ts | 99 - .../users/_userID/pfp/router.test.ts | 87 - .../v1postgres/users/_userID/pfp/router.ts | 134 - .../v1postgres/users/_userID/router.test.ts | 481 -- .../api/v1postgres/users/_userID/router.ts | 842 --- .../users/_userID/sessions/router.ts | 40 - .../users/_userID/settings/router.test.ts | 205 - .../users/_userID/settings/router.ts | 115 - .../router/api/v1postgres/users/router.ts | 54 - .../{router.test.ts => router.oldtest.ts} | 4 +- .../{auth.test.ts => auth.oldtest.ts} | 0 ...ores.test.ts => convert-scores.oldtest.ts} | 0 .../{router.test.ts => router.oldtest.ts} | 8 +- .../beatoraja/charts/_chartSHA256/router.ts | 10 +- .../{router.test.ts => router.oldtest.ts} | 30 +- .../src/server/router/ir/beatoraja/router.ts | 16 +- .../{router.test.ts => router.oldtest.ts} | 12 +- .../{router.test.ts => router.oldtest.ts} | 75 +- .../src/server/router/ir/fervidex/router.ts | 8 +- .../{router.test.ts => router.oldtest.ts} | 10 +- .../server/router/ir/kshook/sv6c/router.ts | 6 +- .../{router.test.ts => router.oldtest.ts} | 10 +- .../src/server/router/ir/lr2hook/router.ts | 4 +- .../{router.test.ts => router.oldtest.ts} | 56 +- .../server/router/ir/usc/_playtype/router.ts | 14 +- .../_playtype/{usc.test.ts => usc.oldtest.ts} | 44 +- .../src/server/router/ir/usc/_playtype/usc.ts | 12 +- typescript/server/src/server/router/router.ts | 4 +- .../mongo/{db.test.ts => db.oldtest.ts} | 24 +- typescript/server/src/services/mongo/db.ts | 6 +- .../src/services/mongo/sequence-docs.ts | 12 +- typescript/server/src/services/pg/db.ts | 6 +- .../server/src/test-utils/api-common.ts | 6 +- typescript/server/src/test-utils/fake-auth.ts | 4 +- typescript/server/src/test-utils/resets.ts | 10 +- .../src/test-utils/single-process-tap.ts | 2 +- typescript/server/src/test-utils/test-data.ts | 10 +- .../calculations/recalc-game-profiles.ts | 6 +- .../src/utils/calculations/recalc-scores.ts | 14 +- .../src/utils/calculations/recalc-sessions.ts | 15 +- typescript/server/src/utils/class.ts | 10 +- typescript/server/src/utils/conversion.ts | 17 + .../{crypto.test.ts => crypto.oldtest.ts} | 0 .../src/utils/{db.test.ts => db.oldtest.ts} | 8 +- typescript/server/src/utils/db.ts | 109 +- .../{folder.test.ts => folder.oldtest.ts} | 8 +- typescript/server/src/utils/folder.ts | 38 +- .../utils/{misc.test.ts => misc.oldtest.ts} | 0 typescript/server/src/utils/misc.ts | 19 + ...er.test.ts => naive-csv-parser.oldtest.ts} | 0 .../server/src/utils/queries/api-clients.ts | 11 + typescript/server/src/utils/queries/auth.ts | 6 +- .../server/src/utils/queries/blacklist.ts | 4 +- typescript/server/src/utils/queries/charts.ts | 78 +- typescript/server/src/utils/queries/pbs.ts | 6 +- typescript/server/src/utils/queries/scores.ts | 6 +- .../server/src/utils/queries/settings.ts | 11 + typescript/server/src/utils/queries/songs.ts | 8 +- typescript/server/src/utils/queries/stats.ts | 4 +- .../server/src/utils/queries/summary.ts | 16 +- .../src/utils/reset-state/destroy-ugpt.ts | 18 +- typescript/server/src/utils/response.ts | 25 + typescript/server/src/utils/scores.ts | 6 +- .../{session.test.ts => session.oldtest.ts} | 4 +- typescript/server/src/utils/session.ts | 6 +- typescript/server/src/utils/time.ts | 7 + .../utils/{user.test.ts => user.oldtest.ts} | 4 +- typescript/server/src/utils/user.ts | 125 +- typescript/server/tsconfig.json | 2 +- typescript/server/vitest.config.ts | 51 + typescript/server/vitest.globalSetup.ts | 11 + typescript/server/vitest.setup.ts | 112 + 695 files changed, 6132 insertions(+), 49249 deletions(-) delete mode 100644 typescript-vendor/tap/.gitattributes delete mode 100644 typescript-vendor/tap/.gitignore delete mode 100644 typescript-vendor/tap/CHANGELOG.md delete mode 100644 typescript-vendor/tap/CONTRIBUTING.md delete mode 100644 typescript-vendor/tap/LICENSE delete mode 100644 typescript-vendor/tap/README.md delete mode 100644 typescript-vendor/tap/bin/jack.js delete mode 100644 typescript-vendor/tap/bin/jsx.js delete mode 100755 typescript-vendor/tap/bin/run.js delete mode 100644 typescript-vendor/tap/coverage-map.js delete mode 100644 typescript-vendor/tap/example/lib/math.js delete mode 100644 typescript-vendor/tap/example/long-slow-many.js delete mode 100644 typescript-vendor/tap/example/mocha-example.js delete mode 100644 typescript-vendor/tap/lib/cb-promise.js delete mode 100644 typescript-vendor/tap/lib/mocha.js delete mode 100644 typescript-vendor/tap/lib/repl.js delete mode 100644 typescript-vendor/tap/lib/synonyms.js delete mode 100644 typescript-vendor/tap/lib/tap.js delete mode 100644 typescript-vendor/tap/lib/tap.mjs delete mode 100644 typescript-vendor/tap/lib/watch.js delete mode 100644 typescript-vendor/tap/netlify.toml delete mode 100644 typescript-vendor/tap/package-lock.json delete mode 100644 typescript-vendor/tap/package.json delete mode 100644 typescript-vendor/tap/postpublish.sh delete mode 100755 typescript-vendor/tap/scripts/snap.sh delete mode 100644 typescript-vendor/tap/scripts/unit.sh delete mode 100644 typescript-vendor/tap/settings.js delete mode 100644 typescript-vendor/tap/tap-snapshots/test/repl.js.test.cjs delete mode 100644 typescript-vendor/tap/tap-snapshots/test/run/basic.js.test.cjs delete mode 100644 typescript-vendor/tap/tap-snapshots/test/run/before-after.js.test.cjs delete mode 100644 typescript-vendor/tap/tap-snapshots/test/run/cat.js.test.cjs delete mode 100644 typescript-vendor/tap/tap-snapshots/test/run/coverage.js.test.cjs delete mode 100644 typescript-vendor/tap/tap-snapshots/test/run/dump-config.js.test.cjs delete mode 100644 typescript-vendor/tap/tap-snapshots/test/run/env.js.test.cjs delete mode 100644 typescript-vendor/tap/tap-snapshots/test/run/executables.js.test.cjs delete mode 100644 typescript-vendor/tap/tap-snapshots/test/run/files.js.test.cjs delete mode 100644 typescript-vendor/tap/tap-snapshots/test/run/flow.js.test.cjs delete mode 100644 typescript-vendor/tap/tap-snapshots/test/run/invalid-option.js.test.cjs delete mode 100644 typescript-vendor/tap/tap-snapshots/test/run/jsx.js.test.cjs delete mode 100644 typescript-vendor/tap/tap-snapshots/test/run/libtap-settings.js.test.cjs delete mode 100644 typescript-vendor/tap/tap-snapshots/test/run/nocolor-env.js.test.cjs delete mode 100644 typescript-vendor/tap/tap-snapshots/test/run/nonparallel.js.test.cjs delete mode 100644 typescript-vendor/tap/tap-snapshots/test/run/output-file.js.test.cjs delete mode 100644 typescript-vendor/tap/tap-snapshots/test/run/parallel.js.test.cjs delete mode 100644 typescript-vendor/tap/tap-snapshots/test/run/reporters.js.test.cjs delete mode 100644 typescript-vendor/tap/tap-snapshots/test/run/save-file.js.test.cjs delete mode 100644 typescript-vendor/tap/tap-snapshots/test/run/stdin.js.test.cjs delete mode 100644 typescript-vendor/tap/tap-snapshots/test/run/test-regex.js.test.cjs delete mode 100644 typescript-vendor/tap/tap-snapshots/test/run/ts.js.test.cjs delete mode 100644 typescript-vendor/tap/tap-snapshots/test/run/watermarks.js.test.cjs delete mode 100644 typescript-vendor/tap/tap-snapshots/test/settings/default.js.test.cjs delete mode 100644 typescript-vendor/tap/tap-snapshots/test/settings/long-stack.js.test.cjs delete mode 100644 typescript-vendor/tap/tap-snapshots/test/synonyms.js.test.cjs delete mode 100644 typescript-vendor/tap/tap-snapshots/test/test.mjs.test.cjs delete mode 100644 typescript-vendor/tap/tap-snapshots/test/watch.js.test.cjs delete mode 100644 typescript-vendor/tap/test/cb-promise.js delete mode 100644 typescript-vendor/tap/test/clean-stacks.js delete mode 100644 typescript-vendor/tap/test/coverage-map.js delete mode 100644 typescript-vendor/tap/test/mocha.js delete mode 100644 typescript-vendor/tap/test/regression-many-asserts-epipe.js delete mode 100644 typescript-vendor/tap/test/regression-pipe-backup.js delete mode 100644 typescript-vendor/tap/test/repl.js delete mode 100644 typescript-vendor/tap/test/run/bad-rcfile.js delete mode 100644 typescript-vendor/tap/test/run/basic.js delete mode 100644 typescript-vendor/tap/test/run/before-after.js delete mode 100644 typescript-vendor/tap/test/run/cat.js delete mode 100644 typescript-vendor/tap/test/run/changed.js delete mode 100644 typescript-vendor/tap/test/run/comments.js delete mode 100644 typescript-vendor/tap/test/run/coverage.js delete mode 100644 typescript-vendor/tap/test/run/dump-config.js delete mode 100644 typescript-vendor/tap/test/run/env.js delete mode 100644 typescript-vendor/tap/test/run/epipe-stdout.js delete mode 100644 typescript-vendor/tap/test/run/executables.js delete mode 100644 typescript-vendor/tap/test/run/files.js delete mode 100644 typescript-vendor/tap/test/run/flow.js delete mode 100644 typescript-vendor/tap/test/run/index.js delete mode 100644 typescript-vendor/tap/test/run/invalid-option.js delete mode 100644 typescript-vendor/tap/test/run/jsx.js delete mode 100644 typescript-vendor/tap/test/run/libtap-settings.js delete mode 100644 typescript-vendor/tap/test/run/nocolor-env.js delete mode 100644 typescript-vendor/tap/test/run/nonparallel.js delete mode 100644 typescript-vendor/tap/test/run/output-file.js delete mode 100644 typescript-vendor/tap/test/run/parallel.js delete mode 100644 typescript-vendor/tap/test/run/rcfile-extensions.js delete mode 100644 typescript-vendor/tap/test/run/reporters.js delete mode 100644 typescript-vendor/tap/test/run/save-file.js delete mode 100644 typescript-vendor/tap/test/run/setup-tap-env.js delete mode 100644 typescript-vendor/tap/test/run/stdin.js delete mode 100644 typescript-vendor/tap/test/run/test-regex.js delete mode 100644 typescript-vendor/tap/test/run/ts.js delete mode 100644 typescript-vendor/tap/test/run/watermarks.js delete mode 100644 typescript-vendor/tap/test/settings/clean-for-snapshot.js delete mode 100644 typescript-vendor/tap/test/settings/default.js delete mode 100644 typescript-vendor/tap/test/settings/long-stack.js delete mode 100644 typescript-vendor/tap/test/settings/overrides.js delete mode 100644 typescript-vendor/tap/test/synonyms.js delete mode 100644 typescript-vendor/tap/test/tap.js delete mode 100644 typescript-vendor/tap/test/test-esm.js delete mode 100644 typescript-vendor/tap/test/test.mjs delete mode 100644 typescript-vendor/tap/test/versions.js delete mode 100644 typescript-vendor/tap/test/watch.js create mode 100644 typescript/bliss/eslint.config.mjs create mode 100644 typescript/common/src/lib/zod-schemas.ts delete mode 100644 typescript/github-bot/tsconfig.tsbuildinfo create mode 100644 typescript/server/.env create mode 100644 typescript/server/buf.gen.yaml delete mode 100644 typescript/server/example/.env delete mode 100644 typescript/server/example/conf.json5 delete mode 100644 typescript/server/nodemon.json create mode 100644 typescript/server/src/actions/install-builtin-client.ts rename typescript/server/src/game-implementations/games/{arcaea.test.ts => arcaea.oldtest.ts} (97%) rename typescript/server/src/game-implementations/games/{bms-pms.test.ts => bms-pms.oldtest.ts} (97%) rename typescript/server/src/game-implementations/games/{chunithm.test.ts => chunithm.oldtest.ts} (96%) rename typescript/server/src/game-implementations/games/{ddr.test.ts => ddr.oldtest.ts} (100%) rename typescript/server/src/game-implementations/games/{gitadora.test.ts => gitadora.oldtest.ts} (97%) rename typescript/server/src/game-implementations/games/{iidx.test.ts => iidx.oldtest.ts} (98%) rename typescript/server/src/game-implementations/games/{jubeat.test.ts => jubeat.oldtest.ts} (96%) rename typescript/server/src/game-implementations/games/{maimai.test.ts => maimai.oldtest.ts} (97%) rename typescript/server/src/game-implementations/games/{maimaidx.test.ts => maimaidx.oldtest.ts} (96%) rename typescript/server/src/game-implementations/games/{museca.test.ts => museca.oldtest.ts} (95%) rename typescript/server/src/game-implementations/games/{ongeki.test.ts => ongeki.oldtest.ts} (94%) rename typescript/server/src/game-implementations/games/{popn.test.ts => popn.oldtest.ts} (97%) rename typescript/server/src/game-implementations/games/{sdvx.test.ts => sdvx.oldtest.ts} (96%) rename typescript/server/src/game-implementations/games/{usc.test.ts => usc.oldtest.ts} (97%) rename typescript/server/src/game-implementations/games/{wacca.test.ts => wacca.oldtest.ts} (97%) rename typescript/server/src/lib/cdn/{cdn.test.ts => cdn.oldtest.ts} (100%) create mode 100644 typescript/server/src/lib/db-formats/api-client.ts create mode 100644 typescript/server/src/lib/db-formats/user-settings.ts create mode 100644 typescript/server/src/lib/db-formats/user.ts rename typescript/server/src/lib/game-settings/{create-game-settings.test.ts => create-game-settings.oldtest.ts} (85%) rename typescript/server/src/lib/imports/{imports.test.ts => imports.oldtest.ts} (74%) rename typescript/server/src/lib/invites/{invites.test.ts => invites.oldtest.ts} (100%) rename typescript/server/src/lib/notifications/{notifications.test.ts => notifications.oldtest.ts} (84%) rename typescript/server/src/lib/score-import/framework/common/{string-asserts.test.ts => string-asserts.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/framework/goals/{goals.test.ts => goals.oldtest.ts} (83%) rename typescript/server/src/lib/score-import/framework/orphans/{orphans.test.ts => orphans.oldtest.ts} (86%) rename typescript/server/src/lib/score-import/framework/pb/{create-pb-doc.test.ts => create-pb-doc.oldtest.ts} (93%) rename typescript/server/src/lib/score-import/framework/pb/{process-pbs.test.ts => process-pbs.oldtest.ts} (83%) rename typescript/server/src/lib/score-import/framework/profile-calculated-data/{classes.test.ts => classes.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/framework/quests/{quests.test.ts => quests.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/framework/{score-import.test.ts => score-import.oldtest.ts} (88%) rename typescript/server/src/lib/score-import/framework/score-importing/{hydrate-score.test.ts => hydrate-score.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/framework/score-importing/{insert-score.test.ts => insert-score.oldtest.ts} (93%) rename typescript/server/src/lib/score-import/framework/score-importing/{score-id.test.ts => score-id.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/framework/score-importing/{score-import-error.test.ts => score-import-error.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/framework/score-importing/{score-importing.test.ts => score-importing.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/framework/score-importing/{validate-score.test.ts => validate-score.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/framework/sessions/{name-generation.test.ts => name-generation.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/framework/sessions/{sessions.test.ts => sessions.oldtest.ts} (80%) rename typescript/server/src/lib/score-import/framework/ugpt-stats/{update-ugpt-stats.test.ts => update-ugpt-stats.oldtest.ts} (79%) rename typescript/server/src/lib/score-import/import-types/api/myt-chunithm/{converter.test.ts => converter.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/api/myt-maimaidx/{converter.test.ts => converter.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/api/myt-ongeki/{converter.test.ts => converter.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/api/myt-wacca/{converter.test.ts => converter.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/common/api-cg/jb/{converter.test.ts => converter.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/common/api-cg/museca/{converter.test.ts => converter.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/common/api-cg/popn/{converter.test.ts => converter.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/common/api-cg/sdvx/{converter.test.ts => converter.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/common/api-kai/iidx/{class-handler.test.ts => class-handler.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/common/api-kai/iidx/{converter.test.ts => converter.oldtest.ts} (96%) rename typescript/server/src/lib/score-import/import-types/common/api-kai/iidx/{parser.test.ts => parser.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/common/api-kai/{reauth.test.ts => reauth.oldtest.ts} (83%) rename typescript/server/src/lib/score-import/import-types/common/api-kai/sdvx/{class-handler.test.ts => class-handler.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/common/api-kai/sdvx/{converter.test.ts => converter.oldtest.ts} (97%) rename typescript/server/src/lib/score-import/import-types/common/api-kai/sdvx/{parser.test.ts => parser.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/common/api-kai/{traverse-api.test.ts => traverse-api.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/common/batch-manual/{converter.test.ts => converter.oldtest.ts} (99%) rename typescript/server/src/lib/score-import/import-types/common/batch-manual/{parser.test.ts => parser.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/common/eamusement-iidx-csv/{converter.test.ts => converter.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/common/eamusement-iidx-csv/{parser.test.ts => parser.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/file/eamusement-sdvx-csv/{converter.test.ts => converter.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/file/eamusement-sdvx-csv/{parser.test.ts => parser.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/file/solid-state-squad/{converter.test.ts => converter.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/file/solid-state-squad/{parser.test.ts => parser.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/file/wacca-mypage-scraper/{converter.test.ts => converter.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/file/wacca-mypage-scraper/{parser.test.ts => parser.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/ir/barbatos/{converter.test.ts => converter.oldtest.ts} (96%) rename typescript/server/src/lib/score-import/import-types/ir/barbatos/{parser.test.ts => parser.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/ir/fervidex-static/{class-handler.test.ts => class-handler.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/ir/fervidex-static/{converter.test.ts => converter.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/ir/fervidex-static/{parser.test.ts => parser.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/ir/fervidex/{converter.test.ts => converter.oldtest.ts} (96%) rename typescript/server/src/lib/score-import/import-types/ir/fervidex/{parser.test.ts => parser.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/ir/kshook-sv6c-static/{converter.test.ts => converter.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/ir/kshook-sv6c-static/{parser.test.ts => parser.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/ir/kshook-sv6c/{converter.test.ts => converter.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/ir/kshook-sv6c/{parser.test.ts => parser.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/ir/lr2hook/{converter.test.ts => converter.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/ir/lr2hook/{parser.test.ts => parser.oldtest.ts} (100%) rename typescript/server/src/lib/score-import/import-types/ir/usc/{converter.test.ts => converter.oldtest.ts} (97%) rename typescript/server/src/lib/score-import/import-types/ir/usc/{parser.test.ts => parser.oldtest.ts} (100%) rename typescript/server/src/lib/score-mutation/{delete-scores.test.ts => delete-scores.oldtest.ts} (80%) rename typescript/server/src/lib/score-mutation/{update-score.test.ts => update-score.oldtest.ts} (84%) rename typescript/server/src/lib/search/{search.test.ts => search.oldtest.ts} (91%) rename typescript/server/src/lib/showcase/{evaluator.test.ts => evaluator.oldtest.ts} (91%) rename typescript/server/src/lib/showcase/{get-stats.test.ts => get-stats.oldtest.ts} (86%) rename typescript/server/src/lib/showcase/{showcase.test.ts => showcase.oldtest.ts} (88%) rename typescript/server/src/lib/targets/{goals.test.ts => goals.oldtest.ts} (86%) create mode 100644 typescript/server/src/proto/generated/cards/cards_connect.ts delete mode 100644 typescript/server/src/proto/generated/cards/cards_grpc_pb.d.ts delete mode 100644 typescript/server/src/proto/generated/cards/cards_grpc_pb.js delete mode 100644 typescript/server/src/proto/generated/cards/cards_pb.d.ts delete mode 100644 typescript/server/src/proto/generated/cards/cards_pb.js create mode 100644 typescript/server/src/proto/generated/cards/cards_pb.ts delete mode 100644 typescript/server/src/proto/generated/chunithm/common_grpc_pb.d.ts delete mode 100644 typescript/server/src/proto/generated/chunithm/common_grpc_pb.js delete mode 100644 typescript/server/src/proto/generated/chunithm/common_pb.d.ts delete mode 100644 typescript/server/src/proto/generated/chunithm/common_pb.js create mode 100644 typescript/server/src/proto/generated/chunithm/common_pb.ts delete mode 100644 typescript/server/src/proto/generated/chunithm/playlog_grpc_pb.d.ts delete mode 100644 typescript/server/src/proto/generated/chunithm/playlog_grpc_pb.js delete mode 100644 typescript/server/src/proto/generated/chunithm/playlog_pb.d.ts delete mode 100644 typescript/server/src/proto/generated/chunithm/playlog_pb.js create mode 100644 typescript/server/src/proto/generated/chunithm/playlog_pb.ts create mode 100644 typescript/server/src/proto/generated/chunithm/user_connect.ts delete mode 100644 typescript/server/src/proto/generated/chunithm/user_grpc_pb.d.ts delete mode 100644 typescript/server/src/proto/generated/chunithm/user_grpc_pb.js delete mode 100644 typescript/server/src/proto/generated/chunithm/user_pb.d.ts delete mode 100644 typescript/server/src/proto/generated/chunithm/user_pb.js create mode 100644 typescript/server/src/proto/generated/chunithm/user_pb.ts delete mode 100644 typescript/server/src/proto/generated/maimai/common_grpc_pb.d.ts delete mode 100644 typescript/server/src/proto/generated/maimai/common_grpc_pb.js delete mode 100644 typescript/server/src/proto/generated/maimai/common_pb.d.ts delete mode 100644 typescript/server/src/proto/generated/maimai/common_pb.js create mode 100644 typescript/server/src/proto/generated/maimai/common_pb.ts delete mode 100644 typescript/server/src/proto/generated/maimai/playlog_grpc_pb.d.ts delete mode 100644 typescript/server/src/proto/generated/maimai/playlog_grpc_pb.js delete mode 100644 typescript/server/src/proto/generated/maimai/playlog_pb.d.ts delete mode 100644 typescript/server/src/proto/generated/maimai/playlog_pb.js create mode 100644 typescript/server/src/proto/generated/maimai/playlog_pb.ts create mode 100644 typescript/server/src/proto/generated/maimai/user_connect.ts delete mode 100644 typescript/server/src/proto/generated/maimai/user_grpc_pb.d.ts delete mode 100644 typescript/server/src/proto/generated/maimai/user_grpc_pb.js delete mode 100644 typescript/server/src/proto/generated/maimai/user_pb.d.ts delete mode 100644 typescript/server/src/proto/generated/maimai/user_pb.js create mode 100644 typescript/server/src/proto/generated/maimai/user_pb.ts delete mode 100644 typescript/server/src/proto/generated/ongeki/common_grpc_pb.d.ts delete mode 100644 typescript/server/src/proto/generated/ongeki/common_grpc_pb.js delete mode 100644 typescript/server/src/proto/generated/ongeki/common_pb.d.ts delete mode 100644 typescript/server/src/proto/generated/ongeki/common_pb.js create mode 100644 typescript/server/src/proto/generated/ongeki/common_pb.ts delete mode 100644 typescript/server/src/proto/generated/ongeki/playlog_grpc_pb.d.ts delete mode 100644 typescript/server/src/proto/generated/ongeki/playlog_grpc_pb.js delete mode 100644 typescript/server/src/proto/generated/ongeki/playlog_pb.d.ts delete mode 100644 typescript/server/src/proto/generated/ongeki/playlog_pb.js create mode 100644 typescript/server/src/proto/generated/ongeki/playlog_pb.ts create mode 100644 typescript/server/src/proto/generated/ongeki/user_connect.ts delete mode 100644 typescript/server/src/proto/generated/ongeki/user_grpc_pb.d.ts delete mode 100644 typescript/server/src/proto/generated/ongeki/user_grpc_pb.js delete mode 100644 typescript/server/src/proto/generated/ongeki/user_pb.d.ts delete mode 100644 typescript/server/src/proto/generated/ongeki/user_pb.js create mode 100644 typescript/server/src/proto/generated/ongeki/user_pb.ts delete mode 100644 typescript/server/src/proto/generated/wacca/common_grpc_pb.d.ts delete mode 100644 typescript/server/src/proto/generated/wacca/common_grpc_pb.js delete mode 100644 typescript/server/src/proto/generated/wacca/common_pb.d.ts delete mode 100644 typescript/server/src/proto/generated/wacca/common_pb.js create mode 100644 typescript/server/src/proto/generated/wacca/common_pb.ts create mode 100644 typescript/server/src/proto/generated/wacca/user_connect.ts delete mode 100644 typescript/server/src/proto/generated/wacca/user_grpc_pb.d.ts delete mode 100644 typescript/server/src/proto/generated/wacca/user_grpc_pb.js delete mode 100644 typescript/server/src/proto/generated/wacca/user_pb.d.ts delete mode 100644 typescript/server/src/proto/generated/wacca/user_pb.js create mode 100644 typescript/server/src/proto/generated/wacca/user_pb.ts rename typescript/server/src/server/middleware/{auth.test.ts => auth.oldtest.ts} (90%) rename typescript/server/src/server/middleware/{prudence-validate.test.ts => prudence-validate.oldtest.ts} (100%) rename typescript/server/src/server/middleware/{sanitise-qstring.test.ts => sanitise-qstring.oldtest.ts} (100%) rename typescript/server/src/server/router/api/{v1postgres/admin/router.test.ts => v1/admin/router.oldtest.ts} (84%) delete mode 100644 typescript/server/src/server/router/api/v1/admin/router.test.ts rename typescript/server/src/server/router/api/{v1postgres/auth/auth.test.ts => v1/auth/auth.oldtest.ts} (90%) rename typescript/server/src/server/router/api/{v1postgres/auth/router.test.ts => v1/auth/router.oldtest.ts} (91%) delete mode 100644 typescript/server/src/server/router/api/v1/auth/router.test.ts rename typescript/server/src/server/router/api/v1/clients/{middleware.test.ts => middleware.oldtest.ts} (100%) rename typescript/server/src/server/router/api/{v1postgres/clients/router.test.ts => v1/clients/router.oldtest.ts} (90%) delete mode 100644 typescript/server/src/server/router/api/v1/clients/router.test.ts rename typescript/server/src/server/router/api/v1/config/{router.test.ts => router.oldtest.ts} (100%) rename typescript/server/src/server/router/api/{v1postgres/games/@gameSpecificRoutes/bms/router.test.ts => v1/games/@gameSpecificRoutes/bms/router.oldtest.ts} (94%) delete mode 100644 typescript/server/src/server/router/api/v1/games/@gameSpecificRoutes/bms/router.test.ts rename typescript/server/src/server/router/api/{v1postgres/games/_game/_playtype/charts/_chartID/router.test.ts => v1/games/_game/_playtype/charts/_chartID/router.oldtest.ts} (92%) delete mode 100644 typescript/server/src/server/router/api/v1/games/_game/_playtype/charts/_chartID/router.test.ts rename typescript/server/src/server/router/api/v1/games/_game/_playtype/charts/{router.test.ts => router.oldtest.ts} (95%) rename typescript/server/src/server/router/api/{v1postgres/games/_game/_playtype/folders/router.test.ts => v1/games/_game/_playtype/folders/router.oldtest.ts} (92%) delete mode 100644 typescript/server/src/server/router/api/v1/games/_game/_playtype/folders/router.test.ts rename typescript/server/src/server/router/api/v1/games/_game/_playtype/{router.test.ts => router.oldtest.ts} (94%) rename typescript/server/src/server/router/api/v1/games/_game/_playtype/songs/_songID/{router.test.ts => router.oldtest.ts} (100%) rename typescript/server/src/server/router/api/{v1postgres/games/_game/_playtype/tables/router.test.ts => v1/games/_game/_playtype/tables/router.oldtest.ts} (94%) delete mode 100644 typescript/server/src/server/router/api/v1/games/_game/_playtype/tables/router.test.ts rename typescript/server/src/server/router/api/{v1postgres/games/_game/_playtype/targets/goals/router.test.ts => v1/games/_game/_playtype/targets/goals/router.oldtest.ts} (91%) delete mode 100644 typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/goals/router.test.ts rename typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/questlines/{router.test.ts => router.oldtest.ts} (93%) rename typescript/server/src/server/router/api/{v1postgres/games/_game/_playtype/targets/quests/router.test.ts => v1/games/_game/_playtype/targets/quests/router.oldtest.ts} (89%) delete mode 100644 typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/quests/router.test.ts rename typescript/server/src/server/router/api/{v1postgres/games/_game/_playtype/targets/router.test.ts => v1/games/_game/_playtype/targets/router.oldtest.ts} (93%) delete mode 100644 typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/router.test.ts rename typescript/server/src/server/router/api/v1/games/_game/{router.test.ts => router.oldtest.ts} (100%) rename typescript/server/src/server/router/api/v1/games/{router.test.ts => router.oldtest.ts} (100%) rename typescript/server/src/server/router/api/{v1postgres/import/router.test.ts => v1/import/router.oldtest.ts} (93%) delete mode 100644 typescript/server/src/server/router/api/v1/import/router.test.ts rename typescript/server/src/server/router/api/v1/imports/{router.test.ts => router.oldtest.ts} (88%) rename typescript/server/src/server/router/api/{v1postgres/oauth/router.test.ts => v1/oauth/router.oldtest.ts} (92%) delete mode 100644 typescript/server/src/server/router/api/v1/oauth/router.test.ts rename typescript/server/src/server/router/api/v1/{router.test.ts => router.oldtest.ts} (100%) rename typescript/server/src/server/router/api/{v1postgres/scores/_scoreID/router.test.ts => v1/scores/_scoreID/router.oldtest.ts} (89%) delete mode 100644 typescript/server/src/server/router/api/v1/scores/_scoreID/router.test.ts rename typescript/server/src/server/router/api/v1/search/{router.test.ts => router.oldtest.ts} (84%) rename typescript/server/src/server/router/api/{v1postgres/sessions/_sessionID/router.test.ts => v1/sessions/_sessionID/router.oldtest.ts} (91%) delete mode 100644 typescript/server/src/server/router/api/v1/sessions/_sessionID/router.test.ts rename typescript/server/src/server/router/api/v1/status/{router.test.ts => router.oldtest.ts} (100%) rename typescript/server/src/server/router/api/{v1postgres/users/_userID/api-tokens/router.test.ts => v1/users/_userID/api-tokens/router.oldtest.ts} (93%) delete mode 100644 typescript/server/src/server/router/api/v1/users/_userID/api-tokens/router.test.ts rename typescript/server/src/server/router/api/{v1postgres/users/_userID/banner/router.test.ts => v1/users/_userID/banner/router.oldtest.ts} (90%) delete mode 100644 typescript/server/src/server/router/api/v1/users/_userID/banner/router.test.ts rename typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/folders/_folderID/{router.test.ts => router.oldtest.ts} (90%) rename typescript/server/src/server/router/api/{v1postgres/users/_userID/games/_game/_playtype/pbs/router.test.ts => v1/users/_userID/games/_game/_playtype/pbs/router.oldtest.ts} (94%) delete mode 100644 typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/pbs/router.test.ts rename typescript/server/src/server/router/api/{v1postgres/users/_userID/games/_game/_playtype/rivals/router.test.ts => v1/users/_userID/games/_game/_playtype/rivals/router.oldtest.ts} (85%) delete mode 100644 typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/rivals/router.test.ts rename typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/{router.test.ts => router.oldtest.ts} (89%) rename typescript/server/src/server/router/api/{v1postgres/users/_userID/games/_game/_playtype/scores/router.test.ts => v1/users/_userID/games/_game/_playtype/scores/router.oldtest.ts} (94%) delete mode 100644 typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/scores/router.test.ts rename typescript/server/src/server/router/api/{v1postgres/users/_userID/games/_game/_playtype/sessions/router.test.ts => v1/users/_userID/games/_game/_playtype/sessions/router.oldtest.ts} (92%) delete mode 100644 typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/sessions/router.test.ts rename typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/settings/{router.test.ts => router.oldtest.ts} (90%) rename typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/showcase/{router.test.ts => router.oldtest.ts} (92%) rename typescript/server/src/server/router/api/{v1postgres/users/_userID/games/_game/_playtype/tables/router.test.ts => v1/users/_userID/games/_game/_playtype/tables/router.oldtest.ts} (89%) delete mode 100644 typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/tables/router.test.ts rename typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/{router.test.ts => router.oldtest.ts} (91%) rename typescript/server/src/server/router/api/{v1postgres/users/_userID/games/_game/_playtype/targets/quests/router.test.ts => v1/users/_userID/games/_game/_playtype/targets/quests/router.oldtest.ts} (81%) delete mode 100644 typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/quests/router.test.ts rename typescript/server/src/server/router/api/{v1postgres/users/_userID/games/_game/_playtype/targets/router.test.ts => v1/users/_userID/games/_game/_playtype/targets/router.oldtest.ts} (89%) delete mode 100644 typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/router.test.ts rename typescript/server/src/server/router/api/v1/users/_userID/imports/{router.test.ts => router.oldtest.ts} (100%) rename typescript/server/src/server/router/api/{v1postgres/users/_userID/integrations/fervidex/router.test.ts => v1/users/_userID/integrations/fervidex/router.oldtest.ts} (84%) delete mode 100644 typescript/server/src/server/router/api/v1/users/_userID/integrations/fervidex/router.test.ts rename typescript/server/src/server/router/api/v1/users/_userID/integrations/kai/_kaiType/{middleware.test.ts => middleware.oldtest.ts} (100%) rename typescript/server/src/server/router/api/{v1postgres/users/_userID/integrations/kai/_kaiType/router.test.ts => v1/users/_userID/integrations/kai/_kaiType/router.oldtest.ts} (91%) delete mode 100644 typescript/server/src/server/router/api/v1/users/_userID/integrations/kai/_kaiType/router.test.ts rename typescript/server/src/server/router/api/{v1postgres/users/_userID/integrations/kshook-sv6c/router.test.ts => v1/users/_userID/integrations/kshook-sv6c/router.oldtest.ts} (87%) delete mode 100644 typescript/server/src/server/router/api/v1/users/_userID/integrations/kshook-sv6c/router.test.ts rename typescript/server/src/server/router/api/v1/users/_userID/invites/{router.test.ts => router.oldtest.ts} (89%) rename typescript/server/src/server/router/api/{v1postgres/users/_userID/notifications/router.test.ts => v1/users/_userID/notifications/router.oldtest.ts} (92%) delete mode 100644 typescript/server/src/server/router/api/v1/users/_userID/notifications/router.test.ts rename typescript/server/src/server/router/api/v1/users/_userID/pfp/{router.test.ts => router.oldtest.ts} (90%) rename typescript/server/src/server/router/api/v1/users/_userID/{router.test.ts => router.oldtest.ts} (90%) rename typescript/server/src/server/router/api/v1/users/_userID/settings/{router.test.ts => router.oldtest.ts} (87%) rename typescript/server/src/server/router/api/{v1postgres/users/router.test.ts => v1/users/router.oldtest.ts} (93%) delete mode 100644 typescript/server/src/server/router/api/v1/users/router.test.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/activity/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/admin/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/auth/auth.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/auth/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/clients/middleware.test.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/clients/middleware.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/clients/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/config/router.test.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/config/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/games/@gameSpecificRoutes/bms/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/games/@gameSpecificRoutes/iidx/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/games/@gameSpecificRoutes/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/charts/_chartID/middleware.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/charts/_chartID/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/charts/router.test.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/charts/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/folders/middleware.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/folders/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/middleware.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/router.test.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/songs/_songID/middleware.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/songs/_songID/router.test.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/songs/_songID/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/tables/middleware.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/tables/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/targets/goals/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/targets/questlines/router.test.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/targets/questlines/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/targets/quests/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/targets/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/games/_game/middleware.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/games/_game/router.test.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/games/_game/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/games/router.test.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/games/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/import/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/imports/middleware.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/imports/router.test.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/imports/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/oauth/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/router.test.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/scores/_scoreID/middleware.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/scores/_scoreID/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/scores/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/search/router.test.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/search/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/seeds/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/sessions/_sessionID/middleware.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/sessions/_sessionID/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/sessions/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/status/router.test.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/status/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/api-tokens/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/banner/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/following/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/games/@gameSpecificRoutes/bms/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/games/@gameSpecificRoutes/iidx/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/games/@gameSpecificRoutes/jubeat/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/games/@gameSpecificRoutes/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/folders/_folderID/router.test.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/folders/_folderID/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/folders/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/middleware.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/pbs/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/rivals/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/router.test.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/scores/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/sessions/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/settings/router.test.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/settings/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/showcase/router.test.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/showcase/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/tables/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/targets/goals/router.test.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/targets/goals/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/targets/quests/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/targets/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/imports/router.test.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/imports/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/cg/_cgType/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/fervidex/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/kai/_kaiType/middleware.test.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/kai/_kaiType/middleware.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/kai/_kaiType/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/kshook-sv6c/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/myt/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/invites/router.test.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/invites/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/middleware.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/notifications/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/pfp/router.test.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/pfp/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/router.test.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/sessions/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/settings/router.test.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/_userID/settings/router.ts delete mode 100644 typescript/server/src/server/router/api/v1postgres/users/router.ts rename typescript/server/src/server/router/ir/barbatos/{router.test.ts => router.oldtest.ts} (94%) rename typescript/server/src/server/router/ir/beatoraja/{auth.test.ts => auth.oldtest.ts} (100%) rename typescript/server/src/server/router/ir/beatoraja/charts/_chartSHA256/{convert-scores.test.ts => convert-scores.oldtest.ts} (100%) rename typescript/server/src/server/router/ir/beatoraja/charts/_chartSHA256/{router.test.ts => router.oldtest.ts} (91%) rename typescript/server/src/server/router/ir/beatoraja/{router.test.ts => router.oldtest.ts} (94%) rename typescript/server/src/server/router/ir/direct-manual/{router.test.ts => router.oldtest.ts} (95%) rename typescript/server/src/server/router/ir/fervidex/{router.test.ts => router.oldtest.ts} (90%) rename typescript/server/src/server/router/ir/kshook/sv6c/{router.test.ts => router.oldtest.ts} (96%) rename typescript/server/src/server/router/ir/lr2hook/{router.test.ts => router.oldtest.ts} (93%) rename typescript/server/src/server/router/ir/usc/_playtype/{router.test.ts => router.oldtest.ts} (92%) rename typescript/server/src/server/router/ir/usc/_playtype/{usc.test.ts => usc.oldtest.ts} (85%) rename typescript/server/src/services/mongo/{db.test.ts => db.oldtest.ts} (53%) create mode 100644 typescript/server/src/utils/conversion.ts rename typescript/server/src/utils/{crypto.test.ts => crypto.oldtest.ts} (100%) rename typescript/server/src/utils/{db.test.ts => db.oldtest.ts} (81%) rename typescript/server/src/utils/{folder.test.ts => folder.oldtest.ts} (96%) rename typescript/server/src/utils/{misc.test.ts => misc.oldtest.ts} (100%) rename typescript/server/src/utils/{naive-csv-parser.test.ts => naive-csv-parser.oldtest.ts} (100%) create mode 100644 typescript/server/src/utils/queries/api-clients.ts create mode 100644 typescript/server/src/utils/queries/settings.ts create mode 100644 typescript/server/src/utils/response.ts rename typescript/server/src/utils/{session.test.ts => session.oldtest.ts} (84%) create mode 100644 typescript/server/src/utils/time.ts rename typescript/server/src/utils/{user.test.ts => user.oldtest.ts} (94%) create mode 100644 typescript/server/vitest.config.ts create mode 100644 typescript/server/vitest.globalSetup.ts create mode 100644 typescript/server/vitest.setup.ts diff --git a/Justfile-apps b/Justfile-apps index 0bec8cfdb..dc8d61cc9 100644 --- a/Justfile-apps +++ b/Justfile-apps @@ -21,4 +21,4 @@ server: bun --watch src/main.ts client: - cd typescript/client && vite --open + cd typescript/client && vite \ No newline at end of file diff --git a/Justfile-db b/Justfile-db index 77d207b44..86a8da015 100644 --- a/Justfile-db +++ b/Justfile-db @@ -9,7 +9,7 @@ db POSTGRES_DB=DEFAULT_DB: db-reset POSTGRES_DB=DEFAULT_DB: #!/bin/bash - export DATABASE_URL="{{POSTGRES_URL}}/{{POSTGRES_DB}}" + export POSTGRES_URL="{{POSTGRES_URL}}/{{POSTGRES_DB}}" tachidb database drop tachidb database create tachidb migrate run @@ -25,7 +25,16 @@ db-migrate-status POSTGRES_DB=DEFAULT_DB: # Workers clone from this template rather than re-running migrations each time. bot-db-test-template-reset: #!/bin/bash - export DATABASE_URL="{{POSTGRES_URL}}/tachi_bot_test_template" + export POSTGRES_URL="{{POSTGRES_URL}}/tachi_bot_test_template" + tachidb database drop || true + tachidb database create + tachidb migrate run + +# Create (or recreate) the server test template database with all migrations applied. +# Workers clone from this template rather than re-running migrations each time. +server-db-test-template-reset: + #!/bin/bash + export POSTGRES_URL="{{POSTGRES_URL}}/tachi_server_test_template" tachidb database drop || true tachidb database create tachidb migrate run diff --git a/Justfile-gen b/Justfile-gen index d3a59af9b..c6356432b 100644 --- a/Justfile-gen +++ b/Justfile-gen @@ -9,7 +9,9 @@ gen-kanel: (db-reset "templatedb") bun run kanel -d "{{ POSTGRES_URL }}/templatedb" -c db/kanel_config.js just fmt -# Generate protobuf types from the proto definitions in the server +# Generate protobuf types from the proto definitions in the server. +# Uses buf + protoc-gen-es (ESM message types) and protoc-gen-connect-es +# (Connect/gRPC service clients), both generating proper TypeScript/ESM. gen-proto: - cd typescript/server && grpc_tools_node_protoc --plugin=protoc-gen-ts=$(which protoc-gen-ts) --js_out=import_style=commonjs,binary:src/proto/generated --ts_out=service=grpc-node,mode=grpc-js:src/proto/generated --grpc_out=grpc_js:src/proto/generated --proto_path=src/proto/myt src/proto/myt/*/*.proto + cd typescript/server && PATH="../../node_modules/.bin:$PATH" buf generate just fmt \ No newline at end of file diff --git a/bun.lock b/bun.lock index b254f3436..45ae22a69 100644 --- a/bun.lock +++ b/bun.lock @@ -4,14 +4,20 @@ "workspaces": { "": { "name": "tachi", + "dependencies": { + "@bufbuild/protobuf": "^2.11.0", + "@connectrpc/connect": "^2.1.1", + "@connectrpc/connect-node": "^2.1.1", + }, "devDependencies": { "@biomejs/biome": "^2.0.6", + "@bufbuild/buf": "^1.66.1", + "@bufbuild/protoc-gen-es": "^2.11.0", + "@connectrpc/protoc-gen-connect-es": "^1.7.0", "@types/node": "20.19.37", - "@types/tap": "15.0.3", "eslint": "^9.20.1", "kanel": "^3.16.1", "kanel-kysely": "^0.8.0", - "tap": "workspace:*", "ts-node": "10.9.1", "tsc-alias": "1.7.1", "typescript": "5.9.3", @@ -23,57 +29,6 @@ "mongodb": "3.6.3", }, }, - "typescript-vendor/tap": { - "name": "tap", - "version": "15.0.9", - "bin": { - "tap": "bin/run.js", - }, - "dependencies": { - "chokidar": "catalog:", - "coveralls": "catalog:", - "findit": "catalog:", - "foreground-child": "catalog:", - "fs-exists-cached": "catalog:", - "glob": "catalog:", - "import-jsx": "catalog:", - "ink": "catalog:", - "isexe": "catalog:", - "istanbul-lib-processinfo": "catalog:", - "jackspeak": "catalog:", - "libtap": "catalog:", - "minipass": "catalog:", - "mkdirp": "catalog:", - "nyc": "catalog:", - "opener": "catalog:", - "rimraf": "catalog:", - "signal-exit": "catalog:", - "source-map-support": "catalog:", - "tap-mocha-reporter": "catalog:", - "tap-parser": "catalog:", - "tap-yaml": "catalog:", - "tcompare": "catalog:", - "treport": "catalog:", - "which": "catalog:", - }, - "devDependencies": { - "flow-remove-types": "catalog:", - "node-preload": "catalog:", - "process-on-spawn": "catalog:", - "ts-node": "catalog:", - "typescript": "catalog:", - }, - "peerDependencies": { - "flow-remove-types": ">=2.112.0", - "ts-node": ">=8.5.2", - "typescript": ">=3.7.2", - }, - "optionalPeers": [ - "flow-remove-types", - "ts-node", - "typescript", - ], - }, "typescript/api-parity": { "name": "api-parity", "version": "1.0.0", @@ -89,7 +44,7 @@ "dependencies": { "pino": "catalog:", "pino-pretty": "catalog:", - "zod": "^4.3.6", + "zod": "catalog:", }, "devDependencies": { "@typescript/native-preview": "catalog:", @@ -219,10 +174,8 @@ "zod": "catalog:", }, "devDependencies": { - "@types/tap": "catalog:", "@typescript/native-preview": "catalog:", "eslint-config-tachi": "workspace:*", - "tap": "workspace:*", "typescript": "catalog:", }, }, @@ -418,6 +371,7 @@ "html-entities": "catalog:", "json5": "catalog:", "kysely": "catalog:", + "lodash": "^4.17.23", "mkdirp": "catalog:", "mongodb": "catalog:", "monk": "catalog:", @@ -450,8 +404,8 @@ "@types/google-protobuf": "catalog:", "@types/pg": "catalog:", "@types/supertest": "catalog:", - "@types/tap": "catalog:", "@typescript/native-preview": "catalog:", + "@vitest/coverage-v8": "catalog:", "eslint-config-tachi": "workspace:*", "glob": "catalog:", "google-protobuf": "catalog:", @@ -459,9 +413,9 @@ "node-mocks-http": "catalog:", "nodemon": "catalog:", "supertest": "catalog:", - "tap": "workspace:*", "ts-protoc-gen": "catalog:", "tsconfig-paths": "catalog:", + "vitest": "catalog:", }, }, "typescript/sieglinde": { @@ -552,7 +506,6 @@ "@types/safe-json-stringify": "1.1.2", "@types/semver": "^7.3.9", "@types/supertest": "2.0.11", - "@types/tap": "15.0.5", "@typescript-eslint/eslint-plugin": "^4.27.0", "@typescript-eslint/parser": "^4.27.0", "@typescript-eslint/utils": "^8.24.1", @@ -669,7 +622,7 @@ "rg-stats": "1.2.1", "rimraf": "^3.0.0", "safe-json-stringify": "1.2.0", - "sass": "1.64.1", + "sass": "^1.77.0", "semver": "^7.3.7", "seq-logging": "1.1.1", "signal-exit": "^3.0.0", @@ -864,46 +817,18 @@ "@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="], - "@babel/compat-data": ["@babel/compat-data@7.29.0", "", {}, "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg=="], - - "@babel/core": ["@babel/core@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA=="], - "@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="], - "@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="], - "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="], "@babel/helper-module-imports": ["@babel/helper-module-imports@7.28.6", "", { "dependencies": { "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw=="], - "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.6", "", { "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA=="], - - "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.28.6", "", {}, "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug=="], - "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], - "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="], - - "@babel/helpers": ["@babel/helpers@7.28.6", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw=="], - "@babel/parser": ["@babel/parser@7.29.0", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww=="], - "@babel/plugin-proposal-object-rest-spread": ["@babel/plugin-proposal-object-rest-spread@7.20.7", "", { "dependencies": { "@babel/compat-data": "^7.20.5", "@babel/helper-compilation-targets": "^7.20.7", "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-transform-parameters": "^7.20.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg=="], - - "@babel/plugin-syntax-jsx": ["@babel/plugin-syntax-jsx@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w=="], - - "@babel/plugin-syntax-object-rest-spread": ["@babel/plugin-syntax-object-rest-spread@7.8.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA=="], - - "@babel/plugin-transform-destructuring": ["@babel/plugin-transform-destructuring@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw=="], - - "@babel/plugin-transform-parameters": ["@babel/plugin-transform-parameters@7.27.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg=="], - - "@babel/plugin-transform-react-jsx": ["@babel/plugin-transform-react-jsx@7.28.6", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-module-imports": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6", "@babel/plugin-syntax-jsx": "^7.28.6", "@babel/types": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-61bxqhiRfAACulXSLd/GxqmAedUSrRZIu/cbaT18T1CetkTmtDN15it7i80ru4DVqRK1WMxQhXs+Lf9kajm5Ow=="], - "@babel/runtime": ["@babel/runtime@7.28.6", "", {}, "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA=="], "@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="], @@ -932,8 +857,36 @@ "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.6", "", { "os": "win32", "cpu": "x64" }, "sha512-7++XhnsPlr1HDbor5amovPjOH6vsrFOCdp93iKXhFn6bcMUI6soodj3WWKfgEO6JosKU1W5n3uky3WW9RlRjTg=="], + "@bufbuild/buf": ["@bufbuild/buf@1.66.1", "", { "optionalDependencies": { "@bufbuild/buf-darwin-arm64": "1.66.1", "@bufbuild/buf-darwin-x64": "1.66.1", "@bufbuild/buf-linux-aarch64": "1.66.1", "@bufbuild/buf-linux-armv7": "1.66.1", "@bufbuild/buf-linux-x64": "1.66.1", "@bufbuild/buf-win32-arm64": "1.66.1", "@bufbuild/buf-win32-x64": "1.66.1" }, "bin": { "buf": "bin/buf", "protoc-gen-buf-breaking": "bin/protoc-gen-buf-breaking", "protoc-gen-buf-lint": "bin/protoc-gen-buf-lint" } }, "sha512-TelMLptEDfdcl8D+WjKQLzGGYKAwOSVjCPJ21L9X9xT7KZHigTAhHP5idrTCtXKHHPPQB6Sq12wVnhWgFQ9s7w=="], + + "@bufbuild/buf-darwin-arm64": ["@bufbuild/buf-darwin-arm64@1.66.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Yl7vvFhKrA87ctsIiwN2XWbJq34VxWoEzNBe9dhrutKLpy7hM7l/ylDCuctqPh+CGdSUijQfmHiA4bQgK8s2yQ=="], + + "@bufbuild/buf-darwin-x64": ["@bufbuild/buf-darwin-x64@1.66.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-pRbu0l7jtbXBBCq/7wrs2ZUFzCkDBDGuT3xlQt04k1SgIoW1lznpDnUZ6vbQj1rIU94dCbnsuL7M6k+rCFafOA=="], + + "@bufbuild/buf-linux-aarch64": ["@bufbuild/buf-linux-aarch64@1.66.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-V/dwOAcQ9r5B6BUEtUeVPvkpNxYhmHLILrJZmIqNdjpInVQWknpGm1Mmpy2xANuOhRh7O/Huq7kaoQvNjSdnWQ=="], + + "@bufbuild/buf-linux-armv7": ["@bufbuild/buf-linux-armv7@1.66.1", "", { "os": "linux", "cpu": "arm" }, "sha512-WDmspvGLq/lNVj4jAb2TDWVKLzG9l5nwhNsZOo1I5UKrE0PD2LdbM7YdnMne15AIg3qlUxHqq2Eg8pVyxHqCqw=="], + + "@bufbuild/buf-linux-x64": ["@bufbuild/buf-linux-x64@1.66.1", "", { "os": "linux", "cpu": "x64" }, "sha512-at0JeAlc+pquaNB/80R5F6bQs1gtdSuJRj+naei+lH2L2M1yQTiYU3f9dJnY4hVbq4/JbxSdxqJx2VOLPt1ztw=="], + + "@bufbuild/buf-win32-arm64": ["@bufbuild/buf-win32-arm64@1.66.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-gb2LDuqkY1RSFIOMYJQTimTLIsW2/pqOvcPWBDApUZBOQS5CJdrAtuoBqRzpqrI02OF45uinKmqq+9dun/X92A=="], + + "@bufbuild/buf-win32-x64": ["@bufbuild/buf-win32-x64@1.66.1", "", { "os": "win32", "cpu": "x64" }, "sha512-QzDiv9dAUr89HjHsKOrC5EQB+2aWVwKmhZFYzAGgHTt+ONAgz9RgLhwIm6LTtrLigB0LIHk/nLMIXB3sXUhyrg=="], + + "@bufbuild/protobuf": ["@bufbuild/protobuf@2.11.0", "", {}, "sha512-sBXGT13cpmPR5BMgHE6UEEfEaShh5Ror6rfN3yEK5si7QVrtZg8LEPQb0VVhiLRUslD2yLnXtnRzG035J/mZXQ=="], + + "@bufbuild/protoc-gen-es": ["@bufbuild/protoc-gen-es@2.11.0", "", { "dependencies": { "@bufbuild/protobuf": "2.11.0", "@bufbuild/protoplugin": "2.11.0" }, "bin": { "protoc-gen-es": "bin/protoc-gen-es" } }, "sha512-VzQuwEQDXipbZ1soWUuAWm1Z0C3B/IDWGeysnbX6ogJ6As91C2mdvAND/ekQ4YIWgen4d5nqLfIBOWLqCCjYUA=="], + + "@bufbuild/protoplugin": ["@bufbuild/protoplugin@2.11.0", "", { "dependencies": { "@bufbuild/protobuf": "2.11.0", "@typescript/vfs": "^1.6.2", "typescript": "5.4.5" } }, "sha512-lyZVNFUHArIOt4W0+dwYBe5GBwbKzbOy8ObaloEqsw9Mmiwv2O48TwddDoHN4itylC+BaEGqFdI1W8WQt2vWJQ=="], + "@colors/colors": ["@colors/colors@1.6.0", "", {}, "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA=="], + "@connectrpc/connect": ["@connectrpc/connect@2.1.1", "", { "peerDependencies": { "@bufbuild/protobuf": "^2.7.0" } }, "sha512-JzhkaTvM73m2K1URT6tv53k2RwngSmCXLZJgK580qNQOXRzZRR/BCMfZw3h+90JpnG6XksP5bYT+cz0rpUzUWQ=="], + + "@connectrpc/connect-node": ["@connectrpc/connect-node@2.1.1", "", { "peerDependencies": { "@bufbuild/protobuf": "^2.7.0", "@connectrpc/connect": "2.1.1" } }, "sha512-s3TfsI1XF+n+1z6MBS9rTnFsxxR4Rw5wmdEnkQINli81ESGxcsfaEet8duzq8LVuuCupmhUsgpRo0Nv9pZkufg=="], + + "@connectrpc/protoc-gen-connect-es": ["@connectrpc/protoc-gen-connect-es@1.7.0", "", { "dependencies": { "@bufbuild/protobuf": "^1.10.0", "@bufbuild/protoplugin": "^1.10.0" }, "peerDependencies": { "@bufbuild/protoc-gen-es": "^1.10.0", "@connectrpc/connect": "1.7.0" }, "optionalPeers": ["@bufbuild/protoc-gen-es", "@connectrpc/connect"], "bin": { "protoc-gen-connect-es": "bin/protoc-gen-connect-es" } }, "sha512-g2rE799dxGgXtwSTBOJoSlzCy3HN0IX/Es8uKsCgXRmco8o277/bb5nz1X8TmvBooCBGNdtEdUDG50olcvS9jQ=="], + "@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="], "@dabh/diagnostics": ["@dabh/diagnostics@2.0.8", "", { "dependencies": { "@so-ric/colorspace": "^1.1.6", "enabled": "2.0.x", "kuler": "^2.0.0" } }, "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q=="], @@ -1078,14 +1031,10 @@ "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="], - "@istanbuljs/load-nyc-config": ["@istanbuljs/load-nyc-config@1.1.0", "", { "dependencies": { "camelcase": "^5.3.1", "find-up": "^4.1.0", "get-package-type": "^0.1.0", "js-yaml": "^3.13.1", "resolve-from": "^5.0.0" } }, "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ=="], - "@istanbuljs/schema": ["@istanbuljs/schema@0.1.3", "", {}, "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA=="], "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], - "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], - "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], "@jridgewell/source-map": ["@jridgewell/source-map@0.3.11", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25" } }, "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA=="], @@ -1234,6 +1183,34 @@ "@paralleldrive/cuid2": ["@paralleldrive/cuid2@2.3.1", "", { "dependencies": { "@noble/hashes": "^1.1.5" } }, "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw=="], + "@parcel/watcher": ["@parcel/watcher@2.5.6", "", { "dependencies": { "detect-libc": "^2.0.3", "is-glob": "^4.0.3", "node-addon-api": "^7.0.0", "picomatch": "^4.0.3" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.6", "@parcel/watcher-darwin-arm64": "2.5.6", "@parcel/watcher-darwin-x64": "2.5.6", "@parcel/watcher-freebsd-x64": "2.5.6", "@parcel/watcher-linux-arm-glibc": "2.5.6", "@parcel/watcher-linux-arm-musl": "2.5.6", "@parcel/watcher-linux-arm64-glibc": "2.5.6", "@parcel/watcher-linux-arm64-musl": "2.5.6", "@parcel/watcher-linux-x64-glibc": "2.5.6", "@parcel/watcher-linux-x64-musl": "2.5.6", "@parcel/watcher-win32-arm64": "2.5.6", "@parcel/watcher-win32-ia32": "2.5.6", "@parcel/watcher-win32-x64": "2.5.6" } }, "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ=="], + + "@parcel/watcher-android-arm64": ["@parcel/watcher-android-arm64@2.5.6", "", { "os": "android", "cpu": "arm64" }, "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A=="], + + "@parcel/watcher-darwin-arm64": ["@parcel/watcher-darwin-arm64@2.5.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA=="], + + "@parcel/watcher-darwin-x64": ["@parcel/watcher-darwin-x64@2.5.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg=="], + + "@parcel/watcher-freebsd-x64": ["@parcel/watcher-freebsd-x64@2.5.6", "", { "os": "freebsd", "cpu": "x64" }, "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng=="], + + "@parcel/watcher-linux-arm-glibc": ["@parcel/watcher-linux-arm-glibc@2.5.6", "", { "os": "linux", "cpu": "arm" }, "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ=="], + + "@parcel/watcher-linux-arm-musl": ["@parcel/watcher-linux-arm-musl@2.5.6", "", { "os": "linux", "cpu": "arm" }, "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg=="], + + "@parcel/watcher-linux-arm64-glibc": ["@parcel/watcher-linux-arm64-glibc@2.5.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA=="], + + "@parcel/watcher-linux-arm64-musl": ["@parcel/watcher-linux-arm64-musl@2.5.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA=="], + + "@parcel/watcher-linux-x64-glibc": ["@parcel/watcher-linux-x64-glibc@2.5.6", "", { "os": "linux", "cpu": "x64" }, "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ=="], + + "@parcel/watcher-linux-x64-musl": ["@parcel/watcher-linux-x64-musl@2.5.6", "", { "os": "linux", "cpu": "x64" }, "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg=="], + + "@parcel/watcher-win32-arm64": ["@parcel/watcher-win32-arm64@2.5.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q=="], + + "@parcel/watcher-win32-ia32": ["@parcel/watcher-win32-ia32@2.5.6", "", { "os": "win32", "cpu": "ia32" }, "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g=="], + + "@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.6", "", { "os": "win32", "cpu": "x64" }, "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw=="], + "@pinojs/redact": ["@pinojs/redact@0.4.0", "", {}, "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg=="], "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], @@ -1512,8 +1489,6 @@ "@types/supertest": ["@types/supertest@2.0.11", "", { "dependencies": { "@types/superagent": "*" } }, "sha512-uci4Esokrw9qGb9bvhhSVEjd6rkny/dk5PK/Qz4yxKiyppEI+dOPlNrZBahE3i+PoKFYyDxChVXZ/ysS/nrm1Q=="], - "@types/tap": ["@types/tap@15.0.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-usOLtmMm1jfjPdkt9R9HMTU/J6m9QNYjgFs5U2tFiOzQSd7lDd/aAMprExYLnJnZb6tM4ujmGv7ITe+1smIRgg=="], - "@types/triple-beam": ["@types/triple-beam@1.3.5", "", {}, "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw=="], "@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], @@ -1522,8 +1497,6 @@ "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], - "@types/yoga-layout": ["@types/yoga-layout@1.9.2", "", {}, "sha512-S9q47ByT2pPvD65IvrWp7qppVMpk9WGMbVq9wbWZOHg6tnXSD4vyhao6nOSBwwfDdV2p3Kx9evA9vI+XWTfDvw=="], - "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@4.33.0", "", { "dependencies": { "@typescript-eslint/experimental-utils": "4.33.0", "@typescript-eslint/scope-manager": "4.33.0", "debug": "^4.3.1", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", "regexpp": "^3.1.0", "semver": "^7.3.5", "tsutils": "^3.21.0" }, "peerDependencies": { "@typescript-eslint/parser": "^4.0.0", "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg=="], "@typescript-eslint/experimental-utils": ["@typescript-eslint/experimental-utils@4.33.0", "", { "dependencies": { "@types/json-schema": "^7.0.7", "@typescript-eslint/scope-manager": "4.33.0", "@typescript-eslint/types": "4.33.0", "@typescript-eslint/typescript-estree": "4.33.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, "peerDependencies": { "eslint": "*" } }, "sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q=="], @@ -1562,6 +1535,8 @@ "@typescript/native-preview-win32-x64": ["@typescript/native-preview-win32-x64@7.0.0-dev.20260309.1", "", { "os": "win32", "cpu": "x64" }, "sha512-/rEvAKowcoEdL2VeNju8apkGHEmbat10jIn1Sncny1zIaWvaMFw6bhmny+kKwX+9deitMfo9ihLlo5GCPJuMPQ=="], + "@typescript/vfs": ["@typescript/vfs@1.6.4", "", { "dependencies": { "debug": "^4.4.3" }, "peerDependencies": { "typescript": "*" } }, "sha512-PJFXFS4ZJKiJ9Qiuix6Dz/OwEIqHD7Dme1UwZhTK11vR+5dqW2ACbdndWQexBzCx+CPuMe5WBYQWCsFyGlQLlQ=="], + "@unrs/resolver-binding-android-arm-eabi": ["@unrs/resolver-binding-android-arm-eabi@1.11.1", "", { "os": "android", "cpu": "arm" }, "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw=="], "@unrs/resolver-binding-android-arm64": ["@unrs/resolver-binding-android-arm64@1.11.1", "", { "os": "android", "cpu": "arm64" }, "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g=="], @@ -1634,31 +1609,21 @@ "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], - "aggregate-error": ["aggregate-error@3.1.0", "", { "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" } }, "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA=="], - "ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="], - "ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="], - - "ansi-regex": ["ansi-regex@4.1.1", "", {}, "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="], + "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - "ansicolors": ["ansicolors@0.3.2", "", {}, "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg=="], - "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], "api-parity": ["api-parity@workspace:typescript/api-parity"], "append-field": ["append-field@1.0.0", "", {}, "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="], - "append-transform": ["append-transform@2.0.0", "", { "dependencies": { "default-require-extensions": "^3.0.0" } }, "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg=="], - - "archy": ["archy@1.0.0", "", {}, "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw=="], - "arg": ["arg@4.1.3", "", {}, "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="], - "argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], "aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="], @@ -1682,44 +1647,28 @@ "arraybuffer.prototype.slice": ["arraybuffer.prototype.slice@1.0.4", "", { "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "is-array-buffer": "^3.0.4" } }, "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ=="], - "arrify": ["arrify@2.0.1", "", {}, "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug=="], - "asap": ["asap@2.0.6", "", {}, "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA=="], - "asn1": ["asn1@0.2.6", "", { "dependencies": { "safer-buffer": "~2.1.0" } }, "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ=="], - - "assert-plus": ["assert-plus@1.0.0", "", {}, "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw=="], - "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], "ast-types-flow": ["ast-types-flow@0.0.8", "", {}, "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ=="], "ast-v8-to-istanbul": ["ast-v8-to-istanbul@0.3.12", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.31", "estree-walker": "^3.0.3", "js-tokens": "^10.0.0" } }, "sha512-BRRC8VRZY2R4Z4lFIL35MwNXmwVqBityvOIwETtsCSwvjl0IdgFsy9NhdaA6j74nUdtJJlIypeRhpDam19Wq3g=="], - "astral-regex": ["astral-regex@2.0.0", "", {}, "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ=="], - "async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="], "async-exit-hook": ["async-exit-hook@2.0.1", "", {}, "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw=="], "async-function": ["async-function@1.0.0", "", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="], - "async-hook-domain": ["async-hook-domain@2.0.4", "", {}, "sha512-14LjCmlK1PK8eDtTezR6WX8TMaYNIzBIsd2D1sGoGjgx0BuNMMoSdk7i/drlbtamy0AWv9yv2tkB+ASdmeqFIw=="], - "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], "atomic-sleep": ["atomic-sleep@1.0.0", "", {}, "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ=="], - "auto-bind": ["auto-bind@4.0.0", "", {}, "sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ=="], - "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], "aws-crt": ["aws-crt@1.29.1", "", { "dependencies": { "@aws-sdk/util-utf8-browser": "^3.259.0", "@httptoolkit/websocket-stream": "^6.0.1", "axios": "^1.12.2", "buffer": "^6.0.3", "crypto-js": "^4.2.0", "mqtt": "^4.3.8", "process": "^0.11.10" } }, "sha512-YeuwHbh9SjRkMpaY6hXi8NzNn2yjuwEen8Vg/Wasg4hOoqiwxINwX3g7LZEc+mMCOoxQWOQfTJcaOAbU24HroQ=="], - "aws-sign2": ["aws-sign2@0.7.0", "", {}, "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA=="], - - "aws4": ["aws4@1.13.2", "", {}, "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw=="], - "axe-core": ["axe-core@4.11.1", "", {}, "sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A=="], "axios": ["axios@1.13.6", "", { "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", "proxy-from-env": "^1.1.0" } }, "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ=="], @@ -1734,10 +1683,6 @@ "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], - "baseline-browser-mapping": ["baseline-browser-mapping@2.10.0", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA=="], - - "bcrypt-pbkdf": ["bcrypt-pbkdf@1.0.2", "", { "dependencies": { "tweetnacl": "^0.14.3" } }, "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w=="], - "bcryptjs": ["bcryptjs@2.4.3", "", {}, "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ=="], "before-after-hook": ["before-after-hook@3.0.2", "", {}, "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A=="], @@ -1750,8 +1695,6 @@ "binary-parser": ["binary-parser@2.3.0", "", {}, "sha512-TXOlPrhvi+kZUv3vA1qZ+MBdqZdihF8gWw7j/MRINiJqXH652GtLSqFTHNPjG8kHbiMQSCATG9oat9ao6aA8dA=="], - "bind-obj-methods": ["bind-obj-methods@3.0.0", "", {}, "sha512-nLEaaz3/sEzNSyPWRsN9HNsqwk1AUyECtGj+XwGdIi3xABnEqecvXtIJ0wehQXuuER5uZ/5fTs2usONgYjG+iw=="], - "bindings": ["bindings@1.5.0", "", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="], "bintrees": ["bintrees@1.0.2", "", {}, "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw=="], @@ -1776,8 +1719,6 @@ "broadcast-channel": ["broadcast-channel@3.7.0", "", { "dependencies": { "@babel/runtime": "^7.7.2", "detect-node": "^2.1.0", "js-sha3": "0.8.0", "microseconds": "0.2.0", "nano-time": "1.0.0", "oblivious-set": "1.0.0", "rimraf": "3.0.2", "unload": "2.2.0" } }, "sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg=="], - "browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="], - "bson": ["bson@1.1.6", "", {}, "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg=="], "btoa": ["btoa@1.2.1", "", { "bin": { "btoa": "bin/btoa.js" } }, "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g=="], @@ -1796,30 +1737,16 @@ "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], - "caching-transform": ["caching-transform@4.0.0", "", { "dependencies": { "hasha": "^5.0.0", "make-dir": "^3.0.0", "package-hash": "^4.0.0", "write-file-atomic": "^3.0.0" } }, "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA=="], - "call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="], "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], - "caller-callsite": ["caller-callsite@4.1.0", "", { "dependencies": { "callsites": "^3.1.0" } }, "sha512-99nnnGlJexTc41xwQTr+mWl15OI5PPczUJzM4YRE7QjkefMKCXGa5gfQjCOuVrD+1TjI/fevIDHg2nz3iYN5Ig=="], - - "caller-path": ["caller-path@3.0.1", "", { "dependencies": { "caller-callsite": "^4.1.0" } }, "sha512-fhmztL4wURO/BzwJUJ4aVRdnKEFskPBbrJ8fNgl7XdUiD1ygzzlt+nhPgUBSRq2ciEVubo6x+W8vJQzm55QLLQ=="], - "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], "camel-case": ["camel-case@4.1.2", "", { "dependencies": { "pascal-case": "^3.1.2", "tslib": "^2.0.3" } }, "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw=="], - "camelcase": ["camelcase@5.3.1", "", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="], - - "caniuse-lite": ["caniuse-lite@1.0.30001777", "", {}, "sha512-tmN+fJxroPndC74efCdp12j+0rk0RHwV5Jwa1zWaFVyw2ZxAuPeG8ZgWC3Wz7uSjT3qMRQ5XHZ4COgQmsCMJAQ=="], - - "cardinal": ["cardinal@2.1.1", "", { "dependencies": { "ansicolors": "~0.3.2", "redeyed": "~2.1.0" }, "bin": { "cdl": "./bin/cdl.js" } }, "sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw=="], - - "caseless": ["caseless@0.12.0", "", {}, "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw=="], - "chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="], "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], @@ -1836,21 +1763,13 @@ "chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="], - "ci-info": ["ci-info@2.0.0", "", {}, "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ=="], - "classnames": ["classnames@2.5.1", "", {}, "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow=="], "clean-css": ["clean-css@5.3.3", "", { "dependencies": { "source-map": "~0.6.0" } }, "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg=="], - "clean-stack": ["clean-stack@2.2.0", "", {}, "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A=="], - - "cli-cursor": ["cli-cursor@3.1.0", "", { "dependencies": { "restore-cursor": "^3.1.0" } }, "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw=="], - "cli-progress": ["cli-progress@3.12.0", "", { "dependencies": { "string-width": "^4.2.3" } }, "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A=="], - "cli-truncate": ["cli-truncate@2.1.0", "", { "dependencies": { "slice-ansi": "^3.0.0", "string-width": "^4.2.0" } }, "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg=="], - - "cliui": ["cliui@7.0.4", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" } }, "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ=="], + "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], "clone": ["clone@2.1.2", "", {}, "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w=="], @@ -1864,8 +1783,6 @@ "color-string": ["color-string@2.1.4", "", { "dependencies": { "color-name": "^2.0.0" } }, "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg=="], - "color-support": ["color-support@1.1.3", "", { "bin": { "color-support": "bin.js" } }, "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg=="], - "colorette": ["colorette@2.0.20", "", {}, "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="], "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], @@ -1876,8 +1793,6 @@ "commist": ["commist@1.1.0", "", { "dependencies": { "leven": "^2.1.0", "minimist": "^1.1.0" } }, "sha512-rraC8NXWOEjhADbZe9QBNzLAN5Q3fsTPQtBV+fEVj6xKIgDgNiEVE6ZNfHpZOqfQ21YUzfVNUXLOEZquYvQPPg=="], - "commondir": ["commondir@1.0.1", "", {}, "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg=="], - "component-emitter": ["component-emitter@1.3.1", "", {}, "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ=="], "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], @@ -1908,8 +1823,6 @@ "cosmiconfig": ["cosmiconfig@7.1.0", "", { "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", "parse-json": "^5.0.0", "path-type": "^4.0.0", "yaml": "^1.10.0" } }, "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA=="], - "coveralls": ["coveralls@3.1.1", "", { "dependencies": { "js-yaml": "^3.13.1", "lcov-parse": "^1.0.0", "log-driver": "^1.2.7", "minimist": "^1.2.5", "request": "^2.88.2" }, "bin": { "coveralls": "bin/coveralls.js" } }, "sha512-+dxnG2NHncSD1NrqbSM3dn/lE57O6Qf/koe9+I7c+wzkqRmEvcp0kgJdxKInzYzkICKkFMZsX3Vct3++tsF9ww=="], - "create-require": ["create-require@1.1.1", "", {}, "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ=="], "cron-parser": ["cron-parser@2.18.0", "", { "dependencies": { "is-nan": "^1.3.0", "moment-timezone": "^0.5.31" } }, "sha512-s4odpheTyydAbTBQepsqd2rNWGa2iV3cyo8g7zbI2QQYGLVsfbhmwukayS1XHppe02Oy1fg7mg6xoaraVJeEcg=="], @@ -1950,8 +1863,6 @@ "damerau-levenshtein": ["damerau-levenshtein@1.0.8", "", {}, "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA=="], - "dashdash": ["dashdash@1.14.1", "", { "dependencies": { "assert-plus": "^1.0.0" } }, "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g=="], - "data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="], "data-view-byte-length": ["data-view-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ=="], @@ -1962,8 +1873,6 @@ "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], - "decamelize": ["decamelize@1.2.0", "", {}, "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="], - "decode-named-character-reference": ["decode-named-character-reference@1.3.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q=="], "decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="], @@ -1978,8 +1887,6 @@ "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], - "default-require-extensions": ["default-require-extensions@3.0.1", "", { "dependencies": { "strip-bom": "^4.0.0" } }, "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw=="], - "defaults": ["defaults@1.0.4", "", { "dependencies": { "clone": "^1.0.2" } }, "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A=="], "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], @@ -2042,14 +1949,10 @@ "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], - "ecc-jsbn": ["ecc-jsbn@0.1.2", "", { "dependencies": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" } }, "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw=="], - "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], "ejs": ["ejs@3.1.10", "", { "dependencies": { "jake": "^10.8.5" }, "bin": { "ejs": "bin/cli.js" } }, "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA=="], - "electron-to-chromium": ["electron-to-chromium@1.5.307", "", {}, "sha512-5z3uFKBWjiNR44nFcYdkcXjKMbg5KXNdciu7mhTPo9tB7NbqSNP2sSnGR+fqknZSCwKkBN+oxiiajWs4dT6ORg=="], - "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], "enabled": ["enabled@2.0.0", "", {}, "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ=="], @@ -2086,8 +1989,6 @@ "es-to-primitive": ["es-to-primitive@1.3.0", "", { "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", "is-symbol": "^1.0.4" } }, "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g=="], - "es6-error": ["es6-error@4.1.1", "", {}, "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg=="], - "esbuild": ["esbuild@0.27.3", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.3", "@esbuild/android-arm": "0.27.3", "@esbuild/android-arm64": "0.27.3", "@esbuild/android-x64": "0.27.3", "@esbuild/darwin-arm64": "0.27.3", "@esbuild/darwin-x64": "0.27.3", "@esbuild/freebsd-arm64": "0.27.3", "@esbuild/freebsd-x64": "0.27.3", "@esbuild/linux-arm": "0.27.3", "@esbuild/linux-arm64": "0.27.3", "@esbuild/linux-ia32": "0.27.3", "@esbuild/linux-loong64": "0.27.3", "@esbuild/linux-mips64el": "0.27.3", "@esbuild/linux-ppc64": "0.27.3", "@esbuild/linux-riscv64": "0.27.3", "@esbuild/linux-s390x": "0.27.3", "@esbuild/linux-x64": "0.27.3", "@esbuild/netbsd-arm64": "0.27.3", "@esbuild/netbsd-x64": "0.27.3", "@esbuild/openbsd-arm64": "0.27.3", "@esbuild/openbsd-x64": "0.27.3", "@esbuild/openharmony-arm64": "0.27.3", "@esbuild/sunos-x64": "0.27.3", "@esbuild/win32-arm64": "0.27.3", "@esbuild/win32-ia32": "0.27.3", "@esbuild/win32-x64": "0.27.3" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg=="], "esbuild-android-64": ["esbuild-android-64@0.15.18", "", { "os": "android", "cpu": "x64" }, "sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA=="], @@ -2170,8 +2071,6 @@ "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], - "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], - "esquery": ["esquery@1.7.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g=="], "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], @@ -2186,8 +2085,6 @@ "event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="], - "events-to-array": ["events-to-array@1.1.2", "", {}, "sha512-inRWzRY7nG+aXZxBzEqYKB3HPgwflZRopAjDCHv0whhRx+MTUr1ei0ICZUypdyE0HRm4L2d5VEcIqLD6yl+BFA=="], - "expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="], "expect-type": ["expect-type@1.3.0", "", {}, "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA=="], @@ -2206,8 +2103,6 @@ "extract-pg-schema": ["extract-pg-schema@5.8.1", "", { "dependencies": { "knex": "3.1.0", "knex-pglite": "^0.12.0", "pg": "8.17.2", "pg-query-emscripten": "^5.1.0", "ramda": "^0.31.0" }, "bin": { "extract-pg-schema": "bin/extract-pg-schema" } }, "sha512-rNotRBXxWQnvjcErsX3k9cZwY4KQbVniEp+KAd6RghfDvt29kOtnDdfO7S+Y6b/5tge6caovbJq8s9cgsUtBUA=="], - "extsprintf": ["extsprintf@1.3.0", "", {}, "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g=="], - "fast-content-type-parse": ["fast-content-type-parse@2.0.1", "", {}, "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q=="], "fast-copy": ["fast-copy@4.0.2", "", {}, "sha512-ybA6PDXIXOXivLJK/z9e+Otk7ve13I4ckBvGO5I2RRmBU1gMHLVDJYEuJYhGwez7YNlYji2M2DvVU+a9mSFDlw=="], @@ -2244,29 +2139,21 @@ "finalhandler": ["finalhandler@1.2.0", "", { "dependencies": { "debug": "2.6.9", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", "statuses": "2.0.1", "unpipe": "~1.0.0" } }, "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg=="], - "find-cache-dir": ["find-cache-dir@3.3.2", "", { "dependencies": { "commondir": "^1.0.1", "make-dir": "^3.0.2", "pkg-dir": "^4.1.0" } }, "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig=="], - "find-root": ["find-root@1.1.0", "", {}, "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="], "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], - "findit": ["findit@2.0.0", "", {}, "sha512-ENZS237/Hr8bjczn5eKuBohLgaD0JyUd0arxretR1f9RO46vZHA1b2y0VorgGV3WaOT3c+78P8h7v4JGJ1i/rg=="], - "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], "flatted": ["flatted@3.4.0", "", {}, "sha512-kC6Bb+ooptOIvWj5B63EQWkF0FEnNjV2ZNkLMLZRDDduIiWeFF4iKnslwhiWxjAdbg4NzTNo6h0qLuvFrcx+Sw=="], - "flow-remove-types": ["flow-remove-types@2.304.0", "", { "dependencies": { "hermes-parser": "0.33.3", "pirates": "^3.0.2", "vlq": "^0.2.1" }, "bin": { "flow-remove-types": "flow-remove-types", "flow-node": "flow-node" } }, "sha512-3/c6h0ELxBY5I4MEDig6c0gjlT6GYGfxH0VlpgPOF2/78VaI01RPPnVoVyaixJs8uexsAeycxlxd4feIHPYPrQ=="], - "fn.name": ["fn.name@1.1.0", "", {}, "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="], "follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="], "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], - "foreground-child": ["foreground-child@2.0.0", "", { "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^3.0.2" } }, "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA=="], - - "forever-agent": ["forever-agent@0.6.1", "", {}, "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw=="], + "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], "form-data": ["form-data@3.0.4", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.35" } }, "sha512-f0cRzm6dkyVYV3nPoooP8XlccPQukegwhAnpoLcXy+X+A8KfpGOoXwDr9FLZd3wzgLaBGQBE3lY93Zm/i1JvIQ=="], @@ -2278,12 +2165,8 @@ "fresh": ["fresh@0.5.2", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="], - "fromentries": ["fromentries@1.3.2", "", {}, "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg=="], - "fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="], - "fs-exists-cached": ["fs-exists-cached@1.0.0", "", {}, "sha512-kSxoARUDn4F2RPXX48UXnaFKwVU7Ivd/6qpzZL29MCDmr9sTvybv4gFCp+qaI4fM9m0z9fgz/yJvi56GAz+BZg=="], - "fs-extra": ["fs-extra@7.0.1", "", { "dependencies": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw=="], "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="], @@ -2292,8 +2175,6 @@ "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], - "function-loop": ["function-loop@2.0.1", "", {}, "sha512-ktIR+O6i/4h+j/ZhZJNdzeI4i9lEPeEK6UPR2EVyTVBqOwcU3Za9xYKLH64ZR9HmcROyRrOkizNyjjtWJzDDkQ=="], - "function.prototype.name": ["function.prototype.name@1.1.8", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "functions-have-names": "^1.2.3", "hasown": "^2.0.2", "is-callable": "^1.2.7" } }, "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q=="], "functional-red-black-tree": ["functional-red-black-tree@1.0.1", "", {}, "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g=="], @@ -2302,8 +2183,6 @@ "generator-function": ["generator-function@2.0.1", "", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="], - "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], - "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], @@ -2320,8 +2199,6 @@ "getopts": ["getopts@2.3.0", "", {}, "sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA=="], - "getpass": ["getpass@0.1.7", "", { "dependencies": { "assert-plus": "^1.0.0" } }, "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng=="], - "github-from-package": ["github-from-package@0.0.0", "", {}, "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="], "glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], @@ -2344,10 +2221,6 @@ "grpc-tools": ["grpc-tools@1.13.1", "", { "dependencies": { "@mapbox/node-pre-gyp": "^2.0.0" }, "bin": { "grpc_tools_node_protoc": "bin/protoc.js", "grpc_tools_node_protoc_plugin": "bin/protoc_plugin.js" } }, "sha512-0sttMUxThNIkCTJq5qI0xXMz5zWqV2u3yG1kR3Sj9OokGIoyRBFjoInK9NyW7x5fH7knj48Roh1gq5xbl0VoDQ=="], - "har-schema": ["har-schema@2.0.0", "", {}, "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q=="], - - "har-validator": ["har-validator@5.1.5", "", { "dependencies": { "ajv": "^6.12.3", "har-schema": "^2.0.0" } }, "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w=="], - "has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="], "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], @@ -2362,8 +2235,6 @@ "hash.js": ["hash.js@1.1.7", "", { "dependencies": { "inherits": "^2.0.3", "minimalistic-assert": "^1.0.1" } }, "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA=="], - "hasha": ["hasha@5.2.2", "", { "dependencies": { "is-stream": "^2.0.0", "type-fest": "^0.8.0" } }, "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ=="], - "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], "he": ["he@1.2.0", "", { "bin": { "he": "bin/he" } }, "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="], @@ -2372,10 +2243,6 @@ "help-me": ["help-me@5.0.0", "", {}, "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg=="], - "hermes-estree": ["hermes-estree@0.33.3", "", {}, "sha512-6kzYZHCk8Fy1Uc+t3HGYyJn3OL4aeqKLTyina4UFtWl8I0kSL7OmKThaiX+Uh2f8nGw3mo4Ifxg0M5Zk3/Oeqg=="], - - "hermes-parser": ["hermes-parser@0.33.3", "", { "dependencies": { "hermes-estree": "0.33.3" } }, "sha512-Yg3HgaG4CqgyowtYjX/FsnPAuZdHOqSMtnbpylbptsQ9nwwSKsy6uRWcGO5RK0EqiX12q8HvDWKgeAVajRO5DA=="], - "history": ["history@4.10.1", "", { "dependencies": { "@babel/runtime": "^7.1.2", "loose-envify": "^1.2.0", "resolve-pathname": "^3.0.0", "tiny-invariant": "^1.0.2", "tiny-warning": "^1.0.0", "value-equal": "^1.0.1" } }, "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew=="], "hoist-non-react-statics": ["hoist-non-react-statics@3.3.2", "", { "dependencies": { "react-is": "^16.7.0" } }, "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw=="], @@ -2390,8 +2257,6 @@ "http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="], - "http-signature": ["http-signature@1.2.0", "", { "dependencies": { "assert-plus": "^1.0.0", "jsprim": "^1.2.2", "sshpk": "^1.7.0" } }, "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ=="], - "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], "humanize-duration": ["humanize-duration@3.33.2", "", {}, "sha512-K7Ny/ULO1hDm2nnhvAY+SJV1skxFb61fd073SG1IWJl+D44ULrruCuTyjHKjBVVcSuTlnY99DKtgEG39CM5QOQ=="], @@ -2404,24 +2269,18 @@ "ignore-by-default": ["ignore-by-default@1.0.1", "", {}, "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA=="], - "immutable": ["immutable@4.3.8", "", {}, "sha512-d/Ld9aLbKpNwyl0KiM2CT1WYvkitQ1TSvmRtkcV8FKStiDoA7Slzgjmb/1G2yhKM1p0XeNOieaTbFZmU1d3Xuw=="], + "immutable": ["immutable@5.1.5", "", {}, "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A=="], "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], - "import-jsx": ["import-jsx@4.0.1", "", { "dependencies": { "@babel/core": "^7.5.5", "@babel/plugin-proposal-object-rest-spread": "^7.5.5", "@babel/plugin-transform-destructuring": "^7.5.0", "@babel/plugin-transform-react-jsx": "^7.3.0", "caller-path": "^3.0.1", "find-cache-dir": "^3.2.0", "make-dir": "^3.0.2", "resolve-from": "^3.0.0", "rimraf": "^3.0.0" } }, "sha512-2Cj4nWRuAmvokFRU6UNo3xgzXKh+4nq/LBtD6mTp3V9c9nYV7O+dRvPChPOM34Qcj1+Ijz3oK6HqkixG0GP9Rg=="], - "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], - "indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="], - "inflight": ["inflight@1.0.6", "", { "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA=="], "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], "ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], - "ink": ["ink@2.7.1", "", { "dependencies": { "ansi-escapes": "^4.2.1", "arrify": "^2.0.1", "auto-bind": "^4.0.0", "chalk": "^3.0.0", "cli-cursor": "^3.1.0", "cli-truncate": "^2.1.0", "is-ci": "^2.0.0", "lodash.throttle": "^4.1.1", "log-update": "^3.0.0", "prop-types": "^15.6.2", "react-reconciler": "^0.24.0", "scheduler": "^0.18.0", "signal-exit": "^3.0.2", "slice-ansi": "^3.0.0", "string-length": "^3.1.0", "widest-line": "^3.1.0", "wrap-ansi": "^6.2.0", "yoga-layout-prebuilt": "^1.9.3" }, "peerDependencies": { "@types/react": ">=16.8.0", "react": ">=16.8.0" }, "optionalPeers": ["@types/react"] }, "sha512-s7lJuQDJEdjqtaIWhp3KYHl6WV3J04U9zoQ6wVc+Xoa06XM27SXUY57qC5DO46xkF0CfgXMKkKNcgvSu/SAEpA=="], - "inline-style-parser": ["inline-style-parser@0.1.1", "", {}, "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q=="], "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="], @@ -2454,8 +2313,6 @@ "is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="], - "is-ci": ["is-ci@2.0.0", "", { "dependencies": { "ci-info": "^2.0.0" }, "bin": { "is-ci": "bin.js" } }, "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w=="], - "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], "is-data-view": ["is-data-view@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" } }, "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw=="], @@ -2500,16 +2357,12 @@ "is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="], - "is-typedarray": ["is-typedarray@1.0.0", "", {}, "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="], - "is-weakmap": ["is-weakmap@2.0.2", "", {}, "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w=="], "is-weakref": ["is-weakref@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew=="], "is-weakset": ["is-weakset@2.0.4", "", { "dependencies": { "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ=="], - "is-windows": ["is-windows@1.0.2", "", {}, "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA=="], - "isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="], "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], @@ -2518,16 +2371,8 @@ "isomorphic-ws": ["isomorphic-ws@4.0.1", "", { "peerDependencies": { "ws": "*" } }, "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w=="], - "isstream": ["isstream@0.1.2", "", {}, "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g=="], - "istanbul-lib-coverage": ["istanbul-lib-coverage@3.2.2", "", {}, "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg=="], - "istanbul-lib-hook": ["istanbul-lib-hook@3.0.0", "", { "dependencies": { "append-transform": "^2.0.0" } }, "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ=="], - - "istanbul-lib-instrument": ["istanbul-lib-instrument@4.0.3", "", { "dependencies": { "@babel/core": "^7.7.5", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.0.0", "semver": "^6.3.0" } }, "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ=="], - - "istanbul-lib-processinfo": ["istanbul-lib-processinfo@2.0.3", "", { "dependencies": { "archy": "^1.0.0", "cross-spawn": "^7.0.3", "istanbul-lib-coverage": "^3.2.0", "p-map": "^3.0.0", "rimraf": "^3.0.0", "uuid": "^8.3.2" } }, "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg=="], - "istanbul-lib-report": ["istanbul-lib-report@3.0.1", "", { "dependencies": { "istanbul-lib-coverage": "^3.0.0", "make-dir": "^4.0.0", "supports-color": "^7.1.0" } }, "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw=="], "istanbul-lib-source-maps": ["istanbul-lib-source-maps@5.0.6", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.23", "debug": "^4.1.1", "istanbul-lib-coverage": "^3.0.0" } }, "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A=="], @@ -2536,7 +2381,7 @@ "iterator.prototype": ["iterator.prototype@1.1.5", "", { "dependencies": { "define-data-property": "^1.1.4", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "get-proto": "^1.0.0", "has-symbols": "^1.1.0", "set-function-name": "^2.0.2" } }, "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g=="], - "jackspeak": ["jackspeak@1.4.2", "", { "dependencies": { "cliui": "^7.0.4" } }, "sha512-GHeGTmnuaHnvS+ZctRB01bfxARuu9wW83ENbuiweu07SFcVlZrJpcshSre/keGT7YGBhLHg/+rXCNSrsEHKU4Q=="], + "jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="], "jake": ["jake@10.9.4", "", { "dependencies": { "async": "^3.2.6", "filelist": "^1.0.4", "picocolors": "^1.1.1" }, "bin": { "jake": "bin/cli.js" } }, "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA=="], @@ -2548,9 +2393,7 @@ "js-tokens": ["js-tokens@10.0.0", "", {}, "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q=="], - "js-yaml": ["js-yaml@3.14.2", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="], - - "jsbn": ["jsbn@0.1.1", "", {}, "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg=="], + "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], @@ -2560,20 +2403,14 @@ "json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="], - "json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="], - "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], - "json-stringify-safe": ["json-stringify-safe@5.0.1", "", {}, "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="], - "json5": ["json5@2.2.0", "", { "dependencies": { "minimist": "^1.2.5" }, "bin": { "json5": "lib/cli.js" } }, "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA=="], "jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="], - "jsprim": ["jsprim@1.4.2", "", { "dependencies": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", "json-schema": "0.4.0", "verror": "1.10.0" } }, "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw=="], - "jsx-ast-utils": ["jsx-ast-utils@3.3.5", "", { "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", "object.assign": "^4.1.4", "object.values": "^1.1.6" } }, "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ=="], "kanel": ["kanel@3.16.1", "", { "dependencies": { "@kristiandupont/recase": "^1.2.1", "chalk": "4.1.2", "cli-progress": "^3.11.2", "extract-pg-schema": "^5.8.1", "optionator": "^0.9.1", "pg": "^8.10.0", "ramda": "^0.32.0", "rimraf": "^6.0.0", "tagged-comment-parser": "^1.3.5" }, "bin": { "kanel": "bin/kanel" } }, "sha512-DDc0dTrsdBWpq15Q9dw8DB2pKYOHl23QDHI6vrB01v1Fy4TGv2LZoLUi4eE9iBarEWE3DAQ3GTWq5arsDG2nSQ=="], @@ -2596,14 +2433,10 @@ "language-tags": ["language-tags@1.0.9", "", { "dependencies": { "language-subtag-registry": "^0.3.20" } }, "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA=="], - "lcov-parse": ["lcov-parse@1.0.0", "", { "bin": { "lcov-parse": "./bin/cli.js" } }, "sha512-aprLII/vPzuQvYZnDRU78Fns9I2Ag3gi4Ipga/hxnVMCZC8DnR2nI7XBqrPoywGfxqIx/DgarGvDJZAD3YBTgQ=="], - "leven": ["leven@2.1.0", "", {}, "sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA=="], "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], - "libtap": ["libtap@1.4.1", "", { "dependencies": { "async-hook-domain": "^2.0.4", "bind-obj-methods": "^3.0.0", "diff": "^4.0.2", "function-loop": "^2.0.1", "minipass": "^3.1.5", "own-or": "^1.0.0", "own-or-env": "^1.0.2", "signal-exit": "^3.0.4", "stack-utils": "^2.0.4", "tap-parser": "^11.0.0", "tap-yaml": "^1.0.0", "tcompare": "^5.0.6", "trivial-deferred": "^1.0.1" } }, "sha512-S9v19shLTigoMn3c02V7LZ4t09zxmVP3r3RbEAwuHFYeKgF+ESFJxoQ0PMFKW4XdgQhcjVBEwDoopG6WROq/gw=="], - "lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], "lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="], @@ -2642,8 +2475,6 @@ "lodash.flatten": ["lodash.flatten@4.4.0", "", {}, "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g=="], - "lodash.flattendeep": ["lodash.flattendeep@4.4.0", "", {}, "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ=="], - "lodash.get": ["lodash.get@4.4.2", "", {}, "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ=="], "lodash.isarguments": ["lodash.isarguments@3.1.0", "", {}, "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg=="], @@ -2652,12 +2483,6 @@ "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], - "lodash.throttle": ["lodash.throttle@4.1.1", "", {}, "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ=="], - - "log-driver": ["log-driver@1.2.7", "", {}, "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg=="], - - "log-update": ["log-update@3.4.0", "", { "dependencies": { "ansi-escapes": "^3.2.0", "cli-cursor": "^2.1.0", "wrap-ansi": "^5.0.0" } }, "sha512-ILKe88NeMt4gmDvk/eb615U/IVn7K9KWGkoYbdatQ69Z65nj1ZzjM6fHXfcs0Uge+e+EGnMW7DY4T9yko8vWFg=="], - "logform": ["logform@2.7.0", "", { "dependencies": { "@colors/colors": "1.6.0", "@types/triple-beam": "^1.3.2", "fecha": "^4.2.0", "ms": "^2.1.1", "safe-stable-stringify": "^2.3.1", "triple-beam": "^1.3.0" } }, "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ=="], "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="], @@ -2676,7 +2501,7 @@ "magicast": ["magicast@0.3.5", "", { "dependencies": { "@babel/parser": "^7.25.4", "@babel/types": "^7.25.4", "source-map-js": "^1.2.0" } }, "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ=="], - "make-dir": ["make-dir@3.1.0", "", { "dependencies": { "semver": "^6.0.0" } }, "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw=="], + "make-dir": ["make-dir@4.0.0", "", { "dependencies": { "semver": "^7.5.3" } }, "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw=="], "make-error": ["make-error@1.3.6", "", {}, "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="], @@ -2758,8 +2583,6 @@ "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], - "mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], - "mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="], "mini-create-react-context": ["mini-create-react-context@0.3.3", "", { "dependencies": { "@babel/runtime": "^7.12.1", "tiny-warning": "^1.0.3" }, "peerDependencies": { "prop-types": "^15.0.0", "react": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-TtF6hZE59SGmS4U8529qB+jJFeW6asTLDIpPgvPLSCsooAwJS7QprHIFTqv9/Qh3NdLwQxFYgiHX5lqb6jqzPA=="], @@ -2770,7 +2593,7 @@ "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], - "minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + "minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], "minizlib": ["minizlib@3.1.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="], @@ -2838,6 +2661,8 @@ "node-abi": ["node-abi@3.87.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ=="], + "node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="], + "node-cache": ["node-cache@5.1.2", "", { "dependencies": { "clone": "2.x" } }, "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg=="], "node-exports-info": ["node-exports-info@1.6.0", "", { "dependencies": { "array.prototype.flatmap": "^1.3.3", "es-errors": "^1.3.0", "object.entries": "^1.1.9", "semver": "^6.3.1" } }, "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw=="], @@ -2850,12 +2675,6 @@ "node-mocks-http": ["node-mocks-http@1.11.0", "", { "dependencies": { "accepts": "^1.3.7", "content-disposition": "^0.5.3", "depd": "^1.1.0", "fresh": "^0.5.2", "merge-descriptors": "^1.0.1", "methods": "^1.1.2", "mime": "^1.3.4", "parseurl": "^1.3.3", "range-parser": "^1.2.0", "type-is": "^1.6.18" } }, "sha512-jS/WzSOcKbOeGrcgKbenZeNhxUNnP36Yw11+hL4TTxQXErGfqYZ+MaYNNvhaTiGIJlzNSqgQkk9j8dSu1YWSuw=="], - "node-modules-regexp": ["node-modules-regexp@1.0.0", "", {}, "sha512-JMaRS9L4wSRIR+6PTVEikTrq/lMGEZR43a48ETeilY0Q0iMwVnccMFrUM1k+tNzmYuIU0Vh710bCUqHX+/+ctQ=="], - - "node-preload": ["node-preload@0.2.1", "", { "dependencies": { "process-on-spawn": "^1.0.0" } }, "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ=="], - - "node-releases": ["node-releases@2.0.36", "", {}, "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA=="], - "nodemailer": ["nodemailer@7.0.11", "", {}, "sha512-gnXhNRE0FNhD7wPSCGhdNh46Hs6nm+uTyg+Kq0cZukNQiYdnCsoQjodNP9BQVG9XrcK/v6/MgpAPBUFyzh9pvw=="], "nodemon": ["nodemon@2.0.22", "", { "dependencies": { "chokidar": "^3.5.2", "debug": "^3.2.7", "ignore-by-default": "^1.0.1", "minimatch": "^3.1.2", "pstree.remy": "^1.1.8", "semver": "^5.7.1", "simple-update-notifier": "^1.0.7", "supports-color": "^5.5.0", "touch": "^3.1.0", "undefsafe": "^2.0.5" }, "bin": { "nodemon": "bin/nodemon.js" } }, "sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ=="], @@ -2868,10 +2687,6 @@ "number-allocator": ["number-allocator@1.0.14", "", { "dependencies": { "debug": "^4.3.1", "js-sdsl": "4.3.0" } }, "sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA=="], - "nyc": ["nyc@15.1.0", "", { "dependencies": { "@istanbuljs/load-nyc-config": "^1.0.0", "@istanbuljs/schema": "^0.1.2", "caching-transform": "^4.0.0", "convert-source-map": "^1.7.0", "decamelize": "^1.2.0", "find-cache-dir": "^3.2.0", "find-up": "^4.1.0", "foreground-child": "^2.0.0", "get-package-type": "^0.1.0", "glob": "^7.1.6", "istanbul-lib-coverage": "^3.0.0", "istanbul-lib-hook": "^3.0.0", "istanbul-lib-instrument": "^4.0.0", "istanbul-lib-processinfo": "^2.0.2", "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.0.2", "make-dir": "^3.0.0", "node-preload": "^0.2.1", "p-map": "^3.0.0", "process-on-spawn": "^1.0.0", "resolve-from": "^5.0.0", "rimraf": "^3.0.0", "signal-exit": "^3.0.2", "spawn-wrap": "^2.0.0", "test-exclude": "^6.0.0", "yargs": "^15.0.2" }, "bin": { "nyc": "bin/nyc.js" } }, "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A=="], - - "oauth-sign": ["oauth-sign@0.9.0", "", {}, "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="], - "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], @@ -2900,29 +2715,17 @@ "one-time": ["one-time@1.0.0", "", { "dependencies": { "fn.name": "1.x.x" } }, "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g=="], - "onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], - - "opener": ["opener@1.5.2", "", { "bin": { "opener": "bin/opener-bin.js" } }, "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A=="], - "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], "ow": ["ow@0.27.0", "", { "dependencies": { "@sindresorhus/is": "^4.0.1", "callsites": "^3.1.0", "dot-prop": "^6.0.1", "lodash.isequal": "^4.5.0", "type-fest": "^1.2.1", "vali-date": "^1.0.0" } }, "sha512-SGnrGUbhn4VaUGdU0EJLMwZWSupPmF46hnTRII7aCLCrqixTAC5eKo8kI4/XXf1eaaI8YEVT+3FeGNJI9himAQ=="], "own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="], - "own-or": ["own-or@1.0.0", "", {}, "sha512-NfZr5+Tdf6MB8UI9GLvKRs4cXY8/yB0w3xtt84xFdWy8hkGjn+JFc60VhzS/hFRfbyxFcGYMTjnF4Me+RbbqrA=="], - - "own-or-env": ["own-or-env@1.0.2", "", { "dependencies": { "own-or": "^1.0.0" } }, "sha512-NQ7v0fliWtK7Lkb+WdFqe6ky9XAzYmlkXthQrBbzlYbmFKoAYbDDcwmOm6q8kOuwSRXW8bdL5ORksploUJmWgw=="], - "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], - "p-map": ["p-map@3.0.0", "", { "dependencies": { "aggregate-error": "^3.0.0" } }, "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ=="], - - "p-try": ["p-try@2.2.0", "", {}, "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="], - - "package-hash": ["package-hash@4.0.0", "", { "dependencies": { "graceful-fs": "^4.1.15", "hasha": "^5.0.0", "lodash.flattendeep": "^4.4.0", "release-zalgo": "^1.0.0" } }, "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ=="], + "p-map": ["p-map@2.1.0", "", {}, "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw=="], "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], @@ -2960,8 +2763,6 @@ "pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], - "performance-now": ["performance-now@2.1.0", "", {}, "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="], - "pg": ["pg@8.20.0", "", { "dependencies": { "pg-connection-string": "^2.12.0", "pg-pool": "^3.13.0", "pg-protocol": "^1.13.0", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.3.0" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA=="], "pg-cloudflare": ["pg-cloudflare@1.3.0", "", {}, "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ=="], @@ -2992,10 +2793,6 @@ "pino-std-serializers": ["pino-std-serializers@7.1.0", "", {}, "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw=="], - "pirates": ["pirates@3.0.2", "", { "dependencies": { "node-modules-regexp": "^1.0.0" } }, "sha512-c5CgUJq6H2k6MJz72Ak1F5sN9n9wlSlJyEnwvpm9/y3WB4E3pHBDT2c6PEiS1vyJvq2bUxUAIu0EGf8Cx4Ic7Q=="], - - "pkg-dir": ["pkg-dir@4.2.0", "", { "dependencies": { "find-up": "^4.0.0" } }, "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ=="], - "plimit-lit": ["plimit-lit@1.6.1", "", { "dependencies": { "queue-lit": "^1.5.1" } }, "sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA=="], "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], @@ -3024,8 +2821,6 @@ "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="], - "process-on-spawn": ["process-on-spawn@1.1.0", "", { "dependencies": { "fromentries": "^1.2.0" } }, "sha512-JOnOPQ/8TZgjs1JIH/m9ni7FfimjNa/PRx7y/Wb5qdItsnhO0jE4AT7fC0HjC28DUQWDr50dwSYZLdRMlqDq3Q=="], - "process-warning": ["process-warning@5.0.0", "", {}, "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA=="], "prom-client": ["prom-client@15.1.3", "", { "dependencies": { "@opentelemetry/api": "^1.4.0", "tdigest": "^0.1.1" } }, "sha512-6ZiOBfCywsD4k1BN9IX0uZhF+tJkV8q8llP64G5Hajs4JOeVLPCwpPVcpXy3BwYiUGgyJzsJJQeOIv7+hDSq8g=="], @@ -3046,8 +2841,6 @@ "prudence": ["prudence@0.10.0", "", {}, "sha512-QPyNYQR4tx+9xp+GuceW55MWeZIC/nNT3qWUmU8eij0FmO90x3L08oY9bJiIW1weB7mvH63JBra1cY+GH70YKw=="], - "psl": ["psl@1.15.0", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w=="], - "pstree.remy": ["pstree.remy@1.1.8", "", {}, "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w=="], "pump": ["pump@3.0.4", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA=="], @@ -3098,8 +2891,6 @@ "react-query": ["react-query@3.18.1", "", { "dependencies": { "@babel/runtime": "^7.5.5", "broadcast-channel": "^3.4.1", "match-sorter": "^6.0.2" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0" } }, "sha512-17lv3pQxU9n+cB5acUv0/cxNTjo9q8G+RsedC6Ax4V9D8xEM7Q5xf9xAbCPdEhDrrnzPjTls9fQEABKRSi7OJA=="], - "react-reconciler": ["react-reconciler@0.24.0", "", { "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", "prop-types": "^15.6.2", "scheduler": "^0.18.0" }, "peerDependencies": { "react": "^16.0.0" } }, "sha512-gAGnwWkf+NOTig9oOowqid9O0HjTDC+XVGBCAmJYYJ2A2cN/O4gDdIuuUQjv8A4v6GDwVfJkagpBBLW5OW9HSw=="], - "react-router": ["react-router@5.1.2", "", { "dependencies": { "@babel/runtime": "^7.1.2", "history": "^4.9.0", "hoist-non-react-statics": "^3.1.0", "loose-envify": "^1.3.1", "mini-create-react-context": "^0.3.0", "path-to-regexp": "^1.7.0", "prop-types": "^15.6.2", "react-is": "^16.6.0", "tiny-invariant": "^1.0.2", "tiny-warning": "^1.0.0" }, "peerDependencies": { "react": ">=15" } }, "sha512-yjEuMFy1ONK246B+rsa0cUam5OeAQ8pyclRDgpxuSCrAlJ1qN9uZ5IgyKC7gQg0w8OM50NXHEegPh/ks9YuR2A=="], "react-router-bootstrap": ["react-router-bootstrap@0.25.0", "", { "dependencies": { "prop-types": "^15.5.10" }, "peerDependencies": { "react": ">=0.14.0", "react-router-dom": ">=4.0.0" } }, "sha512-/22eqxjn6Zv5fvY2rZHn57SKmjmJfK7xzJ6/G1OgxAjLtKVfWgV5sn41W2yiqzbtV5eE4/i4LeDLBGYTqx7jbA=="], @@ -3118,8 +2909,6 @@ "rechoir": ["rechoir@0.8.0", "", { "dependencies": { "resolve": "^1.20.0" } }, "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ=="], - "redeyed": ["redeyed@2.1.1", "", { "dependencies": { "esprima": "~4.0.0" } }, "sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ=="], - "redis": ["redis@3.1.2", "", { "dependencies": { "denque": "^1.5.0", "redis-commands": "^1.7.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0" } }, "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw=="], "redis-commands": ["redis-commands@1.7.0", "", {}, "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ=="], @@ -3138,32 +2927,24 @@ "relateurl": ["relateurl@0.2.7", "", {}, "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog=="], - "release-zalgo": ["release-zalgo@1.0.0", "", { "dependencies": { "es6-error": "^4.0.1" } }, "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA=="], - "remark-parse": ["remark-parse@10.0.2", "", { "dependencies": { "@types/mdast": "^3.0.0", "mdast-util-from-markdown": "^1.0.0", "unified": "^10.0.0" } }, "sha512-3ydxgHa/ZQzG8LvC7jTXccARYDcRld3VfcgIIFs7bI6vbRSxJJmzgLEIIoYKyrfhaY+ujuWaf/PJiMZXoiCXgw=="], "remark-rehype": ["remark-rehype@9.1.0", "", { "dependencies": { "@types/hast": "^2.0.0", "@types/mdast": "^3.0.0", "mdast-util-to-hast": "^11.0.0", "unified": "^10.0.0" } }, "sha512-oLa6YmgAYg19zb0ZrBACh40hpBLteYROaPLhBXzLgjqyHQrN+gVP9N/FJvfzuNNuzCutktkroXEZBrxAxKhh7Q=="], "remove-accents": ["remove-accents@0.5.0", "", {}, "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A=="], - "request": ["request@2.88.2", "", { "dependencies": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", "caseless": "~0.12.0", "combined-stream": "~1.0.6", "extend": "~3.0.2", "forever-agent": "~0.6.1", "form-data": "~2.3.2", "har-validator": "~5.1.3", "http-signature": "~1.2.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", "json-stringify-safe": "~5.0.1", "mime-types": "~2.1.19", "oauth-sign": "~0.9.0", "performance-now": "^2.1.0", "qs": "~6.5.2", "safe-buffer": "^5.1.2", "tough-cookie": "~2.5.0", "tunnel-agent": "^0.6.0", "uuid": "^3.3.2" } }, "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw=="], - "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], - "require-main-filename": ["require-main-filename@2.0.0", "", {}, "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="], - "require_optional": ["require_optional@1.0.1", "", { "dependencies": { "resolve-from": "^2.0.0", "semver": "^5.1.0" } }, "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g=="], "resolve": ["resolve@2.0.0-next.6", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "node-exports-info": "^1.6.0", "object-keys": "^1.1.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA=="], - "resolve-from": ["resolve-from@3.0.0", "", {}, "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw=="], + "resolve-from": ["resolve-from@2.0.0", "", {}, "sha512-qpFcKaXsq8+oRoLilkwyc7zHGF5i9Q2/25NIgLQQ/+VVv9rU4qvr6nXVAw1DsnXJyQkZsR4Ytfbtg5ehfcUssQ=="], "resolve-pathname": ["resolve-pathname@3.0.0", "", {}, "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng=="], "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], - "restore-cursor": ["restore-cursor@3.1.0", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA=="], - "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], @@ -3196,7 +2977,7 @@ "saslprep": ["saslprep@1.0.3", "", { "dependencies": { "sparse-bitfield": "^3.0.3" } }, "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag=="], - "sass": ["sass@1.64.1", "", { "dependencies": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", "source-map-js": ">=0.6.2 <2.0.0" }, "bin": { "sass": "sass.js" } }, "sha512-16rRACSOFEE8VN7SCgBu1MpYCyN7urj9At898tyzdXFhC+a+yOX5dXwAR7L8/IdPJ1NB8OYoXmD55DM30B2kEQ=="], + "sass": ["sass@1.98.0", "", { "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.1.5", "source-map-js": ">=0.6.2 <2.0.0" }, "optionalDependencies": { "@parcel/watcher": "^2.4.1" }, "bin": { "sass": "sass.js" } }, "sha512-+4N/u9dZ4PrgzGgPlKnaaRQx64RO0JBKs9sDhQ2pLgN6JQZ25uPQZKQYaBJU48Kd5BxgXoJ4e09Dq7nMcOUW3A=="], "scheduler": ["scheduler@0.20.2", "", { "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" } }, "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ=="], @@ -3210,8 +2991,6 @@ "serve-static": ["serve-static@1.16.0", "", { "dependencies": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "0.18.0" } }, "sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA=="], - "set-blocking": ["set-blocking@2.0.0", "", {}, "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="], - "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], "set-function-name": ["set-function-name@2.0.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="], @@ -3236,7 +3015,7 @@ "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="], - "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], "simple-concat": ["simple-concat@1.0.1", "", {}, "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="], @@ -3246,8 +3025,6 @@ "slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], - "slice-ansi": ["slice-ansi@3.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", "is-fullwidth-code-point": "^3.0.0" } }, "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ=="], - "snappy": ["snappy@7.3.3", "", { "optionalDependencies": { "@napi-rs/snappy-android-arm-eabi": "7.3.3", "@napi-rs/snappy-android-arm64": "7.3.3", "@napi-rs/snappy-darwin-arm64": "7.3.3", "@napi-rs/snappy-darwin-x64": "7.3.3", "@napi-rs/snappy-freebsd-x64": "7.3.3", "@napi-rs/snappy-linux-arm-gnueabihf": "7.3.3", "@napi-rs/snappy-linux-arm64-gnu": "7.3.3", "@napi-rs/snappy-linux-arm64-musl": "7.3.3", "@napi-rs/snappy-linux-ppc64-gnu": "7.3.3", "@napi-rs/snappy-linux-riscv64-gnu": "7.3.3", "@napi-rs/snappy-linux-s390x-gnu": "7.3.3", "@napi-rs/snappy-linux-x64-gnu": "7.3.3", "@napi-rs/snappy-linux-x64-musl": "7.3.3", "@napi-rs/snappy-openharmony-arm64": "7.3.3", "@napi-rs/snappy-wasm32-wasi": "7.3.3", "@napi-rs/snappy-win32-arm64-msvc": "7.3.3", "@napi-rs/snappy-win32-ia32-msvc": "7.3.3", "@napi-rs/snappy-win32-x64-msvc": "7.3.3" } }, "sha512-UDJVCunvgblRpfTOjo/uT7pQzfrTsSICJ4yVS4aq7SsGBaUSpJwaVP15nF//jqinSLpN7boe/BqbUmtWMTQ5MQ=="], "sonic-boom": ["sonic-boom@4.2.1", "", { "dependencies": { "atomic-sleep": "^1.0.0" } }, "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q=="], @@ -3262,20 +3039,12 @@ "sparse-bitfield": ["sparse-bitfield@3.0.3", "", { "dependencies": { "memory-pager": "^1.0.2" } }, "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ=="], - "spawn-wrap": ["spawn-wrap@2.0.0", "", { "dependencies": { "foreground-child": "^2.0.0", "is-windows": "^1.0.2", "make-dir": "^3.0.0", "rimraf": "^3.0.0", "signal-exit": "^3.0.2", "which": "^2.0.1" } }, "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg=="], - "split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="], - "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], - - "sshpk": ["sshpk@1.18.0", "", { "dependencies": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", "bcrypt-pbkdf": "^1.0.0", "dashdash": "^1.12.0", "ecc-jsbn": "~0.1.1", "getpass": "^0.1.1", "jsbn": "~0.1.0", "safer-buffer": "^2.0.2", "tweetnacl": "~0.14.0" }, "bin": { "sshpk-conv": "bin/sshpk-conv", "sshpk-sign": "bin/sshpk-sign", "sshpk-verify": "bin/sshpk-verify" } }, "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ=="], - "stable-hash": ["stable-hash@0.0.5", "", {}, "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA=="], "stack-trace": ["stack-trace@0.0.10", "", {}, "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg=="], - "stack-utils": ["stack-utils@2.0.6", "", { "dependencies": { "escape-string-regexp": "^2.0.0" } }, "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ=="], - "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], "standard-as-callback": ["standard-as-callback@2.1.0", "", {}, "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A=="], @@ -3290,8 +3059,6 @@ "streamsearch": ["streamsearch@1.1.0", "", {}, "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="], - "string-length": ["string-length@3.1.0", "", { "dependencies": { "astral-regex": "^1.0.0", "strip-ansi": "^5.2.0" } }, "sha512-Ttp5YvkGm5v9Ijagtaz1BnN+k9ObpvS0eIBblPMp2YWL8FBmi9qblQ9fexc2k/CXFgrTIteU3jAw3payCnwSTA=="], - "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -3310,7 +3077,7 @@ "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], - "strip-ansi": ["strip-ansi@5.2.0", "", { "dependencies": { "ansi-regex": "^4.1.0" } }, "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA=="], + "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], "strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], @@ -3360,14 +3127,6 @@ "tagged-comment-parser": ["tagged-comment-parser@1.3.8", "", {}, "sha512-HYkOQ/8ha113ln/Vp48wiDTPtbB59AWOdU44AXjhLyJLJjWWfn2kjnXUcyWLQepwbixA/mEeo1iW2Z9OwaBClQ=="], - "tap": ["tap@workspace:typescript-vendor/tap"], - - "tap-mocha-reporter": ["tap-mocha-reporter@5.0.4", "", { "dependencies": { "color-support": "^1.1.0", "debug": "^4.1.1", "diff": "^4.0.1", "escape-string-regexp": "^2.0.0", "glob": "^7.0.5", "tap-parser": "^11.0.0", "tap-yaml": "^1.0.0", "unicode-length": "^2.0.2" }, "bin": { "tap-mocha-reporter": "index.js" } }, "sha512-J+YMO8B7lq1O6Zxd/jeuG27vJ+Y4tLiRMKPSb7KR6FVh86k3Rq1TwYc2GKPyIjCbzzdMdReh3Vfz9L5cg1Z2Bw=="], - - "tap-parser": ["tap-parser@10.1.0", "", { "dependencies": { "events-to-array": "^1.0.1", "minipass": "^3.0.0", "tap-yaml": "^1.0.0" }, "bin": { "tap-parser": "bin/cmd.js" } }, "sha512-FujQeciDaOiOvaIVGS1Rpb0v4R6XkOjvWCWowlz5oKuhPkEJ8U6pxgqt38xuzYhPt8dWEnfHn2jqpZdJEkW7pA=="], - - "tap-yaml": ["tap-yaml@1.0.2", "", { "dependencies": { "yaml": "^1.10.2" } }, "sha512-GegASpuqBnRNdT1U+yuUPZ8rEU64pL35WPBpCISWwff4dErS2/438barz7WFJl4Nzh3Y05tfPidZnH+GaV1wMg=="], - "tar": ["tar@7.5.10", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-8mOPs1//5q/rlkNSPcCegA6hiHJYDmSLEI8aMH/CdSQJNWztHC9WHNam5zdQlfpTwB9Xp7IBEsHfV5LKMJGVAw=="], "tar-fs": ["tar-fs@2.1.4", "", { "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^2.1.4" } }, "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ=="], @@ -3376,8 +3135,6 @@ "tarn": ["tarn@3.0.2", "", {}, "sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ=="], - "tcompare": ["tcompare@5.0.7", "", { "dependencies": { "diff": "^4.0.2" } }, "sha512-d9iddt6YYGgyxJw5bjsN7UJUO1kGOtjSlNy/4PoGYAjQS5pAT/hzIoLf1bZCw+uUxRmZJh7Yy1aA7xKVRT9B4w=="], - "tdigest": ["tdigest@0.1.2", "", { "dependencies": { "bintrees": "1.0.2" } }, "sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA=="], "terser": ["terser@5.46.0", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" } }, "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg=="], @@ -3414,16 +3171,10 @@ "touch": ["touch@3.1.1", "", { "bin": { "nodetouch": "bin/nodetouch.js" } }, "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA=="], - "tough-cookie": ["tough-cookie@2.5.0", "", { "dependencies": { "psl": "^1.1.28", "punycode": "^2.1.1" } }, "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g=="], - "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], - "treport": ["treport@2.0.2", "", { "dependencies": { "cardinal": "^2.1.1", "chalk": "^3.0.0", "import-jsx": "^4.0.0", "ink": "^2.6.0", "ms": "^2.1.2", "string-length": "^3.1.0", "tap-parser": "^10.0.1", "unicode-length": "^2.0.2" }, "peerDependencies": { "react": "^16.8.6" } }, "sha512-AnHKgHMy3II7Arfvf1tSHAwv9rzcvgbWrOixFJgdExVKd0mMsOp9wD2LGP9RbXy9j8AZoerBVu3OR2Uz9MpUJw=="], - "triple-beam": ["triple-beam@1.4.1", "", {}, "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg=="], - "trivial-deferred": ["trivial-deferred@1.1.2", "", {}, "sha512-vDPiDBC3hyP6O4JrJYMImW3nl3c03Tsj9fEXc7Qc/XKa1O7gf5ZtFfIR/E0dun9SnDHdwjna1Z2rSzYgqpxh/g=="], - "trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="], "ts-api-utils": ["ts-api-utils@2.4.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA=="], @@ -3444,8 +3195,6 @@ "tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="], - "tweetnacl": ["tweetnacl@0.14.5", "", {}, "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA=="], - "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], "type-fest": ["type-fest@1.4.0", "", {}, "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA=="], @@ -3462,8 +3211,6 @@ "typedarray": ["typedarray@0.0.6", "", {}, "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="], - "typedarray-to-buffer": ["typedarray-to-buffer@3.1.5", "", { "dependencies": { "is-typedarray": "^1.0.0" } }, "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q=="], - "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], "typescript-eslint": ["typescript-eslint@8.56.1", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.56.1", "@typescript-eslint/parser": "8.56.1", "@typescript-eslint/typescript-estree": "8.56.1", "@typescript-eslint/utils": "8.56.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-U4lM6pjmBX7J5wk4szltF7I1cGBHXZopnAXCMXb3+fZ3B/0Z3hq3wS/CCUB2NZBNAExK92mCU2tEohWuwVMsDQ=="], @@ -3482,8 +3229,6 @@ "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "unicode-length": ["unicode-length@2.1.0", "", { "dependencies": { "punycode": "^2.0.0" } }, "sha512-4bV582zTV9Q02RXBxSUMiuN/KHo5w4aTojuKTNT96DIKps/SIawFp7cS5Mu25VuY1AioGXrmYyzKZUzh8OqoUw=="], - "unified": ["unified@10.1.2", "", { "dependencies": { "@types/unist": "^2.0.0", "bail": "^2.0.0", "extend": "^3.0.0", "is-buffer": "^2.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^5.0.0" } }, "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q=="], "unist-builder": ["unist-builder@3.0.1", "", { "dependencies": { "@types/unist": "^2.0.0" } }, "sha512-gnpOw7DIpCA0vpr6NqdPvTWnlPTApCTRzr+38E6hCWx3rz/cjo83SsKIlS1Z+L5ttScQ2AwutNnb8+tAvpb6qQ=="], @@ -3512,8 +3257,6 @@ "unrs-resolver": ["unrs-resolver@1.11.1", "", { "dependencies": { "napi-postinstall": "^0.3.0" }, "optionalDependencies": { "@unrs/resolver-binding-android-arm-eabi": "1.11.1", "@unrs/resolver-binding-android-arm64": "1.11.1", "@unrs/resolver-binding-darwin-arm64": "1.11.1", "@unrs/resolver-binding-darwin-x64": "1.11.1", "@unrs/resolver-binding-freebsd-x64": "1.11.1", "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-musl": "1.11.1", "@unrs/resolver-binding-wasm32-wasi": "1.11.1", "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg=="], - "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], - "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], "url-polyfill": ["url-polyfill@1.1.14", "", {}, "sha512-p4f3TTAG6ADVF3mwbXw7hGw+QJyw5CnNGvYh5fCuQQZIiuKUswqcznyV3pGDP9j0TSmC4UvRKm8kl1QsX1diiQ=="], @@ -3540,8 +3283,6 @@ "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], - "verror": ["verror@1.10.0", "", { "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", "extsprintf": "^1.2.0" } }, "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw=="], - "vfile": ["vfile@5.3.7", "", { "dependencies": { "@types/unist": "^2.0.0", "is-buffer": "^2.0.0", "unist-util-stringify-position": "^3.0.0", "vfile-message": "^3.0.0" } }, "sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g=="], "vfile-message": ["vfile-message@3.1.4", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-stringify-position": "^3.0.0" } }, "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw=="], @@ -3554,8 +3295,6 @@ "vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], - "vlq": ["vlq@0.2.3", "", {}, "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow=="], - "warning": ["warning@4.0.3", "", { "dependencies": { "loose-envify": "^1.0.0" } }, "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w=="], "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], @@ -3576,14 +3315,10 @@ "which-collection": ["which-collection@1.0.2", "", { "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", "is-weakmap": "^2.0.2", "is-weakset": "^2.0.3" } }, "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw=="], - "which-module": ["which-module@2.0.1", "", {}, "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ=="], - "which-typed-array": ["which-typed-array@1.1.20", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg=="], "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="], - "widest-line": ["widest-line@3.1.0", "", { "dependencies": { "string-width": "^4.0.0" } }, "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg=="], - "winston": ["winston@3.5.1", "", { "dependencies": { "@dabh/diagnostics": "^2.0.2", "async": "^3.2.3", "is-stream": "^2.0.0", "logform": "^2.3.2", "one-time": "^1.0.0", "readable-stream": "^3.4.0", "safe-stable-stringify": "^2.3.1", "stack-trace": "0.0.x", "triple-beam": "^1.3.0", "winston-transport": "^4.4.2" } }, "sha512-tbRtVy+vsSSCLcZq/8nXZaOie/S2tPXPFt4be/Q3vI/WtYwm7rrwidxVw2GRa38FIXcJ1kUM6MOZ9Jmnk3F3UA=="], "winston-loki": ["winston-loki@6.1.4", "", { "dependencies": { "async-exit-hook": "2.0.1", "btoa": "^1.2.1", "protobufjs": "^7.2.4", "url-polyfill": "^1.1.12", "winston-transport": "^4.3.0" }, "optionalDependencies": { "snappy": "^7.2.2" } }, "sha512-/j/Zf7TGLjgSBck29BkPnpJlEnGQr5xqlx8A0N6LZnXYYYvyK7lFk6FPpWiD+VMO8xjaxOu1KNF9SzWaRDsigA=="], @@ -3592,34 +3327,30 @@ "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], - "wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="], + "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], "wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], - "write-file-atomic": ["write-file-atomic@3.0.3", "", { "dependencies": { "imurmurhash": "^0.1.4", "is-typedarray": "^1.0.0", "signal-exit": "^3.0.2", "typedarray-to-buffer": "^3.1.5" } }, "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q=="], - "ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="], "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], - "y18n": ["y18n@4.0.3", "", {}, "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="], + "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], - "yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], + "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], "yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="], - "yargs": ["yargs@15.4.1", "", { "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", "find-up": "^4.1.0", "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", "yargs-parser": "^18.1.2" } }, "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A=="], + "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], - "yargs-parser": ["yargs-parser@18.1.3", "", { "dependencies": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" } }, "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ=="], + "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], "yn": ["yn@3.1.1", "", {}, "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q=="], "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], - "yoga-layout-prebuilt": ["yoga-layout-prebuilt@1.10.0", "", { "dependencies": { "@types/yoga-layout": "1.9.2" } }, "sha512-YnOmtSbv4MTf7RGJMK0FvZ+KD8OEe/J5BNnR0GHhD8J/XcG/Qvxgszm0Un6FTHWW4uHlTgP0IztiXQnGyIR45g=="], - "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], "@ampproject/remapping/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], @@ -3648,17 +3379,11 @@ "@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], - "@babel/core/convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], - - "@babel/core/json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], - - "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - "@babel/generator/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], - "@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], + "@connectrpc/protoc-gen-connect-es/@bufbuild/protobuf": ["@bufbuild/protobuf@1.10.1", "", {}, "sha512-wJ8ReQbHxsAfXhrf9ixl0aYbZorRuOWpBNzm8pL8ftmSxQx/wnJD5Eg861NwJU/czy2VXFIebCeZnZrI9rktIQ=="], - "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "@connectrpc/protoc-gen-connect-es/@bufbuild/protoplugin": ["@bufbuild/protoplugin@1.10.1", "", { "dependencies": { "@bufbuild/protobuf": "1.10.1", "@typescript/vfs": "^1.4.0", "typescript": "4.5.2" } }, "sha512-LaSbfwabAFIvbVnbn8jWwElRoffCIxhVraO8arliVwWupWezHLXgqPHEYLXZY/SsAR+/YsFBQJa8tAGtNPJyaQ=="], "@discordjs/rest/discord-api-types": ["discord-api-types@0.18.1", "", {}, "sha512-hNC38R9ZF4uaujaZQtQfm5CdQO58uhdkoHQAVvMfIL0LgOSZeW575W8H6upngQOuoxWd8tiRII3LLJm9zuQKYg=="], @@ -3670,12 +3395,8 @@ "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], - "@eslint/eslintrc/js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], - "@eslint/eslintrc/strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], - "@grpc/proto-loader/yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], - "@httptoolkit/websocket-stream/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], @@ -3684,16 +3405,8 @@ "@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], - "@isaacs/fs-minipass/minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], - - "@istanbuljs/load-nyc-config/find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="], - - "@istanbuljs/load-nyc-config/resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="], - "@jridgewell/gen-mapping/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], - "@jridgewell/remapping/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], - "@jridgewell/source-map/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], "@kristiandupont/recase/ramda": ["ramda@0.30.1", "", {}, "sha512-tEF5I22zJnuclswcZMc8bDIrwRHRzf+NqVEmqg50ShAZMP7MWeR/RGDthfM/p+BlqvF2fXAzpn8i+SJcYD3alw=="], @@ -3770,8 +3483,6 @@ "@vitest/mocker/vite": ["vite@3.2.11", "", { "dependencies": { "esbuild": "^0.15.9", "postcss": "^8.4.18", "resolve": "^1.22.1", "rollup": "^2.79.1" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@types/node": ">= 14", "less": "*", "sass": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "sass", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-K/jGKL/PgbIgKCiJo5QbASQhFiV02X9Jh+Qq0AKCRCRKZtOTVi4t6wh75FDpGf2N9rYOnzH87OEFQNaFy6pdxQ=="], - "ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="], - "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], "ast-v8-to-istanbul/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], @@ -3806,10 +3517,6 @@ "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], - "cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - - "cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], - "color/color-convert": ["color-convert@3.1.3", "", { "dependencies": { "color-name": "^2.0.0" } }, "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg=="], "color-string/color-name": ["color-name@2.1.0", "", {}, "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg=="], @@ -3818,8 +3525,6 @@ "css-select/domutils": ["domutils@2.8.0", "", { "dependencies": { "dom-serializer": "^1.0.1", "domelementtype": "^2.2.0", "domhandler": "^4.2.0" } }, "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A=="], - "default-require-extensions/strip-bom": ["strip-bom@4.0.0", "", {}, "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w=="], - "defaults/clone": ["clone@1.0.4", "", {}, "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg=="], "discord.js/@discordjs/builders": ["@discordjs/builders@0.11.0", "", { "dependencies": { "@sindresorhus/is": "^4.2.0", "discord-api-types": "^0.26.0", "ts-mixer": "^6.0.0", "tslib": "^2.3.1", "zod": "^3.11.6" } }, "sha512-ZTB8yJdJKrKlq44dpWkNUrAtEJEq0gqpb7ASdv4vmq6/mZal5kOv312hQ56I/vxwMre+VIkoHquNUAfnTbiYtg=="], @@ -3882,28 +3587,14 @@ "formik/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="], - "hasha/type-fest": ["type-fest@0.8.1", "", {}, "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA=="], - "hoist-non-react-statics/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], "htmlparser2/entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="], "import-fresh/resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], - "ink/chalk": ["chalk@3.0.0", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg=="], - - "ink/prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], - - "ink/scheduler": ["scheduler@0.18.0", "", { "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" } }, "sha512-agTSHR1Nbfi6ulI0kYNK0203joW2Y5W4po4l+v03tOoiJKpTBbxpNhWDvqc/4IcOw+KLmSiQLTasZ4cab2/UWQ=="], - - "ioredis/p-map": ["p-map@2.1.0", "", {}, "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw=="], - "isomorphic-fetch/node-fetch": ["node-fetch@1.7.3", "", { "dependencies": { "encoding": "^0.1.11", "is-stream": "^1.0.1" } }, "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ=="], - "istanbul-lib-instrument/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - - "istanbul-lib-report/make-dir": ["make-dir@4.0.0", "", { "dependencies": { "semver": "^7.5.3" } }, "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw=="], - "istanbul-lib-source-maps/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], "kanel/rimraf": ["rimraf@6.1.3", "", { "dependencies": { "glob": "^13.0.3", "package-json-from-dist": "^1.0.1" }, "bin": { "rimraf": "dist/esm/bin.mjs" } }, "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA=="], @@ -3920,22 +3611,12 @@ "knex/resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="], - "libtap/tap-parser": ["tap-parser@11.0.2", "", { "dependencies": { "events-to-array": "^1.0.1", "minipass": "^3.1.6", "tap-yaml": "^1.0.0" }, "bin": { "tap-parser": "bin/cmd.js" } }, "sha512-6qGlC956rcORw+fg7Fv1iCRAY8/bU9UabUAhs3mXRH6eRmVZcNPLheSXCYaVaYeSwx5xa/1HXZb1537YSvwDZg=="], - - "log-update/ansi-escapes": ["ansi-escapes@3.2.0", "", {}, "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ=="], - - "log-update/cli-cursor": ["cli-cursor@2.1.0", "", { "dependencies": { "restore-cursor": "^2.0.0" } }, "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw=="], - - "log-update/wrap-ansi": ["wrap-ansi@5.1.0", "", { "dependencies": { "ansi-styles": "^3.2.0", "string-width": "^3.0.0", "strip-ansi": "^5.0.0" } }, "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q=="], - "loose-envify/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], - "make-dir/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "lru-cache/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - "minizlib/minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], - "mqtt/duplexify": ["duplexify@4.1.3", "", { "dependencies": { "end-of-stream": "^1.4.1", "inherits": "^2.0.3", "readable-stream": "^3.1.1", "stream-shift": "^1.0.2" } }, "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA=="], "mqtt/help-me": ["help-me@3.0.0", "", { "dependencies": { "glob": "^7.1.6", "readable-stream": "^3.6.0" } }, "sha512-hx73jClhyk910sidBB7ERlnhMlFsJJIBqSVMFDwPN8o2v9nmp5KgLq1Xz1Bf1fCMMZ6mPrX159iG0VLy/fPMtQ=="], @@ -3962,22 +3643,10 @@ "nodemon/supports-color": ["supports-color@5.5.0", "", { "dependencies": { "has-flag": "^3.0.0" } }, "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="], - "nyc/find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="], - - "nyc/istanbul-lib-source-maps": ["istanbul-lib-source-maps@4.0.1", "", { "dependencies": { "debug": "^4.1.1", "istanbul-lib-coverage": "^3.0.0", "source-map": "^0.6.1" } }, "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw=="], - - "nyc/resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="], - - "nyc/test-exclude": ["test-exclude@6.0.0", "", { "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", "minimatch": "^3.0.4" } }, "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w=="], - "parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], "path-scurry/lru-cache": ["lru-cache@11.2.6", "", {}, "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ=="], - "path-scurry/minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], - - "pkg-dir/find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="], - "postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], "prop-types-extra/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], @@ -3994,10 +3663,6 @@ "react-markdown/prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], - "react-reconciler/prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], - - "react-reconciler/scheduler": ["scheduler@0.18.0", "", { "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" } }, "sha512-agTSHR1Nbfi6ulI0kYNK0203joW2Y5W4po4l+v03tOoiJKpTBbxpNhWDvqc/4IcOw+KLmSiQLTasZ4cab2/UWQ=="], - "react-router/path-to-regexp": ["path-to-regexp@1.9.0", "", { "dependencies": { "isarray": "0.0.1" } }, "sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g=="], "react-router/prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], @@ -4016,14 +3681,6 @@ "rechoir/resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], - "request/form-data": ["form-data@2.3.3", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", "mime-types": "^2.1.12" } }, "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ=="], - - "request/qs": ["qs@6.5.5", "", {}, "sha512-mzR4sElr1bfCaPJe7m8ilJ6ZXdDaGoObcYR0ZHSsktM/Lt21MVHj5De30GQH2eiZ1qGRTO7LCAzQsUeXTNexWQ=="], - - "request/uuid": ["uuid@3.4.0", "", { "bin": { "uuid": "./bin/uuid" } }, "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="], - - "require_optional/resolve-from": ["resolve-from@2.0.0", "", {}, "sha512-qpFcKaXsq8+oRoLilkwyc7zHGF5i9Q2/25NIgLQQ/+VVv9rU4qvr6nXVAw1DsnXJyQkZsR4Ytfbtg5ehfcUssQ=="], - "require_optional/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], "rolldown/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.9", "", {}, "sha512-w6oiRWgEBl04QkFZgmW+jnU1EC9b57Oihi2ot3HNWIQRqgHp5PnYDia5iZ5FF7rpa4EQdiqMDXjlqKGXBhsoXw=="], @@ -4032,6 +3689,8 @@ "safe-push-apply/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], + "sass/chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], + "send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], "send/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="], @@ -4042,20 +3701,10 @@ "simple-update-notifier/semver": ["semver@7.0.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A=="], - "stack-utils/escape-string-regexp": ["escape-string-regexp@2.0.0", "", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="], - - "string-length/astral-regex": ["astral-regex@1.0.0", "", {}, "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg=="], - "string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], - "string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - "string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], - "string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - - "strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "strip-literal/js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="], "superagent/form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="], @@ -4064,20 +3713,10 @@ "superagent/qs": ["qs@6.13.0", "", { "dependencies": { "side-channel": "^1.0.6" } }, "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg=="], - "tachi-common/@types/tap": ["@types/tap@15.0.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-MaP+EgZNFAGRvVjHWv8ldrLvYBn4PnmAlzY7IL3/RPAPkOXdggTSTgLFONbnTpdQTe8+ixYGAySKAm9ESlZm1A=="], - - "tachi-server/@types/tap": ["@types/tap@15.0.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-MaP+EgZNFAGRvVjHWv8ldrLvYBn4PnmAlzY7IL3/RPAPkOXdggTSTgLFONbnTpdQTe8+ixYGAySKAm9ESlZm1A=="], - - "tap-mocha-reporter/escape-string-regexp": ["escape-string-regexp@2.0.0", "", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="], - - "tap-mocha-reporter/tap-parser": ["tap-parser@11.0.2", "", { "dependencies": { "events-to-array": "^1.0.1", "minipass": "^3.1.6", "tap-yaml": "^1.0.0" }, "bin": { "tap-parser": "bin/cmd.js" } }, "sha512-6qGlC956rcORw+fg7Fv1iCRAY8/bU9UabUAhs3mXRH6eRmVZcNPLheSXCYaVaYeSwx5xa/1HXZb1537YSvwDZg=="], + "tachi-server/lodash": ["lodash@4.17.23", "", {}, "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w=="], "tar/chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], - "tar/minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], - - "tar/yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], - "tar-stream/bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="], "terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], @@ -4086,8 +3725,6 @@ "test-exclude/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="], - "treport/chalk": ["chalk@3.0.0", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg=="], - "tsc-alias/commander": ["commander@9.0.0", "", {}, "sha512-JJfP2saEKbQqvW+FI93OYUB4ByV5cizMpFMiiJI8xDbBvQvSkIk0VvQdn1CZ8mqAO8Loq2h0gYTYtDFUZUeERw=="], "tsconfig-paths/@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="], @@ -4104,8 +3741,6 @@ "uvu/diff": ["diff@5.2.2", "", {}, "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A=="], - "verror/core-util-is": ["core-util-is@1.0.2", "", {}, "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="], - "vite-node/vite": ["vite@7.3.1", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="], "vite-plugin-html/fs-extra": ["fs-extra@10.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ=="], @@ -4116,30 +3751,12 @@ "which-builtin-type/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], - "wrap-ansi/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - - "wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - - "yargs/cliui": ["cliui@6.0.0", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^6.2.0" } }, "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ=="], - - "yargs/find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="], - "@aws-crypto/crc32/@aws-sdk/types/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "@aws-crypto/util/@aws-sdk/types/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "@aws-crypto/util/@aws-sdk/util-utf8-browser/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - "@babel/helper-compilation-targets/lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], - - "@eslint/eslintrc/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], - - "@grpc/proto-loader/yargs/cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], - - "@grpc/proto-loader/yargs/y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], - - "@grpc/proto-loader/yargs/yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], - "@httptoolkit/websocket-stream/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], "@httptoolkit/websocket-stream/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], @@ -4148,8 +3765,6 @@ "@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], - "@istanbuljs/load-nyc-config/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], - "@octokit/auth-app/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@25.1.0", "", {}, "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA=="], "@octokit/auth-oauth-app/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@25.1.0", "", {}, "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA=="], @@ -4198,8 +3813,6 @@ "body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], - "cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "color/color-convert/color-name": ["color-name@2.1.0", "", {}, "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg=="], "css-select/domutils/dom-serializer": ["dom-serializer@1.4.1", "", { "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.2.0", "entities": "^2.0.0" } }, "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag=="], @@ -4226,28 +3839,16 @@ "finalhandler/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], - "ink/prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], - "isomorphic-fetch/node-fetch/is-stream": ["is-stream@1.1.0", "", {}, "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ=="], "kanel/rimraf/glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="], "knex/debug/ms": ["ms@2.1.2", "", {}, "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="], - "log-update/cli-cursor/restore-cursor": ["restore-cursor@2.0.0", "", { "dependencies": { "onetime": "^2.0.0", "signal-exit": "^3.0.2" } }, "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q=="], - - "log-update/wrap-ansi/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], - - "log-update/wrap-ansi/string-width": ["string-width@3.1.0", "", { "dependencies": { "emoji-regex": "^7.0.1", "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^5.1.0" } }, "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w=="], - "mv/rimraf/glob": ["glob@6.0.4", "", { "dependencies": { "inflight": "^1.0.4", "inherits": "2", "minimatch": "2 || 3", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A=="], "nodemon/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="], - "nyc/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], - - "pkg-dir/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], - "react-async-script/prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], "react-bootstrap/prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], @@ -4256,8 +3857,6 @@ "react-markdown/prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], - "react-reconciler/prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], - "react-router-bootstrap/prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], "react-router-dom/prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], @@ -4268,22 +3867,14 @@ "react-transition-group/prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], + "sass/chokidar/readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], + "send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], "serve-static/send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], - "string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - - "string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - - "test-exclude/glob/foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], - - "test-exclude/glob/jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="], - "test-exclude/glob/minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="], - "test-exclude/glob/minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], - "test-exclude/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], "test-exclude/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="], @@ -4310,20 +3901,6 @@ "vite-plugin-html/fs-extra/universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="], - "wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - - "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - - "yargs/cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - - "yargs/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], - - "@grpc/proto-loader/yargs/cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - - "@grpc/proto-loader/yargs/cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], - - "@istanbuljs/load-nyc-config/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], - "@types/minimatch/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], "@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="], @@ -4342,24 +3919,8 @@ "kanel/rimraf/glob/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="], - "kanel/rimraf/glob/minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], - - "log-update/cli-cursor/restore-cursor/onetime": ["onetime@2.0.1", "", { "dependencies": { "mimic-fn": "^1.0.0" } }, "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ=="], - - "log-update/wrap-ansi/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], - - "log-update/wrap-ansi/string-width/emoji-regex": ["emoji-regex@7.0.3", "", {}, "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA=="], - - "log-update/wrap-ansi/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@2.0.0", "", {}, "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w=="], - - "nyc/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], - - "pkg-dir/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], - "serve-static/send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], - "test-exclude/glob/foreground-child/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], - "test-exclude/glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], "test-exclude/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], @@ -4378,32 +3939,14 @@ "typescript-eslint/@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="], - "yargs/cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - - "yargs/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], - - "@grpc/proto-loader/yargs/cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - - "@istanbuljs/load-nyc-config/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], - "@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], "@typescript-eslint/utils/@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], "kanel/rimraf/glob/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="], - "log-update/cli-cursor/restore-cursor/onetime/mimic-fn": ["mimic-fn@1.2.0", "", {}, "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="], - - "log-update/wrap-ansi/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], - - "nyc/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], - - "pkg-dir/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], - "typescript-eslint/@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], - "yargs/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], - "kanel/rimraf/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], } } diff --git a/db/migrations/20260301154256_genesis.sql b/db/migrations/20260301154256_genesis.sql index 208668885..5a5372883 100644 --- a/db/migrations/20260301154256_genesis.sql +++ b/db/migrations/20260301154256_genesis.sql @@ -179,8 +179,12 @@ CREATE TABLE "account" ( sm_youtube TEXT, sm_twitch TEXT, + bd_alpha BOOLEAN NOT NULL DEFAULT FALSE, + bd_beta BOOLEAN NOT NULL DEFAULT FALSE, + bd_dev_team BOOLEAN NOT NULL DEFAULT FALSE, + joined TIMESTAMPTZ NOT NULL DEFAULT NOW(), - about TEXT CHECK (LENGTH(about) <= 2000), + about TEXT CHECK (LENGTH(about) <= 2000) NOT NULL, status TEXT CHECK (LENGTH(status) <= 140), custom_pfp_location TEXT, @@ -188,7 +192,8 @@ CREATE TABLE "account" ( last_seen TIMESTAMPTZ NOT NULL DEFAULT NOW(), - auth_level AUTH_LEVEL NOT NULL DEFAULT 'user' + auth_level AUTH_LEVEL NOT NULL DEFAULT 'user', + is_supporter BOOLEAN NOT NULL DEFAULT FALSE ); -- ==> Another Zenith Essential @@ -234,13 +239,6 @@ CREATE TABLE "account_following" ( CHECK (user_id != followee) ); -CREATE TABLE "account_badge" ( - user_id BIGINT REFERENCES account(id) NOT NULL, - badge ACCOUNT_BADGE_KIND NOT NULL, - - PRIMARY KEY (user_id, badge) -); - CREATE TABLE "account_username_change" ( row_id UUID PRIMARY KEY NOT NULL DEFAULT uuidv7(), @@ -274,7 +272,10 @@ CREATE TABLE "priv_api_client" ( pm_manage_challenges BOOLEAN, api_key_template TEXT CHECK (api_key_template ~ '%%TACHI_KEY%%'), - api_key_filename TEXT + api_key_filename TEXT, + webhook_uri TEXT, + redirect_uri TEXT, + is_builtin BOOLEAN NOT NULL DEFAULT FALSE ); CREATE TABLE "priv_api_token" ( diff --git a/dev/bootstrap.sh b/dev/bootstrap.sh index 0f0493999..acbdb7b3d 100755 --- a/dev/bootstrap.sh +++ b/dev/bootstrap.sh @@ -31,15 +31,9 @@ function mvExampleFiles { cp --update=none typescript/client/example/.env typescript/client/.env - cp --update=none typescript/server/example/conf.json5 typescript/server/conf.json5 - cp --update=none typescript/server/example/.env typescript/server/.env - mkdir -p typescript/server/local-cdn cp -r --update=none typescript/server/example/default-cdn-contents/* typescript/server/local-cdn - cp --update=none typescript/bot/example/conf.json5 typescript/bot/conf.json5 - cp --update=none typescript/bot/example/example.env typescript/bot/.env - echo "Moved!" } diff --git a/package.json b/package.json index ef16785ec..1d1effc19 100644 --- a/package.json +++ b/package.json @@ -10,12 +10,13 @@ "author": "zk", "devDependencies": { "@biomejs/biome": "^2.0.6", + "@bufbuild/buf": "^1.66.1", + "@bufbuild/protoc-gen-es": "^2.11.0", + "@connectrpc/protoc-gen-connect-es": "^1.7.0", "@types/node": "20.19.37", - "@types/tap": "15.0.3", "eslint": "^9.20.1", "kanel": "^3.16.1", "kanel-kysely": "^0.8.0", - "tap": "workspace:*", "ts-node": "10.9.1", "tsc-alias": "1.7.1", "typescript": "5.9.3" @@ -43,7 +44,7 @@ "@types/react-router-dom": "5.1.7", "@vitejs/plugin-react": "^6.0.1", "bootstrap-scss": "^5.3.0", - "sass": "1.64.1", + "sass": "^1.77.0", "vite": "^8.0.0", "@babel/core": "7.1.0", "@fortawesome/fontawesome-free": "5.13.0", @@ -89,7 +90,6 @@ "rg-stats": "1.2.1", "sync-fetch": "^0.3.1", "vite-plugin-html": "^3.2.0", - "@types/tap": "15.0.5", "@types/express-serve-static-core": "4.17.29", "@types/mongodb": "3.6.20", "zod": "^4.3.6", @@ -247,5 +247,10 @@ }, "engines": { "bun": ">=1.2.0" + }, + "dependencies": { + "@bufbuild/protobuf": "^2.11.0", + "@connectrpc/connect": "^2.1.1", + "@connectrpc/connect-node": "^2.1.1" } } diff --git a/tsconfig.json b/tsconfig.json index 8a1ed80ef..a6ac42e7a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,11 +1,12 @@ { "files": [], "references": [ + { "path": "./typescript/api-parity" }, { "path": "./typescript/bliss" }, { "path": "./typescript/common" }, { "path": "./typescript/db" }, { "path": "./typescript/db-migration-engine" }, - { "path": "./typescript/db-migration-engine-cli" }, + { "path": "./typescript/db-cli" }, { "path": "./typescript/server" }, { "path": "./typescript/bot" }, { "path": "./typescript/client" }, diff --git a/typescript-vendor/tap/.gitattributes b/typescript-vendor/tap/.gitattributes deleted file mode 100644 index 22d91ea64..000000000 --- a/typescript-vendor/tap/.gitattributes +++ /dev/null @@ -1,11 +0,0 @@ -# Set the default behavior, in case people don't have core.autocrlf set -* text=auto - -# Require Unix line endings -* text eol=lf - -# Denote all files that are truly binary and should not be modified. -*.png binary -*.jpg binary -*.ico binary -*.gif binary diff --git a/typescript-vendor/tap/.gitignore b/typescript-vendor/tap/.gitignore deleted file mode 100644 index 0f08d2eec..000000000 --- a/typescript-vendor/tap/.gitignore +++ /dev/null @@ -1,27 +0,0 @@ -/* -/.* - -!bin/ -!lib/ -!types/ -!settings.js -!docs/ -/docs/public -/docs/node_modules -/docs/.cache -!package.json -!package-lock.json -!README.md -!CONTRIBUTING.md -!LICENSE -!CHANGELOG.md -!example/ -!scripts/ -!tap-snapshots/ -!test/ -!.travis.yml -!.gitignore -!.gitattributes -!coverage-map.js -!postpublish.sh -!netlify.toml diff --git a/typescript-vendor/tap/CHANGELOG.md b/typescript-vendor/tap/CHANGELOG.md deleted file mode 100644 index 1ce0c0784..000000000 --- a/typescript-vendor/tap/CHANGELOG.md +++ /dev/null @@ -1,2 +0,0 @@ -Please see [the tap website](http://www.node-tap.org/changelog/) for -the curated changelog. diff --git a/typescript-vendor/tap/CONTRIBUTING.md b/typescript-vendor/tap/CONTRIBUTING.md deleted file mode 100644 index 50d60e621..000000000 --- a/typescript-vendor/tap/CONTRIBUTING.md +++ /dev/null @@ -1,13 +0,0 @@ -Please consider signing [the neveragain.tech pledge](http://neveragain.tech/) - -- Check the [issues](https://github.com/tapjs/node-tap/issues) to see - stuff that is likely to be accepted. -- Every patch should have a new test that fails without the patch and - passes with the patch. -- All tests should pass on Node 8 and above. If some tests have to be - skipped for very old Node versions that's fine, but the functionality - should still work as intended. -- Run `npm run snap` to re-generate the output tests whenever output is - changed. However, when you do this, make sure to check the change to - ensure that it's what you intended, and that it didn't cause any other - inadvertent changes. diff --git a/typescript-vendor/tap/LICENSE b/typescript-vendor/tap/LICENSE deleted file mode 100644 index 19129e315..000000000 --- a/typescript-vendor/tap/LICENSE +++ /dev/null @@ -1,15 +0,0 @@ -The ISC License - -Copyright (c) Isaac Z. Schlueter and Contributors - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR -IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/typescript-vendor/tap/README.md b/typescript-vendor/tap/README.md deleted file mode 100644 index 8b65d4a1e..000000000 --- a/typescript-vendor/tap/README.md +++ /dev/null @@ -1,158 +0,0 @@ -# node-tap - -A TAP test framework for -Node.js. - -![Build Status](https://github.com/tapjs/node-tap/workflows/ci/badge.svg) - -_Just wanna see some code? [Get started!](http://www.node-tap.org/basics/)_ - -It includes a command line test runner for consuming TAP-generating test -scripts, and a JavaScript framework for writing such scripts. - -* [Getting started guide](http://www.node-tap.org/basics/) -* Built-in [test coverage](http://www.node-tap.org/coverage/) -* Many [reporter formats](http://www.node-tap.org/reporting/) -* Extensive [API](http://www.node-tap.org/api/) featuring: - * Great [promise support](http://www.node-tap.org/promises/) - * Comprehensive [assert library](http://www.node-tap.org/asserts/) - * Other [advanced stuff](http://www.node-tap.org/advanced/) - * Mocha-like [BDD DSL](http://www.node-tap.org/mochalike/) - * [Parallel Testing](http://www.node-tap.org/parallel/) -* [Command-line interface](http://www.node-tap.org/cli/) for running tests - (whether they use node-tap or not) - -See [the changelog](http://www.node-tap.org/changelog/) for recent updates, -or just get started with [the basics](http://www.node-tap.org/basics/). - -All this is too much to manage in a single README file, so head over to -[the website](http://www.node-tap.org/) to learn more. - -## Why TAP? - -Why should you use this thing!? **LET ME TELL YOU!** - -Just kidding. - -Most frameworks spend a lot of their documentation telling you why they're -the greatest. I'm not going to do that. - -### tutti i gusti sono gusti - -Software testing is a software and user experience design challenge that -balances on the intersection of many conflicting demands. - -Node-tap is based on [my](http://izs.me) opinions about how a test -framework should work, and what it should let you do. I do _not_ have any -opinion about whether or not you share those opinions. If you do share -them, you will probably enjoy this test library. - -1. **Test files should be "normal" programs that can be run directly.** - - That means that it can't require a special runner that puts magic - functions into a global space. `node test.js` is a perfectly ok way to - run a test, and it ought to function exactly the same as when it's run - by the fancy runner with reporting and such. JavaScript tests should be - JavaScript programs; not english-language poems with weird punctuation. - -2. **Test output should be connected to the structure of the test file in a - way that is easy to determine.** - - That means not unnecessarily deferring test functions until `nextTick`, - because that would shift the order of `console.log` output. Synchronous - tests should be synchronous. - -3. **Test files should be run in separate processes.** - - That means that it can't use `require()` to load test files. Doing - `node ./test.js` must be the exact same sort of environment for the test - as doing `test-runner ./test.js`. Doing `node test/1.js; node - test/2.js` should be equivalent (from the test's point of view) to doing - `test-runner test/*.js`. This prevents tests from becoming implicitly - dependent on one anothers' globals. - -4. **Assertions should not normally throw (but throws MUST be handled - nicely).** - - I frequently write programs that have many hundreds of assertions based - on some list of test cases. If the first failure throws, then I don't - know if I've failed 100 tests or 1, without wrapping everything in a - try-catch. Furthermore, I usually want to see some kind of output or - reporting to verify that each one actually ran. - - Basically, it should be your decision whether you want to throw or not. - The test framework shouldn't force that on you, and should make either - case easy. - -5. **Test reporting should be separate from the test process, included in - the framework, and enabled by default for humans.** - - The [raw test output](https://www.node-tap.org/tap-format/) should be - machine-parseable and human-intelligible, and a separate process should - consume test output and turn it into a [pretty summarized - report](https://www.node-tap.org/reporting/). This means that test data - can be stored and parsed later, dug into for additional details, and so - on. Also: nyan cat. - -6. **Writing tests should be easy, maybe even fun.** - - The lower the barrier to entry for writing new tests, the more tests get - written. That means that there should be a relatively small vocabulary - of actions that I need to remember as a test author. There is no - benefit to having a distinction between a "suite" and a "subtest". - Fancy DSLs are pretty, but more to remember. - - That being said, if you return a Promise, or use a DSL that throws a - decorated error, then the test framework should Just Work in a way that - helps a human being understand the situation. - -7. **Tests should output enough data to diagnose a failure, and no more or - less.** - - Stack traces pointing at JS internals or the guts of the test framework - itself are not helpful. A test framework is a serious UX challenge, and - should be treated with care. - -8. **Test coverage should be included.** - - Running tests with coverage changes the way that you think about your - programs, and provides much deeper insight. Node-tap bundles - [NYC](https://istanbul.js.org/) for this. - - It _does_ necessarily change the nature of the environment a little bit. - But in this case, it's worth it, and NYC has come a long way towards - maintaining this promise. - - Coverage _enforcement_ is not on by default, but I strongly encourage - it. You can put `"tap":{"check-coverage":true}` in your package.json, - or pass [`--100`](https://www.node-tap.org/100/) on the command line. - In a future version, it will likely be enabled by default. - -9. **Tests should not require more building than your code.** - - Babel and Webpack are lovely and fine. But if your code doesn't require - compilation, then I think your tests shouldn't either. Tap is extremely - [promise-aware](https://www.node-tap.org/promises/). JSX, TypeScript, - Flow, and ES-Modules are - [built-in](https://www.node-tap.org/using-with/) when tests are run by - the tap CLI. - -10. **Tests should run as fast as possible, given all the prior - considerations.** - - As of version 10, tap supports [parallel - tests](https://www.node-tap.org/parallel/). As of version 13, the test - runner defaults to running the same number of parallel tests as there - are CPUs on the system. - - This makes tests significantly faster in almost every case, on any machine - with multiple cores. - -Software testing should help you build software. It should be a security -blanket and a quality ratchet, giving you the support to undertake massive -refactoring and fix bugs without worrying. It shouldn't be a purification -rite or a hazing ritual. - -There are many opinions left off of this list! Reasonable people can -disagree. But if you find yourself nodding along, [maybe tap is for -you](https://www.node-tap.org/basics/). diff --git a/typescript-vendor/tap/bin/jack.js b/typescript-vendor/tap/bin/jack.js deleted file mode 100644 index 204ffae5b..000000000 --- a/typescript-vendor/tap/bin/jack.js +++ /dev/null @@ -1,837 +0,0 @@ -const { jack, num, opt, list, flag, env } = require('jackspeak') - -const {cpus} = require('os') - -const reporters = [...new Set([ - ...(require('tap-mocha-reporter').types), - ...(require('treport/types')), -])] -const fs = require('fs') -// nyc bundles its deps, pull reporters out of it -const nycReporters = [ - 'clover', - 'cobertura', - 'html', - 'json', - 'json-summary', - 'lcov', - 'lcovonly', - 'none', - 'teamcity', - 'text', - 'text-lcov', - 'text-summary', -] - -const pkg = require('../package.json') - -module.exports = main => jack({ - main, - usage: 'tap [options] []', - help:` -${pkg.name} v${pkg.version} - ${pkg.description} - -Executes all the files and interprets their output as TAP -formatted test result data. If no files are specified, then -tap will search for testy-looking files, and run those. -(See '--test-regex' below.) - -To parse TAP data from stdin, specify "-" as a filename. - -Short options are parsed gnu-style, so for example '-bCRspec' would be -equivalent to '--bail --no-color --reporter=spec' - -If the --check-coverage or --coverage-report options are provided -explicitly, and no test files are specified, then a coverage report or -coverage check will be run on the data from the last test run. - -Coverage is never enabled for stdin. - -Much more documentation available at: https://www.node-tap.org/ -`, - -}, { - description: 'Basic Options', - - reporter: opt({ - hint: 'type', - short: 'R', - default: null, - description: `Use the specified reporter. Defaults to - 'base' when colors are in use, or 'tap' - when colors are disabled. - - In addition to the built-in reporters provided by - the treport and tap-mocha-reporter modules, the - reporter option can also specify a command-line - program or a module to load via require(). - - Command-line programs receive the raw TAP output - on their stdin. - - Modules loaded via require() must export either a - writable stream class or a React.Component subclass. - Writable streams are instantiated and piped into. - React components are rendered using Ink, with tap={tap} - as their only property. - - Available built-in reporters: - ${reporters.join(' ')}`, - }), - - 'reporter-arg': list({ - hint: 'arg', - short: 'r', - description: `Args to pass to command-line reporters. Ignored when using - built-in reporters or module reporters.`, - }), - - 'save-fixture': flag({ - short: 'F', - envDefault: 'TAP_SAVE_FIXTURE', - description: 'Do not clean up fixtures created with t.testdir()', - }), - - bail: flag({ - short: 'b', - envDefault: 'TAP_BAIL', - description: 'Bail out on first failure', - negate: { - short: 'B', - description: 'Do not bail out on first failure (default)' - } - }), - - comments: flag({ - description: 'Print all tap comments to process.stderr' - }), - - color: flag({ - short: 'c', - envDefault: 'TAP_COLORS', - default: process.stdout.isTTY, - description: 'Use colors (Default for TTY)', - negate: { - short: 'C', - description: 'Do not use colors (Default for non-TTY)' - } - }), - - snapshot: flag({ - short: 'S', - envDefault: 'TAP_SNAPSHOT', - default: /^snap(shot)?$/.test('' + process.env.npm_lifecycle_event), - description: `Set to generate snapshot files for - 't.matchSnapshot()' assertions.`, - }), - - watch: flag({ - short: 'w', - description: `Watch for changes in the test suite or covered program. - - Runs the suite normally one time, and from then on, - re-run just the portions of the suite that are required - whenever a file changes. - - Opens a REPL to trigger tests and perform various actions.`, - }), - - changed: flag({ - short: 'n', - description: `Only run tests for files that have changed since the last - run. - - This requires coverage to be enabled, because tap uses - NYC's process info tracking to monitor which file is loaded - by which tests. - - If no prior test run data exists, then all default files are - run, as if --changed was not specified.`, - }), - 'only-changed': flag({ - alias: '--changed', - hidden: true, - }), - 'onlyChanged': flag({ - alias: '--changed', - hidden: true, - }), - - save: opt({ - short: 's', - hint: 'file', - default: null, - description: `If exists, then it should be a line- - delimited list of test files to run. If - is not present, then all command-line - positional arguments are run. - - After the set of test files are run, any - failed test files are written back to the - save file. - - This way, repeated runs with -s will - re-run failures until all the failures are - passing, and then once again run all tests. - - Its a good idea to .gitignore the file - used for this purpose, as it will churn a - lot.`, - }), - - only: flag({ - short: 'O', - envDefault: 'TAP_ONLY', - description: `Only run tests with {only: true} option, - or created with t.only(...) function.`, - }), - - grep: list({ - hint: 'pattern', - short: 'g', - envDefault: 'TAP_GREP', - delimiter: '\n', - description: `Only run subtests tests matching the specified - pattern. - - Patterns are matched against top-level - subtests in each file. To filter tests - at subsequent levels, specify this - option multiple times. - - To specify regular expression flags, - format pattern like a JavaScript RegExp - literal. For example: '/xyz/i' for - case-insensitive matching.`, - }), - invert: flag({ - envDefault: 'TAP_GREP_INVERT', - default: false, - short: 'i', - description: 'Invert the matches to --grep patterns. (Like grep -v)', - negate: { short: 'I' } - }), - - timeout: num({ - min: 0, - short: 't', - hint: 'n', - envDefault: 'TAP_TIMEOUT', - default: 30, - description: `Time out test files after seconds. - Defaults to 30, or the value of the - TAP_TIMEOUT environment variable. - Setting to 0 allows tests to run - forever. - - When a test process calls t.setTimeout(n) on the top-level - tap object, it also updates this value for that specific - process.`, - }), - - 'no-timeout': flag({ - short: 'T', - alias: '--timeout=0', - description: 'Do not time out tests. Equivalent to --timeout=0.', - }), - - files: list({ - description: `Alternative way to specify test set rather than using - positional arguments. Supported as an option so that - test file arguments can be specified in .taprc and - package.json files.` - }), - -}, { - - description: 'Running Parallel Tests', - - help: `Tap can run multiple test files in parallel. This generally - results in a speedier test run, but can also cause problems if - your test files are not designed to be independent from one - another. - - To designate a set of files as ok to run in parallel, add them - to a folder containing a file named 'tap-parallel-ok'. - - To designate a set of files as not ok to run in parallel, add - them to a folder containing a file named 'tap-parallel-not-ok'. - - These folders may be nested within one another, and tap will - do the right thing.`, - - jobs: num({ - short: 'j', - hint: 'n', - min: 1, - default: Math.min(cpus().length, 8), - description: `Run up to test files in parallel. - - By default, this will be set to the number of CPUs on - the system. - - Set --jobs=1 to disable parallelization entirely.` - }), - - 'jobs-auto': flag({ - short: 'J', - alias: '--jobs=' + cpus().length, - description: `Run test files in parallel (auto calculated) - - This is the default as of v13, so this option serves - little purpose except to re-set the parallelization - back to the default if an earlier option (or config file) - set it differently. - ` - }), - - before: opt({ - hint: 'module', - description: `A node program to be run before test files are executed. - - Exiting with a non-zero status code or a signal will fail - the test run and exit the process in error.`, - }), - after: opt({ - hint: 'module', - description: `A node program to be executed after tests are finished. - - This will be run even if a test in the series fails with - a bailout, but it will *not* be run if a --before script - fails. - - Exiting with a non-zero status code or a signal will fail - the test run and exit the process in error.`, - }), - -}, { - - description: 'Code Coverage Options', - - help: `Tap uses the nyc module internally to provide code coverage, so - there is no need to invoke nyc yourself or depend on it - directly unless you want to use it in other scenarios.`, - - '100': flag({ - alias: [ - '--branches=100', - '--lines=100', - '--functions=100', - '--statements=100' - ], - description: `Enforce full coverage, 100%. - Sets branches, statements, functions, - and lines to 100. - - This is the default. To specify a lower limit - (or no limit) set --lines, --branches, --functions, - or --statements to a lower number than 100, or disable - coverage checking with --no-check-coverage, or disable - coverage entirely with --no-coverage.`, - }), - - 'coverage-map': opt({ - short: 'M', - hint: 'module', - description: `Provide a path to a node module that exports a single - function. That function takes a test file as an argument, - and returns an array of files to instrument with coverage - when that file is run. - - This is useful in cases where a unit test should cover - a single portion of the system under test. - - Return 'null' to not cover any files by this test. - - Return an empty array [] to cover the set that nyc would - pull in by default. Ie, returning [] is equivalent to not - using a coverage map at all.`, - }), - - 'no-coverage-map': flag({ - description: `Do not use a coverage map. - Primarily useful for disabling a coverage-map that is - set in a config file.`, - }), - - coverage: flag({ - default: true, - short: 'cov', - description: `Capture coverage information using 'nyc' - This is enabled by default. - - If a COVERALLS_REPO_TOKEN environment - variable is set, then coverage is - sent to the coveralls.io service.`, - negate: { - short: 'no-cov', - description: `Do not capture coverage information. - Note that if nyc is already loaded, then - the coverage info will still be captured.`, - } - }), - - 'coverage-report': list({ - hint: 'type', - valid: nycReporters, - description: `Output coverage information using the - specified istanbul/nyc reporter type. - - Default is 'text' when running on the - command line, or 'text-lcov' when piping - to coveralls. - - If 'html' is used, then the report will - be opened in a web browser after running. - - This can be run on its own at any time - after a test run that included coverage. - - Available NYC reporters: - ${nycReporters.join(' ')}`, - }), - - 'no-coverage-report': flag({ - description: `Do not output a coverage report, even if coverage - information is generated.` - }), - - browser: flag({ - description: `Open a browser when an html coverage report is generated. - (this is the default behavior)`, - default: true, - negate: { - description: `Do not open a web browser after generating - an html coverage report`, - } - }), - - 'show-process-tree': flag({ - description: `Enable coverage and display the tree of - spawned processes.`, - implies: { - coverage: true - }, - short: 'pstree', - }), - -}, { - description: 'Coverage Enfocement Options', - help: ` - These options enable you to specify that the test will fail - if a given coverage level is not met. Setting any of the options - below will trigger the --coverage and --check-coverage flags. - - The most stringent is --100. You can find a list of projects - running their tests like this at: https://www.node-tap.org/100 - - If you run tests in this way, please add your project to the list.`, - - 'check-coverage': flag({ - default: true, - description: `Check whether coverage is within - thresholds provided. Setting this - explicitly will default --coverage to - true. - - This can be run on its own any time - after a test run that included coverage.`, - implies: { - coverage: true - }, - }), - - branches: num({ - min: 0, - max:100, - default: 100, - hint: 'n', - implies: { - 'check-coverage': true, - coverage: true - }, - description: `what % of branches must be covered?`, - }), - - functions: num({ - min: 0, - max:100, - default: 100, - hint: 'n', - implies: { - 'check-coverage': true, - coverage: true - }, - description: `what % of functions must be covered?`, - }), - - lines: num({ - min: 0, - max:100, - default: 100, - hint: 'n', - implies: { - 'check-coverage': true, - coverage: true - }, - description: `what % of lines must be covered?`, - }), - - statements: num({ - min: 0, - max:100, - default: 100, - hint: 'n', - implies: { - 'check-coverage': true, - coverage: true - }, - description: `what % of statements must be covered?`, - }), - -}, { - description: 'Other Options', - - help: flag({ - short: 'h', - description: 'Show this helpful output' - }), - - version: flag({ - short: 'v', - description: 'Show the version of this program.', - }), - - 'test-regex': opt({ - hint: 'pattern', - // anything in a test/ or tests/ folder, or a /tests.js or /test.js, - // or anything ending in *.test.js or *.spec.js - default: '((\\/|^)(tests?|__tests?__)\\/.*|\\.(tests?|spec)|^\\/?tests?)\\.([mc]js|[jt]sx?)$', - description: `A regular expression pattern indicating tests to run if no - positional arguments are provided. - - By default, tap will search for all files ending in - .ts, .tsx, .js, .jsx, .cjs, or .mjs, in a top-level folder - named test, tests, or __tests__, or any file ending in - '.spec.' or '.test.' before a supported extension, or a - top-level file named 'test.(js,jsx,...)' or - 'tests.(js,jsx,...)' - - Ie, the default value for this option is: - ((\\/|^)(tests?|__tests?__)\\/.*|\\.(tests?|spec)|^\\/?tests?)\\.([mc]js|[jt]sx?)$ - - Note that .jsx files will only be run when --jsx is enabled, - .ts files will only be run when --ts is enabled, and .tsx - files will only be run with both --ts and --jsx are enabled. - ` - }), - - 'test-ignore': opt({ - hint: 'pattern', - default: '$.', - description: `When no positional arguments are provided, use the supplied - regular expression pattern to exclude tests that would - otherwise be matched by the test-regexp. - - Defaults to '$.', which intentionally matches nothing. - - Note: folders named tap-snapshots, node_modules, .git, and - .hg are ALWAYS excluded from the default test file set. If - you wish to run tests in these folders, then name the test - files on the command line as positional arguments.`, - }), - - 'test-arg': list({ - hint: 'arg', - description: `Pass an argument to test files spawned - by the tap command line executable. - This can be specified multiple times to - pass multiple args to test scripts.`, - }), - - 'test-env': list({ - hint: 'key[=]', - description: `Pass a key=value (ie, --test-env=key=value) to set an - environment variable in the process where tests are run. - - If a value is not provided, then the key is ensured to - not be set in the environment. To set a key to the empty - string, use --test-env=key=`, - }), - - 'nyc-arg': list({ - hint: 'arg', - description: `Pass an argument to nyc when running - child processes with coverage enabled. - This can be specified multiple times to - pass multiple args to nyc.`, - }), - - 'node-arg': list({ - hint: 'arg', - description: `Pass an argument to Node binary in all - child processes. Run 'node --help' to - see a list of all relevant arguments. - This can be specified multiple times to - pass multiple args to Node.`, - }), - 'expose-gc': flag({ - short: 'gc', - alias: '--node-arg=--expose-gc', - description: 'Expose the gc() function to Node.js tests', - }), - debug: flag({ - alias: '--node-arg=--debug', - description: 'Run JavaScript tests with node --debug', - }), - 'debug-brk': flag({ - alias: '--node-arg=--debug-brk', - description: 'Run JavaScript tests with node --debug-brk', - }), - harmony: flag({ - alias: '--node-arg=--harmony', - description: 'Enable all Harmony flags in JavaScript tests', - }), - strict: flag({ - alias: '--node-arg=--use-strict', - description: `Run JS tests in 'use strict' mode`, - }), - - flow: flag({ - description: `Removes flow types`, - }), - - ts: flag({ - default: process.env.TAP_TS === '1', - description: `Automatically load .ts and .tsx tests with tap's bundled - ts-node module (Default: false)`, - }), - - jsx: flag({ - default: process.env.TAP_JSX === '1', - description: `Automatically load .jsx tests using tap's bundled import-jsx - loader (Default: false)`, - }), - - 'nyc-help': flag({ - description: `Print nyc usage banner. Useful for - viewing options for --nyc-arg.`, - }), - - 'nyc-version': flag({ - description: 'Print version of nyc used by tap.', - }), - - 'parser-version': flag({ - description: 'Print the version of tap-parser used by tap.', - }), - - versions: flag({ - description: 'Print versions of tap, nyc, and tap-parser', - }), - - 'dump-config': flag({ - description: 'Dump the config options in YAML format', - }), - - rcfile: opt({ - hint: 'file', - description: `Load any of these configurations from a YAML-formatted - file at the path specified. Defaults to .taprc in the - current working directory. - - Run 'tap --dump-config' to see available options and - formatting.`, - envDefault: 'TAP_RCFILE', - default: `${process.cwd()}/.taprc`, - }), - - 'libtap-settings': opt({ - hint: 'module', - description: `A module which exports an object of fields to assign onto - 'libtap/settings'. These are advanced configuration options - for modifying the behavior of tap's internal runtime. - - Module path is resolved relative to the current working - directory. - - Allowed fields: rmdirRecursive, rmdirRecursiveSync, - StackUtils, stackUtils, output, snapshotFile. - - See libtap documentation for expected values and usage. - - https://github.com/tapjs/libtap`, - envDefault: 'TAP_LIBTAP_SETTINGS', - default: null, - }), - - 'output-file': opt({ - short: 'o', - hint: 'file', - default: null, - description: `Send the raw TAP output to the specified - file. Reporter output will still be - printed to stdout, but the file will - contain the raw TAP for later replay or - analysis.`, - }), - - 'output-dir': opt({ - short: 'd', - hint: 'dir', - default: null, - description: `Send the raw TAP output to the specified - directory. A separate .tap file will be created - for each test file that is run. Reporter output - will still be printed to stdout, but the files - will contain the raw TAP for later replay or - analysis. - - Files will be created to match the folder structure - and filenames of test files run, but with '.tap' - appended to the filenames.`, - }), - - debug: flag({ - envDefault: 'TAP_DEBUG', - description: 'Turn on debug mode', - }), - - '--': flag({ - description: `Stop parsing flags, and treat any additional - command line arguments as filenames.` - }), - -}, { - - description: 'Environment Variables', - - COVERALLS_REPO_TOKEN: env({ - description: `Set to a Coveralls token to automatically - send coverage information to https://coveralls.io`, - implies: { - coverage: true - }, - default: null, - }), - - TAP_CHILD_ID: env(num({ - description: `Test files have this value set to a - numeric value when run through the test - runner. It also appears on the root tap - object as \`tap.childId\`.`, - })), - - TAP_SNAPSHOT: env(flag({ - description: `Set to '1' to generate snapshot files - for 't.matchSnapshot()' assertions.`, - })), - - TAP_RCFILE: env({ - description: `A yaml formatted file which can set any - of the above options. Defaults to - ./.taprc` - }), - - TAP_LIBTAP_SETTINGS: env({ - description: `A path (relative to current working directory) of a file - that exports fields to override the default libtap settings`, - }), - - TAP_TIMEOUT: env(num({ - min: 0, - default: 30, - description: `Default value for --timeout option.` - })), - - TAP_COLORS: env(flag({ - description: `Set to '1' to force color output, or '0' - to prevent color output.` - })), - - TAP_BAIL: flag(env({ - description: `Bail out on the first test failure. - Used internally when '--bailout' is set.` - })), - - TAP: flag(env({ - implies: { - reporter: 'tap' - }, - description: `Set to '1' to force standard TAP output, - and suppress any reporters. Used when - running child tests so that their output - is parseable by the test harness.` - })), - - TAP_DIAG: env(flag({ - description: `Set to '1' to show diagnostics by - default for passing tests. Set to '0' - to NOT show diagnostics by default for - failing tests. If not one of these two - values, then diagnostics are printed by - default for failing tests, and not for - passing tests.` - })), - - TAP_BUFFER: env(flag({ - description: `Set to '1' to run subtests in buffered - mode by default.` - })), - - TAP_DEV_LONGSTACK: env(flag({ - description: `Set to '1' to include node-tap internals - in stack traces. By default, these are - included only when the current working - directory is the tap project itself. - Note that node internals are always - excluded.` - })), - - TAP_DEBUG: env(flag({ - description: `Set to '1' to turn on debug mode.` - })), - - NODE_DEBUG: env({ - description: `Include 'tap' to turn on debug mode.` - }), - - TAP_GREP: env(list({ - delimiter: '\n', - description: `A '\\n'-delimited list of grep patterns - to apply to root level test objects. - (This is an implementation detail for how - the '--grep' option works.)` - })), - - TAP_GREP_INVERT: env(flag({ - description: `Set to '1' to invert the meaning of the - patterns in TAP_GREP. (Implementation - detail for how the '--invert' flag - works.)` - })), - - TAP_ONLY: env(flag({ - description: `Set to '1' to set the --only flag` - })), - - TAP_TS: env(flag({ - description: `Set to '1' to enable automatic typescript support` - })), - - TAP_JSX: env(flag({ - description: `Set to '1' to enable automatic jsx support` - })), - -}, { - - // a section that's just a description. This is totally fine. - // You can break up the usage output this way. - description: 'Config Files', - help: `You can create a yaml file with any of the options above. By - default, the file at ./.taprc will be loaded, but the - --rcfile option or TAP_RCFILE environment variable can modify this. - - Run 'tap --dump-config' for a listing of what can be set in that - file. Each of the keys corresponds to one of the options above.`, -}) diff --git a/typescript-vendor/tap/bin/jsx.js b/typescript-vendor/tap/bin/jsx.js deleted file mode 100644 index ad9dbd747..000000000 --- a/typescript-vendor/tap/bin/jsx.js +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env node -if (__filename !== process.argv[1] || process.argv.length < 3) - throw new Error('this should only be used to load a jsx file') -process.argv.splice(1, 2, require('path').resolve(process.argv[2])) -require('import-jsx')(process.argv[1]) diff --git a/typescript-vendor/tap/bin/run.js b/typescript-vendor/tap/bin/run.js deleted file mode 100755 index 469a5b65f..000000000 --- a/typescript-vendor/tap/bin/run.js +++ /dev/null @@ -1,875 +0,0 @@ -#!/usr/bin/env node - -// default to no color if requested via standard environment var -if (process.env.NO_COLOR === '1') { - process.env.TAP_COLORS = '0' - process.env.FORCE_COLOR = '0' -} - -const signalExit = require('signal-exit') -const opener = require('opener') -const node = process.execPath -const fs = require('fs') -const fg = require('foreground-child') -const {spawn, spawnSync} = require('child_process') -const nycBin = require.resolve( - 'nyc/' + require('nyc/package.json').bin.nyc -) -const glob = require('glob') -const isexe = require('isexe') -const yaml = require('tap-yaml') -const path = require('path') -const exists = require('fs-exists-cached').sync -const os = require('os') - -const maybeResolve = id => { - try { - return require.resolve(id) - } catch (er) {} -} -const tsNode = maybeResolve('ts-node/register') -const flowNode = maybeResolve('flow-remove-types/register') -const jsx = require.resolve('./jsx.js') - -const which = require('which') -const {ProcessDB} = require('istanbul-lib-processinfo') -const rimraf = require('rimraf').sync -const {Repl} = require('../lib/repl.js') - - -/* istanbul ignore next */ -const debug = process.env.TAP_DEBUG === '1' - || /\btap\b/.test(process.env.NODE_DEBUG) ? (...args) => { - const {format} = require('util') - const prefix = `TAP ${process.pid} RUN: ` - const msg = format(...args).trim() - console.error(prefix + msg.split('\n').join(`\n${prefix}`)) - } : () => {} - -const filesFromTest = exports.filesFromTest = (index, testFile) => { - const set = index.externalIds[testFile] - return !set ? null - : Object.keys(index.files).filter(file => - index.files[file].includes(set.root) || - set.children.some(c => index.files[file].includes(c))) -} - -// returns a function that tells whether a given file should be run, -// because one or more of its deps have changed. -const getChangedFilter = exports.getChangedFilter = options => { - if (!options.changed) - return () => true - - if (!options.coverage) - throw new Error('--changed requires coverage to be enabled') - - const indexFile = '.nyc_output/processinfo/index.json' - - if (!fs.existsSync(indexFile)) - return () => true - - const indexDate = fs.statSync(indexFile).mtime - const index = JSON.parse(fs.readFileSync(indexFile, 'utf8')) - - return testFile => { - if (fs.statSync(testFile).mtime > indexDate) - return true - - const files = filesFromTest(index, testFile) - - // if not found, probably a test file not run last time, so run it - if (!files) - return true - - // if the file is gone, that's a pretty big change. - return files.some(f => !fs.existsSync(f) || fs.statSync(f).mtime > indexDate) - } -} - -const defaultFiles = options => new Promise((res, rej) => { - debug('try to get default files') - const findit = require('findit') - const good = strToRegExp(options['test-regex']) - const bad = strToRegExp(options['test-ignore']) - const fileFilter = f => { - f = f.replace(/\\/g, '/') - debug('fileFilter', f) - const parts = f.split('/') - const include = good.test(f) && - !bad.test(f) && - !parts.includes('node_modules') && - !parts.includes('tap-snapshots') && - !parts.includes('fixtures') && - !parts.includes('.git') && - !parts.includes('.hg') && - !parts.some(p => /^tap-testdir-/.test(p)) - debug('include?', f, include) - return include - } - - const addFile = files => f => fileFilter(f) && files.push(f) - - // search in any folder that isn't node_modules, .git, or tap generated - // these can get pretty huge, so just walking them at all is costly - const entryFilter = - /^((node_modules|tap-snapshots|.git|.hg|fixtures)$|tap-testdir-)/ - fs.readdir(process.cwd(), (er, entries) => { - debug('readdir cwd', er, entries) - Promise.all(entries.filter(entry => !entryFilter.test(entry)) - .map(entry => new Promise((res, rej) => { - fs.lstat(entry, (er, stat) => { - debug('lstat', entry, er, stat) - // It's pretty unusual to have a file in cwd you can't even stat - /* istanbul ignore next */ - if (er) - return rej(er) - if (stat.isFile()) - return res(fileFilter(entry) ? [entry] : []) - if (!stat.isDirectory()) - return res([]) - const finder = findit(entry) - const files = [] - finder.on('file', addFile(files)) - finder.on('end', () => res(files)) - finder.on('error', /* istanbul ignore next */ er => rej(er)) - }) - }))).then(a => res(a.reduce((a, i) => a.concat(i), []))).catch(rej) - }) -}) - -const main = options => - mainAsync(options).catch(er => onError(er)) - -const mainAsync = async options => { - debug('main', options) - - if (require.main !== module) - return debug('not main module, do not run tests') - - const rc = parseRcFile(options.rcfile) - debug('rc options', rc) - options._.update(rc) - - const pj = parsePackageJson() - debug('package.json options', pj) - options._.update(pj) - - if (options.files.length && !options._.length) - options._.push(...options.files) - - // tell chalk if we want color or not. - if (!options.color) { - process.env.NO_COLOR = '1' - delete process.env.FORCE_COLOR - } else { - delete process.env.NO_COLOR - process.env.FORCE_COLOR = '3' - } - - if (options.reporter === null) - options.reporter = options.color ? 'base' : 'tap' - - if (options['dump-config']) { - console.log(yaml.stringify(Object.keys(options).filter(k => - k !== 'dump-config' && k !== '_' && !/^[A-Z_]+$/.test(k) - ).sort().reduce((set, k) => - (set[k] = options[k], set), {}))) - return - } - - if (options.versions) { - const {libtap, tapParser, tapYaml, tcompare} = require('libtap/versions') - return console.log(yaml.stringify({ - tap: require('../package.json').version, - libtap, - 'tap-parser': tapParser, - nyc: require('nyc/package.json').version, - 'tap-yaml': tapYaml, - treport: require('treport/package.json').version, - tcompare - })) - } - - if (options.version) - return console.log(require('../package.json').version) - - if (options['parser-version']) - return console.log(require('libtap/versions').tapParser) - - if (options['nyc-version']) - return console.log(require('nyc/package.json').version) - - if (options['nyc-help']) - return nycHelp() - - process.stdout.on('error', er => { - /* istanbul ignore else */ - if (er.code === 'EPIPE') - process.exit() - else - throw er - }) - - if (options['libtap-settings']) - process.env.TAP_LIBTAP_SETTINGS = path.resolve(options['libtap-settings']) - - require('../settings.js') - - // we test this directly, not from here. - /* istanbul ignore next */ - if (options.watch) - return new Repl(options, process.stdin, process.stdout) - - options.grep = options.grep.map(strToRegExp) - - // this is only testable by escaping from the covered environment - /* istanbul ignore next */ - if (fs.existsSync('.nyc_output') && - options._.length === 0 && - (options['coverage-report'] && - options._.explicit.has('coverage-report') || - options['check-coverage'] && - options._.explicit.has('check-coverage'))) - return runCoverageReportOnly(options) - - try { - debug('try to get default files?', options._.length === 0) - if (options._.length === 0) - options._.push.apply(options._, await defaultFiles(options)) - debug('added default files', options._) - } /* istanbul ignore next */ catch (er) /* istanbul ignore next */ { - // This gets tested on Mac, but not Linux/travis, and is - // somewhat challenging to do in a cross-platform way. - /* istanbul ignore next */ - return require('../lib/tap.js').threw(er) - } - - options.files = globFiles(options._) - debug('after globbing', options.files) - - if (options['output-dir'] !== null) - require('../settings.js').mkdirRecursiveSync(options['output-dir']) - - if (options.files.length === 1 && options.files[0] === '-') { - debug('do stdin only') - setupTapEnv(options) - stdinOnly(options) - return - } - - options.saved = readSaveFile(options) - options.changedFilter = getChangedFilter(options) - - /* istanbul ignore next */ - if (options.coverage && !process.env.NYC_CONFIG) - respawnWithCoverage(options) - else { - setupTapEnv(options) - runTests(options) - } -} - -/* istanbul ignore next */ -const nycReporter = options => - options['coverage-report'] === false ? ['--silent'] - : options['coverage-report'].map(cr => - cr === 'html' ? '--reporter=lcov' : `--reporter=${cr}`) - -const defaultNycExcludes = [ - 'coverage/**', - 'packages/*/test/**', - 'test/**', - 'test{,-*}.js', - '**/*{.,-}test.js', - '**/__tests__/**', - '**/{ava,babel,jest,nyc,rollup,webpack}.config.js', -] - -/* istanbul ignore next */ -const runNyc = (cmd, programArgs, options, spawnOpts) => { - const reporter = nycReporter(options) - - // note: these are forced to be numeric in the option parsing phase - const branches = Math.min(options.branches, 100) - const lines = Math.min(options.lines, 100) - const functions = Math.min(options.functions, 100) - const statements = Math.min(options.statements, 100) - const excludes = defaultNycExcludes.concat(options.files).map(f => - '--exclude=' + f) - if (options.before) - excludes.push('--exclude=' + options.before) - if (options.after) - excludes.push('--exclude=' + options.after) - - const args = [ - nycBin, - ...cmd, - ...(options['show-process-tree'] ? ['--show-process-tree'] : []), - ...excludes, - '--produce-source-map', - '--cache=true', - '--branches=' + branches, - '--watermarks.branches=' + branches, - '--watermarks.branches=' + (branches + (100 - branches)/2), - '--functions=' + functions, - '--watermarks.functions=' + functions, - '--watermarks.functions=' + (functions + (100 - functions)/2), - '--lines=' + lines, - '--watermarks.lines=' + lines, - '--watermarks.lines=' + (lines + (100 - lines)/2), - '--statements=' + statements, - '--watermarks.statements=' + statements, - '--watermarks.statements=' + (statements + (100 - statements)/2), - ...reporter, - '--extension=.js', - '--extension=.jsx', - '--extension=.mjs', - '--extension=.cjs', - '--extension=.ts', - '--extension=.tsx', - ...(options['nyc-arg'] || []), - ] - if (options['check-coverage']) - args.push('--check-coverage') - - args.push.apply(args, programArgs) - - if (spawnOpts) - return fg(node, args, spawnOpts) - - // fake it - process.argv = [ - node, - ...args, - ] - require(nycBin) - - if (reporter.includes('--reporter=lcov') && options.browser) - process.on('exit', () => openHtmlCoverageReport(options)) -} - -/* istanbul ignore next */ -const runCoverageReportOnly = options => { - runNyc(['report'], [], options) - if (process.env.COVERALLS_REPO_TOKEN || - process.env.__TAP_COVERALLS_TEST__) { - pipeToCoveralls() - } -} - -/* istanbul ignore next */ -const pipeToCoveralls = async options => { - const reporter = spawn(node, [nycBin, 'report', '--reporter=text-lcov'], { - stdio: [ 0, 'pipe', 2 ] - }) - - const bin = process.env.__TAP_COVERALLS_TEST__ - || require.resolve('coveralls/bin/coveralls.js') - - const ca = spawn(node, [bin], { stdio: ['pipe', 1, 2] }) - reporter.stdout.pipe(ca.stdin) - await new Promise(resolve => ca.on('close', resolve)) -} - -/* istanbul ignore next */ -const respawnWithCoverage = options => { - debug('respawn with coverage') - // If we have a coverage map, then include nothing by default here. - runNyc(options['coverage-map'] ? [ - '--include=', - '--no-exclude-after-remap', - ...(options.saved || options.changed ? ['--no-clean'] : []), - ] : [], [ - '--', - node, - ...process.execArgv, - ...process.argv.slice(1) - ], options) -} - -/* istanbul ignore next */ -const openHtmlCoverageReport = (options, code, signal) => { - opener('coverage/lcov-report/index.html') - if (signal) { - setTimeout(() => {}, 200) - process.kill(process.pid, signal) - } else if (code) { - process.exitCode = code - } -} - -const nycHelp = _ => fg(node, [nycBin, '--help']) - -// export for easier testing -const setupTapEnv = exports.setupTapEnv = options => { - process.env.TAP_TIMEOUT = options.timeout - if (options.color) - process.env.TAP_COLORS = '1' - else - process.env.TAP_COLORS = '0' - - if (options.snapshot) - process.env.TAP_SNAPSHOT = '1' - - if (options.bail) - process.env.TAP_BAIL = '1' - - if (options['save-fixture']) - process.env.TAP_SAVE_FIXTURE = '1' - - if (options.invert) - process.env.TAP_GREP_INVERT = '1' - - if (options.grep.length) - process.env.TAP_GREP = options.grep.map(p => p.toString()) - .join('\n') - - if (options.only) - process.env.TAP_ONLY = '1' -} - -const globFiles = files => Array.from(files.reduce((acc, f) => - acc.concat(f === '-' ? f : glob.sync(f, { nonull: true })), []) - .reduce((set, f) => { - set.add(f) - return set - }, new Set())) - -const makeReporter = exports.makeReporter = (tap, options) => { - const treportTypes = require('treport/types') - const tapMochaReporter = require('tap-mocha-reporter') - // if it's a treport type, use that - const reporter = options.reporter - if (reporter === 'tap') - tap.pipe(process.stdout) - else if (treportTypes.includes(reporter)) - require('treport')(tap, reporter) - else if (tapMochaReporter.types.includes(reporter)) - tap.pipe(new tapMochaReporter(options.reporter)) - else { - // might be a child process or a module - try { - which.sync(reporter) - // it's a cli reporter! - const c = spawn(reporter, options['reporter-arg'], { - stdio: ['pipe', 1, 2] - }) - tap.pipe(c.stdin) - } catch (_) { - // resolve to cwd if it's a relative path - const rmod = /^\.\.?[\\\/]/.test(reporter) ? path.resolve(reporter) : reporter - // it'll often be jsx, and this is harmless if it isn't. - const R = require('import-jsx')(rmod) - if (typeof R !== 'function' || !R.prototype) - throw new Error( - `Invalid reporter: non-class exported by ${reporter}`) - else if (R.prototype.isReactComponent) - require('treport')(tap, R) - else if (R.prototype.write && R.prototype.end) - tap.pipe(new R(...(options['reporter-arg']))) - else - throw new Error( - `Invalid reporter: not a stream or react component ${reporter}`) - } - } -} - -const stdinOnly = options => { - // if we didn't specify any files, then just passthrough - // to the reporter, so we don't get '/dev/stdin' in the suite list. - // We have to pause() before piping to switch streams2 into old-mode - const tap = require('../lib/tap.js') - tap.writeSnapshot = false - tap.stdinOnly() - makeReporter(tap, options) - - if (options['output-file'] !== null) - process.stdin.pipe(fs.createWriteStream(options['output-file'])) - if (options['output-dir'] !== null) - process.stdin.pipe(fs.createWriteStream(options['output-dir'] + - '/stdin.tap')) - process.stdin.resume() -} - -const readSaveFile = options => { - if (options.save) - try { - const s = fs.readFileSync(options.save, 'utf8').trim() - if (s) - return s.split('\n') - } catch (er) {} - - return null -} - -const saveFails = (options, tap) => { - if (!options.save) - return - - let fails = [] - const successes = [] - tap.on('result', res => { - // we will continue to re-run todo tests, even though they're - // not technically "failures". - if (!res.ok && !res.extra.skip) - fails.push(res.extra.file) - else - successes.push(res.extra.file) - }) - - const save = () => { - fails = fails.reduce((set, f) => { - f = f.replace(/\\/g, '/') - if (set.indexOf(f) === -1) - set.push(f) - return set - }, []) - - if (!fails.length) - rimraf(options.save) - else - try { - fs.writeFileSync(options.save, fails.join('\n') + '\n') - } catch (er) {} - } - - tap.on('bailout', reason => { - // add any pending test files to the fails list. - fails.push.apply(fails, options.files.filter(file => - successes.indexOf(file) === -1)) - save() - }) - - tap.on('end', save) -} - -const filesMatch = (a, b) => - a && b && path.resolve(a) === path.resolve(b) - -const filterFiles = exports.filterFiles = (files, options, parallelOk) => - files.filter(file => - path.basename(file) === 'tap-parallel-ok' ? - ((parallelOk[path.resolve(path.dirname(file))] = true), false) - : path.basename(file) === 'tap-parallel-not-ok' ? - parallelOk[path.resolve(path.dirname(file))] = false - // don't include the --before and --after scripts as files, - // so they're not run as tests if they would be found normally. - // This allows test/setup.js and test/teardown.js for example. - : filesMatch(file, options.before) ? false - : filesMatch(file, options.after) ? false - : options.saved && options.saved.length ? onSavedList(options.saved, file) - : options.changed ? options.changedFilter(file) - : true - ) - -// check if the file is on the list, or if it's a parent dir of -// any items that are on the list. -const onSavedList = (saved, file) => - saved.indexOf(file) !== -1 ? true - : saved.some(f => f.indexOf(file + '/') === 0) - -const isParallelOk = (parallelOk, file) => { - const dir = path.resolve(path.dirname(file)) - return (dir in parallelOk) ? parallelOk[dir] - : exists(dir + '/tap-parallel-ok') - ? parallelOk[dir] = true - : exists(dir + '/tap-parallel-not-ok') - ? parallelOk[dir] = false - : dir.length >= process.cwd().length - ? isParallelOk(parallelOk, dir) - : true -} - -const getEnv = options => options['test-env'].reduce((env, kv) => { - const split = kv.split('=') - const key = split.shift() - if (!split.length) - delete env[key] - else - env[key] = split.join('=') - return env -}, {...process.env}) - -// the test that checks this escapes from NYC, so it'll never show up -/* istanbul ignore next */ -const coverageMapOverride = (env, file, coverageMap) => { - if (coverageMap) { - /* istanbul ignore next */ - env.NYC_CONFIG_OVERRIDE = JSON.stringify({ - include: coverageMap(file) || '' - }) - } -} - -const runAllFiles = (options, env, tap, processDB) => { - debug('run all files') - let doStdin = false - let parallelOk = Object.create(null) - - if (options['output-dir'] !== null) { - tap.on('spawn', t => { - const dir = options['output-dir'] + '/' + path.dirname(t.name) - require('../settings.js').mkdirRecursiveSync(dir) - const file = dir + '/' + path.basename(t.name) + '.tap' - t.proc.stdout.pipe(fs.createWriteStream(file)) - }) - tap.on('stdin', t => { - const file = options['output-dir'] + '/stdin.tap' - t.stream.pipe(fs.createWriteStream(file)) - }) - } - - options.files = filterFiles(options.files, options, parallelOk) - - if (options.files.length === 0 && !doStdin && !options.changed) { - tap.fail('no tests specified') - } - - /* istanbul ignore next */ - const coverageMap = options['coverage-map'] - ? require(path.resolve(options['coverage-map'])) - : null - - const seen = new Set() - for (let i = 0; i < options.files.length; i++) { - const file = options.files[i] - if (seen.has(file)) - continue - - debug('run file', file) - seen.add(file) - - // Pick up stdin after all the other files are handled. - if (file === '-') { - doStdin = true - continue - } - - let st - try { - st = fs.statSync(file) - } catch (er) { - tap.test(file, t => t.threw(er)) - continue - } - - if (st.isDirectory()) { - debug('is a directory', file) - const dir = filterFiles(fs.readdirSync(file).map(f => - file.replace(/[\/\\]+$/, '') + '/' + f), options, parallelOk) - options.files.splice(i, 1, ...dir) - i-- - } else { - const opt = { env, file, processDB } - - coverageMapOverride(env, file, coverageMap) - - if (options.timeout) - opt.timeout = options.timeout * 1000 - - if (options.jobs > 1) - opt.buffered = isParallelOk(parallelOk, file) !== false - - if (options.flow && flowNode) - options['node-arg'].push('-r', flowNode) - - if (options.ts && tsNode && /\.tsx?$/.test(file)) { - debug('typescript file', file) - const compilerOpts = JSON.parse(env.TS_NODE_COMPILER_OPTIONS || '{}') - if (options.jsx) - compilerOpts.jsx = 'react' - - opt.env = { - ...env, - TS_NODE_COMPILER_OPTIONS: JSON.stringify(compilerOpts), - } - const args = [ - '-r', tsNode, - ...options['node-arg'], - file, - ...options['test-arg'] - ] - tap.spawn(node, args, opt, file) - } else if (options.jsx && /\.jsx$/.test(file)) { - debug('jsx file', file) - const args = [ - ...(options['node-arg']), - jsx, - file, - ...(options['test-arg']), - ] - tap.spawn(node, args, opt, file) - } else if (/\.jsx$|\.tsx?$|\.[mc]?js$/.test(file)) { - debug('js file', file) - /* istanbul ignore next - version specific behavior */ - const experimental = /^v10\./.test(process.version) && /\.mjs$/.test(file) - ? ['--experimental-modules'] : [] - - const args = [ - ...options['node-arg'], - ...experimental, - file, - ...options['test-arg'] - ] - tap.spawn(node, args, opt, file) - } else if (/\.tap$/.test(file)) { - debug('tap file', file) - tap.spawn('cat', [file], opt, file) - } else if (isexe.sync(options.files[i])) { - debug('executable', file) - tap.spawn(options.files[i], options['test-arg'], opt, file) - } else { - debug('not a test file', file) - } - } - } - - if (doStdin) - tap.stdin() - - debug('scheduled all files for execution') -} - -const runTests = options => { - debug('run tests') - // At this point, we know we need to use the tap root, - // because there are 1 or more files to spawn. - const tap = require('../lib/tap.js') - tap.writeSnapshot = false - if (options.comments) { - const onComment = c => { - if (!c.match(/^# (timeout=[0-9]+|time=[0-9\.]+m?s|Subtest(: .+)?)\n$/)) - console.error(c.substr(2).trim()) - } - const onChild = p => { - p.on('comment', onComment) - p.on('child', onChild) - } - tap.parser.on('comment', onComment) - tap.parser.on('child', onChild) - } - - tap.runOnly = false - - // greps are passed to children, but not the runner itself - tap.grep = [] - tap.jobs = options.jobs - - const env = getEnv(options) - - /* istanbul ignore next */ - const processDB = options.coverage && process.env.NYC_CONFIG - ? new ProcessDB() : null - - // run --before before everything, and --after as the very last thing - runBeforeAfter(options, env, tap, processDB) - - tap.patchProcess() - - // if not -Rtap, then output what the user wants. - // otherwise just dump to stdout - /* istanbul ignore next */ - makeReporter(tap, options) - - // need to replay the first version line, because the previous - // line will have flushed it out to stdout or the reporter already. - if (options['output-file'] !== null) - tap.pipe(fs.createWriteStream(options['output-file'])).write('TAP version 13\n') - - saveFails(options, tap) - - runAllFiles(options, env, tap, processDB) - - /* istanbul ignore next */ - if (process.env.COVERALLS_REPO_TOKEN || - process.env.__TAP_COVERALLS_TEST__) { - tap.teardown(() => pipeToCoveralls()) - } - - tap.end() - debug('called tap.end()') -} - -const beforeAfter = (env, script) => { - const {status, signal} = spawnSync(process.execPath, [script], { - env, - stdio: 'inherit', - }) - - if (status || signal) { - const msg = `\n# failed ${script}\n# code=${status} signal=${signal}\n` - console.error(msg) - process.exitCode = status || 1 - process.exit(status || 1) - } -} - -const runBeforeAfter = (options, env, tap, processDB) => { - // Have to write the index before running a script so that this - // process is included in the DB, or else it'll crash when it - // tries to get the parent info. - /* istanbul ignore next */ - if (processDB && (options.before || options.after)) - processDB.writeIndex() - - if (options.before) - beforeAfter(env, options.before) - - if (options.after) { - /* istanbul ignore next - run after istanbul's report */ - signalExit(() => beforeAfter(env, options.after), { alwaysLast: true }) - } -} - -const parsePackageJson = () => { - try { - return JSON.parse(fs.readFileSync('package.json', 'utf8')).tap || {} - } catch (er) { - return {} - } -} - -const parseRcFile = path => { - let contents - try { - contents = fs.readFileSync(path, 'utf8') - } catch (_) { - try { - contents = fs.readFileSync(path + '.yaml', 'utf8') - } catch (_) { - try { - contents = fs.readFileSync(path + '.yml', 'utf8') - } catch (_) {} - } - } - if (!contents) { - // if no dotfile exists, just return an empty object - return {} - } - return yaml.parse(contents) -} - -const strToRegExp = g => { - const p = g.match(/^\/(.*)\/([a-z]*)$/) - g = p ? p[1] : g - const flags = p ? p[2] : '' - return new RegExp(g, flags) -} - -const onError = er => { - /* istanbul ignore else - parse errors are the only ones we ever expect */ - if (er.name.match(/^AssertionError/) && !er.generatedMessage) { - console.error('Error: ' + er.message) - console.error('Run `tap --help` for usage information') - process.exit(1) - } else { - console.error(er) - process.exit(1) - } -} - -try { - require('./jack.js')(main) -} catch (er) { - onError(er) -} diff --git a/typescript-vendor/tap/coverage-map.js b/typescript-vendor/tap/coverage-map.js deleted file mode 100644 index 09ec0cf66..000000000 --- a/typescript-vendor/tap/coverage-map.js +++ /dev/null @@ -1,22 +0,0 @@ -const glob = require('glob') -const path = require('path') -module.exports = t => { - const parts = path.relative(process.cwd(), path.resolve(t)).split(/\\|\//) - const unit = path.basename(parts[1], '.js') - if (unit === 'run') - return glob.sync('bin/*.js') - if (unit === 'coverage-map' || unit === 'settings') - return [ `${unit}.js` ] - const cov = glob.sync(`lib/${unit}.js`) - if (!cov.length) - return null - return cov -} - -/* istanbul ignore next */ -if (module === require.main) { - const tests = process.argv.length > 2 - ? process.argv.slice(2) - : glob.sync('test/**/*.js') - console.log(tests.map(t => [t, module.exports(t)])) -} diff --git a/typescript-vendor/tap/example/lib/math.js b/typescript-vendor/tap/example/lib/math.js deleted file mode 100644 index f79862602..000000000 --- a/typescript-vendor/tap/example/lib/math.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = Math diff --git a/typescript-vendor/tap/example/long-slow-many.js b/typescript-vendor/tap/example/long-slow-many.js deleted file mode 100644 index cfe1a4f41..000000000 --- a/typescript-vendor/tap/example/long-slow-many.js +++ /dev/null @@ -1,37 +0,0 @@ -var t = require('../lib/tap.js') -t.plan(2) - -t.test('gun show', function (t) { - t.plan(100) - - var n = 0 - var i = setInterval(function () { - if (n % 123 === 5) { - t.fail('FIRE!') - t.fail('THE BUILDING IS ON FIRE') - } else { - t.pass('this is ok') - t.pass('i am ok with how things are proceeding') - } - if (++n === 50) { - return clearInterval(i) - } - }, 100) -}) - -t.test('wondermark', function (t) { - t.plan(500) - var n = 0 - var j = setInterval(function () { - if (n % 123 === 35) { - t.fail('I AM EATING BREAKFAST') - t.fail('FUCKING SEALIONS') - } else { - t.pass('excuse me') - t.pass('pardon me') - } - if (++n === 250) { - return clearInterval(j) - } - }, 10) -}) diff --git a/typescript-vendor/tap/example/mocha-example.js b/typescript-vendor/tap/example/mocha-example.js deleted file mode 100644 index 240522253..000000000 --- a/typescript-vendor/tap/example/mocha-example.js +++ /dev/null @@ -1,89 +0,0 @@ -require('../lib/tap.js').mochaGlobals() -/* standard ignore next */ -describe('parent', function () { - - before('name', function () { - console.error('before') - }); - - after(function () { - console.error('after') - }); - - beforeEach(function () { - console.error('beforeEach') - }); - - afterEach(function () { - console.error('afterEach') - }); - - it('first', function () { - console.error('first it') - }) - it('second', function () { - console.error('second it') - }) - - describe('child 1', function () { - console.error('in child 1') - before(function () { - console.error('before 2') - }); - - after(function () { - console.error('after 2') - }); - - beforeEach(function () { - console.error('beforeEach 2') - }); - - afterEach(function () { - console.error('afterEach 2') - }); - - it('first x', function () { - console.error('first it') - }) - it('second', function (done) { - console.error('second it') - setTimeout(done) - }) - describe('gc 1', function () { - it('first y', function () { - console.error('first it') - }) - it('second', function (done) { - console.error('second it') - setTimeout(done) - }) - it('third', function (done) { - console.error('third it') - done() - }) - }) - it('third after gc 1', function () { - console.error('second it') - }) - }) - - describe('child 2', function () { - console.error('in child 2') - it('first z', function () { - console.error('first it') - }) - it('second', function (done) { - console.error('second it') - setTimeout(done) - }) - it('third', function (done) { - console.error('third it') - done() - }) - }) - - it('third', function () { - console.error('second it') - }) -}) diff --git a/typescript-vendor/tap/lib/cb-promise.js b/typescript-vendor/tap/lib/cb-promise.js deleted file mode 100644 index a9474b7cf..000000000 --- a/typescript-vendor/tap/lib/cb-promise.js +++ /dev/null @@ -1,9 +0,0 @@ -// Used by mocha.js, for when we want a callback to pass to -// a child function, but still want to return a Promise. -module.exports = () => { - let cb - const p = new Promise((res, rej) => { - cb = er => er ? rej(er) : res() - }) - return [cb, p] -} diff --git a/typescript-vendor/tap/lib/mocha.js b/typescript-vendor/tap/lib/mocha.js deleted file mode 100644 index 1c5b4776a..000000000 --- a/typescript-vendor/tap/lib/mocha.js +++ /dev/null @@ -1,162 +0,0 @@ -'use strict' -const t = require('./tap.js') -t.jobs = 1 -const tapStack = [ t ] -let level = 0 -const suiteStack = [] - -const describe = (name, fn, opt) => - new Suite(name, fn, opt) - -class Suite { - constructor (name, fn, opt) { - this.parent = suiteStack[ suiteStack.length - 1 ] - if (typeof name === 'function') - fn = name, name = null - if (fn && fn.name && !name) - name = fn.name - this.options = opt || {} - this.options.todo = this.options.todo || !fn - this.fn = fn - this.name = name - this.after = [] - this.test = null - - this.run() - } - - run () { - const t = tapStack[ tapStack.length - 1 ] - t.test(this.name, this.options, tt => { - this.test = tt - tapStack.push(tt) - suiteStack.push(this) - const ret = this.fn() - this.runAfter() - suiteStack.pop() - return ret - }) - } - - runAfter () { - this.after.forEach(a => - before(a[0], a[1], a[2])) - let t - do { - t = tapStack.pop() - } while (t && t !== this.test) - if (this.test && !this.test.results) - t.end() - } -} - -const before = (name, fn, options) => { - if (typeof name === 'function') - fn = name, name = null - if (fn && fn.name && !name) - name = fn.name - options = options || {} - const todo = !fn - options.todo = options.todo || todo - options.silent = true - const suite = suiteStack[ suiteStack.length - 1 ] - if (!suite) - throw new Error('cannot call "before" outside of describe()') - const t = tapStack[ tapStack.length - 1 ] - if (!name) - name = '' - - const done = tt => er => er ? tt.threw(er) : tt.end() - t.test(name, options, tt => { - const ret = fn.call(suite, done(tt)) - if (!ret && fn.length === 0) - tt.end() - else - return ret - }) -} - -const it = (name, fn, options) => { - if (typeof name === 'function') - fn = name, name = null - if (fn && fn.name && !name) - name = fn.name - options = options || {} - const todo = !fn - const suite = suiteStack[ suiteStack.length - 1 ] - const t = tapStack[ tapStack.length - 1 ] - if (!name) - name = '' - - const done = tt => er => er ? tt.threw(er) : tt.end() - options.todo = options.todo || todo - options.tapMochaTest = true - t.test(name, options, tt => { - const ret = fn.call(tt, done(tt)) - if (ret && ret.then) - return ret - else if (fn.length === 0) - tt.end() - }) -} - -it.skip = (name, fn) => it(name, fn, { skip: true }) -it.todo = (name, fn) => it(name, fn, { todo: true }) - -function after (name, fn, options) { - const suite = suiteStack[ suiteStack.length - 1 ] - if (!suite) - throw new Error('cannot call "after" outside of describe()') - suite.after.push([name, fn, options]) -} - -const cbPromise = require('./cb-promise.js') - -function moment (when, fn) { - const t = tapStack[ tapStack.length - 1 ] - // need function because 'this' tells us which tap object - // has the tapMochaTest thing in its options object - t[when](function () { - if (!this.options.tapMochaTest) - return - const suite = suiteStack[ suiteStack.length - 1 ] - - const [cb, p] = cbPromise() - const ret = fn.call(this, cb) - if (ret && ret.then) - return ret - else if (fn.length !== 0) - return p - }) -} - -const beforeEach = fn => - moment('beforeEach', fn) - -const afterEach = fn => - moment('afterEach', fn) - -exports.it = exports.specify = it -exports.context = exports.describe = describe -exports.before = before -exports.after = after -exports.beforeEach = beforeEach -exports.afterEach = afterEach - -let saved -exports.global = _ => { - if (!saved) - saved = new Map() - - Object.keys(exports).filter(g => g !== 'global').forEach(g => { - if (!saved.has(g)) - saved.set(g, global[g]) - global[g] = exports[g] - }) -} - -exports.deglobal = _ => - Object.keys(exports).filter(g => g !== 'global').forEach(g => { - if (saved && saved.has(g)) - global[g] = saved.get(g) - }) diff --git a/typescript-vendor/tap/lib/repl.js b/typescript-vendor/tap/lib/repl.js deleted file mode 100644 index 83452dbda..000000000 --- a/typescript-vendor/tap/lib/repl.js +++ /dev/null @@ -1,242 +0,0 @@ -const {Watch} = require('./watch.js') -const repl = require('repl') -const rimraf = require('rimraf').sync -const {stringify} = require('tap-yaml') -const path = require('path') -const fs = require('fs') -/* istanbul ignore next */ -const noop = () => {} - -// XXX useGlobal = true, because it doesn't matter, so save the cycles -class Repl { - constructor (options, input, output) { - this.output = output || /* istanbul ignore next */ process.stdout - this.input = input || /* istanbul ignore next */ process.stdin - - this.repl = null - this._cb = null - this.watch = new Watch(options) - this.watch.on('afterProcess', (...args) => this.afterProcess(...args)) - this.watch.on('main', () => this.start(options, input, output)) - } - - start (options, input, output) { - this.repl = repl.start({ - useColors: options.color, - input, - output, - prompt: 'TAP> ', - eval: (...args) => this.parseCommand(...args), - completer: /* istanbul ignore next */ input => this.completer(input), - writer: res => stringify(res), - }) - this.repl.history = this.loadHistory() - // doens't really make sense to have all default Node.js repl commands - // since we're not parsing JavaScript - this.repl.commands = {} - this.repl.removeAllListeners('SIGINT') - this.repl.on('SIGINT', () => { - if (this.watch.proc) { - this.watch.queue.length = 0 - rimraf(this.watch.saveFile) - this.watch.kill('SIGTERM') - } else - this.parseCommand('exit', null, null, noop) - }) - this.repl.on('close', () => { - this.saveHistory() - this.watch.pause() - }) - } - - loadHistory () { - const dir = process.env.HOME || 'node_modules/.cache/tap' - - try { - return fs.readFileSync(dir + '/.tap_repl_history', 'utf8') - .trim().split('\n') - } catch (_) { - return [] - } - } - - saveHistory () { - const dir = process.env.HOME || 'node_modules/.cache/tap' - - require('../settings.js').mkdirRecursiveSync(dir) - try { - fs.writeFileSync(dir + '/.tap_repl_history', - this.repl.history.join('\n').trim()) - } catch (e) {} - } - - get running () { - return !!this.watch.proc - } - - parseCommand (input, _, __, cb) { - if (this.running) - return cb(null, 'test in progress, please wait') - - input = input.trimLeft().split(' ') - const cmd = input.shift().trim() - const arg = input.join(' ').trim() - - switch (cmd) { - case 'r': - return this.run(arg, cb) - case 'u': - return this.update(arg, cb) - case 'n': - return this.changed(cb) - case 'p': - return this.pauseResume(cb) - case 'c': - return this.coverageReport(arg, cb) - case 'exit': - return this.exit(cb) - case 'clear': - return this.clear(cb) - case 'cls': - this.repl.output.write('\u001b[2J\u001b[H') - return cb() - default: - return this.help(cb) - } - } - - run (arg, cb) { - this.watch.queue.length = 0 - rimraf(this.watch.saveFile) - if (arg) { - const tests = this.watch.positionals - if (tests.length && !tests.includes(arg)) { - tests.push(arg) - this.watch.args.push(arg) - } - this.watch.queue.push(arg) - } - this._cb = cb - this.watch.run() - } - - afterProcess (res) { - if (this._cb) { - const cb = this._cb - this._cb = null - cb(null, res) - } else { - this.output.write(stringify(res)) - this.repl.displayPrompt(true) - } - } - - update (arg, cb) { - const envBefore = this.watch.env - this.watch.env = { - ...this.watch.env, - TAP_SNAPSHOT: '1' - } - this.run(arg, (er, res) => { - this.watch.env = envBefore - cb(er, res) - }) - } - - changed (cb) { - this.watch.args.push('--changed') - this.run(null, (er, res) => { - this.watch.args.pop() - cb(er, res) - }) - } - - pauseResume (cb) { - if (this.watch.watcher) - this.watch.pause() - else - this.watch.resume() - this.output.write(this.watch.watcher ? 'resumed\n' : 'paused\n') - cb() - } - - coverageReport (arg, cb) { - const report = arg || 'text' - const args = this.watch.args - this.watch.args = [this.watch.args[0], '--coverage-report=' + report] - this.run(null, (er, res) => { - this.watch.args = args - cb(er, res) - }) - } - - clear (cb) { - rimraf('.nyc_output') - this.run(null, cb) - } - - exit (cb) { - this.watch.pause() - this.watch.kill('SIGTERM') - this.repl.close() - } - - help (cb) { - this.output.write(`TAP Repl Commands: - -r [] - run test suite, or the supplied filename - -u [] - update snapshots in the suite, or in the supplied filename - -n - run the suite with --changed - -p - pause/resume the file watcher - -c [] - run coverage report. Default to 'text' style. - -exit - exit the repl - -clear - delete all coverage info and re-run the test suite - -cls - clear the screen -`) - cb() - } - - filterCompletions (list, input) { - const hits = list.filter(l => l.startsWith(input)) - return hits.length ? hits : list - } - - completer (input) { - const cmdArg = input.trimLeft().split(' ') - const cmd = cmdArg.shift() - const arg = cmdArg.join(' ').trimLeft() - const commands = ['r', 'u', 'n', 'p', 'c', 'exit', 'clear', 'cls'] - if (cmd === 'r' || cmd === 'u') { - const d = path.dirname(arg) - const dir = arg.slice(-1) === '/' ? arg : d === '.' ? '' : d + '/' - try { - const set = this.filterCompletions( - fs.readdirSync(dir || '.') - .map(f => fs.statSync(dir + f).isDirectory() ? f + '/' : f) - .map(f => cmd + ' ' + dir + f), input) - return [set, input] - } catch (er) { - return [[cmd], input] - } - } else { - return [this.filterCompletions(commands, input), input] - } - } -} - -module.exports = {Repl} diff --git a/typescript-vendor/tap/lib/synonyms.js b/typescript-vendor/tap/lib/synonyms.js deleted file mode 100644 index 13d9d685c..000000000 --- a/typescript-vendor/tap/lib/synonyms.js +++ /dev/null @@ -1,80 +0,0 @@ -'use strict' -// A list of all the synonyms of assert methods. -// In addition to these, multi-word camelCase are also synonymized to -// all lowercase and snake_case - -const multiword = obj => - Object.keys(obj).reduce((s, i) => - (s[i] = [ multiword_(i) ] - .concat(obj[i].map(multiword_)) - .reduce((s, i) => (s.push.apply(s, i), s), []), s), obj) - -const multiword_ = str => - str.match(/[A-Z]/) ? [ - str, - str.toLowerCase(), - str.replace(/[A-Z]/g, $0 => '_' + $0.toLowerCase()) - ] : [str] - -module.exports = multiword({ - ok: ['true', 'assert'], - notOk: ['false', 'assertNot'], - - error: ['ifError', 'ifErr'], - throws: ['throw'], - doesNotThrow: ['notThrow'], - - // exactly the same. === - equal: [ - 'equals', 'isEqual', 'is', 'strictEqual', 'strictEquals', 'strictIs', - 'isStrict', 'isStrictly', 'identical' - ], - - // not equal. !== - not: [ - 'inequal', 'notEqual', 'notEquals', 'notStrictEqual', 'notStrictEquals', - 'isNotEqual', 'isNot', 'doesNotEqual', 'isInequal' - ], - - // deep equivalence. == for scalars - same: [ - 'equivalent', 'looseEqual', 'looseEquals', 'deepEqual', - 'deepEquals', 'isLoose', 'looseIs', 'isEquivalent' - ], - - // deep inequivalence. != for scalars - notSame: [ - 'inequivalent', 'looseInequal', 'notDeep', 'deepInequal', - 'notLoose', 'looseNot', 'notEquivalent', 'isNotDeepEqual', - 'isNotDeeply', 'notDeepEqual', 'isInequivalent', - 'isNotEquivalent' - ], - - // deep equivalence, === for scalars - strictSame: [ - 'strictEquivalent', 'strictDeepEqual', 'sameStrict', 'deepIs', - 'isDeeply', 'isDeep', 'strictDeepEquals' - ], - - // deep inequivalence, !== for scalars - strictNotSame: [ - 'strictInequivalent', 'strictDeepInequal', 'notSameStrict', 'deepNot', - 'notDeeply', 'strictDeepInequals', 'notStrictSame' - ], - - // found has the fields in wanted, string matches regexp - match: [ - 'matches', 'similar', 'like', 'isLike', 'isSimilar' - ], - - has: [ - 'hasFields', 'includes', 'include', 'contains' - ], - - notMatch: [ - 'dissimilar', 'unsimilar', 'notSimilar', 'unlike', 'isUnlike', - 'notLike', 'isNotLike', 'doesNotHave', 'isNotSimilar', 'isDissimilar' - ], - - type: ['isA'] -}) diff --git a/typescript-vendor/tap/lib/tap.js b/typescript-vendor/tap/lib/tap.js deleted file mode 100644 index 73f0ffb0a..000000000 --- a/typescript-vendor/tap/lib/tap.js +++ /dev/null @@ -1,36 +0,0 @@ -'use strict' -const {deprecate} = require('util') -const settings = require('../settings.js') -const tap = require('libtap') - -// Needs to be set before requiring mocha.js -module.exports = tap - -tap.mocha = require('./mocha.js') -tap.mochaGlobals = tap.mocha.global -tap.synonyms = require('./synonyms.js') - -/* istanbul ignore next: unsure how to test this function */ -tap.Test.prototype.tearDown = deprecate(function (fn) { - this.teardown(fn) -}, 'tearDown() is deprecated, use teardown() instead') - -tap.tearDown = tap.tearDown.bind(tap) - -Object.keys(tap.synonyms).forEach(c => { - tap.synonyms[c].forEach(s => { - if (c === s) { - return - } - - Object.defineProperty(tap.Test.prototype, s, { - value: deprecate(tap.Test.prototype[c], `${s}() is deprecated, use ${c}() instead`), - enumerable: false, - configurable: true, - writable: true - }) - - // Manually bind methods for the already created instance - tap[s] = tap[s].bind(tap) - }) -}) diff --git a/typescript-vendor/tap/lib/tap.mjs b/typescript-vendor/tap/lib/tap.mjs deleted file mode 100644 index ed24e7b55..000000000 --- a/typescript-vendor/tap/lib/tap.mjs +++ /dev/null @@ -1,31 +0,0 @@ -import tap from './tap.js' - -export const { - Test, Spawn, Stdin, - spawn, sub, - todo, skip, only, test, - stdinOnly, stdin, - bailout, - comment, - timeout, - main, - process, - processSubtest, - addAssert, - pragma, - plan, end, - beforeEach, - afterEach, - teardown, - autoend, - pass, fail, ok, notOk, - emits, - error, equal, not, same, notSame, strictSame, strictNotSame, - testdir, fixture, - matchSnapshot, - hasStrict, match, notMatch, type, - expectUncaughtException, throwsArgs, throws, doesNotThrow, - rejects, resolves, resolveMatch, resolveMatchSnapshot -} = tap - -export default tap diff --git a/typescript-vendor/tap/lib/watch.js b/typescript-vendor/tap/lib/watch.js deleted file mode 100644 index 9dade6045..000000000 --- a/typescript-vendor/tap/lib/watch.js +++ /dev/null @@ -1,180 +0,0 @@ -const chokidar = require('chokidar') -const EE = require('events') -const Minipass = require('minipass') -const bin = require.resolve('../bin/run.js') -const {spawn} = require('child_process') -const onExit = require('signal-exit') -const {writeFileSync, readFileSync} = require('fs') -const {stringify} = require('tap-yaml') -const {resolve} = require('path') - -class Watch extends Minipass { - constructor (options) { - if (!options.coverage) - throw new Error('--watch requires coverage to be enabled') - super() - this.args = [bin, ...options._.parsed, '--no-watch'] - this.positionals = [...options._] - this.log('initial test run', this.args) - this.proc = spawn(process.execPath, this.args, { - stdio: 'inherit' - }) - this.proc.on('close', () => this.main()) - const saveFolder = 'node_modules/.cache/tap' - require('../settings.js').mkdirRecursiveSync(saveFolder) - this.saveFile = saveFolder + '/watch-' + process.pid - /* istanbul ignore next */ - onExit(() => require('../settings.js').rmdirRecursiveSync(this.saveFile)) - this.index = null - this.indexFile = '.nyc_output/processinfo/index.json' - this.fileList = [] - this.queue = [] - this.watcher = null - this.env = { ...process.env } - } - - readIndex () { - this.index = JSON.parse(readFileSync(this.indexFile, 'utf8')) - } - - kill (signal) { - if (this.proc) - this.proc.kill(signal) - } - - watchList () { - if (!this.index) - this.readIndex() - // externalIds are the relative path to a test file - // the files object keys are fully resolved. - // If a test is covered, it'll show up in both! - // Since a covered test was definitely included in its own - // test run, don't add it a second time, so we don't get - // two chokidar events for the same file change. - const cwd = process.cwd() - const fileSet = new Set(Object.keys(this.index.files)) - Object.keys(this.index.externalIds) - .filter(f => !fileSet.has(resolve(f))) - .forEach(f => fileSet.add(f)) - return [...fileSet] - } - - pause () { - if (this.watcher) - this.watcher.close() - this.watcher = null - } - - resume () { - if (!this.watcher) - this.watch() - } - - main () { - this.emit('main') - this.proc = null - this.fileList = this.watchList() - this.watch() - } - - watch () { - this.pause() - const sawAdd = new Map() - const watcher = this.watcher = chokidar.watch(this.fileList) - // ignore the first crop of add events, since we already ran the tests - watcher.on('all', (ev, file) => { - if (ev === 'add' && !sawAdd.get(file)) - sawAdd.set(file, true) - else - this.onChange(ev, file) - }) - return watcher - } - - onChange (ev, file) { - const tests = this.testsFromChange(file) - - this.queue.push(...tests) - this.log(ev + ' ' + file) - - if (this.proc) - return this.log('test in progress, queuing for next run') - - this.run() - } - - run (env) { - const set = [...new Set(this.queue)] - this.log('running tests', set) - writeFileSync(this.saveFile, set.join('\n') + '\n') - this.queue.length = 0 - - this.proc = spawn(process.execPath, [ - ...this.args, '--save=' + this.saveFile, '--nyc-arg=--no-clean' - ], { - stdio: 'inherit', - env: this.env, - }) - this.proc.on('close', (code, signal) => this.onClose(code, signal)) - this.emit('process', this.proc) - } - - onClose (code, signal) { - this.readIndex() - this.proc = null - - // only add if it's not already there as either a test or included file - const newFileList = this.watchList().filter(f => - !this.fileList.includes(f) && - !this.fileList.includes(resolve(f))) - - this.fileList.push(...newFileList) - this.resume() - newFileList.forEach(f => this.watcher.add(f)) - - // if there are any failures (especially, from a bail out) - // then add those, but ignore if it's not there. - const leftover = (() => { - try { - return fs.readFileSync(saveFile, 'utf8').trim().split('\n') - } catch (er) { - return [] - } - })() - // run again if something was added during the process - const runAgain = this.queue.length - this.queue.push(...leftover) - - if (runAgain) - this.run() - else - this.emit('afterProcess', {code, signal}) - } - - log (msg, arg) { - if (arg && typeof arg !== 'string') - msg += '\n' + stringify(arg) - this.write(msg + '\n') - } - - testsFromChange (file) { - return this.index.externalIds[file] ? [file] - : this.testsFromFile(file) - } - - testsFromFile (file) { - const reducer = (set, uuid) => { - for (let process = this.index.processes[uuid]; - process; - process = process.parent && this.index.processes[process.parent]) { - if (process.externalId) - set.add(process.externalId) - } - return set - } - const procs = this.index.files[file] || /* istanbul ignore next */ [] - return [...procs.reduce(reducer, new Set())] - } -} - -module.exports = {Watch} diff --git a/typescript-vendor/tap/netlify.toml b/typescript-vendor/tap/netlify.toml deleted file mode 100644 index a1facacf6..000000000 --- a/typescript-vendor/tap/netlify.toml +++ /dev/null @@ -1,4 +0,0 @@ -[build] - base = "docs" - command = "gatsby build" - publish = "docs/public" diff --git a/typescript-vendor/tap/package-lock.json b/typescript-vendor/tap/package-lock.json deleted file mode 100644 index 1c60d2aea..000000000 --- a/typescript-vendor/tap/package-lock.json +++ /dev/null @@ -1,6245 +0,0 @@ -{ - "name": "tap", - "version": "15.0.9", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "version": "15.0.9", - "bundleDependencies": [ - "ink", - "treport", - "@types/react" - ], - "license": "ISC", - "dependencies": { - "@types/react": "^16.9.23", - "chokidar": "^3.3.0", - "coveralls": "^3.0.11", - "findit": "^2.0.0", - "foreground-child": "^2.0.0", - "fs-exists-cached": "^1.0.0", - "glob": "^7.1.6", - "import-jsx": "^4.0.0", - "ink": "^2.7.1", - "isexe": "^2.0.0", - "istanbul-lib-processinfo": "^2.0.2", - "jackspeak": "^1.4.0", - "libtap": "^1.1.1", - "minipass": "^3.1.1", - "mkdirp": "^1.0.4", - "nyc": "^15.1.0", - "opener": "^1.5.1", - "react": "^16.12.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.0", - "source-map-support": "^0.5.16", - "tap-mocha-reporter": "^5.0.0", - "tap-parser": "^10.0.1", - "tap-yaml": "^1.0.0", - "tcompare": "^5.0.6", - "treport": "^2.0.2", - "which": "^2.0.2" - }, - "bin": { - "tap": "bin/run.js" - }, - "devDependencies": { - "flow-remove-types": "^2.112.0", - "node-preload": "^0.2.1", - "process-on-spawn": "^1.0.0", - "ts-node": "^8.5.2", - "typescript": "^3.7.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "peerDependencies": { - "flow-remove-types": ">=2.112.0", - "ts-node": ">=8.5.2", - "typescript": ">=3.7.2" - }, - "peerDependenciesMeta": { - "flow-remove-types": { - "optional": true - }, - "ts-node": { - "optional": true - }, - "typescript": { - "optional": true - } - } - }, - "node_modules/@babel/code-frame": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", - "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", - "inBundle": true, - "dependencies": { - "@babel/highlight": "^7.12.13" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.0.tgz", - "integrity": "sha512-vu9V3uMM/1o5Hl5OekMUowo3FqXLJSw+s+66nt0fSWVWTtmosdzn45JHOB3cPtZoe6CTBDzvSw0RdOY85Q37+Q==", - "inBundle": true - }, - "node_modules/@babel/core": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.0.tgz", - "integrity": "sha512-8YqpRig5NmIHlMLw09zMlPTvUVMILjqCOtVgu+TVNWEBvy9b5I3RRyhqnrV4hjgEK7n8P9OqvkWJAFmEL6Wwfw==", - "inBundle": true, - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.14.0", - "@babel/helper-compilation-targets": "^7.13.16", - "@babel/helper-module-transforms": "^7.14.0", - "@babel/helpers": "^7.14.0", - "@babel/parser": "^7.14.0", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.14.0", - "@babel/types": "^7.14.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0", - "source-map": "^0.5.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.1.tgz", - "integrity": "sha512-TMGhsXMXCP/O1WtQmZjpEYDhCYC9vFhayWZPJSZCGkPJgUqX0rF0wwtrYvnzVxIjcF80tkUertXVk5cwqi5cAQ==", - "inBundle": true, - "dependencies": { - "@babel/types": "^7.14.1", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz", - "integrity": "sha512-7YXfX5wQ5aYM/BOlbSccHDbuXXFPxeoUmfWtz8le2yTkTZc+BxsiEnENFoi2SlmA8ewDkG2LgIMIVzzn2h8kfw==", - "inBundle": true, - "dependencies": { - "@babel/types": "^7.12.13" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.13.16", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.16.tgz", - "integrity": "sha512-3gmkYIrpqsLlieFwjkGgLaSHmhnvlAYzZLlYVjlW+QwI+1zE17kGxuJGmIqDQdYp56XdmGeD+Bswx0UTyG18xA==", - "inBundle": true, - "dependencies": { - "@babel/compat-data": "^7.13.15", - "@babel/helper-validator-option": "^7.12.17", - "browserslist": "^4.14.5", - "semver": "^6.3.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", - "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", - "inBundle": true, - "dependencies": { - "@babel/helper-get-function-arity": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/types": "^7.12.13" - } - }, - "node_modules/@babel/helper-get-function-arity": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", - "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", - "inBundle": true, - "dependencies": { - "@babel/types": "^7.12.13" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz", - "integrity": "sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw==", - "inBundle": true, - "dependencies": { - "@babel/types": "^7.13.12" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz", - "integrity": "sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA==", - "inBundle": true, - "dependencies": { - "@babel/types": "^7.13.12" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.0.tgz", - "integrity": "sha512-L40t9bxIuGOfpIGA3HNkJhU9qYrf4y5A5LUSw7rGMSn+pcG8dfJ0g6Zval6YJGd2nEjI7oP00fRdnhLKndx6bw==", - "inBundle": true, - "dependencies": { - "@babel/helper-module-imports": "^7.13.12", - "@babel/helper-replace-supers": "^7.13.12", - "@babel/helper-simple-access": "^7.13.12", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/helper-validator-identifier": "^7.14.0", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.14.0", - "@babel/types": "^7.14.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz", - "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==", - "inBundle": true, - "dependencies": { - "@babel/types": "^7.12.13" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz", - "integrity": "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==", - "inBundle": true - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.13.12.tgz", - "integrity": "sha512-Gz1eiX+4yDO8mT+heB94aLVNCL+rbuT2xy4YfyNqu8F+OI6vMvJK891qGBTqL9Uc8wxEvRW92Id6G7sDen3fFw==", - "inBundle": true, - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.13.12", - "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/traverse": "^7.13.0", - "@babel/types": "^7.13.12" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz", - "integrity": "sha512-7FEjbrx5SL9cWvXioDbnlYTppcZGuCY6ow3/D5vMggb2Ywgu4dMrpTJX0JdQAIcRRUElOIxF3yEooa9gUb9ZbA==", - "inBundle": true, - "dependencies": { - "@babel/types": "^7.13.12" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", - "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", - "inBundle": true, - "dependencies": { - "@babel/types": "^7.12.13" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz", - "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==", - "inBundle": true - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.12.17", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz", - "integrity": "sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw==", - "inBundle": true - }, - "node_modules/@babel/helpers": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.0.tgz", - "integrity": "sha512-+ufuXprtQ1D1iZTO/K9+EBRn+qPWMJjZSw/S0KlFrxCw4tkrzv9grgpDHkY9MeQTjTY8i2sp7Jep8DfU6tN9Mg==", - "inBundle": true, - "dependencies": { - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.14.0", - "@babel/types": "^7.14.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz", - "integrity": "sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==", - "inBundle": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.14.0", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.1.tgz", - "integrity": "sha512-muUGEKu8E/ftMTPlNp+mc6zL3E9zKWmF5sDHZ5MSsoTP9Wyz64AhEf9kD08xYJ7w6Hdcu8H550ircnPyWSIF0Q==", - "inBundle": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.13.8.tgz", - "integrity": "sha512-DhB2EuB1Ih7S3/IRX5AFVgZ16k3EzfRbq97CxAVI1KSYcW+lexV8VZb7G7L8zuPVSdQMRn0kiBpf/Yzu9ZKH0g==", - "inBundle": true, - "dependencies": { - "@babel/compat-data": "^7.13.8", - "@babel/helper-compilation-targets": "^7.13.8", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.13.tgz", - "integrity": "sha512-d4HM23Q1K7oq/SLNmG6mRt85l2csmQ0cHRaxRXjKW0YFdEXqlZ5kzFQKH5Uc3rDJECgu+yCRgPkG04Mm98R/1g==", - "inBundle": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "inBundle": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.13.17", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.13.17.tgz", - "integrity": "sha512-UAUqiLv+uRLO+xuBKKMEpC+t7YRNVRqBsWWq1yKXbBZBje/t3IXCiSinZhjn/DC3qzBfICeYd2EFGEbHsh5RLA==", - "inBundle": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.13.0.tgz", - "integrity": "sha512-Jt8k/h/mIwE2JFEOb3lURoY5C85ETcYPnbuAJ96zRBzh1XHtQZfs62ChZ6EP22QlC8c7Xqr9q+e1SU5qttwwjw==", - "inBundle": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.13.12.tgz", - "integrity": "sha512-jcEI2UqIcpCqB5U5DRxIl0tQEProI2gcu+g8VTIqxLO5Iidojb4d77q+fwGseCvd8af/lJ9masp4QWzBXFE2xA==", - "inBundle": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-module-imports": "^7.13.12", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-jsx": "^7.12.13", - "@babel/types": "^7.13.12" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", - "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", - "inBundle": true, - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@babel/parser": "^7.12.13", - "@babel/types": "^7.12.13" - } - }, - "node_modules/@babel/traverse": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.0.tgz", - "integrity": "sha512-dZ/a371EE5XNhTHomvtuLTUyx6UEoJmYX+DT5zBCQN3McHemsuIaKKYqsc/fs26BEkHs/lBZy0J571LP5z9kQA==", - "inBundle": true, - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.14.0", - "@babel/helper-function-name": "^7.12.13", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/parser": "^7.14.0", - "@babel/types": "^7.14.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - } - }, - "node_modules/@babel/types": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.1.tgz", - "integrity": "sha512-S13Qe85fzLs3gYRUnrpyeIrBJIMYv33qSTg1qoBwiG6nPKwUWAD9odSzWhEedpwOIzSEI6gbdQIWEMiCI42iBA==", - "inBundle": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.14.0", - "to-fast-properties": "^2.0.0" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@types/prop-types": { - "version": "15.7.3", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", - "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==", - "inBundle": true - }, - "node_modules/@types/react": { - "version": "16.14.6", - "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.6.tgz", - "integrity": "sha512-Ol/aFKune+P0FSFKIgf+XbhGzYGyz0p7g5befSt4rmbzfGLaZR0q7jPew9k7d3bvrcuaL8dPy9Oz3XGZmf9n+w==", - "inBundle": true, - "dependencies": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/scheduler": { - "version": "0.16.1", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.1.tgz", - "integrity": "sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA==", - "inBundle": true - }, - "node_modules/@types/yoga-layout": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@types/yoga-layout/-/yoga-layout-1.9.2.tgz", - "integrity": "sha512-S9q47ByT2pPvD65IvrWp7qppVMpk9WGMbVq9wbWZOHg6tnXSD4vyhao6nOSBwwfDdV2p3Kx9evA9vI+XWTfDvw==", - "inBundle": true - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "inBundle": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "engines": { - "node": ">=4" - } - }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "inBundle": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/ansicolors": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", - "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=", - "inBundle": true - }, - "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/append-transform": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", - "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", - "dependencies": { - "default-require-extensions": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=" - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/arrify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", - "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", - "inBundle": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "inBundle": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/async-hook-domain": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/async-hook-domain/-/async-hook-domain-2.0.3.tgz", - "integrity": "sha512-MadiLLDEZRZzZwcm0dgS+K99qXZ4H2saAUwUgwzFulbAkXrKi3AX5FvWS3FFTQtLMwrqcGqAJe6o12KrObejQA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "node_modules/auto-bind": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-4.0.0.tgz", - "integrity": "sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ==", - "inBundle": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "inBundle": true - }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/bind-obj-methods": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bind-obj-methods/-/bind-obj-methods-3.0.0.tgz", - "integrity": "sha512-nLEaaz3/sEzNSyPWRsN9HNsqwk1AUyECtGj+XwGdIi3xABnEqecvXtIJ0wehQXuuER5uZ/5fTs2usONgYjG+iw==", - "engines": { - "node": ">=10" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "inBundle": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.16.6", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", - "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", - "inBundle": true, - "dependencies": { - "caniuse-lite": "^1.0.30001219", - "colorette": "^1.2.2", - "electron-to-chromium": "^1.3.723", - "escalade": "^3.1.1", - "node-releases": "^1.1.71" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - } - }, - "node_modules/buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" - }, - "node_modules/caching-transform": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", - "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", - "dependencies": { - "hasha": "^5.0.0", - "make-dir": "^3.0.0", - "package-hash": "^4.0.0", - "write-file-atomic": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/caller-callsite": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", - "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", - "inBundle": true, - "dependencies": { - "callsites": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/caller-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", - "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", - "inBundle": true, - "dependencies": { - "caller-callsite": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/callsites": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", - "inBundle": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001223", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001223.tgz", - "integrity": "sha512-k/RYs6zc/fjbxTjaWZemeSmOjO0JJV+KguOBA3NwPup8uzxM1cMhR2BD9XmO86GuqaqTCO8CgkgH9Rz//vdDiA==", - "inBundle": true - }, - "node_modules/cardinal": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", - "integrity": "sha1-fMEFXYItISlU0HsIXeolHMe8VQU=", - "inBundle": true, - "dependencies": { - "ansicolors": "~0.3.2", - "redeyed": "~2.1.0" - }, - "bin": { - "cdl": "bin/cdl.js" - } - }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "inBundle": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", - "dependencies": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.1" - } - }, - "node_modules/ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "inBundle": true - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "inBundle": true, - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", - "inBundle": true, - "dependencies": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "dependencies": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - } - }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cliui/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "engines": { - "node": ">=4" - } - }, - "node_modules/cliui/node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dependencies": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cliui/node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dependencies": { - "number-is-nan": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cliui/node_modules/wrap-ansi/node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cliui/node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "inBundle": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "inBundle": true - }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "bin": { - "color-support": "bin.js" - } - }, - "node_modules/colorette": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", - "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", - "inBundle": true - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "inBundle": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "inBundle": true - }, - "node_modules/convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "inBundle": true, - "dependencies": { - "safe-buffer": "~5.1.1" - } - }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "node_modules/coveralls": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.1.0.tgz", - "integrity": "sha512-sHxOu2ELzW8/NC1UP5XVLbZDzO4S3VxfFye3XYCznopHy02YjNkHcj5bKaVw2O7hVaBdBjEdQGpie4II1mWhuQ==", - "dependencies": { - "js-yaml": "^3.13.1", - "lcov-parse": "^1.0.0", - "log-driver": "^1.2.7", - "minimist": "^1.2.5", - "request": "^2.88.2" - }, - "bin": { - "coveralls": "bin/coveralls.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/csstype": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.8.tgz", - "integrity": "sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==", - "inBundle": true - }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "inBundle": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/default-require-extensions": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", - "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", - "dependencies": { - "strip-bom": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.3.727", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.727.tgz", - "integrity": "sha512-Mfz4FIB4FSvEwBpDfdipRIrwd6uo8gUDoRDF4QEYb4h4tSuI3ov594OrjU6on042UlFHouIJpClDODGkPcBSbg==", - "inBundle": true - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "inBundle": true - }, - "node_modules/es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==" - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "inBundle": true, - "engines": { - "node": ">=6" - } - }, - "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=", - "inBundle": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "inBundle": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/events-to-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/events-to-array/-/events-to-array-1.1.2.tgz", - "integrity": "sha1-LUH1Y+H+QA7Uli/hpNXGp1Od9/Y=", - "inBundle": true - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "engines": [ - "node >=0.6.0" - ] - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-cache-dir": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", - "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", - "inBundle": true, - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "inBundle": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/findit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/findit/-/findit-2.0.0.tgz", - "integrity": "sha1-ZQnwEmr0wXhVHPqZOU4DLhOk1W4=" - }, - "node_modules/flow-parser": { - "version": "0.150.1", - "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.150.1.tgz", - "integrity": "sha512-BGzmgjz6Ma6LN6rfrnVGDo/2wsAGHbcCASDVpWu8/GAriikDQCiiIND/6HBVBxhy1Uga1Sgmfl8iiIln+t3kCw==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/flow-remove-types": { - "version": "2.150.1", - "resolved": "https://registry.npmjs.org/flow-remove-types/-/flow-remove-types-2.150.1.tgz", - "integrity": "sha512-bZ3A1068GuWeBWd+J7ZnSNLoJYFEPbajzMEmov4KkJA0rm/3Pz8t2TGEH6cz+jQmDuO08F75jvAkjXs1KnHqqw==", - "dev": true, - "dependencies": { - "flow-parser": "^0.150.1", - "pirates": "^3.0.2", - "vlq": "^0.2.1" - }, - "bin": { - "flow-node": "flow-node", - "flow-remove-types": "flow-remove-types" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/foreground-child": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "engines": { - "node": "*" - } - }, - "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/fromentries": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", - "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/fs-exists-cached": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-exists-cached/-/fs-exists-cached-1.0.0.tgz", - "integrity": "sha1-zyVVTKBQ3EmuZla0HeQiWJidy84=" - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "inBundle": true - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-loop": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/function-loop/-/function-loop-2.0.1.tgz", - "integrity": "sha512-ktIR+O6i/4h+j/ZhZJNdzeI4i9lEPeEK6UPR2EVyTVBqOwcU3Za9xYKLH64ZR9HmcROyRrOkizNyjjtWJzDDkQ==" - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "inBundle": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dependencies": { - "assert-plus": "^1.0.0" - } - }, - "node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "inBundle": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "inBundle": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" - }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "deprecated": "this library is no longer supported", - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "inBundle": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/hasha": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", - "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", - "dependencies": { - "is-stream": "^2.0.0", - "type-fest": "^0.8.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/hasha/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==" - }, - "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, - "node_modules/import-jsx": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-jsx/-/import-jsx-4.0.0.tgz", - "integrity": "sha512-CnjJ2BZFJzbFDmYG5S47xPQjMlSbZLyLJuG4znzL4TdPtJBxHtFP1xVmR+EYX4synFSldiY3B6m00XkPM3zVnA==", - "inBundle": true, - "dependencies": { - "@babel/core": "^7.5.5", - "@babel/plugin-proposal-object-rest-spread": "^7.5.5", - "@babel/plugin-transform-destructuring": "^7.5.0", - "@babel/plugin-transform-react-jsx": "^7.3.0", - "caller-path": "^2.0.0", - "find-cache-dir": "^3.2.0", - "make-dir": "^3.0.2", - "resolve-from": "^3.0.0", - "rimraf": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "inBundle": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "inBundle": true - }, - "node_modules/ink": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/ink/-/ink-2.7.1.tgz", - "integrity": "sha512-s7lJuQDJEdjqtaIWhp3KYHl6WV3J04U9zoQ6wVc+Xoa06XM27SXUY57qC5DO46xkF0CfgXMKkKNcgvSu/SAEpA==", - "inBundle": true, - "dependencies": { - "ansi-escapes": "^4.2.1", - "arrify": "^2.0.1", - "auto-bind": "^4.0.0", - "chalk": "^3.0.0", - "cli-cursor": "^3.1.0", - "cli-truncate": "^2.1.0", - "is-ci": "^2.0.0", - "lodash.throttle": "^4.1.1", - "log-update": "^3.0.0", - "prop-types": "^15.6.2", - "react-reconciler": "^0.24.0", - "scheduler": "^0.18.0", - "signal-exit": "^3.0.2", - "slice-ansi": "^3.0.0", - "string-length": "^3.1.0", - "widest-line": "^3.1.0", - "wrap-ansi": "^6.2.0", - "yoga-layout-prebuilt": "^1.9.3" - }, - "engines": { - "node": ">=8" - }, - "peerDependencies": { - "@types/react": ">=16.8.0", - "react": ">=16.8.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/ink/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "inBundle": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/ink/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "inBundle": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ink/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "inBundle": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/ink/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "inBundle": true - }, - "node_modules/ink/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "inBundle": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ink/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "inBundle": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "inBundle": true, - "dependencies": { - "ci-info": "^2.0.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "inBundle": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", - "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-hook": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", - "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", - "dependencies": { - "append-transform": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dependencies": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-processinfo": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", - "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", - "dependencies": { - "archy": "^1.0.0", - "cross-spawn": "^7.0.0", - "istanbul-lib-coverage": "^3.0.0-alpha.1", - "make-dir": "^3.0.0", - "p-map": "^3.0.0", - "rimraf": "^3.0.0", - "uuid": "^3.3.3" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", - "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/istanbul-reports": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", - "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jackspeak": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-1.4.0.tgz", - "integrity": "sha512-VDcSunT+wcccoG46FtzuBAyQKlzhHjli4q31e1fIHGOsRspqNUFjVzGb+7eIFDlTvqLygxapDHPHS0ouT2o/tw==", - "dependencies": { - "cliui": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "inBundle": true - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "inBundle": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, - "node_modules/json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "inBundle": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "node_modules/lcov-parse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-1.0.0.tgz", - "integrity": "sha1-6w1GtUER68VhrLTECO+TY73I9+A=", - "bin": { - "lcov-parse": "bin/cli.js" - } - }, - "node_modules/libtap": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/libtap/-/libtap-1.1.1.tgz", - "integrity": "sha512-Fye8fh1+G7E8qqmjQaY+pXGxy7HM0S6bqCCJFLa16+g2jODBByxbJFDpjbDNF69wfRVyvJ+foLZc1WTIv7dx+g==", - "dependencies": { - "async-hook-domain": "^2.0.1", - "bind-obj-methods": "^3.0.0", - "diff": "^4.0.2", - "function-loop": "^2.0.1", - "minipass": "^3.1.1", - "own-or": "^1.0.0", - "own-or-env": "^1.0.1", - "signal-exit": "^3.0.2", - "stack-utils": "^2.0.1", - "tap-parser": "^10.0.1", - "tap-yaml": "^1.0.0", - "tcompare": "^5.0.1", - "trivial-deferred": "^1.0.1", - "yapool": "^1.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "inBundle": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=" - }, - "node_modules/lodash.throttle": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", - "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=", - "inBundle": true - }, - "node_modules/log-driver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", - "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==", - "engines": { - "node": ">=0.8.6" - } - }, - "node_modules/log-update": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-3.4.0.tgz", - "integrity": "sha512-ILKe88NeMt4gmDvk/eb615U/IVn7K9KWGkoYbdatQ69Z65nj1ZzjM6fHXfcs0Uge+e+EGnMW7DY4T9yko8vWFg==", - "inBundle": true, - "dependencies": { - "ansi-escapes": "^3.2.0", - "cli-cursor": "^2.1.0", - "wrap-ansi": "^5.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "inBundle": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/log-update/node_modules/ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "inBundle": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/log-update/node_modules/cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "inBundle": true, - "dependencies": { - "restore-cursor": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/log-update/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "inBundle": true - }, - "node_modules/log-update/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "inBundle": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/log-update/node_modules/mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "inBundle": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/log-update/node_modules/onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "inBundle": true, - "dependencies": { - "mimic-fn": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/log-update/node_modules/restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "inBundle": true, - "dependencies": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/log-update/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "inBundle": true, - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/log-update/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "inBundle": true, - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/log-update/node_modules/wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "inBundle": true, - "dependencies": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "inBundle": true, - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "inBundle": true, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/mime-db": { - "version": "1.47.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz", - "integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.30", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz", - "integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==", - "dependencies": { - "mime-db": "1.47.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "inBundle": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "inBundle": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "inBundle": true - }, - "node_modules/minipass": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", - "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", - "inBundle": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "inBundle": true - }, - "node_modules/node-modules-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", - "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/node-preload": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", - "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", - "dependencies": { - "process-on-spawn": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/node-releases": { - "version": "1.1.71", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.71.tgz", - "integrity": "sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg==", - "inBundle": true - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nyc": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", - "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", - "dependencies": { - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "caching-transform": "^4.0.0", - "convert-source-map": "^1.7.0", - "decamelize": "^1.2.0", - "find-cache-dir": "^3.2.0", - "find-up": "^4.1.0", - "foreground-child": "^2.0.0", - "get-package-type": "^0.1.0", - "glob": "^7.1.6", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-hook": "^3.0.0", - "istanbul-lib-instrument": "^4.0.0", - "istanbul-lib-processinfo": "^2.0.2", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.2", - "make-dir": "^3.0.0", - "node-preload": "^0.2.1", - "p-map": "^3.0.0", - "process-on-spawn": "^1.0.0", - "resolve-from": "^5.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "spawn-wrap": "^2.0.0", - "test-exclude": "^6.0.0", - "yargs": "^15.0.2" - }, - "bin": { - "nyc": "bin/nyc.js" - }, - "engines": { - "node": ">=8.9" - } - }, - "node_modules/nyc/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "engines": { - "node": "*" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "inBundle": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "inBundle": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "inBundle": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/opener": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", - "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", - "bin": { - "opener": "bin/opener-bin.js" - } - }, - "node_modules/own-or": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/own-or/-/own-or-1.0.0.tgz", - "integrity": "sha1-Tod/vtqaLsgAD7wLyuOWRe6L+Nw=" - }, - "node_modules/own-or-env": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/own-or-env/-/own-or-env-1.0.1.tgz", - "integrity": "sha512-y8qULRbRAlL6x2+M0vIe7jJbJx/kmUTzYonRAa2ayesR2qWLswninkVyeJe4x3IEXhdgoNodzjQRKAoEs6Fmrw==", - "dependencies": { - "own-or": "^1.0.0" - } - }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "inBundle": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "inBundle": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "inBundle": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/package-hash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", - "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", - "dependencies": { - "graceful-fs": "^4.1.15", - "hasha": "^5.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "inBundle": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "inBundle": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, - "node_modules/picomatch": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", - "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pirates": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-3.0.2.tgz", - "integrity": "sha512-c5CgUJq6H2k6MJz72Ak1F5sN9n9wlSlJyEnwvpm9/y3WB4E3pHBDT2c6PEiS1vyJvq2bUxUAIu0EGf8Cx4Ic7Q==", - "dev": true, - "dependencies": { - "node-modules-regexp": "^1.0.0" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "inBundle": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/process-on-spawn": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", - "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", - "dependencies": { - "fromentries": "^1.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", - "inBundle": true, - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.8.1" - } - }, - "node_modules/psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "inBundle": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/react": { - "version": "16.14.0", - "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", - "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", - "inBundle": true, - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "inBundle": true - }, - "node_modules/react-reconciler": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.24.0.tgz", - "integrity": "sha512-gAGnwWkf+NOTig9oOowqid9O0HjTDC+XVGBCAmJYYJ2A2cN/O4gDdIuuUQjv8A4v6GDwVfJkagpBBLW5OW9HSw==", - "inBundle": true, - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "scheduler": "^0.18.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "peerDependencies": { - "react": "^16.0.0" - } - }, - "node_modules/readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/redeyed": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz", - "integrity": "sha1-iYS1gV2ZyyIEacme7v/jiRPmzAs=", - "inBundle": true, - "dependencies": { - "esprima": "~4.0.0" - } - }, - "node_modules/release-zalgo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", - "dependencies": { - "es6-error": "^4.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" - }, - "node_modules/resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", - "inBundle": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "inBundle": true, - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "inBundle": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "inBundle": true - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/scheduler": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.18.0.tgz", - "integrity": "sha512-agTSHR1Nbfi6ulI0kYNK0203joW2Y5W4po4l+v03tOoiJKpTBbxpNhWDvqc/4IcOw+KLmSiQLTasZ4cab2/UWQ==", - "inBundle": true, - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" - } - }, - "node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "inBundle": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "inBundle": true - }, - "node_modules/slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", - "inBundle": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "inBundle": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "inBundle": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/slice-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "inBundle": true - }, - "node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "inBundle": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/spawn-wrap": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", - "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", - "dependencies": { - "foreground-child": "^2.0.0", - "is-windows": "^1.0.2", - "make-dir": "^3.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "which": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" - }, - "node_modules/sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-length": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-3.1.0.tgz", - "integrity": "sha512-Ttp5YvkGm5v9Ijagtaz1BnN+k9ObpvS0eIBblPMp2YWL8FBmi9qblQ9fexc2k/CXFgrTIteU3jAw3payCnwSTA==", - "inBundle": true, - "dependencies": { - "astral-regex": "^1.0.0", - "strip-ansi": "^5.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-length/node_modules/ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "inBundle": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/string-length/node_modules/astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", - "inBundle": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/string-length/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "inBundle": true, - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "inBundle": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width/node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "inBundle": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "inBundle": true, - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "inBundle": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/tap-mocha-reporter": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/tap-mocha-reporter/-/tap-mocha-reporter-5.0.1.tgz", - "integrity": "sha512-1knFWOwd4khx/7uSEnUeaP9IPW3w+sqTgJMhrwah6t46nZ8P25atOKAjSvVDsT67lOPu0nfdOqUwoyKn+3E5pA==", - "dependencies": { - "color-support": "^1.1.0", - "debug": "^4.1.1", - "diff": "^4.0.1", - "escape-string-regexp": "^2.0.0", - "glob": "^7.0.5", - "tap-parser": "^10.0.0", - "tap-yaml": "^1.0.0", - "unicode-length": "^2.0.2" - }, - "bin": { - "tap-mocha-reporter": "index.js" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/tap-mocha-reporter/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/tap-parser": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-10.1.0.tgz", - "integrity": "sha512-FujQeciDaOiOvaIVGS1Rpb0v4R6XkOjvWCWowlz5oKuhPkEJ8U6pxgqt38xuzYhPt8dWEnfHn2jqpZdJEkW7pA==", - "inBundle": true, - "dependencies": { - "events-to-array": "^1.0.1", - "minipass": "^3.0.0", - "tap-yaml": "^1.0.0" - }, - "bin": { - "tap-parser": "bin/cmd.js" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/tap-yaml": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/tap-yaml/-/tap-yaml-1.0.0.tgz", - "integrity": "sha512-Rxbx4EnrWkYk0/ztcm5u3/VznbyFJpyXO12dDBHKWiDVxy7O2Qw6MRrwO5H6Ww0U5YhRY/4C/VzWmFPhBQc4qQ==", - "inBundle": true, - "dependencies": { - "yaml": "^1.5.0" - } - }, - "node_modules/tcompare": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/tcompare/-/tcompare-5.0.6.tgz", - "integrity": "sha512-OvO7omN/wkdsKzmOqr3sQFfLbghs/2X5mwSkcfgRiXZshfPnTsAs3IRf1RixR/Pff26qG/r9ogcZMpV0YdeGXg==", - "dependencies": { - "diff": "^4.0.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "inBundle": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/treport": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/treport/-/treport-2.0.2.tgz", - "integrity": "sha512-AnHKgHMy3II7Arfvf1tSHAwv9rzcvgbWrOixFJgdExVKd0mMsOp9wD2LGP9RbXy9j8AZoerBVu3OR2Uz9MpUJw==", - "inBundle": true, - "dependencies": { - "cardinal": "^2.1.1", - "chalk": "^3.0.0", - "import-jsx": "^4.0.0", - "ink": "^2.6.0", - "ms": "^2.1.2", - "string-length": "^3.1.0", - "tap-parser": "^10.0.1", - "unicode-length": "^2.0.2" - }, - "peerDependencies": { - "react": "^16.8.6" - } - }, - "node_modules/treport/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "inBundle": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/treport/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "inBundle": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/treport/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "inBundle": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/treport/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "inBundle": true - }, - "node_modules/treport/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "inBundle": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/treport/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "inBundle": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/trivial-deferred": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trivial-deferred/-/trivial-deferred-1.0.1.tgz", - "integrity": "sha1-N21NKdlR1jaKb3oK6FwvTV4GWPM=" - }, - "node_modules/ts-node": { - "version": "8.10.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz", - "integrity": "sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==", - "dev": true, - "dependencies": { - "arg": "^4.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "source-map-support": "^0.5.17", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "engines": { - "node": ">=6.0.0" - }, - "peerDependencies": { - "typescript": ">=2.7" - } - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "inBundle": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "node_modules/typescript": { - "version": "3.9.9", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.9.tgz", - "integrity": "sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/unicode-length": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unicode-length/-/unicode-length-2.0.2.tgz", - "integrity": "sha512-Ph/j1VbS3/r77nhoY2WU0GWGjVYOHL3xpKp0y/Eq2e5r0mT/6b649vm7KFO6RdAdrZkYLdxphYVgvODxPB+Ebg==", - "inBundle": true, - "dependencies": { - "punycode": "^2.0.0", - "strip-ansi": "^3.0.1" - } - }, - "node_modules/unicode-length/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "inBundle": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unicode-length/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "inBundle": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "bin": { - "uuid": "bin/uuid" - } - }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/vlq": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz", - "integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==", - "dev": true - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" - }, - "node_modules/widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "inBundle": true, - "dependencies": { - "string-width": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "inBundle": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "inBundle": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "inBundle": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "inBundle": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "inBundle": true - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "inBundle": true, - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "inBundle": true - }, - "node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "inBundle": true - }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "inBundle": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/yapool": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/yapool/-/yapool-1.0.0.tgz", - "integrity": "sha1-9pPymjFbUNmp2iZGp6ZkXJaYW2o=" - }, - "node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/yoga-layout-prebuilt": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/yoga-layout-prebuilt/-/yoga-layout-prebuilt-1.10.0.tgz", - "integrity": "sha512-YnOmtSbv4MTf7RGJMK0FvZ+KD8OEe/J5BNnR0GHhD8J/XcG/Qvxgszm0Un6FTHWW4uHlTgP0IztiXQnGyIR45g==", - "inBundle": true, - "dependencies": { - "@types/yoga-layout": "1.9.2" - }, - "engines": { - "node": ">=8" - } - } - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", - "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", - "requires": { - "@babel/highlight": "^7.12.13" - } - }, - "@babel/compat-data": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.0.tgz", - "integrity": "sha512-vu9V3uMM/1o5Hl5OekMUowo3FqXLJSw+s+66nt0fSWVWTtmosdzn45JHOB3cPtZoe6CTBDzvSw0RdOY85Q37+Q==" - }, - "@babel/core": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.0.tgz", - "integrity": "sha512-8YqpRig5NmIHlMLw09zMlPTvUVMILjqCOtVgu+TVNWEBvy9b5I3RRyhqnrV4hjgEK7n8P9OqvkWJAFmEL6Wwfw==", - "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.14.0", - "@babel/helper-compilation-targets": "^7.13.16", - "@babel/helper-module-transforms": "^7.14.0", - "@babel/helpers": "^7.14.0", - "@babel/parser": "^7.14.0", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.14.0", - "@babel/types": "^7.14.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0", - "source-map": "^0.5.0" - } - }, - "@babel/generator": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.1.tgz", - "integrity": "sha512-TMGhsXMXCP/O1WtQmZjpEYDhCYC9vFhayWZPJSZCGkPJgUqX0rF0wwtrYvnzVxIjcF80tkUertXVk5cwqi5cAQ==", - "requires": { - "@babel/types": "^7.14.1", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz", - "integrity": "sha512-7YXfX5wQ5aYM/BOlbSccHDbuXXFPxeoUmfWtz8le2yTkTZc+BxsiEnENFoi2SlmA8ewDkG2LgIMIVzzn2h8kfw==", - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.13.16", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.16.tgz", - "integrity": "sha512-3gmkYIrpqsLlieFwjkGgLaSHmhnvlAYzZLlYVjlW+QwI+1zE17kGxuJGmIqDQdYp56XdmGeD+Bswx0UTyG18xA==", - "requires": { - "@babel/compat-data": "^7.13.15", - "@babel/helper-validator-option": "^7.12.17", - "browserslist": "^4.14.5", - "semver": "^6.3.0" - } - }, - "@babel/helper-function-name": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", - "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", - "requires": { - "@babel/helper-get-function-arity": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", - "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz", - "integrity": "sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw==", - "requires": { - "@babel/types": "^7.13.12" - } - }, - "@babel/helper-module-imports": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz", - "integrity": "sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA==", - "requires": { - "@babel/types": "^7.13.12" - } - }, - "@babel/helper-module-transforms": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.0.tgz", - "integrity": "sha512-L40t9bxIuGOfpIGA3HNkJhU9qYrf4y5A5LUSw7rGMSn+pcG8dfJ0g6Zval6YJGd2nEjI7oP00fRdnhLKndx6bw==", - "requires": { - "@babel/helper-module-imports": "^7.13.12", - "@babel/helper-replace-supers": "^7.13.12", - "@babel/helper-simple-access": "^7.13.12", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/helper-validator-identifier": "^7.14.0", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.14.0", - "@babel/types": "^7.14.0" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz", - "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==", - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz", - "integrity": "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==" - }, - "@babel/helper-replace-supers": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.13.12.tgz", - "integrity": "sha512-Gz1eiX+4yDO8mT+heB94aLVNCL+rbuT2xy4YfyNqu8F+OI6vMvJK891qGBTqL9Uc8wxEvRW92Id6G7sDen3fFw==", - "requires": { - "@babel/helper-member-expression-to-functions": "^7.13.12", - "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/traverse": "^7.13.0", - "@babel/types": "^7.13.12" - } - }, - "@babel/helper-simple-access": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz", - "integrity": "sha512-7FEjbrx5SL9cWvXioDbnlYTppcZGuCY6ow3/D5vMggb2Ywgu4dMrpTJX0JdQAIcRRUElOIxF3yEooa9gUb9ZbA==", - "requires": { - "@babel/types": "^7.13.12" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", - "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz", - "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==" - }, - "@babel/helper-validator-option": { - "version": "7.12.17", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz", - "integrity": "sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw==" - }, - "@babel/helpers": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.0.tgz", - "integrity": "sha512-+ufuXprtQ1D1iZTO/K9+EBRn+qPWMJjZSw/S0KlFrxCw4tkrzv9grgpDHkY9MeQTjTY8i2sp7Jep8DfU6tN9Mg==", - "requires": { - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.14.0", - "@babel/types": "^7.14.0" - } - }, - "@babel/highlight": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz", - "integrity": "sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==", - "requires": { - "@babel/helper-validator-identifier": "^7.14.0", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.1.tgz", - "integrity": "sha512-muUGEKu8E/ftMTPlNp+mc6zL3E9zKWmF5sDHZ5MSsoTP9Wyz64AhEf9kD08xYJ7w6Hdcu8H550ircnPyWSIF0Q==" - }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.13.8.tgz", - "integrity": "sha512-DhB2EuB1Ih7S3/IRX5AFVgZ16k3EzfRbq97CxAVI1KSYcW+lexV8VZb7G7L8zuPVSdQMRn0kiBpf/Yzu9ZKH0g==", - "requires": { - "@babel/compat-data": "^7.13.8", - "@babel/helper-compilation-targets": "^7.13.8", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.13.0" - } - }, - "@babel/plugin-syntax-jsx": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.13.tgz", - "integrity": "sha512-d4HM23Q1K7oq/SLNmG6mRt85l2csmQ0cHRaxRXjKW0YFdEXqlZ5kzFQKH5Uc3rDJECgu+yCRgPkG04Mm98R/1g==", - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.13.17", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.13.17.tgz", - "integrity": "sha512-UAUqiLv+uRLO+xuBKKMEpC+t7YRNVRqBsWWq1yKXbBZBje/t3IXCiSinZhjn/DC3qzBfICeYd2EFGEbHsh5RLA==", - "requires": { - "@babel/helper-plugin-utils": "^7.13.0" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.13.0.tgz", - "integrity": "sha512-Jt8k/h/mIwE2JFEOb3lURoY5C85ETcYPnbuAJ96zRBzh1XHtQZfs62ChZ6EP22QlC8c7Xqr9q+e1SU5qttwwjw==", - "requires": { - "@babel/helper-plugin-utils": "^7.13.0" - } - }, - "@babel/plugin-transform-react-jsx": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.13.12.tgz", - "integrity": "sha512-jcEI2UqIcpCqB5U5DRxIl0tQEProI2gcu+g8VTIqxLO5Iidojb4d77q+fwGseCvd8af/lJ9masp4QWzBXFE2xA==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-module-imports": "^7.13.12", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-jsx": "^7.12.13", - "@babel/types": "^7.13.12" - } - }, - "@babel/template": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", - "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", - "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/parser": "^7.12.13", - "@babel/types": "^7.12.13" - } - }, - "@babel/traverse": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.0.tgz", - "integrity": "sha512-dZ/a371EE5XNhTHomvtuLTUyx6UEoJmYX+DT5zBCQN3McHemsuIaKKYqsc/fs26BEkHs/lBZy0J571LP5z9kQA==", - "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.14.0", - "@babel/helper-function-name": "^7.12.13", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/parser": "^7.14.0", - "@babel/types": "^7.14.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - } - }, - "@babel/types": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.1.tgz", - "integrity": "sha512-S13Qe85fzLs3gYRUnrpyeIrBJIMYv33qSTg1qoBwiG6nPKwUWAD9odSzWhEedpwOIzSEI6gbdQIWEMiCI42iBA==", - "requires": { - "@babel/helper-validator-identifier": "^7.14.0", - "to-fast-properties": "^2.0.0" - } - }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" - } - } - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==" - }, - "@types/prop-types": { - "version": "15.7.3", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", - "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" - }, - "@types/react": { - "version": "16.14.6", - "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.6.tgz", - "integrity": "sha512-Ol/aFKune+P0FSFKIgf+XbhGzYGyz0p7g5befSt4rmbzfGLaZR0q7jPew9k7d3bvrcuaL8dPy9Oz3XGZmf9n+w==", - "requires": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "@types/scheduler": { - "version": "0.16.1", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.1.tgz", - "integrity": "sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA==" - }, - "@types/yoga-layout": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@types/yoga-layout/-/yoga-layout-1.9.2.tgz", - "integrity": "sha512-S9q47ByT2pPvD65IvrWp7qppVMpk9WGMbVq9wbWZOHg6tnXSD4vyhao6nOSBwwfDdV2p3Kx9evA9vI+XWTfDvw==" - }, - "aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - } - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "requires": { - "type-fest": "^0.21.3" - } - }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "ansicolors": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", - "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=" - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "append-transform": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", - "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", - "requires": { - "default-require-extensions": "^3.0.0" - } - }, - "archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=" - }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "arrify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", - "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==" - }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==" - }, - "async-hook-domain": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/async-hook-domain/-/async-hook-domain-2.0.3.tgz", - "integrity": "sha512-MadiLLDEZRZzZwcm0dgS+K99qXZ4H2saAUwUgwzFulbAkXrKi3AX5FvWS3FFTQtLMwrqcGqAJe6o12KrObejQA==" - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "auto-bind": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-4.0.0.tgz", - "integrity": "sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ==" - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" - }, - "aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" - }, - "bind-obj-methods": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bind-obj-methods/-/bind-obj-methods-3.0.0.tgz", - "integrity": "sha512-nLEaaz3/sEzNSyPWRsN9HNsqwk1AUyECtGj+XwGdIi3xABnEqecvXtIJ0wehQXuuER5uZ/5fTs2usONgYjG+iw==" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "requires": { - "fill-range": "^7.0.1" - } - }, - "browserslist": { - "version": "4.16.6", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", - "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", - "requires": { - "caniuse-lite": "^1.0.30001219", - "colorette": "^1.2.2", - "electron-to-chromium": "^1.3.723", - "escalade": "^3.1.1", - "node-releases": "^1.1.71" - } - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" - }, - "caching-transform": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", - "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", - "requires": { - "hasha": "^5.0.0", - "make-dir": "^3.0.0", - "package-hash": "^4.0.0", - "write-file-atomic": "^3.0.0" - } - }, - "caller-callsite": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", - "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", - "requires": { - "callsites": "^2.0.0" - } - }, - "caller-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", - "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", - "requires": { - "caller-callsite": "^2.0.0" - } - }, - "callsites": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=" - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" - }, - "caniuse-lite": { - "version": "1.0.30001223", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001223.tgz", - "integrity": "sha512-k/RYs6zc/fjbxTjaWZemeSmOjO0JJV+KguOBA3NwPup8uzxM1cMhR2BD9XmO86GuqaqTCO8CgkgH9Rz//vdDiA==" - }, - "cardinal": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", - "integrity": "sha1-fMEFXYItISlU0HsIXeolHMe8VQU=", - "requires": { - "ansicolors": "~0.3.2", - "redeyed": "~2.1.0" - } - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.3.1", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" - } - }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" - }, - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" - }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", - "requires": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - } - }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - } - } - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" - }, - "colorette": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", - "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==" - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "coveralls": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.1.0.tgz", - "integrity": "sha512-sHxOu2ELzW8/NC1UP5XVLbZDzO4S3VxfFye3XYCznopHy02YjNkHcj5bKaVw2O7hVaBdBjEdQGpie4II1mWhuQ==", - "requires": { - "js-yaml": "^3.13.1", - "lcov-parse": "^1.0.0", - "log-driver": "^1.2.7", - "minimist": "^1.2.5", - "request": "^2.88.2" - } - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "csstype": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.8.tgz", - "integrity": "sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==" - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "requires": { - "ms": "2.1.2" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" - }, - "default-require-extensions": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", - "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", - "requires": { - "strip-bom": "^4.0.0" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "electron-to-chromium": { - "version": "1.3.727", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.727.tgz", - "integrity": "sha512-Mfz4FIB4FSvEwBpDfdipRIrwd6uo8gUDoRDF4QEYb4h4tSuI3ov594OrjU6on042UlFHouIJpClDODGkPcBSbg==" - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==" - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" - }, - "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=" - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" - }, - "events-to-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/events-to-array/-/events-to-array-1.1.2.tgz", - "integrity": "sha1-LUH1Y+H+QA7Uli/hpNXGp1Od9/Y=" - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-cache-dir": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", - "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", - "requires": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "findit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/findit/-/findit-2.0.0.tgz", - "integrity": "sha1-ZQnwEmr0wXhVHPqZOU4DLhOk1W4=" - }, - "flow-parser": { - "version": "0.150.1", - "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.150.1.tgz", - "integrity": "sha512-BGzmgjz6Ma6LN6rfrnVGDo/2wsAGHbcCASDVpWu8/GAriikDQCiiIND/6HBVBxhy1Uga1Sgmfl8iiIln+t3kCw==", - "dev": true - }, - "flow-remove-types": { - "version": "2.150.1", - "resolved": "https://registry.npmjs.org/flow-remove-types/-/flow-remove-types-2.150.1.tgz", - "integrity": "sha512-bZ3A1068GuWeBWd+J7ZnSNLoJYFEPbajzMEmov4KkJA0rm/3Pz8t2TGEH6cz+jQmDuO08F75jvAkjXs1KnHqqw==", - "dev": true, - "requires": { - "flow-parser": "^0.150.1", - "pirates": "^3.0.2", - "vlq": "^0.2.1" - } - }, - "foreground-child": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", - "requires": { - "cross-spawn": "^7.0.0", - "signal-exit": "^3.0.2" - } - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "fromentries": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", - "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==" - }, - "fs-exists-cached": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-exists-cached/-/fs-exists-cached-1.0.0.tgz", - "integrity": "sha1-zyVVTKBQ3EmuZla0HeQiWJidy84=" - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "optional": true - }, - "function-loop": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/function-loop/-/function-loop-2.0.1.tgz", - "integrity": "sha512-ktIR+O6i/4h+j/ZhZJNdzeI4i9lEPeEK6UPR2EVyTVBqOwcU3Za9xYKLH64ZR9HmcROyRrOkizNyjjtWJzDDkQ==" - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" - }, - "get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==" - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "requires": { - "is-glob": "^4.0.1" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" - }, - "graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" - }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "hasha": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", - "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", - "requires": { - "is-stream": "^2.0.0", - "type-fest": "^0.8.0" - }, - "dependencies": { - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" - } - } - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==" - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "import-jsx": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-jsx/-/import-jsx-4.0.0.tgz", - "integrity": "sha512-CnjJ2BZFJzbFDmYG5S47xPQjMlSbZLyLJuG4znzL4TdPtJBxHtFP1xVmR+EYX4synFSldiY3B6m00XkPM3zVnA==", - "requires": { - "@babel/core": "^7.5.5", - "@babel/plugin-proposal-object-rest-spread": "^7.5.5", - "@babel/plugin-transform-destructuring": "^7.5.0", - "@babel/plugin-transform-react-jsx": "^7.3.0", - "caller-path": "^2.0.0", - "find-cache-dir": "^3.2.0", - "make-dir": "^3.0.2", - "resolve-from": "^3.0.0", - "rimraf": "^3.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "ink": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/ink/-/ink-2.7.1.tgz", - "integrity": "sha512-s7lJuQDJEdjqtaIWhp3KYHl6WV3J04U9zoQ6wVc+Xoa06XM27SXUY57qC5DO46xkF0CfgXMKkKNcgvSu/SAEpA==", - "requires": { - "ansi-escapes": "^4.2.1", - "arrify": "^2.0.1", - "auto-bind": "^4.0.0", - "chalk": "^3.0.0", - "cli-cursor": "^3.1.0", - "cli-truncate": "^2.1.0", - "is-ci": "^2.0.0", - "lodash.throttle": "^4.1.1", - "log-update": "^3.0.0", - "prop-types": "^15.6.2", - "react-reconciler": "^0.24.0", - "scheduler": "^0.18.0", - "signal-exit": "^3.0.2", - "slice-ansi": "^3.0.0", - "string-length": "^3.1.0", - "widest-line": "^3.1.0", - "wrap-ansi": "^6.2.0", - "yoga-layout-prebuilt": "^1.9.3" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "requires": { - "ci-info": "^2.0.0" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, - "istanbul-lib-coverage": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", - "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==" - }, - "istanbul-lib-hook": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", - "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", - "requires": { - "append-transform": "^2.0.0" - } - }, - "istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "requires": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - } - }, - "istanbul-lib-processinfo": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", - "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", - "requires": { - "archy": "^1.0.0", - "cross-spawn": "^7.0.0", - "istanbul-lib-coverage": "^3.0.0-alpha.1", - "make-dir": "^3.0.0", - "p-map": "^3.0.0", - "rimraf": "^3.0.0", - "uuid": "^3.3.3" - } - }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", - "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } - }, - "istanbul-reports": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", - "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "jackspeak": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-1.4.0.tgz", - "integrity": "sha512-VDcSunT+wcccoG46FtzuBAyQKlzhHjli4q31e1fIHGOsRspqNUFjVzGb+7eIFDlTvqLygxapDHPHS0ouT2o/tw==", - "requires": { - "cliui": "^4.1.0" - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, - "json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "requires": { - "minimist": "^1.2.5" - } - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "lcov-parse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-1.0.0.tgz", - "integrity": "sha1-6w1GtUER68VhrLTECO+TY73I9+A=" - }, - "libtap": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/libtap/-/libtap-1.1.1.tgz", - "integrity": "sha512-Fye8fh1+G7E8qqmjQaY+pXGxy7HM0S6bqCCJFLa16+g2jODBByxbJFDpjbDNF69wfRVyvJ+foLZc1WTIv7dx+g==", - "requires": { - "async-hook-domain": "^2.0.1", - "bind-obj-methods": "^3.0.0", - "diff": "^4.0.2", - "function-loop": "^2.0.1", - "minipass": "^3.1.1", - "own-or": "^1.0.0", - "own-or-env": "^1.0.1", - "signal-exit": "^3.0.2", - "stack-utils": "^2.0.1", - "tap-parser": "^10.0.1", - "tap-yaml": "^1.0.0", - "tcompare": "^5.0.1", - "trivial-deferred": "^1.0.1", - "yapool": "^1.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "requires": { - "p-locate": "^4.1.0" - } - }, - "lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=" - }, - "lodash.throttle": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", - "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=" - }, - "log-driver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", - "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==" - }, - "log-update": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-3.4.0.tgz", - "integrity": "sha512-ILKe88NeMt4gmDvk/eb615U/IVn7K9KWGkoYbdatQ69Z65nj1ZzjM6fHXfcs0Uge+e+EGnMW7DY4T9yko8vWFg==", - "requires": { - "ansi-escapes": "^3.2.0", - "cli-cursor": "^2.1.0", - "wrap-ansi": "^5.0.0" - }, - "dependencies": { - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==" - }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - } - } - } - }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "requires": { - "semver": "^6.0.0" - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "mime-db": { - "version": "1.47.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz", - "integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==" - }, - "mime-types": { - "version": "2.1.30", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz", - "integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==", - "requires": { - "mime-db": "1.47.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "minipass": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", - "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", - "requires": { - "yallist": "^4.0.0" - } - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node-modules-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", - "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", - "dev": true - }, - "node-preload": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", - "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", - "requires": { - "process-on-spawn": "^1.0.0" - } - }, - "node-releases": { - "version": "1.1.71", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.71.tgz", - "integrity": "sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg==" - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "nyc": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", - "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", - "requires": { - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "caching-transform": "^4.0.0", - "convert-source-map": "^1.7.0", - "decamelize": "^1.2.0", - "find-cache-dir": "^3.2.0", - "find-up": "^4.1.0", - "foreground-child": "^2.0.0", - "get-package-type": "^0.1.0", - "glob": "^7.1.6", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-hook": "^3.0.0", - "istanbul-lib-instrument": "^4.0.0", - "istanbul-lib-processinfo": "^2.0.2", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.2", - "make-dir": "^3.0.0", - "node-preload": "^0.2.1", - "p-map": "^3.0.0", - "process-on-spawn": "^1.0.0", - "resolve-from": "^5.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "spawn-wrap": "^2.0.0", - "test-exclude": "^6.0.0", - "yargs": "^15.0.2" - }, - "dependencies": { - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" - } - } - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "opener": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", - "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==" - }, - "own-or": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/own-or/-/own-or-1.0.0.tgz", - "integrity": "sha1-Tod/vtqaLsgAD7wLyuOWRe6L+Nw=" - }, - "own-or-env": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/own-or-env/-/own-or-env-1.0.1.tgz", - "integrity": "sha512-y8qULRbRAlL6x2+M0vIe7jJbJx/kmUTzYonRAa2ayesR2qWLswninkVyeJe4x3IEXhdgoNodzjQRKAoEs6Fmrw==", - "requires": { - "own-or": "^1.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "requires": { - "aggregate-error": "^3.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" - }, - "package-hash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", - "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", - "requires": { - "graceful-fs": "^4.1.15", - "hasha": "^5.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, - "picomatch": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", - "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==" - }, - "pirates": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-3.0.2.tgz", - "integrity": "sha512-c5CgUJq6H2k6MJz72Ak1F5sN9n9wlSlJyEnwvpm9/y3WB4E3pHBDT2c6PEiS1vyJvq2bUxUAIu0EGf8Cx4Ic7Q==", - "dev": true, - "requires": { - "node-modules-regexp": "^1.0.0" - } - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "requires": { - "find-up": "^4.0.0" - } - }, - "process-on-spawn": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", - "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", - "requires": { - "fromentries": "^1.2.0" - } - }, - "prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.8.1" - } - }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" - }, - "react": { - "version": "16.14.0", - "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", - "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2" - } - }, - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, - "react-reconciler": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.24.0.tgz", - "integrity": "sha512-gAGnwWkf+NOTig9oOowqid9O0HjTDC+XVGBCAmJYYJ2A2cN/O4gDdIuuUQjv8A4v6GDwVfJkagpBBLW5OW9HSw==", - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "scheduler": "^0.18.0" - } - }, - "readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", - "requires": { - "picomatch": "^2.2.1" - } - }, - "redeyed": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz", - "integrity": "sha1-iYS1gV2ZyyIEacme7v/jiRPmzAs=", - "requires": { - "esprima": "~4.0.0" - } - }, - "release-zalgo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", - "requires": { - "es6-error": "^4.0.1" - } - }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" - }, - "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=" - }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "scheduler": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.18.0.tgz", - "integrity": "sha512-agTSHR1Nbfi6ulI0kYNK0203joW2Y5W4po4l+v03tOoiJKpTBbxpNhWDvqc/4IcOw+KLmSiQLTasZ4cab2/UWQ==", - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" - }, - "slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - } - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" - }, - "source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } - }, - "spawn-wrap": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", - "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", - "requires": { - "foreground-child": "^2.0.0", - "is-windows": "^1.0.2", - "make-dir": "^3.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "which": "^2.0.1" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" - }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", - "requires": { - "escape-string-regexp": "^2.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==" - } - } - }, - "string-length": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-3.1.0.tgz", - "integrity": "sha512-Ttp5YvkGm5v9Ijagtaz1BnN+k9ObpvS0eIBblPMp2YWL8FBmi9qblQ9fexc2k/CXFgrTIteU3jAw3payCnwSTA==", - "requires": { - "astral-regex": "^1.0.0", - "strip-ansi": "^5.2.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - }, - "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==" - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==" - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - }, - "tap-mocha-reporter": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/tap-mocha-reporter/-/tap-mocha-reporter-5.0.1.tgz", - "integrity": "sha512-1knFWOwd4khx/7uSEnUeaP9IPW3w+sqTgJMhrwah6t46nZ8P25atOKAjSvVDsT67lOPu0nfdOqUwoyKn+3E5pA==", - "requires": { - "color-support": "^1.1.0", - "debug": "^4.1.1", - "diff": "^4.0.1", - "escape-string-regexp": "^2.0.0", - "glob": "^7.0.5", - "tap-parser": "^10.0.0", - "tap-yaml": "^1.0.0", - "unicode-length": "^2.0.2" - }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==" - } - } - }, - "tap-parser": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-10.1.0.tgz", - "integrity": "sha512-FujQeciDaOiOvaIVGS1Rpb0v4R6XkOjvWCWowlz5oKuhPkEJ8U6pxgqt38xuzYhPt8dWEnfHn2jqpZdJEkW7pA==", - "requires": { - "events-to-array": "^1.0.1", - "minipass": "^3.0.0", - "tap-yaml": "^1.0.0" - } - }, - "tap-yaml": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/tap-yaml/-/tap-yaml-1.0.0.tgz", - "integrity": "sha512-Rxbx4EnrWkYk0/ztcm5u3/VznbyFJpyXO12dDBHKWiDVxy7O2Qw6MRrwO5H6Ww0U5YhRY/4C/VzWmFPhBQc4qQ==", - "requires": { - "yaml": "^1.5.0" - } - }, - "tcompare": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/tcompare/-/tcompare-5.0.6.tgz", - "integrity": "sha512-OvO7omN/wkdsKzmOqr3sQFfLbghs/2X5mwSkcfgRiXZshfPnTsAs3IRf1RixR/Pff26qG/r9ogcZMpV0YdeGXg==", - "requires": { - "diff": "^4.0.2" - } - }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - } - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "requires": { - "is-number": "^7.0.0" - } - }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "treport": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/treport/-/treport-2.0.2.tgz", - "integrity": "sha512-AnHKgHMy3II7Arfvf1tSHAwv9rzcvgbWrOixFJgdExVKd0mMsOp9wD2LGP9RbXy9j8AZoerBVu3OR2Uz9MpUJw==", - "requires": { - "cardinal": "^2.1.1", - "chalk": "^3.0.0", - "import-jsx": "^4.0.0", - "ink": "^2.6.0", - "ms": "^2.1.2", - "string-length": "^3.1.0", - "tap-parser": "^10.0.1", - "unicode-length": "^2.0.2" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "trivial-deferred": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trivial-deferred/-/trivial-deferred-1.0.1.tgz", - "integrity": "sha1-N21NKdlR1jaKb3oK6FwvTV4GWPM=" - }, - "ts-node": { - "version": "8.10.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz", - "integrity": "sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==", - "dev": true, - "requires": { - "arg": "^4.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "source-map-support": "^0.5.17", - "yn": "3.1.1" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" - }, - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "typescript": { - "version": "3.9.9", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.9.tgz", - "integrity": "sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w==", - "dev": true - }, - "unicode-length": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unicode-length/-/unicode-length-2.0.2.tgz", - "integrity": "sha512-Ph/j1VbS3/r77nhoY2WU0GWGjVYOHL3xpKp0y/Eq2e5r0mT/6b649vm7KFO6RdAdrZkYLdxphYVgvODxPB+Ebg==", - "requires": { - "punycode": "^2.0.0", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "requires": { - "punycode": "^2.1.0" - } - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "vlq": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz", - "integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" - }, - "widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "requires": { - "string-width": "^4.0.0" - } - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" - }, - "yapool": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/yapool/-/yapool-1.0.0.tgz", - "integrity": "sha1-9pPymjFbUNmp2iZGp6ZkXJaYW2o=" - }, - "yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "requires": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" - }, - "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, - "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true - }, - "yoga-layout-prebuilt": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/yoga-layout-prebuilt/-/yoga-layout-prebuilt-1.10.0.tgz", - "integrity": "sha512-YnOmtSbv4MTf7RGJMK0FvZ+KD8OEe/J5BNnR0GHhD8J/XcG/Qvxgszm0Un6FTHWW4uHlTgP0IztiXQnGyIR45g==", - "requires": { - "@types/yoga-layout": "1.9.2" - } - } - } -} diff --git a/typescript-vendor/tap/package.json b/typescript-vendor/tap/package.json deleted file mode 100644 index 85825dbd0..000000000 --- a/typescript-vendor/tap/package.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "name": "tap", - "version": "15.0.9", - "author": "Isaac Z. Schlueter (http://blog.izs.me)", - "description": "A Test-Anything-Protocol library for JavaScript", - "homepage": "http://www.node-tap.org/", - "bin": { - "tap": "bin/run.js" - }, - "main": "lib/tap.js", - "exports": { - ".": { - "import": "./lib/tap.mjs", - "default": "./lib/tap.js" - }, - "./*": "./*", - "./": "./" - }, - "engines": { - "node": ">=10" - }, - "dependencies": { - "chokidar": "catalog:", - "coveralls": "catalog:", - "findit": "catalog:", - "foreground-child": "catalog:", - "fs-exists-cached": "catalog:", - "glob": "catalog:", - "import-jsx": "catalog:", - "ink": "catalog:", - "isexe": "catalog:", - "istanbul-lib-processinfo": "catalog:", - "jackspeak": "catalog:", - "libtap": "catalog:", - "minipass": "catalog:", - "mkdirp": "catalog:", - "nyc": "catalog:", - "opener": "catalog:", - "rimraf": "catalog:", - "signal-exit": "catalog:", - "source-map-support": "catalog:", - "tap-mocha-reporter": "catalog:", - "tap-parser": "catalog:", - "tap-yaml": "catalog:", - "tcompare": "catalog:", - "treport": "catalog:", - "which": "catalog:" - }, - "devDependencies": { - "flow-remove-types": "catalog:", - "node-preload": "catalog:", - "process-on-spawn": "catalog:", - "ts-node": "catalog:", - "typescript": "catalog:" - }, - "peerDependencies": { - "flow-remove-types": ">=2.112.0", - "ts-node": ">=8.5.2", - "typescript": ">=3.7.2" - }, - "peerDependenciesMeta": { - "flow-remove-types": { - "optional": true - }, - "ts-node": { - "optional": true - }, - "typescript": { - "optional": true - } - }, - "keywords": [ - "assert", - "tap", - "test", - "testing" - ], - "license": "ISC", - "repository": "https://github.com/tapjs/node-tap.git", - "scripts": { - "snap": "node bin/run.js -M coverage-map.js", - "test": "node bin/run.js -M coverage-map.js", - "unit": "bash scripts/unit.sh", - "posttest": "rm -rf cli-tests-*", - "postunit": "npm run posttest", - "t": "node bin/run.js -J -sfails.txt", - "preversion": "npm test", - "postversion": "npm publish", - "postpublish": "bash postpublish.sh", - "www:build": "cd docs; npm ci; npm run build", - "www:develop": "cd docs; npm run develop", - "start": "npm run www:develop", - "www:serve": "cd docs; npm run serve" - }, - "tap": { - "test-regex": "^test/.*\\.js$", - "check-coverage": true - }, - "nyc": { - "include": [ - "bin/run.js", - "bin/jsx.js", - "lib/*.js", - "bin/jack.js" - ] - }, - "files": [ - "settings.js", - "bin/run.js", - "bin/jsx.js", - "bin/jack.js", - "lib" - ], - "bundleDependencies": [ - "ink", - "treport", - "@types/react" - ], - "funding": { - "url": "https://github.com/sponsors/isaacs" - } -} \ No newline at end of file diff --git a/typescript-vendor/tap/postpublish.sh b/typescript-vendor/tap/postpublish.sh deleted file mode 100644 index 49189cd88..000000000 --- a/typescript-vendor/tap/postpublish.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash -set -e -set -x -node docs/src/content/docs/cli/index.template.js -git add docs/src/content/docs/cli -git commit -m 'update cli doc' -git push origin --follow-tags diff --git a/typescript-vendor/tap/scripts/snap.sh b/typescript-vendor/tap/scripts/snap.sh deleted file mode 100755 index c82a545a8..000000000 --- a/typescript-vendor/tap/scripts/snap.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash - -export TAP_SNAPSHOT=1 -export TAP_BAIL=1 - -shopt -s globstar - -echo 'TAP version 13' -failed=0 - -pids=() -snap () { - f=$1 - id=$2 - out=$(node $f 2>&1) - res=$? - let '++id' - if [ $res != 0 ]; then - failed=1 - echo "not ok $id - $f {" - echo "$out" | sed "s|^| |g" - echo "}" - exit 1 - else - echo "ok $id - $f" - fi -} - -files=(test/**/*.js) - -echo "1..${#files[@]}" - -for (( id=0 ; id < ${#files[@]} ; id++ )) do - t=${files[$id]} - snap $t $id & - pids+=($!) - if [ $failed -eq 1 ]; then - break - fi -done - -for p in "${pids[@]}"; do - wait $p - if [ $? != 0 ]; then - for q in "${pids[@]}"; do - kill -SIGINT $q &>/dev/null - done - echo "Bail out! # failed test" - break - fi -done diff --git a/typescript-vendor/tap/scripts/unit.sh b/typescript-vendor/tap/scripts/unit.sh deleted file mode 100644 index 95edac068..000000000 --- a/typescript-vendor/tap/scripts/unit.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -if [ "${1}" = "" ]; then - units=($(cd test &>/dev/null; ls)) - for u in "${units[@]}"; do - u=$(basename "$u" .js) - if ! [ $u == "clean-stacks" ]; then - $BASH scripts/unit.sh "$u" || exit 1 - fi - done -else - shopt -s extglob - include=$(echo +(bin|lib)/$1.js) - if [ -f "$include" ]; then - set -x - node bin/run.js test/${1}* --nyc-arg=--include="$include" -Rterse -M coverage-map.js - else - set -x - node bin/run.js test/${1}* --no-cov -Rterse - fi -fi diff --git a/typescript-vendor/tap/settings.js b/typescript-vendor/tap/settings.js deleted file mode 100644 index 5ec87855c..000000000 --- a/typescript-vendor/tap/settings.js +++ /dev/null @@ -1,62 +0,0 @@ -'use strict' - -const sourceMapSupport = require('source-map-support') -const settings = require('libtap/settings') - -sourceMapSupport.install({environment:'node', hookRequire: true}) - -if (+process.env.TAP_DEV_LONGSTACK !== 1) { - settings.stackUtils.ignoredPackages.push( - 'libtap', - 'tap', - 'nyc', - 'import-jsx', - 'function-loop' - ) - settings.stackUtils.internals.push( - /at Generator\.next \(\)/iu - ) -} else { - settings.atTap = true -} - -settings.stackUtils.wrapCallSite = sourceMapSupport.wrapCallSite - -/* istanbul ignore next - version specific */ -if (settings.rimrafNeeded) { - const rimraf = require('rimraf') - settings.rmdirRecursive = (path, cb) => rimraf(path, { glob: false }, cb) - settings.rmdirRecursiveSync = path => rimraf.sync(path, { glob: false }) -} - -/* istanbul ignore next - version specific */ -if (settings.mkdirpNeeded) { - const mkdirp = require('mkdirp') - settings.mkdirRecursive = (path, cb) => mkdirp(path).then(() => cb).catch(cb) - settings.mkdirRecursiveSync = path => mkdirp.sync(path) -} - -if (process.env.TAP_LIBTAP_SETTINGS) { - const overrides = require(process.env.TAP_LIBTAP_SETTINGS) - const type = typeof overrides - const isArray = Array.isArray(overrides) - if (!overrides || isArray || type !== 'object') { - throw new Error('invalid libtap settings: ' + ( - isArray ? 'array' - : type === 'object' ? 'null' - : type - )) - } - - for (const [key, value] of Object.entries(overrides)) { - if (!Object.prototype.hasOwnProperty.call(settings, key)) - throw new Error('Unrecognized libtap setting: ' + key) - if (typeof value !== typeof settings[key]) { - throw new Error(`Invalid type for libtap setting ${key}. Expected ${ - typeof settings[key]}, received ${typeof value}.`) - } - settings[key] = value - } -} - -module.exports = settings diff --git a/typescript-vendor/tap/tap-snapshots/test/repl.js.test.cjs b/typescript-vendor/tap/tap-snapshots/test/repl.js.test.cjs deleted file mode 100644 index 2534eed67..000000000 --- a/typescript-vendor/tap/tap-snapshots/test/repl.js.test.cjs +++ /dev/null @@ -1,187 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/repl.js TAP cls > clear screen 1`] = ` -"\\u001b[2J\\u001b[HTAP> " -` - -exports[`test/repl.js TAP completer > empty 1`] = ` -Array [ - Array [ - "r", - "u", - "n", - "p", - "c", - "exit", - "clear", - "cls", - ], - "", -] -` - -exports[`test/repl.js TAP completer > ex 1`] = ` -Array [ - Array [ - "exit", - ], - "ex", -] -` - -exports[`test/repl.js TAP completer > r te 1`] = ` -Array [ - Array [ - "r temp/", - "r test/", - ], - "r te", -] -` - -exports[`test/repl.js TAP completer > r tes 1`] = ` -Array [ - Array [ - "r test/", - ], - "r tes", -] -` - -exports[`test/repl.js TAP completer > r test/ 1`] = ` -Array [ - Array [ - "r test/follow.js", - "r test/foo/", - ], - "r test/", -] -` - -exports[`test/repl.js TAP completer > r test/blerg 1`] = ` -Array [ - Array [ - "r test/follow.js", - "r test/foo/", - ], - "r test/blerg", -] -` - -exports[`test/repl.js TAP completer > r test/fo 1`] = ` -Array [ - Array [ - "r test/follow.js", - "r test/foo/", - ], - "r test/fo", -] -` - -exports[`test/repl.js TAP completer > r test/fol 1`] = ` -Array [ - Array [ - "r test/follow.js", - ], - "r test/fol", -] -` - -exports[`test/repl.js TAP completer > r test/foo 1`] = ` -Array [ - Array [ - "r test/foo/", - ], - "r test/foo", -] -` - -exports[`test/repl.js TAP completer > u bl/erg 1`] = ` -Array [ - Array [ - "u", - ], - "u bl/erg", -] -` - -exports[`test/repl.js TAP exit > output 1`] = ` -null -` - -exports[`test/repl.js TAP kill process > killed process 1`] = ` -code: null -signal: SIGTERM -TAP> -` - -exports[`test/repl.js TAP manual run tests > ran the suite again 1`] = ` -code: 0 -signal: null - -TAP> -` - -exports[`test/repl.js TAP pause/resume > output 1`] = ` -code: null -signal: fake - -TAP> code: null -signal: fake - -TAP> paused -TAP> resumed -TAP> -` - -exports[`test/repl.js TAP run on change > ran the suite on change 1`] = ` -test in progress, please wait - -TAP> code: 0 -signal: null -TAP> -` - -exports[`test/repl.js TAP save/load history > history file 1`] = ` - -` - -exports[`test/repl.js TAP save/load history > load history 1`] = ` -Array [ - "", -] -` - -exports[`test/repl.js TAP show help > output 1`] = ` -TAP> TAP Repl Commands: - -r [] - run test suite, or the supplied filename - -u [] - update snapshots in the suite, or in the supplied filename - -n - run the suite with --changed - -p - pause/resume the file watcher - -c [] - run coverage report. Default to 'text' style. - -exit - exit the repl - -clear - delete all coverage info and re-run the test suite - -cls - clear the screen -TAP> -` diff --git a/typescript-vendor/tap/tap-snapshots/test/run/basic.js.test.cjs b/typescript-vendor/tap/tap-snapshots/test/run/basic.js.test.cjs deleted file mode 100644 index 9d461127c..000000000 --- a/typescript-vendor/tap/tap-snapshots/test/run/basic.js.test.cjs +++ /dev/null @@ -1,85 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/run/basic.js TAP --parser-version > output 1`] = ` -10.1.0 - -` - -exports[`test/run/basic.js TAP --versions > output 1`] = ` -tap: {version} -libtap: {version} -tap-parser: {version} -nyc: {version} -tap-yaml: {version} -treport: {version} -tcompare: {version} - - -` - -exports[`test/run/basic.js TAP basic test run > ok.js output 1`] = ` -TAP version 13 -ok 1 - cli-tests/ok.js # {time} { - ok 1 - this is fine - 1..1 - # {time} -} - -1..1 -# {time} - -` - -exports[`test/run/basic.js TAP ignored files > stdout 1`] = ` -TAP version 13 -ok 1 - test/ok.js # {time} { - ok 1 - this is fine - 1..1 - # {time} -} - -1..1 -# {time} - -` - -exports[`test/run/basic.js TAP ignored files > stdout 2`] = ` - -` - -exports[`test/run/basic.js TAP nonexistent file > stderr 1`] = ` - -` - -exports[`test/run/basic.js TAP nonexistent file > stdout 1`] = ` -TAP version 13 -not ok 1 - does not exist # {time} { - not ok 1 - ENOENT: no such file or directory, stat 'does not exist' - --- - at: - line: # - column: # - file: #INTERNAL# - code: ENOENT - errno: -2 - path: does not exist - stack: | - {STACK} - syscall: stat - test: does not exist - ... - - 1..1 - # failed 1 test -} - -1..1 -# failed 1 test -# {time} - -` diff --git a/typescript-vendor/tap/tap-snapshots/test/run/before-after.js.test.cjs b/typescript-vendor/tap/tap-snapshots/test/run/before-after.js.test.cjs deleted file mode 100644 index ea8897f7a..000000000 --- a/typescript-vendor/tap/tap-snapshots/test/run/before-after.js.test.cjs +++ /dev/null @@ -1,211 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/run/before-after.js TAP basic > stderr 1`] = ` - -` - -exports[`test/run/before-after.js TAP basic > stdout 1`] = ` -slow -TAP version 13 -ok 1 - cli-tests/t1.js # {time} { - ok 1 - this is fine - 1..1 - # {time} -} - -ok 2 - cli-tests/t2.js # {time} { - # Subtest: sub - ok 1 - this is fine - 1..1 - ok 1 - sub # {time} - - 1..1 - # {time} -} - -not ok 3 - cli-tests/t3.js # {time} - --- - args: - - cli-tests/t3.js - command: {NODE} - cwd: {CWD} - env: {} - exitCode: 1 - file: cli-tests/t3.js - stdio: - - 0 - - pipe - - 2 - timeout: {default} - ... -{ - # Subtest: sub - not ok 1 - not fine - --- - at: - line: # - column: # - file: cli-tests/t3.js - source: | - const t = require("{CWD}/") - t.test('sub', async t => t.fail('not fine')) - --^ - stack: | - {STACK} - ... - - 1..1 - # failed 1 test - not ok 1 - sub # {time} - - 1..1 - # failed 1 test - # {time} -} - -1..3 -# failed 1 of 3 tests -# {time} -ok - -` - -exports[`test/run/before-after.js TAP failing after > stderr 1`] = ` -{CWD}/cli-tests/fail.js:2 - throw new Error('fail') - ^ - -Error: fail - {STACK} - -# failed cli-tests/fail.js -# code=1 signal=null - - -` - -exports[`test/run/before-after.js TAP failing after > stdout 1`] = ` -TAP version 13 -ok 1 - cli-tests/t1.js # {time} { - ok 1 - this is fine - 1..1 - # {time} -} - -1..1 -# {time} - -` - -exports[`test/run/before-after.js TAP failing before > stderr 1`] = ` -{CWD}/cli-tests/fail.js:2 - throw new Error('fail') - ^ - -Error: fail - {STACK} - -# failed cli-tests/fail.js -# code=1 signal=null - - -` - -exports[`test/run/before-after.js TAP failing before > stdout 1`] = ` - -` - -exports[`test/run/before-after.js TAP run after even on bailout > stderr 1`] = ` - -` - -exports[`test/run/before-after.js TAP run after even on bailout > stdout 1`] = ` -TAP version 13 -ok 1 - cli-tests/t1.js # {time} { - ok 1 - this is fine - 1..1 - # {time} -} - -ok 2 - cli-tests/t2.js # {time} { - # Subtest: sub - ok 1 - this is fine - 1..1 - ok 1 - sub # {time} - - 1..1 - # {time} -} - -not ok 3 - cli-tests/t3.js # {time} - --- - args: - - cli-tests/t3.js - command: {NODE} - cwd: {CWD} - env: {} - exitCode: 1 - file: cli-tests/t3.js - stdio: - - 0 - - pipe - - 2 - timeout: {default} - ... -{ - # Subtest: sub - not ok 1 - not fine - --- - at: - line: # - column: # - file: cli-tests/t3.js - source: | - const t = require("{CWD}/") - t.test('sub', async t => t.fail('not fine')) - --^ - stack: | - {STACK} - ... - - Bail out! not fine -} -Bail out! not fine -ok - -` - -exports[`test/run/before-after.js TAP signal fail after > stderr 1`] = ` - -# failed cli-tests/sigfail.js -# code=null signal=SIGKILL - - -` - -exports[`test/run/before-after.js TAP signal fail after > stdout 1`] = ` - -` - -exports[`test/run/before-after.js TAP slow fail before > stderr 1`] = ` -{CWD}/cli-tests/slow-fail.js:2 - throw new Error('slow fail') - ^ - -Error: slow fail - {STACK} - -# failed cli-tests/slow-fail.js -# code=1 signal=null - - -` - -exports[`test/run/before-after.js TAP slow fail before > stdout 1`] = ` - -` diff --git a/typescript-vendor/tap/tap-snapshots/test/run/cat.js.test.cjs b/typescript-vendor/tap/tap-snapshots/test/run/cat.js.test.cjs deleted file mode 100644 index e69bcce09..000000000 --- a/typescript-vendor/tap/tap-snapshots/test/run/cat.js.test.cjs +++ /dev/null @@ -1,23 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/run/cat.js TAP cat > must match snapshot 1`] = ` -TAP version 13 -ok 1 - cli-tests/ts/ok.tap # SKIP no tests found { - - # Subtest - 1..1 - ok 1 - this is fine - - 1..0 # no tests found -} - -1..1 -# skip: 1 -# {time} - -` diff --git a/typescript-vendor/tap/tap-snapshots/test/run/coverage.js.test.cjs b/typescript-vendor/tap/tap-snapshots/test/run/coverage.js.test.cjs deleted file mode 100644 index 381413df5..000000000 --- a/typescript-vendor/tap/tap-snapshots/test/run/coverage.js.test.cjs +++ /dev/null @@ -1,217 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/run/coverage.js TAP borked coverage map means no includes > output 1`] = ` -TAP version 13 -ok 1 - 1.test.js # {time} { - ok 1 - should be equal - 1..1 - # {time} -} - -ok 2 - 2.test.js # {time} { - ok 1 - should be equal - 1..1 - # {time} -} - -1..2 -# {time} --|-|-|-|-|- -File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Lines --|-|-|-|-|- -All files | 0 | 0 | 0 | 0 | --|-|-|-|-|- - -` - -exports[`test/run/coverage.js TAP generate some coverage > output 1`] = ` -TAP version 13 -ok 1 - 1.test.js # {time} { - ok 1 - should be equal - 1..1 - # {time} -} - -ok 2 - 2.test.js # {time} { - ok 1 - should be equal - 1..1 - # {time} -} - -1..2 -# {time} --|-|-|-|-|- -File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Lines --|-|-|-|-|- -All files | 75 | 75 | 100 | 75 | - ok.js | 75 | 75 | 100 | 75 | 6 --|-|-|-|-|- - -` - -exports[`test/run/coverage.js TAP in 100 mode, <100 is red, not yellow > text output and 100 check 1`] = ` --|-|-|-|-|- -File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Lines --|-|-|-|-|- -All files |  75 |  75 |  100 |  75 |   - ok.js  |  75 |  75 |  100 |  75 | 6  --|-|-|-|-|- - -` - -exports[`test/run/coverage.js TAP pipe to service > human output 1`] = ` --|-|-|-|-|- -File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Lines --|-|-|-|-|- -All files | 75 | 75 | 100 | 75 | - ok.js | 75 | 75 | 100 | 75 | 6 --|-|-|-|-|- - -` - -exports[`test/run/coverage.js TAP pipe to service > piped to coverage service cat 1`] = ` -TN: -SF:ok.js -FN:2,(anonymous_0) -FNF:1 -FNH:1 -FNDA:2,(anonymous_0) -DA:2,2 -DA:3,2 -DA:4,2 -DA:6,0 -LF:4 -LH:3 -BRDA:3,0,0,2 -BRDA:3,0,1,0 -BRDA:4,1,0,2 -BRDA:4,1,1,1 -BRF:4 -BRH:3 -end_of_record - -` - -exports[`test/run/coverage.js TAP pipe to service along with tests > human output 1`] = ` -TAP version 13 -ok 1 - 1.test.js # {time} { - ok 1 - should be equal - 1..1 - # {time} -} - -ok 2 - 2.test.js # {time} { - ok 1 - should be equal - 1..1 - # {time} -} - -1..2 -# {time} --|-|-|-|-|- -File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Lines --|-|-|-|-|- -All files | 75 | 75 | 100 | 75 | - ok.js | 75 | 75 | 100 | 75 | 6 --|-|-|-|-|- - -` - -exports[`test/run/coverage.js TAP pipe to service along with tests > piped to coverage service cat 1`] = ` -TN: -SF:ok.js -FN:2,(anonymous_0) -FNF:1 -FNH:1 -FNDA:2,(anonymous_0) -DA:2,2 -DA:3,2 -DA:4,2 -DA:6,0 -LF:4 -LH:3 -BRDA:3,0,0,2 -BRDA:3,0,1,0 -BRDA:4,1,0,2 -BRDA:4,1,1,1 -BRF:4 -BRH:3 -end_of_record - -` - -exports[`test/run/coverage.js TAP report only > lcov output 1`] = ` -TN: -SF:ok.js -FN:2,(anonymous_0) -FNF:1 -FNH:1 -FNDA:2,(anonymous_0) -DA:2,2 -DA:3,2 -DA:4,2 -DA:6,0 -LF:4 -LH:3 -BRDA:3,0,0,2 -BRDA:3,0,1,0 -BRDA:4,1,0,2 -BRDA:4,1,1,1 -BRF:4 -BRH:3 -end_of_record - -` - -exports[`test/run/coverage.js TAP report with checks > lcov output and 100 check 1`] = ` -TN: -SF:ok.js -FN:2,(anonymous_0) -FNF:1 -FNH:1 -FNDA:2,(anonymous_0) -DA:2,2 -DA:3,2 -DA:4,2 -DA:6,0 -LF:4 -LH:3 -BRDA:3,0,0,2 -BRDA:3,0,1,0 -BRDA:4,1,0,2 -BRDA:4,1,1,1 -BRF:4 -BRH:3 -end_of_record - -` - -exports[`test/run/coverage.js TAP use a coverage map > output 1`] = ` -TAP version 13 -ok 1 - 1.test.js # {time} { - ok 1 - should be equal - 1..1 - # {time} -} - -ok 2 - 2.test.js # {time} { - ok 1 - should be equal - 1..1 - # {time} -} - -1..2 -# {time} --|-|-|-|-|- -File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Lines --|-|-|-|-|- -All files | 75 | 75 | 100 | 75 | - ok.js | 75 | 75 | 100 | 75 | 6 --|-|-|-|-|- - -` diff --git a/typescript-vendor/tap/tap-snapshots/test/run/dump-config.js.test.cjs b/typescript-vendor/tap/tap-snapshots/test/run/dump-config.js.test.cjs deleted file mode 100644 index af122168b..000000000 --- a/typescript-vendor/tap/tap-snapshots/test/run/dump-config.js.test.cjs +++ /dev/null @@ -1,435 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/run/dump-config.js TAP empty rc file > output 1`] = ` -after: null -bail: false -before: null -branches: 100 -browser: true -changed: false -check-coverage: true -color: true -comments: false -coverage: false -coverage-map: null -coverage-report: [] -debug: false -files: [] -flow: false -functions: 100 -grep: [] -help: false -invert: false -jobs: {number} -jsx: false -libtap-settings: null -lines: 100 -node-arg: [] -nyc-arg: [] -nyc-help: false -nyc-version: false -only: false -output-dir: null -output-file: null -parser-version: false -rcfile: cli-tests/taprc -reporter: base -reporter-arg: [] -save: null -save-fixture: false -show-process-tree: false -statements: 100 -test-arg: [] -test-env: [] -test-ignore: $. -test-regex: ^test/.*\\.js$ -timeout: 30 -ts: false -version: false -versions: false -watch: false - - -` - -exports[`test/run/dump-config.js TAP good rc file > output 1`] = ` -after: null -bail: false -before: null -branches: 100 -browser: true -changed: false -check-coverage: true -color: false -comments: false -coverage: false -coverage-map: null -coverage-report: [] -debug: false -files: [] -flow: false -functions: 100 -grep: [] -help: false -invert: false -jobs: {number} -jsx: false -libtap-settings: null -lines: 100 -node-arg: [] -nyc-arg: [] -nyc-help: false -nyc-version: false -only: false -output-dir: null -output-file: null -parser-version: false -rcfile: cli-tests/taprc -reporter: spec -reporter-arg: [] -save: null -save-fixture: false -show-process-tree: false -statements: 100 -test-arg: [] -test-env: [] -test-ignore: $. -test-regex: ^test/.*\\.js$ -timeout: 30 -ts: false -version: false -versions: false -watch: false - - -` - -exports[`test/run/dump-config.js TAP package.json parsing bad > output 1`] = ` -after: null -bail: false -before: null -branches: 100 -browser: true -changed: false -check-coverage: true -color: false -comments: false -coverage: false -coverage-map: null -coverage-report: [] -debug: false -files: [] -flow: false -functions: 100 -grep: [] -help: false -invert: false -jobs: {number} -jsx: false -libtap-settings: null -lines: 100 -node-arg: [] -nyc-arg: [] -nyc-help: false -nyc-version: false -only: false -output-dir: null -output-file: null -parser-version: false -rcfile: {CWD}/cli-tests/.taprc -reporter: tap -reporter-arg: [] -save: null -save-fixture: false -show-process-tree: false -statements: 100 -test-arg: [] -test-env: [] -test-ignore: $. -test-regex: ((\\/|^)(tests?|__tests?__)\\/.*|\\.(tests?|spec)|^\\/?tests?)\\.([mc]js|[jt]sx?)$ -timeout: 30 -ts: false -version: false -versions: false -watch: false - - -` - -exports[`test/run/dump-config.js TAP package.json parsing good > output 1`] = ` -after: null -bail: false -before: null -branches: 100 -browser: true -changed: false -check-coverage: true -color: false -comments: false -coverage: false -coverage-map: null -coverage-report: [] -debug: false -files: [] -flow: false -functions: 100 -grep: [] -help: false -invert: false -jobs: {number} -jsx: false -libtap-settings: null -lines: 100 -node-arg: [] -nyc-arg: [] -nyc-help: false -nyc-version: false -only: false -output-dir: null -output-file: null -parser-version: false -rcfile: {CWD}/cli-tests/.taprc -reporter: tap -reporter-arg: [] -save: null -save-fixture: false -show-process-tree: false -statements: 100 -test-arg: [] -test-env: [] -test-ignore: $. -test-regex: ((\\/|^)(tests?|__tests?__)\\/.*|\\.(tests?|spec)|^\\/?tests?)\\.([mc]js|[jt]sx?)$ -timeout: 30 -ts: false -version: false -versions: false -watch: false - - -` - -exports[`test/run/dump-config.js TAP package.json parsing missing > output 1`] = ` -after: null -bail: false -before: null -branches: 100 -browser: true -changed: false -check-coverage: true -color: false -comments: false -coverage: false -coverage-map: null -coverage-report: [] -debug: false -files: [] -flow: false -functions: 100 -grep: [] -help: false -invert: false -jobs: {number} -jsx: false -libtap-settings: null -lines: 100 -node-arg: [] -nyc-arg: [] -nyc-help: false -nyc-version: false -only: false -output-dir: null -output-file: null -parser-version: false -rcfile: {CWD}/cli-tests/.taprc -reporter: tap -reporter-arg: [] -save: null -save-fixture: false -show-process-tree: false -statements: 100 -test-arg: [] -test-env: [] -test-ignore: $. -test-regex: ((\\/|^)(tests?|__tests?__)\\/.*|\\.(tests?|spec)|^\\/?tests?)\\.([mc]js|[jt]sx?)$ -timeout: 30 -ts: false -version: false -versions: false -watch: false - - -` - -exports[`test/run/dump-config.js TAP short options as well as short flags > output 1`] = ` -after: null -bail: true -before: null -branches: 100 -browser: true -changed: false -check-coverage: true -color: false -comments: false -coverage: false -coverage-map: null -coverage-report: [] -debug: false -files: [] -flow: false -functions: 100 -grep: [] -help: false -invert: false -jobs: {number} -jsx: false -libtap-settings: null -lines: 100 -node-arg: [] -nyc-arg: [] -nyc-help: false -nyc-version: false -only: false -output-dir: null -output-file: null -parser-version: false -rcfile: {CWD}/.taprc -reporter: tap -reporter-arg: [] -save: null -save-fixture: false -show-process-tree: false -statements: 100 -test-arg: [] -test-env: [] -test-ignore: $. -test-regex: ^test/.*\\.js$ -timeout: 0 -ts: false -version: false -versions: false -watch: false - - -` - -exports[`test/run/dump-config.js TAP shotgun a bunch of option parsing junk > output 1`] = ` -after: null -bail: false -before: null -branches: 99 -browser: false -changed: false -check-coverage: true -color: false -comments: true -coverage: true -coverage-map: false -coverage-report: - - json - - html -debug: true -files: [] -flow: false -functions: 100 -grep: - - x - - /y/i -help: false -invert: false -jobs: {number} -jsx: false -libtap-settings: null -lines: 100 -node-arg: - - --expose-gc - - --use-strict - - --debug-brk - - --harmony - - xyz - - abc -nyc-arg: - - abc -nyc-help: false -nyc-version: false -only: true -output-dir: null -output-file: out.txt -parser-version: false -rcfile: {CWD}/.taprc -reporter: spec -reporter-arg: [] -save: foo.txt -save-fixture: false -show-process-tree: false -statements: 100 -test-arg: - - xyz - - abc -test-env: [] -test-ignore: $. -test-regex: ^test/.*\\.js$ -timeout: 99 -ts: false -version: false -versions: false -watch: false - - -` - -exports[`test/run/dump-config.js TAP turn color off and back on again > output 1`] = ` -after: null -bail: false -before: null -branches: 100 -browser: true -changed: false -check-coverage: true -color: true -comments: false -coverage: false -coverage-map: null -coverage-report: [] -debug: false -files: [] -flow: false -functions: 100 -grep: [] -help: false -invert: false -jobs: {number} -jsx: false -libtap-settings: null -lines: 100 -node-arg: [] -nyc-arg: [] -nyc-help: false -nyc-version: false -only: false -output-dir: null -output-file: null -parser-version: false -rcfile: {CWD}/.taprc -reporter: base -reporter-arg: [] -save: null -save-fixture: false -show-process-tree: false -statements: 100 -test-arg: [] -test-env: [] -test-ignore: $. -test-regex: ^test/.*\\.js$ -timeout: 30 -ts: false -version: false -versions: false -watch: false - - -` diff --git a/typescript-vendor/tap/tap-snapshots/test/run/env.js.test.cjs b/typescript-vendor/tap/tap-snapshots/test/run/env.js.test.cjs deleted file mode 100644 index 2e5a78991..000000000 --- a/typescript-vendor/tap/tap-snapshots/test/run/env.js.test.cjs +++ /dev/null @@ -1,21 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/run/env.js TAP > must match snapshot 1`] = ` -TAP version 13 -ok 1 - cli-tests/ok.js # {time} { - ok 1 - should be equal - ok 2 - should be equal - ok 3 - should be equal - 1..3 - # {time} -} - -1..1 -# {time} - -` diff --git a/typescript-vendor/tap/tap-snapshots/test/run/executables.js.test.cjs b/typescript-vendor/tap/tap-snapshots/test/run/executables.js.test.cjs deleted file mode 100644 index 5a6df434e..000000000 --- a/typescript-vendor/tap/tap-snapshots/test/run/executables.js.test.cjs +++ /dev/null @@ -1,18 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/run/executables.js TAP executables > must match snapshot 1`] = ` -TAP version 13 -ok 1 - exe/ok.sh # {time} { - 1..1 - ok 1 File with executable bit should be executed -} - -1..1 -# {time} - -` diff --git a/typescript-vendor/tap/tap-snapshots/test/run/files.js.test.cjs b/typescript-vendor/tap/tap-snapshots/test/run/files.js.test.cjs deleted file mode 100644 index 3182fa1f5..000000000 --- a/typescript-vendor/tap/tap-snapshots/test/run/files.js.test.cjs +++ /dev/null @@ -1,38 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/run/files.js TAP --files do not override explicit positional argument > must match snapshot 1`] = ` -TAP version 13 -ok 1 - cli-tests/two.js # {time} { - ok 1 - three - 1..1 - # {time} -} - -1..1 -# {time} - -` - -exports[`test/run/files.js TAP --files work like explicit positional argument > must match snapshot 1`] = ` -TAP version 13 -ok 1 - cli-tests/one.js # {time} { - ok 1 - one - 1..1 - # {time} -} - -ok 2 - cli-tests/two.js # {time} { - ok 1 - three - 1..1 - # {time} -} - -1..2 -# {time} - -` diff --git a/typescript-vendor/tap/tap-snapshots/test/run/flow.js.test.cjs b/typescript-vendor/tap/tap-snapshots/test/run/flow.js.test.cjs deleted file mode 100644 index 05c118352..000000000 --- a/typescript-vendor/tap/tap-snapshots/test/run/flow.js.test.cjs +++ /dev/null @@ -1,32 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/run/flow.js TAP flow > must match snapshot 1`] = ` -TAP version 13 -ok 1 - cli-tests/flow/ok.js # {time} { - ok 1 - this is fine - 1..1 - # {time} -} - -1..1 -# {time} - -` - -exports[`test/run/flow.js TAP flow manually > must match snapshot 1`] = ` -TAP version 13 -ok 1 - cli-tests/flow/ok2.js # {time} { - ok 1 - this is fine - 1..1 - # {time} -} - -1..1 -# {time} - -` diff --git a/typescript-vendor/tap/tap-snapshots/test/run/invalid-option.js.test.cjs b/typescript-vendor/tap/tap-snapshots/test/run/invalid-option.js.test.cjs deleted file mode 100644 index 177f061d4..000000000 --- a/typescript-vendor/tap/tap-snapshots/test/run/invalid-option.js.test.cjs +++ /dev/null @@ -1,14 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/run/invalid-option.js TAP print a nicer message on invalid argument errors > must match snapshot 1`] = ` - -Error: no value provided for option: reporter - {STACK} -Run \`tap --help\` for usage information - -` diff --git a/typescript-vendor/tap/tap-snapshots/test/run/jsx.js.test.cjs b/typescript-vendor/tap/tap-snapshots/test/run/jsx.js.test.cjs deleted file mode 100644 index 7da442d2d..000000000 --- a/typescript-vendor/tap/tap-snapshots/test/run/jsx.js.test.cjs +++ /dev/null @@ -1,19 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/run/jsx.js TAP jsx > must match snapshot 1`] = ` -TAP version 13 -ok 1 - cli-tests/jsx/ok.jsx # {time} { - ok 1 - this is fine - 1..1 - # {time} -} - -1..1 -# {time} - -` diff --git a/typescript-vendor/tap/tap-snapshots/test/run/libtap-settings.js.test.cjs b/typescript-vendor/tap/tap-snapshots/test/run/libtap-settings.js.test.cjs deleted file mode 100644 index 0917008b3..000000000 --- a/typescript-vendor/tap/tap-snapshots/test/run/libtap-settings.js.test.cjs +++ /dev/null @@ -1,34 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/run/libtap-settings.js TAP print out a different snapshot file location > must match snapshot 1`] = ` -TAP version 13 -ok 1 - {CWD}/test/run/tap-testdir-libtap-settings/test.js # SKIP { - # some-path/test.js.test.cjs - 1..0 - # {time} -} - -1..1 -# skip: 1 -# {time} - -` - -exports[`test/run/libtap-settings.js TAP print out the normal snapshot file location > must match snapshot 1`] = ` -TAP version 13 -ok 1 - {CWD}/test/run/tap-testdir-libtap-settings/test.js # SKIP { - # {CWD}/test/run/tap-testdir-libtap-settings/tap-snapshots/test.js.test.cjs - 1..0 - # {time} -} - -1..1 -# skip: 1 -# {time} - -` diff --git a/typescript-vendor/tap/tap-snapshots/test/run/nocolor-env.js.test.cjs b/typescript-vendor/tap/tap-snapshots/test/run/nocolor-env.js.test.cjs deleted file mode 100644 index 3909012b6..000000000 --- a/typescript-vendor/tap/tap-snapshots/test/run/nocolor-env.js.test.cjs +++ /dev/null @@ -1,19 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/run/nocolor-env.js TAP > must match snapshot 1`] = ` -TAP version 13 -ok 1 - cli-tests/ok.js # {time} { - ok 1 - should be equal - 1..1 - # {time} -} - -1..1 -# {time} - -` diff --git a/typescript-vendor/tap/tap-snapshots/test/run/nonparallel.js.test.cjs b/typescript-vendor/tap/tap-snapshots/test/run/nonparallel.js.test.cjs deleted file mode 100644 index 6eb3a3a0b..000000000 --- a/typescript-vendor/tap/tap-snapshots/test/run/nonparallel.js.test.cjs +++ /dev/null @@ -1,31 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/run/nonparallel.js TAP > output 1`] = ` -TAP version 13 -# Subtest: 1.js - start one - ok 1 - fine in 1 - 1..1 - # {time} -ok 1 - 1.js # {time} - -# Subtest: 2.js - start two - ok 1 - fine in 2 - 1..1 - # {time} -ok 2 - 2.js # {time} - -1..2 -# {time} - -` - -exports[`test/run/nonparallel.js TAP > stderr 1`] = ` - -` diff --git a/typescript-vendor/tap/tap-snapshots/test/run/output-file.js.test.cjs b/typescript-vendor/tap/tap-snapshots/test/run/output-file.js.test.cjs deleted file mode 100644 index 41a923eef..000000000 --- a/typescript-vendor/tap/tap-snapshots/test/run/output-file.js.test.cjs +++ /dev/null @@ -1,176 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/run/output-file.js TAP output-file file and stdin together > ok.js output file 1`] = ` -TAP version 13 -ok 1 - this is fine -1..1 -# {time} - -` - -exports[`test/run/output-file.js TAP output-file file and stdin together > output 1`] = ` -TAP version 13 -ok 1 - cli-tests/ok.js # {time} { - ok 1 - this is fine - 1..1 - # {time} -} - -ok 2 - /dev/stdin # {time} { - 1..1 - ok 1 - totally fine result from stdin -} - -1..2 -# {time} - -` - -exports[`test/run/output-file.js TAP output-file file and stdin together > output 2`] = ` -TAP version 13 -ok 1 - cli-tests/ok.js # {time} { - ok 1 - this is fine - 1..1 - # {time} -} - -ok 2 - /dev/stdin # {time} { - 1..1 - ok 1 - totally fine result from stdin -} - -1..2 -# {time} - -` - -exports[`test/run/output-file.js TAP output-file file and stdin together > output file 1`] = ` -TAP version 13 -ok 1 - cli-tests/ok.js # {time} { - ok 1 - this is fine - 1..1 - # {time} -} - -ok 2 - /dev/stdin # {time} { - 1..1 - ok 1 - totally fine result from stdin -} - -1..2 -# {time} - -` - -exports[`test/run/output-file.js TAP output-file file and stdin together > stderr 1`] = ` - -` - -exports[`test/run/output-file.js TAP output-file file and stdin together > stderr 2`] = ` - -` - -exports[`test/run/output-file.js TAP output-file file and stdin together > stdin output file 1`] = ` -TAP version 13 -1..1 -ok 1 - totally fine result from stdin - -` - -exports[`test/run/output-file.js TAP output-file ok.js > output 1`] = ` -TAP version 13 -ok 1 - cli-tests/ok.js # {time} { - ok 1 - this is fine - 1..1 - # {time} -} - -1..1 -# {time} - -` - -exports[`test/run/output-file.js TAP output-file ok.js > output 2`] = ` -TAP version 13 -ok 1 - cli-tests/ok.js # {time} { - ok 1 - this is fine - 1..1 - # {time} -} - -1..1 -# {time} - -` - -exports[`test/run/output-file.js TAP output-file ok.js > output file 1`] = ` -TAP version 13 -ok 1 - cli-tests/ok.js # {time} { - ok 1 - this is fine - 1..1 - # {time} -} - -1..1 -# {time} - -` - -exports[`test/run/output-file.js TAP output-file ok.js > output file 2`] = ` -TAP version 13 -ok 1 - this is fine -1..1 -# {time} - -` - -exports[`test/run/output-file.js TAP output-file ok.js > stderr 1`] = ` - -` - -exports[`test/run/output-file.js TAP output-file ok.js > stderr 2`] = ` - -` - -exports[`test/run/output-file.js TAP output-file stdin > output 1`] = ` -TAP version 13 -1..1 -ok 1 - totally fine result from stdin -# {time} - -` - -exports[`test/run/output-file.js TAP output-file stdin > output 2`] = ` -TAP version 13 -1..1 -ok 1 - totally fine result from stdin -# {time} - -` - -exports[`test/run/output-file.js TAP output-file stdin > output file 1`] = ` -TAP version 13 -1..1 -ok 1 - totally fine result from stdin - -` - -exports[`test/run/output-file.js TAP output-file stdin > output file 2`] = ` -TAP version 13 -1..1 -ok 1 - totally fine result from stdin - -` - -exports[`test/run/output-file.js TAP output-file stdin > stderr 1`] = ` - -` - -exports[`test/run/output-file.js TAP output-file stdin > stderr 2`] = ` - -` diff --git a/typescript-vendor/tap/tap-snapshots/test/run/parallel.js.test.cjs b/typescript-vendor/tap/tap-snapshots/test/run/parallel.js.test.cjs deleted file mode 100644 index 991fa7ae7..000000000 --- a/typescript-vendor/tap/tap-snapshots/test/run/parallel.js.test.cjs +++ /dev/null @@ -1,79 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/run/parallel.js TAP > output 1`] = ` -TAP version 13 -ok 1 - p/y/1.js # {time} { - ok 1 - one - 1..1 - # {time} -} - -ok 2 - p/y/2.js # {time} { - ok 1 - 2 - 1..1 - # {time} -} - -# Subtest: q/b/f1.js - ok 1 - a/b - 1..1 - # {time} -ok 3 - q/b/f1.js # {time} - -# Subtest: q/b/f2.js - ok 1 - c/d - 1..1 - # {time} -ok 4 - q/b/f2.js # {time} - -# Subtest: r/y/1.js - ok 1 - one - 1..1 - # {time} -ok 5 - r/y/1.js # {time} - -# Subtest: r/y/2.js - ok 1 - 2 - 1..1 - # {time} -ok 6 - r/y/2.js # {time} - -ok 7 - z/y/1.js # {time} { - ok 1 - one - 1..1 - # {time} -} - -ok 8 - z/y/2.js # {time} { - ok 1 - 2 - 1..1 - # {time} -} - -1..8 -# {time} - -` - -exports[`test/run/parallel.js TAP > stderr 1`] = ` -start -start -end -end -f1 -f2 -ry1 -ry1 -ry2 -ry2 -start -start -end -end - -` diff --git a/typescript-vendor/tap/tap-snapshots/test/run/reporters.js.test.cjs b/typescript-vendor/tap/tap-snapshots/test/run/reporters.js.test.cjs deleted file mode 100644 index b6ee67878..000000000 --- a/typescript-vendor/tap/tap-snapshots/test/run/reporters.js.test.cjs +++ /dev/null @@ -1,31 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/run/reporters.js TAP builtin reporter > stdout 1`] = ` -treport output -` - -exports[`test/run/reporters.js TAP cli reporter > stdout 1`] = ` -TAP version 13 -ok 1 - cli-tests/ok.js > this is fine -# {time} -1..1 - - -` - -exports[`test/run/reporters.js TAP react component > stdout 1`] = ` -treport output -` - -exports[`test/run/reporters.js TAP stream reporter > stdout 1`] = ` -spec output -` - -exports[`test/run/reporters.js TAP tmr builtin reporter > stdout 1`] = ` -spec output -` diff --git a/typescript-vendor/tap/tap-snapshots/test/run/save-file.js.test.cjs b/typescript-vendor/tap/tap-snapshots/test/run/save-file.js.test.cjs deleted file mode 100644 index c4446b027..000000000 --- a/typescript-vendor/tap/tap-snapshots/test/run/save-file.js.test.cjs +++ /dev/null @@ -1,197 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/run/save-file.js TAP empty save file, run all tests > stdout 1`] = ` -TAP version 13 -ok 1 - a/b/2.js # {time} { - ok 1 - 2 - 1..1 - # {time} -} - -ok 2 - a/b/f1.js # {time} { - ok 1 - fine now - 1..1 - # {time} -} - -ok 3 - x/y/1.js # {time} { - ok 1 - one - 1..1 - # {time} -} - -ok 4 - z.js # {time} { - ok 1 - fine now too - 1..1 - # {time} -} - -1..4 -# {time} - -` - -exports[`test/run/save-file.js TAP pass, empty save file > stdout 1`] = ` -TAP version 13 -ok 1 - a/b/f1.js # {time} { - ok 1 - fine now - 1..1 - # {time} -} - -ok 2 - z.js # {time} { - ok 1 - fine now too - 1..1 - # {time} -} - -1..2 -# {time} - -` - -exports[`test/run/save-file.js TAP with bailout, should save all untested > savefile 1`] = ` -a/b/f1.js -x/y/1.js -z.js - -` - -exports[`test/run/save-file.js TAP with bailout, should save all untested > stdout 1`] = ` -TAP version 13 -ok 1 - a/b/2.js # {time} { - ok 1 - 2 - 1..1 - # {time} -} - -not ok 2 - a/b/f1.js # {time} - --- - args: - - a/b/f1.js - command: {NODE} - cwd: {CWD}/cli-tests - env: {} - exitCode: 1 - file: a/b/f1.js - stdio: - - 0 - - pipe - - 2 - timeout: {default} - ... -{ - not ok 1 - a/b - --- - at: - line: # - column: # - file: a/b/f1.js - source: | - //f1.js - require("{CWD}/").fail('a/b') - --^ - stack: | - {STACK} - ... - - Bail out! a/b -} -Bail out! a/b - -` - -exports[`test/run/save-file.js TAP without bailout, run untested, save failures > savefile 1`] = ` -a/b/f1.js -z.js - -` - -exports[`test/run/save-file.js TAP without bailout, run untested, save failures > stdout 1`] = ` -TAP version 13 -not ok 1 - a/b/f1.js # {time} - --- - args: - - a/b/f1.js - command: {NODE} - cwd: {CWD}/cli-tests - env: {} - exitCode: 1 - file: a/b/f1.js - stdio: - - 0 - - pipe - - 2 - timeout: {default} - ... -{ - not ok 1 - a/b - --- - at: - line: # - column: # - file: a/b/f1.js - source: | - //f1.js - require("{CWD}/").fail('a/b') - --^ - stack: | - {STACK} - ... - - 1..1 - # failed 1 test - # {time} -} - -ok 2 - x/y/1.js # {time} { - ok 1 - one - 1..1 - # {time} -} - -not ok 3 - z.js # {time} - --- - args: - - z.js - command: {NODE} - cwd: {CWD}/cli-tests - env: {} - exitCode: 1 - file: z.js - stdio: - - 0 - - pipe - - 2 - timeout: {default} - ... -{ - not ok 1 - c/d - --- - at: - line: # - column: # - file: z.js - source: | - //z.js - require("{CWD}/").fail('c/d') - --^ - stack: | - {STACK} - ... - - 1..1 - # failed 1 test - # {time} -} - -1..3 -# failed 2 of 3 tests -# {time} - -` diff --git a/typescript-vendor/tap/tap-snapshots/test/run/stdin.js.test.cjs b/typescript-vendor/tap/tap-snapshots/test/run/stdin.js.test.cjs deleted file mode 100644 index 88026c05b..000000000 --- a/typescript-vendor/tap/tap-snapshots/test/run/stdin.js.test.cjs +++ /dev/null @@ -1,28 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/run/stdin.js TAP with file > must match snapshot 1`] = ` -TAP version 13 -ok 1 - cli-tests/foo.test.js # {time} { - ok 1 - child # {time} { - ok 1 - this is fine - 1..1 - } - - 1..1 - # {time} -} - -ok 2 - /dev/stdin # {time} { - 1..1 - ok -} - -1..2 -# {time} - -` diff --git a/typescript-vendor/tap/tap-snapshots/test/run/test-regex.js.test.cjs b/typescript-vendor/tap/tap-snapshots/test/run/test-regex.js.test.cjs deleted file mode 100644 index c6ed5fc10..000000000 --- a/typescript-vendor/tap/tap-snapshots/test/run/test-regex.js.test.cjs +++ /dev/null @@ -1,25 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/run/test-regex.js TAP no args, pull in default files, not exclusions > output 1`] = ` -TAP version 13 -ok 1 - file.spec.js # {time} { - ok 1 - this is fine - 1..1 - # {time} -} - -ok 2 - tests.cjs # {time} { - ok 1 - this is also fine - 1..1 - # {time} -} - -1..2 -# {time} - -` diff --git a/typescript-vendor/tap/tap-snapshots/test/run/ts.js.test.cjs b/typescript-vendor/tap/tap-snapshots/test/run/ts.js.test.cjs deleted file mode 100644 index 289eafcd1..000000000 --- a/typescript-vendor/tap/tap-snapshots/test/run/ts.js.test.cjs +++ /dev/null @@ -1,164 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/run/ts.js TAP ts manually > must match snapshot 1`] = ` -TAP version 13 -ok 1 - cli-tests/mixed/ok.js # {time} { - ok 1 - this is fine - 1..1 - # {time} -} - -ok 2 - cli-tests/mixed/foo.ts # {time} { - ok 1 - this is fine - 1..1 - # {time} -} - -1..2 -# {time} - -` - -exports[`test/run/ts.js TAP via cli args ts > must match snapshot 1`] = ` -TAP version 13 -ok 1 - cli-tests/ts/ok.ts # {time} { - ok 1 - this is fine - 1..1 - # {time} -} - -1..1 -# {time} - -` - -exports[`test/run/ts.js TAP via cli args ts, but no tsx > must match snapshot 1`] = ` -TAP version 13 -not ok 1 - cli-tests/tsx/ok.tsx # {time} - --- - args: - - -r - - {CWD}/node_modules/ts-node/register/index.js - - cli-tests/tsx/ok.tsx - command: {NODE} - cwd: {CWD} - env: - TS_NODE_COMPILER_OPTIONS: "{}" - exitCode: 1 - file: cli-tests/tsx/ok.tsx - stdio: - - 0 - - pipe - - 2 - timeout: {default} - ... -{ - 1..0 # no tests found -} - -1..1 -# failed 1 test -# {time} - -` - -exports[`test/run/ts.js TAP via cli args tsx > must match snapshot 1`] = ` -TAP version 13 -ok 1 - cli-tests/tsx/ok.tsx # {time} { - ok 1 - this is fine - 1..1 - # {time} -} - -1..1 -# {time} - -` - -exports[`test/run/ts.js TAP via env no ts > must match snapshot 1`] = ` -TAP version 13 -not ok 1 - cli-tests/ts/ok.ts # {time} - --- - args: - - cli-tests/ts/ok.ts - command: {NODE} - cwd: {CWD} - env: {} - exitCode: 1 - file: cli-tests/ts/ok.ts - stdio: - - 0 - - pipe - - 2 - timeout: {default} - ... -{ - 1..0 # no tests found -} - -1..1 -# failed 1 test -# {time} - -` - -exports[`test/run/ts.js TAP via env ts > must match snapshot 1`] = ` -TAP version 13 -ok 1 - cli-tests/ts/ok.ts # {time} { - ok 1 - this is fine - 1..1 - # {time} -} - -1..1 -# {time} - -` - -exports[`test/run/ts.js TAP via env ts, but no tsx > must match snapshot 1`] = ` -TAP version 13 -not ok 1 - cli-tests/tsx/ok.tsx # {time} - --- - args: - - -r - - {CWD}/node_modules/ts-node/register/index.js - - cli-tests/tsx/ok.tsx - command: {NODE} - cwd: {CWD} - env: - TS_NODE_COMPILER_OPTIONS: "{}" - exitCode: 1 - file: cli-tests/tsx/ok.tsx - stdio: - - 0 - - pipe - - 2 - timeout: {default} - ... -{ - 1..0 # no tests found -} - -1..1 -# failed 1 test -# {time} - -` - -exports[`test/run/ts.js TAP via env tsx > must match snapshot 1`] = ` -TAP version 13 -ok 1 - cli-tests/tsx/ok.tsx # {time} { - ok 1 - this is fine - 1..1 - # {time} -} - -1..1 -# {time} - -` diff --git a/typescript-vendor/tap/tap-snapshots/test/run/watermarks.js.test.cjs b/typescript-vendor/tap/tap-snapshots/test/run/watermarks.js.test.cjs deleted file mode 100644 index 486013c29..000000000 --- a/typescript-vendor/tap/tap-snapshots/test/run/watermarks.js.test.cjs +++ /dev/null @@ -1,114 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/run/watermarks.js TAP default watermarks, all set at 100, red > stderr 1`] = ` - -` - -exports[`test/run/watermarks.js TAP default watermarks, all set at 100, red > stdout 1`] = ` -TAP version 13 -ok 1 - t.js # {time} { - truthy - truthy - gt 5 - ok 1 - this is fine - 1..1 - # {time} -} - -1..1 -# {time} --|-|-|-|-|- -File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Lines --|-|-|-|-|- -All files  |  87.5 |  75 |  50 |  87.5 |   - branch.js |  87.5 |  75 |  50 |  87.5 | 8  --|-|-|-|-|- - -` - -exports[`test/run/watermarks.js TAP less than halfway to 100, yellow > stderr 1`] = ` - -` - -exports[`test/run/watermarks.js TAP less than halfway to 100, yellow > stdout 1`] = ` -TAP version 13 -ok 1 - t.js # {time} { - truthy - truthy - gt 5 - ok 1 - this is fine - 1..1 - # {time} -} - -1..1 -# {time} --|-|-|-|-|- -File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Lines --|-|-|-|-|- -All files  |  87.5 |  75 |  50 |  87.5 |   - branch.js |  87.5 |  75 |  50 |  87.5 | 8  --|-|-|-|-|- - -` - -exports[`test/run/watermarks.js TAP more than halfway to 100, green > stderr 1`] = ` - -` - -exports[`test/run/watermarks.js TAP more than halfway to 100, green > stdout 1`] = ` -TAP version 13 -ok 1 - t.js # {time} { - truthy - truthy - gt 5 - ok 1 - this is fine - 1..1 - # {time} -} - -1..1 -# {time} --|-|-|-|-|- -File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Lines --|-|-|-|-|- -All files  |  87.5 |  75 |  50 |  87.5 |   - branch.js |  87.5 |  75 |  50 |  87.5 | 8  --|-|-|-|-|- - -` - -exports[`test/run/watermarks.js TAP unmet, red > stderr 1`] = ` -ERROR: Coverage for lines (87.5%) does not meet global threshold (88%) -ERROR: Coverage for functions (50%) does not meet global threshold (51%) -ERROR: Coverage for branches (75%) does not meet global threshold (76%) -ERROR: Coverage for statements (87.5%) does not meet global threshold (88%) - -` - -exports[`test/run/watermarks.js TAP unmet, red > stdout 1`] = ` -TAP version 13 -ok 1 - t.js # {time} { - truthy - truthy - gt 5 - ok 1 - this is fine - 1..1 - # {time} -} - -1..1 -# {time} --|-|-|-|-|- -File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Lines --|-|-|-|-|- -All files  |  87.5 |  75 |  50 |  87.5 |   - branch.js |  87.5 |  75 |  50 |  87.5 | 8  --|-|-|-|-|- - -` diff --git a/typescript-vendor/tap/tap-snapshots/test/settings/default.js.test.cjs b/typescript-vendor/tap/tap-snapshots/test/settings/default.js.test.cjs deleted file mode 100644 index 38489d230..000000000 --- a/typescript-vendor/tap/tap-snapshots/test/settings/default.js.test.cjs +++ /dev/null @@ -1,32 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/settings/default.js TAP > must match snapshot 1`] = ` -Object { - "atTap": false, - "mkdirpNeeded": false, - "mkdirRecursive": "function(path, cb)", - "mkdirRecursiveSync": "function(path)", - "output": true, - "rimrafNeeded": false, - "rmdirRecursive": "function(path, cb)", - "rmdirRecursiveSync": "function(path)", - "snapshotFile": Function snapshotFile(cwd, main, argv), - "stackUtils": Object { - "ignoredPackages": Array [ - "libtap", - "tap", - "nyc", - "import-jsx", - "function-loop", - ], - "internals": Array [], - "wrapCallSite": Function wrapCallSite(frame, state), - }, - "StackUtils": Function StackUtils(classStackUtils), -} -` diff --git a/typescript-vendor/tap/tap-snapshots/test/settings/long-stack.js.test.cjs b/typescript-vendor/tap/tap-snapshots/test/settings/long-stack.js.test.cjs deleted file mode 100644 index 9853aad23..000000000 --- a/typescript-vendor/tap/tap-snapshots/test/settings/long-stack.js.test.cjs +++ /dev/null @@ -1,26 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/settings/long-stack.js TAP > must match snapshot 1`] = ` -Object { - "atTap": true, - "mkdirpNeeded": false, - "mkdirRecursive": "function(path, cb)", - "mkdirRecursiveSync": "function(path)", - "output": true, - "rimrafNeeded": false, - "rmdirRecursive": "function(path, cb)", - "rmdirRecursiveSync": "function(path)", - "snapshotFile": Function snapshotFile(cwd, main, argv), - "stackUtils": Object { - "ignoredPackages": Array [], - "internals": Array [], - "wrapCallSite": Function wrapCallSite(frame, state), - }, - "StackUtils": Function StackUtils(classStackUtils), -} -` diff --git a/typescript-vendor/tap/tap-snapshots/test/synonyms.js.test.cjs b/typescript-vendor/tap/tap-snapshots/test/synonyms.js.test.cjs deleted file mode 100644 index 15cf896bf..000000000 --- a/typescript-vendor/tap/tap-snapshots/test/synonyms.js.test.cjs +++ /dev/null @@ -1,270 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/synonyms.js TAP > must match snapshot 1`] = ` -Object { - "doesNotThrow": Array [ - "doesNotThrow", - "doesnotthrow", - "does_not_throw", - "notThrow", - "notthrow", - "not_throw", - ], - "equal": Array [ - "equal", - "equals", - "isEqual", - "isequal", - "is_equal", - "is", - "strictEqual", - "strictequal", - "strict_equal", - "strictEquals", - "strictequals", - "strict_equals", - "strictIs", - "strictis", - "strict_is", - "isStrict", - "isstrict", - "is_strict", - "isStrictly", - "isstrictly", - "is_strictly", - "identical", - ], - "error": Array [ - "error", - "ifError", - "iferror", - "if_error", - "ifErr", - "iferr", - "if_err", - ], - "has": Array [ - "has", - "hasFields", - "hasfields", - "has_fields", - "includes", - "include", - "contains", - ], - "match": Array [ - "match", - "matches", - "similar", - "like", - "isLike", - "islike", - "is_like", - "isSimilar", - "issimilar", - "is_similar", - ], - "not": Array [ - "not", - "inequal", - "notEqual", - "notequal", - "not_equal", - "notEquals", - "notequals", - "not_equals", - "notStrictEqual", - "notstrictequal", - "not_strict_equal", - "notStrictEquals", - "notstrictequals", - "not_strict_equals", - "isNotEqual", - "isnotequal", - "is_not_equal", - "isNot", - "isnot", - "is_not", - "doesNotEqual", - "doesnotequal", - "does_not_equal", - "isInequal", - "isinequal", - "is_inequal", - ], - "notMatch": Array [ - "notMatch", - "notmatch", - "not_match", - "dissimilar", - "unsimilar", - "notSimilar", - "notsimilar", - "not_similar", - "unlike", - "isUnlike", - "isunlike", - "is_unlike", - "notLike", - "notlike", - "not_like", - "isNotLike", - "isnotlike", - "is_not_like", - "doesNotHave", - "doesnothave", - "does_not_have", - "isNotSimilar", - "isnotsimilar", - "is_not_similar", - "isDissimilar", - "isdissimilar", - "is_dissimilar", - ], - "notOk": Array [ - "notOk", - "notok", - "not_ok", - "false", - "assertNot", - "assertnot", - "assert_not", - ], - "notSame": Array [ - "notSame", - "notsame", - "not_same", - "inequivalent", - "looseInequal", - "looseinequal", - "loose_inequal", - "notDeep", - "notdeep", - "not_deep", - "deepInequal", - "deepinequal", - "deep_inequal", - "notLoose", - "notloose", - "not_loose", - "looseNot", - "loosenot", - "loose_not", - "notEquivalent", - "notequivalent", - "not_equivalent", - "isNotDeepEqual", - "isnotdeepequal", - "is_not_deep_equal", - "isNotDeeply", - "isnotdeeply", - "is_not_deeply", - "notDeepEqual", - "notdeepequal", - "not_deep_equal", - "isInequivalent", - "isinequivalent", - "is_inequivalent", - "isNotEquivalent", - "isnotequivalent", - "is_not_equivalent", - ], - "ok": Array [ - "ok", - "true", - "assert", - ], - "same": Array [ - "same", - "equivalent", - "looseEqual", - "looseequal", - "loose_equal", - "looseEquals", - "looseequals", - "loose_equals", - "deepEqual", - "deepequal", - "deep_equal", - "deepEquals", - "deepequals", - "deep_equals", - "isLoose", - "isloose", - "is_loose", - "looseIs", - "looseis", - "loose_is", - "isEquivalent", - "isequivalent", - "is_equivalent", - ], - "strictNotSame": Array [ - "strictNotSame", - "strictnotsame", - "strict_not_same", - "strictInequivalent", - "strictinequivalent", - "strict_inequivalent", - "strictDeepInequal", - "strictdeepinequal", - "strict_deep_inequal", - "notSameStrict", - "notsamestrict", - "not_same_strict", - "deepNot", - "deepnot", - "deep_not", - "notDeeply", - "notdeeply", - "not_deeply", - "strictDeepInequals", - "strictdeepinequals", - "strict_deep_inequals", - "notStrictSame", - "notstrictsame", - "not_strict_same", - ], - "strictSame": Array [ - "strictSame", - "strictsame", - "strict_same", - "strictEquivalent", - "strictequivalent", - "strict_equivalent", - "strictDeepEqual", - "strictdeepequal", - "strict_deep_equal", - "sameStrict", - "samestrict", - "same_strict", - "deepIs", - "deepis", - "deep_is", - "isDeeply", - "isdeeply", - "is_deeply", - "isDeep", - "isdeep", - "is_deep", - "strictDeepEquals", - "strictdeepequals", - "strict_deep_equals", - ], - "throws": Array [ - "throws", - "throw", - ], - "type": Array [ - "type", - "isA", - "isa", - "is_a", - ], -} -` diff --git a/typescript-vendor/tap/tap-snapshots/test/test.mjs.test.cjs b/typescript-vendor/tap/tap-snapshots/test/test.mjs.test.cjs deleted file mode 100644 index 397466da9..000000000 --- a/typescript-vendor/tap/tap-snapshots/test/test.mjs.test.cjs +++ /dev/null @@ -1,64 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/test.mjs TAP tap > must match snapshot 1`] = ` -Array [ - "Spawn", - "Stdin", - "Test", - "addAssert", - "afterEach", - "autoend", - "bailout", - "beforeEach", - "comment", - "default", - "doesNotThrow", - "emits", - "end", - "equal", - "error", - "expectUncaughtException", - "fail", - "fixture", - "hasStrict", - "main", - "match", - "matchSnapshot", - "not", - "notMatch", - "notOk", - "notSame", - "ok", - "only", - "pass", - "plan", - "pragma", - "process", - "processSubtest", - "rejects", - "resolveMatch", - "resolveMatchSnapshot", - "resolves", - "same", - "skip", - "spawn", - "stdin", - "stdinOnly", - "strictNotSame", - "strictSame", - "sub", - "teardown", - "test", - "testdir", - "throws", - "throwsArgs", - "timeout", - "todo", - "type", -] -` diff --git a/typescript-vendor/tap/tap-snapshots/test/watch.js.test.cjs b/typescript-vendor/tap/tap-snapshots/test/watch.js.test.cjs deleted file mode 100644 index 93269f0a0..000000000 --- a/typescript-vendor/tap/tap-snapshots/test/watch.js.test.cjs +++ /dev/null @@ -1,67 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/watch.js TAP run tests on changes change a file > logs 1`] = ` -initial test run -- {TAPDIR}/bin/run.js -- 1.test.js -- 2.test.js -- 3.test.js -- 4.test.js -- --watch -- --no-watch - -change ko.js -running tests -- 4.test.js - - -` - -exports[`test/watch.js TAP run tests on changes change a file > spawn test run on change 1`] = ` -4.test.js - -` - -exports[`test/watch.js TAP run tests on changes change a file mid-test > logs 1`] = ` -change 1.test.js -test in progress, queuing for next run -running tests -- 1.test.js - - -` - -exports[`test/watch.js TAP run tests on changes change a file mid-test > spawn queued test 1`] = ` -1.test.js - -` - -exports[`test/watch.js TAP run tests on changes initial test > logs 1`] = ` - -` - -exports[`test/watch.js TAP run tests on changes initial test > spawn initial test run 1`] = ` -false -` - -exports[`test/watch.js TAP run tests on changes new file added > log after spawn 1`] = ` -change new.js -running tests -- 3.test.js - - -` - -exports[`test/watch.js TAP run tests on changes new file added > logs 1`] = ` - -` - -exports[`test/watch.js TAP run tests on changes new file added > spawn test for new file 1`] = ` -3.test.js - -` diff --git a/typescript-vendor/tap/test/cb-promise.js b/typescript-vendor/tap/test/cb-promise.js deleted file mode 100644 index 8079cf2c5..000000000 --- a/typescript-vendor/tap/test/cb-promise.js +++ /dev/null @@ -1,13 +0,0 @@ -const t = require('../') -const cbPromise = require('../lib/cb-promise.js') -t.test('promise resolved when cb called', async t => { - const [cb, p] = cbPromise() - cb() - return p -}) -t.test('promise rejects when cb failed', t => { - const [cb, p] = cbPromise() - const poop = new Error('poop') - cb(poop) - return t.rejects(p, poop) -}) diff --git a/typescript-vendor/tap/test/clean-stacks.js b/typescript-vendor/tap/test/clean-stacks.js deleted file mode 100644 index 0b5bb9930..000000000 --- a/typescript-vendor/tap/test/clean-stacks.js +++ /dev/null @@ -1,76 +0,0 @@ -'use strict' - -// Just a utility to clean up the snapshots for output tests - -const yaml = require('tap-yaml') -const internals = Object.keys(process.binding('natives')) - -module.exports = out => out - // sort keys in yaml blocks - .replace(/\n( *)---\n((\1.*\n)*)\1\.\.\.\n/g, ($0, $1, $2) => { - let o - try { - o = yaml.parse($2) - } catch (er) { - return $0 - } - const out = Object.keys(o).sort().reduce((s, k) => { - if (k === 'requests' || k === 'handles') - o[k] = o[k].map(r => ({ type: r.type })) - s[k] = o[k] - return s - }, {}) - return '\n' + $1 + '---\n' + $1 + - yaml.stringify(out).trim().split('\n').join('\n' + $1) - + '\n' + $1 + '...\n' - }) - - // https://github.com/nodejs/node/issues/25806 - .replace(/ handles:\n - type: Timer\n/g, '') - - // this key has changed names - .replace(/FSReqWrap/g, 'FSReqCallback') - - // remove time details - .replace(/ # time=[0-9\.]+m?s( \{.*)?\n/g, ' # {time}$1\n') - .replace(/\n# time=.*/g, '\n# {time}') - - // debug output - .replace(/(\n|^)TAP ([0-9]+) /g, '$1TAP {pid} ') - - // stacks are always changing - .replace(/\n(( {2})+)stack: \|-?\n((\1 .*).*\n)+/gm, - '\n$1stack: |\n$1 {STACK}\n') - .replace(/\n(( {2})+)stack: \>-?\n((\1 .*).*\n(\1\n)?)+/gm, - '\n$1stack: |\n$1 {STACK}\n') - .replace(/(?:\n|^)([a-zA-Z]*Error): (.*)\n(( at .*\n)*)+/gm, - '\n$1: $2\n {STACK}\n') - .replace(/:[0-9]+:[0-9]+(\)?\n)/g, '#:#$1') - .replace(/(line|column): [0-9]+/g, '$1: #') - - // type and function change often between node/v8 versions - .replace(/\n( +)function: .*(\n\1 .*)*\n/g, '\n') - .replace(/\n( +)method: .*(\n\1 .*)*\n/g, '\n') - .replace(/\n( +)type: .*\n/g, '\n') - .replace(/\n( +)file: (.*)\n/g, ($0, $1, $2) => - internals.indexOf($2.replace(/\.js$/, '')) === -1 && - !/\bnode:\b/.test($2) ? $0 - : '\n' + $1 + 'file: #INTERNAL#\n' - ) - - // timeout values are different when coverage is present - .replace(/\n( *)timeout: (30000|240000)(\n|$)/g, '\n$1timeout: {default}$3') - - // fix references to cwd - .split(process.cwd()).join('{CWD}') - .split(require('path').resolve(__dirname, '..')).join('{TAPDIR}') - .split(process.execPath).join('{NODE}') - - .split(process.env.HOME).join('{HOME}') - - // the arrows in source printing bits, make that consistent - .replace(/^(\s*)-+\^$/mg, '$1--^') - -// nothing to see here -if (module === require.main) - console.log('TAP version 13\n1..1\nok - 1\n') diff --git a/typescript-vendor/tap/test/coverage-map.js b/typescript-vendor/tap/test/coverage-map.js deleted file mode 100644 index 5b8eec614..000000000 --- a/typescript-vendor/tap/test/coverage-map.js +++ /dev/null @@ -1,10 +0,0 @@ -const t = require('../') -const coverageMap = require('../coverage-map.js') -t.strictSame(coverageMap('test/coverage-map.js'), ['coverage-map.js']) -t.strictSame(coverageMap('test/run/xyz.js'), [ - 'bin/jack.js', - 'bin/jsx.js', - 'bin/run.js' -]) -t.strictSame(coverageMap('test/glorp.js'), null) -t.strictSame(coverageMap('test/mocha.js'), ['lib/mocha.js']) diff --git a/typescript-vendor/tap/test/mocha.js b/typescript-vendor/tap/test/mocha.js deleted file mode 100644 index 65f988aef..000000000 --- a/typescript-vendor/tap/test/mocha.js +++ /dev/null @@ -1,132 +0,0 @@ -'use strict' -const {mocha} = require('../lib/tap.js') -const assert = require('assert') - -mocha.describe('globals', () => { - let beforeEaches = 0 - mocha.beforeEach(() => beforeEaches++) - - mocha.beforeEach(cb => setTimeout(cb)) - mocha.beforeEach(() => new Promise(r => r())) - - let afterEaches = 0 - mocha.afterEach(() => afterEaches++) - - // test that afterEach is happening correct number - // of times. - let eachExpect = 0 - mocha.afterEach(() => new Promise(res => { - eachExpect ++ - assert.equal(beforeEaches, eachExpect, 'before') - assert.equal(afterEaches, eachExpect, 'after') - res() - })) - - mocha.it('has no describe', () => - assert.equal(global.describe, undefined)) - - mocha.it('is ok running deglobal() first', () => { - mocha.deglobal() - assert.equal(global.describe, undefined) - }) - - mocha.it('has describe after call', () => { - mocha.global() - mocha.global() - assert.equal(global.describe, mocha.describe) - }) - - mocha.it('has no describe after deglobal', () => { - deglobal() - assert.equal(global.describe, undefined) - }) - - mocha.it('escape to tap', function () { - const t = this - t.test('should not get a beforeEach', t => - t.test('or an after each', t => { - t.pass('this is fine') - t.end() - })) - }) - - // at this point, beforeEach has been called - // 1 more time than afterEach - mocha.it('called beforeEach/afterEach', () => - new Promise((resolve) => { - assert.equal(beforeEaches, eachExpect + 1) - assert.equal(afterEaches, eachExpect) - resolve() - })) -}) - -if (process.version.match(/v[0-9]\./)) { - assert.throws(_ => mocha.after(), - 'cannot call "after" outside of describe()') - assert.throws(_ => mocha.before(), - 'cannot call "before" outside of describe()') -} else { - assert.throws(_ => mocha.after(), - new Error('cannot call "after" outside of describe()')) - assert.throws(_ => mocha.before(), - new Error('cannot call "before" outside of describe()')) -} - -let calledAfter = false -let calledBefore = false -mocha.describe(function after_and_before () { - mocha.before((cb) => { - assert.equal(calledBefore, false) - calledBefore = true - setTimeout(cb) - }) - mocha.before('named before', () => new Promise(r => { - assert.equal(calledBefore, true) - r() - })) - - mocha.after(() => { - assert.equal(calledAfter, false) - calledAfter = true - }) - - mocha.after('named after', () => { - assert.equal(calledAfter, true) - }) - - mocha.after(function named_after () { - assert.equal(calledAfter, true) - }) - - mocha.it(function this_is_fine () {}) - mocha.it(() => {}) - mocha.it(cb => cb()) -}) -mocha.describe('after after', function () { - this.test.plan(1) - mocha.it('should have called after fn', () => - assert.equal(calledAfter, true)) -}) - -mocha.describe('todo, skip, and failure', () => { - let calledTodoFn = false - let calledSkipFn = false - const it = mocha.it - mocha.describe('expect todo and skip', function () { - /* istanbul ignore next */ - it.todo('expected todo', () => calledTodoFn = true) - /* istanbul ignore next */ - it.skip('expected skip', () => calledSkipFn = true) - }, { silent: true }) - it('expected fail from cb(er)', cb => { - cb(new Error('expected failure')) - }, { expectFail: true }) - it('did not call skip/todo functions', () => { - assert.equal(calledTodoFn, false) - assert.equal(calledSkipFn, false) - }) -}) - -mocha.describe('expected before failure', () => - mocha.before('expect failure', (cb) => - cb(new Error('expected')), { expectFail : true })) diff --git a/typescript-vendor/tap/test/regression-many-asserts-epipe.js b/typescript-vendor/tap/test/regression-many-asserts-epipe.js deleted file mode 100644 index 2d4290cf2..000000000 --- a/typescript-vendor/tap/test/regression-many-asserts-epipe.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict' -// See https://github.com/tapjs/node-tap/issues/422 -const t = require('../') -t.test('just a lot of asserts in rapid succession', t => { - for (let i = 0; i < 5000; i++) { - t.pass('a number is ' + i) - } - t.end() -}) diff --git a/typescript-vendor/tap/test/regression-pipe-backup.js b/typescript-vendor/tap/test/regression-pipe-backup.js deleted file mode 100644 index fc37f7ada..000000000 --- a/typescript-vendor/tap/test/regression-pipe-backup.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict' - -// https://github.com/tapjs/node-tap/pull/506 - -const t = require('../') - -t.plan(5000) -for (let i = 1; i <= 5000; i++) { - t.pass(i) -} diff --git a/typescript-vendor/tap/test/repl.js b/typescript-vendor/tap/test/repl.js deleted file mode 100644 index d30063ba0..000000000 --- a/typescript-vendor/tap/test/repl.js +++ /dev/null @@ -1,242 +0,0 @@ -const EE = require('events') -const fakeWatch = Object.assign(new EE(), { - args: ['tap'], - positionals: [], - proc: null, - fileList: [], - queue: [], - saveFile: 'filename', - kill (signal) { - this.proc && this.proc.emit('close', null, signal) - }, - watcher: {}, - pause () { - this.emit('pause') - this.watcher = null - }, - resume () { - this.emit('resume') - this.watcher = {} - }, - run () { - if (this.proc) - throw new Error('calling run while already with a proc') - this.proc = new EE() - this.proc.once('close', (code, signal) => { - this.proc = null - this.emit('afterProcess', {code, signal}) - }) - setTimeout(() => this.proc && this.proc.emit('close', 0, null)) - }, - main () { - this.watcher = true - this.emit('main') - }, - env: {}, -}) - -const fs = require('fs') -function FakeWatch () { - return fakeWatch -} - -const Minipass = require('minipass') - -require('../lib/watch.js').Watch = FakeWatch - -const t = require('../') - -const originalCwd = process.cwd() -const rimraf = require('rimraf').sync - -const dir = 'repl-test' -const mkdirp = require('mkdirp') -mkdirp.sync(dir) -process.chdir(dir) -t.teardown(() => { - process.chdir(originalCwd) - rimraf(dir) -}) - -const {Repl} = require('../lib/repl.js') - -const input = new Minipass({encoding: 'utf8'}) -const {PassThrough} = require('stream') -const output = new Minipass({encoding: 'utf8'}) -const repl = new Repl({}, input, output) - -// save into node_modules/.cache instead of ~ -process.env.HOME = '' - -t.test('start on main event', t => { - t.equal(repl.repl, null) - fakeWatch.emit('main') - t.ok(repl.repl) - t.notOk(repl.running) - t.end() -}) - -t.test('show help', t => { - input.write('help\n') - setTimeout(() => { - t.matchSnapshot(output.read(), 'output') - t.end() - }) -}) - -t.test('save/load history', t => { - repl.saveHistory() - const hist = 'node_modules/.cache/tap/.tap_repl_history' - t.matchSnapshot(fs.readFileSync(hist, 'utf8'), 'history file') - t.matchSnapshot(repl.loadHistory(), 'load history') - const rfs = fs.readFileSync - fs.readFileSync = () => { - throw new Error('read fail') - } - const wfs = fs.writeFileSync - fs.writeFileSync = () => { - throw new Error('write fail') - } - t.same(repl.loadHistory(), [], 'empty array if read fails') - t.doesNotThrow(() => repl.saveHistory(), 'save ok if write fails') - fs.readFileSync = rfs - fs.writeFileSync = wfs - t.end() -}) - -t.test('run on change', t => { - t.notOk(repl.running) - fakeWatch.run() - t.ok(repl.running) - // try to run, but please wait - input.write('r\n') - fakeWatch.once('afterProcess', () => process.nextTick(() => { - t.matchSnapshot(output.read(), 'ran the suite on change') - t.end() - })) -}) - -t.test('kill process', t => { - fakeWatch.run() - // try to run, but please wait - fakeWatch.once('afterProcess', () => process.nextTick(() => { - t.matchSnapshot(output.read(), 'killed process') - t.end() - })) - repl.repl.emit('SIGINT') -}) - -t.test('manual run tests', t => { - input.write('r\n') - t.ok(repl.running) - fakeWatch.once('afterProcess', () => process.nextTick(() => { - t.matchSnapshot(output.read(), 'ran the suite again') - t.end() - })) -}) - -t.test('add test', t => { - fakeWatch.positionals.push('foo.js') - input.write('r bar.js\n') - t.same(fakeWatch.positionals, ['foo.js', 'bar.js']) - t.same(fakeWatch.queue, ['bar.js']) - fakeWatch.kill('fake') - fakeWatch.queue.length = 0 - fakeWatch.positionals.length = 0 - input.write('r bar.js\n') - t.same(fakeWatch.positionals, []) - t.same(fakeWatch.queue, ['bar.js']) - fakeWatch.args = ['tap'] - fakeWatch.kill('fake') - t.end() -}) - -t.test('pause/resume', t => { - let sawPause = false - fakeWatch.once('pause', () => sawPause = true) - let sawResume = false - fakeWatch.once('resume', () => sawResume = true) - - input.write('p\n') - input.write('p\n') - t.matchSnapshot(output.read(), 'output') - t.same([sawPause, sawResume], [true, true]) - t.end() -}) - -t.test('update', t => { - input.write('u\n') - t.same(fakeWatch.env, { TAP_SNAPSHOT: '1' }) - fakeWatch.once('afterProcess', () => process.nextTick(() => { - output.read() - t.same(fakeWatch.env, {}) - t.end() - })) -}) - -t.test('changed', t => { - input.write('n\n') - t.same(fakeWatch.args, ['tap', '--changed']) - fakeWatch.once('afterProcess', () => process.nextTick(() => { - output.read() - t.same(fakeWatch.args, ['tap']) - t.end() - })) -}) - -t.test('coverage report', t => { - input.write('c\n') - t.same(fakeWatch.args, ['tap', '--coverage-report=text']) - fakeWatch.once('afterProcess', () => process.nextTick(() => { - output.read() - t.same(fakeWatch.args, ['tap']) - t.end() - })) -}) - -t.test('clear', t => { - mkdirp.sync('.nyc_output') - input.write('clear\n') - t.notOk(fs.existsSync('.nyc_output')) - fakeWatch.once('afterProcess', () => process.nextTick(() => { - output.read() - t.end() - })) -}) - -t.test('completer', t => { - mkdirp.sync('test/foo') - mkdirp.sync('temp/orary') - fs.writeFileSync('test/foo/bar.js', '') - fs.writeFileSync('test/follow.js', '') - const tests = [ - '', - 'ex', - 'r te', - 'r tes', - 'r test/', - 'r test/fo', - 'r test/foo', - 'r test/fol', - 'r test/blerg', - 'u bl/erg', - ] - tests.forEach(test => - t.matchSnapshot(repl.completer(test), test || 'empty')) - t.end() -}) - -t.test('cls', t => { - input.write('cls\n') - setTimeout(() => { - // JSON.stringify it so that it doesn't look gross in patches - t.matchSnapshot(JSON.stringify(output.read()), 'clear screen') - t.end() - }) -}) - -t.test('exit', t => { - repl.repl.emit('SIGINT') - t.matchSnapshot(output.read(), 'output') - t.end() -}) diff --git a/typescript-vendor/tap/test/run/bad-rcfile.js b/typescript-vendor/tap/test/run/bad-rcfile.js deleted file mode 100644 index 45fb4ed7b..000000000 --- a/typescript-vendor/tap/test/run/bad-rcfile.js +++ /dev/null @@ -1,16 +0,0 @@ -const { - tmpfile, - run, - tap, - dir, - t, -} = require('./') - -const fs = require('fs') -t.test('bad rc file', t => { - fs.writeFileSync(`${dir}/.taprc`, 'this : is not : valid : yaml') - run(['--dump-config'], { cwd: dir }, (er, o, e) => { - t.match(er, { code: 1 }) - t.end() - }) -}) diff --git a/typescript-vendor/tap/test/run/basic.js b/typescript-vendor/tap/test/run/basic.js deleted file mode 100644 index bd2885ca2..000000000 --- a/typescript-vendor/tap/test/run/basic.js +++ /dev/null @@ -1,143 +0,0 @@ -const fs = require('fs') -const mkdirp = require('mkdirp') -const { - tmpfile, - run, - bin, - tap, - node, - dir, - t, -} = require('./') - -if (process.env.TAP_SNAPSHOT !== '1') - t.jobs = require('os').cpus.length - -t.test('no args', t => { - const c = run([], { - cwd: dir, - }, (er, o, e) => { - t.match(er, { code: 1 }) - t.match(o, /no tests specified/) - t.equal(e, '') - t.end() - }) -}) - -t.test('stdin parsing', t => { - const c = run(['-'], {}, (er, o, e) => { - t.match(er, null) - t.equal(e, '') - t.end() - }) - c.stdin.end('TAP version 13\n1..1\nok\n') -}) - -t.test('--help', t => { - run(['--help'], null, (er, o, e) => { - t.equal(er, null) - t.match(o, /^Usage:/) - t.end() - }) -}) - -t.test('--nyc-help', t => { - run(['--nyc-help'], null, (er, o, e) => { - t.equal(er, null) - t.match(o, /\nOptions:\n/) - t.end() - }) -}) - -t.test('--version', t => { - run(['--version'], null, (er, o, e) => { - t.equal(er, null) - t.equal(o.trim(), require('../../package.json').version) - t.end() - }) -}) - -t.test('--versions', t => { - run(['--versions'], null, (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o.replace(/^([^:]+): (.*)$/gm, '$1: {version}'), 'output') - t.end() - }) -}) - -t.test('--parser-version', t => { - run(['--parser-version'], null, (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o, 'output') - t.end() - }) -}) - -t.test('--nyc-version', t => { - run(['--nyc-version'], null, (er, o, e) => { - t.equal(er, null) - t.equal(o.trim(), require('nyc/package.json').version) - t.end() - }) -}) - -t.test('unknown arg throws', t => { - run(['--blerg'], null, (er, o, e) => { - t.match(er, { code: 1 }) - t.match(e, 'invalid argument: --blerg') - t.end() - }) -}) - -t.test('unknown short opt', t => { - run(['-bCx'], null, (er, o, e) => { - t.match(er, { code: 1 }) - t.match(e, 'invalid argument: -x') - t.end() - }) -}) - -t.test('basic test run', t => { - const ok = tmpfile(t, 'ok.js', `require(${tap}).pass('this is fine')`) - const args = ['-iSCbFt0', '-g/nope/i', '--', ok] - run(args, null, (err, stdout, stderr) => { - t.matchSnapshot(stdout, 'ok.js output') - t.end() - }) -}) - -t.test('ignored files', t => { - mkdirp.sync(`${dir}/ig/test/node_modules`) - mkdirp.sync(`${dir}/ig/node_modules`) - const ok = tmpfile(t, 'ig/test/ok.js', - `require(${tap}).pass('this is fine')`) - const nope = tmpfile(t, 'ig/node_modules/nope.test.js', - `require(${tap}).fail('i should not be included')`) - const nope2 = tmpfile(t, 'ig/test/node_modules/nope.test.js', - `require(${tap}).fail('should also not be included')`) - tmpfile(t, 'ig/test/node_modules/foo.test.js', - `require(${tap}).fail('no foo included')`) - tmpfile(t, 'ig/test/foo.test.js', - `require(${tap}).fail('no foo included')`) - tmpfile(t, 'ig/foo.test.js', - `require(${tap}).fail('no foo included')`) - - const args = ['--test-ignore=foo\\.test\\.js$'] - const env = {} - const cwd = dir + '/ig' - run([args], { cwd, env }, (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o, 'stdout') - t.matchSnapshot(e, 'stdout') - t.end() - }) -}) - -t.test('nonexistent file', t => { - run(['does not exist'], null, (er, o, e) => { - t.match(er, { code: 1 }) - t.matchSnapshot(o, 'stdout') - t.matchSnapshot(e, 'stderr') - t.end() - }) -}) diff --git a/typescript-vendor/tap/test/run/before-after.js b/typescript-vendor/tap/test/run/before-after.js deleted file mode 100644 index c4fe91e8e..000000000 --- a/typescript-vendor/tap/test/run/before-after.js +++ /dev/null @@ -1,84 +0,0 @@ -const { - tmpfile, - run, - tap, - t, -} = require('./') - -const ok = tmpfile(t, 'ok.js', `console.log('ok')`) -const fail = tmpfile(t, 'fail.js', ` - throw new Error('fail') -`) -const slow = tmpfile(t, 'slow.js', `setTimeout(() => console.log('slow'))`) -const slowFail = tmpfile(t, 'slow-fail.js', `setTimeout(() => { - throw new Error('slow fail') -})`) - -const sigfail = tmpfile(t, 'sigfail.js', ` -process.kill(process.pid, 'SIGKILL') -setTimeout(() => {}, 1000000) -`) - -const t1 = tmpfile(t, 't1.js', `const t = require(${tap}) -t.pass('this is fine') -`) -const t2 = tmpfile(t, 't2.js', `const t = require(${tap}) -t.test('sub', async t => t.pass('this is fine')) -`) -const t3 = tmpfile(t, 't3.js', `const t = require(${tap}) -t.test('sub', async t => t.fail('not fine')) -`) - -t.test('basic', t => { - t.plan(3) - run([`--before=${slow}`, `--after=${ok}`, ok, slow, t1, t2, t3], {}, (er, o, e) => { - t.ok(er, 'error') - t.matchSnapshot(o, 'stdout') - t.matchSnapshot(e, 'stderr') - }) -}) - -t.test('failing before', t => { - t.plan(3) - run([`--before=${fail}`, t1, t2, t3, fail], {}, (er, o, e) => { - t.ok(er, 'error') - t.matchSnapshot(o, 'stdout') - t.matchSnapshot(e, 'stderr') - }) -}) - -t.test('failing after', t => { - t.plan(3) - run([`--after=${fail}`, t1, fail], {}, (er, o, e) => { - t.ok(er, 'error') - t.matchSnapshot(o, 'stdout') - t.matchSnapshot(e, 'stderr') - }) -}) - -t.test('slow fail before', t => { - t.plan(3) - run([`--before=${slowFail}`, t1, t2, t3], {}, (er, o, e) => { - t.ok(er, 'error') - t.matchSnapshot(o, 'stdout') - t.matchSnapshot(e, 'stderr') - }) -}) - -t.test('signal fail after', t => { - t.plan(3) - run([`--before=${sigfail}`, t1, t2, t3], {}, (er, o, e) => { - t.ok(er, 'error') - t.matchSnapshot(o, 'stdout') - t.matchSnapshot(e, 'stderr') - }) -}) - -t.test('run after even on bailout', t => { - t.plan(3) - run(['--bail', `--after=${ok}`, t1, t2, t3], {}, (er, o, e) => { - t.ok(er, 'error') - t.matchSnapshot(o, 'stdout') - t.matchSnapshot(e, 'stderr') - }) -}) diff --git a/typescript-vendor/tap/test/run/cat.js b/typescript-vendor/tap/test/run/cat.js deleted file mode 100644 index a387d897d..000000000 --- a/typescript-vendor/tap/test/run/cat.js +++ /dev/null @@ -1,19 +0,0 @@ -const { - tmpfile, - run, - tap, - t, -} = require('./') - -t.test('cat', t => { - const ok = tmpfile(t, 'ts/ok.tap', ` - TAP version 13 - 1..1 - ok 1 - this is fine - `) - run([ok], {}, (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o) - t.end() - }) -}) diff --git a/typescript-vendor/tap/test/run/changed.js b/typescript-vendor/tap/test/run/changed.js deleted file mode 100644 index 3430a7f1a..000000000 --- a/typescript-vendor/tap/test/run/changed.js +++ /dev/null @@ -1,145 +0,0 @@ -const { - tmpfile, - tap, - t, - dir, - bin, -} = require('./') - -const {getChangedFilter, filterFiles} = require(bin) -const fs = require('fs') - -const rewrite = file => fs.writeFileSync(file, fs.readFileSync(file)) - -const mkdirp = require('mkdirp') -mkdirp.sync(dir) -const cwd = process.cwd() -process.chdir(dir) -t.teardown(() => process.chdir(cwd)) - -const {resolve} = require('path') - -const oktest = tmpfile(t, 'ok.test.js', ` -const t = require(${tap}) -t.match(require('./ok.js), { ok: true }) -`) - -const oktest2 = tmpfile(t, 'ok2.test.js', ` -const t = require(${tap}) -t.pass('just ok, no files loaded') -`) - -const oktest3 = tmpfile(t, 'ok3.test.js', ` -const t = require(${tap}) -t.pass('just ok, not tracked in index, though') -`) - -const okjs = tmpfile(t, 'ok.js', `module.exports = { - ok: true, - message: 'this is ok', -}`) - -t.test('allow everything when not using -n', t => { - const filter = getChangedFilter({ changed: false }) - t.ok(filter(), 'no --changed, run everything') - t.end() -}) - -t.test('require coverage', t => { - t.throws(() => getChangedFilter({changed: true}), { - message: '--changed requires coverage to be enabled' - }) - t.end() -}) - -t.test('no index file means we let everything through', t => { - const filter = getChangedFilter({ - changed: true, - coverage: true, - }) - t.ok(filter(), 'no index, let it all through') - t.end() -}) - -t.test('with a real index', t => { - mkdirp.sync('.nyc_output/processinfo') - const pauseLength = 10 - const index = '.nyc_output/processinfo/index.json' - const indexData = { - files: { - [okjs]: [ - 'test root', - ], - }, - externalIds: { - [oktest]: { - root: 'test root', - children: [ - 'test child 1', - 'test child 2', - ] - }, - [oktest2]: { - root: 'irrelevant test', - children: [], - }, - }, - } - fs.writeFileSync(index, JSON.stringify(indexData)) - - let filter = getChangedFilter({ - changed: true, - coverage: true, - }) - - t.same([ - filter(oktest), - filter(oktest2), - ], [false, false], 'should not run any tests, brand new index') - - t.ok(filter(oktest3), 'will run new test not previously run') - - { const until = Date.now() + pauseLength; while (Date.now() < until); } - rewrite(oktest2) - - t.same([ - filter(oktest), - filter(oktest2), - ], [false, true], 're-run a test when it changes') - - rewrite(index) - // need a new filter, because indexDate is cached - filter = getChangedFilter({ - changed: true, - coverage: true, - }) - { const until = Date.now() + pauseLength; while (Date.now() < until); } - rewrite(okjs) - - t.same([ - filter(oktest), - filter(oktest2), - ], [true, false], 're-run a test when covered file changes, root') - - indexData.files[okjs] = ['test child 1'] - fs.writeFileSync(index, JSON.stringify(indexData)) - filter = getChangedFilter({ - changed: true, - coverage: true, - }) - { const until = Date.now() + pauseLength; while (Date.now() < until); } - rewrite(okjs) - - t.same([ - filter(oktest), - filter(oktest2), - ], [true, false], 're-run a test when covered file changes, children') - - const filteredFiles = filterFiles([oktest, oktest2, oktest3], { - changed: true, - changedFilter: filter, - }, {}) - t.same(filteredFiles, [ oktest, oktest3 ], 'filtered files') - - t.end() -}) diff --git a/typescript-vendor/tap/test/run/comments.js b/typescript-vendor/tap/test/run/comments.js deleted file mode 100644 index 01cf7b1db..000000000 --- a/typescript-vendor/tap/test/run/comments.js +++ /dev/null @@ -1,24 +0,0 @@ -const { - tmpfile, - run, - tap, - t, - clean, -} = require('./') - -const ok = tmpfile(t, 'comments/ok.js', `'use strict' - const t = require(${tap}) - t.comment('root') - t.test('parent', t => { - t.comment('parent') - t.test('child', t => { - t.comment('child') - t.end() - }) - t.end() - }) -`) - -t.plan(1) -run(['--comments', ok], {}, (er, o, e) => - t.equal(clean(e), 'root\nparent\nchild\n')) diff --git a/typescript-vendor/tap/test/run/coverage.js b/typescript-vendor/tap/test/run/coverage.js deleted file mode 100644 index ed43ca9e1..000000000 --- a/typescript-vendor/tap/test/run/coverage.js +++ /dev/null @@ -1,151 +0,0 @@ -const { - escapeNYC, - tmpfile, - bin, - tap, - node, - dir, - t, - winSkip, -} = require('./') - -const { execFile } = require('child_process') -const path = require('path') - -const ok = tmpfile(t, 'ok.js', `'use strict' - module.exports = (x, y) => { - if (x) - return y || x - else - return y - }`) - -const t1 = tmpfile(t, '1.test.js', `'use strict' - const ok = require('./ok.js') - require(${tap}).equal(ok(1), 1)`) - -const t2 = tmpfile(t, '2.test.js', `'use strict' - const ok = require('./ok.js') - require(${tap}).equal(ok(1, 2), 2)`) - -const t3 = tmpfile(t, '3.test.js', `'use strict' - const ok = require('./ok.js') - require(${tap}).equal(ok(0, 3), 3)`) - -escapeNYC() - -const escapePath = `${path.dirname(process.execPath)}:${process.env.PATH}` -const esc = tmpfile(t, 'runtest.sh', -`#!/bin/bash -export PATH=${escapePath} -"${node}" "${bin}" "\$@" \\ - --cov \\ - --nyc-arg=--temp-dir="${dir}/.nyc_output" \\ - --nyc-arg=--cache=false -`) - -// convince nyc this is our new home -tmpfile(t, 'package.json', JSON.stringify({ - name: 'escape-from-new-york', - nyc: { - include: 'ok.js' - } -})) - -const escape = (args, options, cb) => { - options = options || {} - options.cwd = dir - const env = Object.keys(process.env).filter( - k => !/^TAP|NYC|SW_ORIG|PWD/.test(k) - ).reduce((env, k) => { - if (!env.hasOwnProperty(k)) - env[k] = process.env[k] - return env - }, options.env || {}) - options.env = env - const a = [path.basename(esc)].concat( - args.map(a => path.basename(a))) - return execFile('bash', a, options, cb) -} - -t.test('generate some coverage', t => { - escape([t1, t2, '--no-check-coverage'], null, (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o, 'output') - t.end() - }) -}) - -t.test('use a coverage map', t => { - const map = tmpfile(t, 'coverage-map.js', ` -module.exports = () => 'ok.js' -`) - escape(['--no-check-coverage', t1, t2, '-M', map], null, (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o, 'output') - t.end() - }) -}) - -t.test('report only', t => { - escape(['--no-check-coverage', '--coverage-report=text-lcov'], null, (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o, 'lcov output', { skip: winSkip }) - t.end() - }) -}) - -t.test('report with checks', t => { - escape(['--100', '--coverage-report=text-lcov'], null, (er, o, e) => { - t.match(er, { code: 1 }) - t.matchSnapshot(o, 'lcov output and 100 check', { skip: winSkip }) - t.end() - }) -}) - -t.test('in 100 mode, <100 is red, not yellow', t => { - escape(['--100', '--coverage-report=text', '--color'], null, (er, o, e) => { - t.match(er, { code: 1 }) - t.matchSnapshot(o, 'text output and 100 check', { skip: winSkip }) - t.end() - }) -}) - -t.test('pipe to service', t => { - const piper = tmpfile(t, 'piper.js', ` - process.stdin.pipe(process.stderr) - `) - escape(['--no-check-coverage', '--coverage-report=text'], { env: { - __TAP_COVERALLS_TEST__: 'piper.js', - }}, (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(e, 'piped to coverage service cat', { skip: winSkip }) - t.matchSnapshot(o, 'human output', { skip: winSkip }) - t.end() - }) -}) - -t.test('pipe to service along with tests', t => { - const piper = tmpfile(t, 'piper.js', ` - process.stdin.pipe(process.stderr) - `) - escape(['--no-check-coverage', t1, t2, '--coverage-report=text'], { env: { - __TAP_COVERALLS_TEST__: 'piper.js', - }}, (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(e, 'piped to coverage service cat', { skip: winSkip }) - t.matchSnapshot(o, 'human output', { skip: winSkip }) - t.end() - }) -}) - -t.test('borked coverage map means no includes', t => { - const map = tmpfile(t, 'coverage-map.js', ` -module.exports = () => {} -`) - escape([t1, t2, '-M', map], null, (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o, 'output') - t.end() - }) -}) diff --git a/typescript-vendor/tap/test/run/dump-config.js b/typescript-vendor/tap/test/run/dump-config.js deleted file mode 100644 index 8bd774d18..000000000 --- a/typescript-vendor/tap/test/run/dump-config.js +++ /dev/null @@ -1,115 +0,0 @@ -const { - tmpfile, - run, - t, - clean, -} = require('./') - -t.cleanSnapshot = o => clean(o).replace(/jobs: \d+/, 'jobs: {number}') - -t.test('shotgun a bunch of option parsing junk', t => { - run([ - '--dump-config', '-J', '--jobs', '4', - '--no-browser', '--no-coverage-report', '--coverage-report', 'json', - '--coverage-report=html', '--no-cov', '--cov', '--save', 'foo.txt', - '--reporter=spec', '--gc', '--strict', '--debug', '--debug-brk', - '--harmony', '--node-arg=xyz', '--check-coverage', '--test-arg=xyz', - '--test-arg', 'abc', '--100', '--branches=99', '--lines', '100', - '--color', '-C', '--output-file=out.txt', '--no-timeout', - '--timeout', '99', '--invert', '--no-invert', '--grep', 'x', - '--grep=/y/i', '--bail', '--no-bail', '--only', '-R', 'spec', - '--node-arg', 'abc', '--nyc-arg', 'abc', '-o', 'out.txt', - '--comments', '-M', 'map.js', '--no-coverage-map' - ], { env: { - TAP: '0', - TAP_BAIL: '0', - }}, (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o, 'output') - t.end() - }) -}) - -t.test('package.json parsing', t => { - const cases = { - good: JSON.stringify({ - tap: { - 100: true, - bail: true, - } - }), - bad: '!$@Q$AERWA#WERSTE$%W', - missing: JSON.stringify({ - foo: { - lines: 69, - bail: true, - } - }) - } - for (const c in cases) { - t.test(c, t => { - const data = cases[c] - const pj = tmpfile(t, 'package.json', data) - const path = require('path') - const dir = path.dirname(pj) - run(['--dump-config', '-B'], { - cwd: dir, - }, (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o, 'output') - t.end() - }) - }) - } - t.end() -}) - -t.test('turn color off and back on again', t => { - run(['--no-color', '-c', '--dump-config'], { env: { - TAP: '0', - TAP_COLORS: '1', - }}, (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o, 'output') - t.end() - }) -}) - -t.test('short options as well as short flags', t => { - run(['--dump-config','-j2','-Cb','-t=0' ], { env: { - TAP: '0' - }}, (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o, 'output') - t.end() - }) -}) - -t.test('good rc file', t => { - const rc = tmpfile(t, 'taprc', ` -reporter: spec -jobs: 3 -100: true -`) - run(['--dump-config', '-j4'], { env: { - TAP_RCFILE: rc, - TAP: 0 - }}, (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o, 'output') - t.end() - }) -}) - -t.test('empty rc file', t => { - const rc = tmpfile(t, 'taprc', '') - run(['--dump-config', '-c'], { env: { - TAP_RCFILE: rc, - TAP: '0', - TAP_COLORS: '1' - }}, (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o, 'output') - t.end() - }) -}) diff --git a/typescript-vendor/tap/test/run/env.js b/typescript-vendor/tap/test/run/env.js deleted file mode 100644 index eb54fb9f2..000000000 --- a/typescript-vendor/tap/test/run/env.js +++ /dev/null @@ -1,21 +0,0 @@ -const { - tmpfile, - run, - tap, - t, - clean, -} = require('./') - -const ok = tmpfile(t, 'ok.js', ` - const t = require(${tap}) - t.equal(process.env.glorp, 'foo') - t.equal(process.env.USER, undefined) - t.equal(process.env.TERM, '') -`) - -t.plan(3) -run(['--test-env=USER', '--test-env=TERM=', '--test-env=glorp=foo', ok], {}, (er, o, e) => { - t.notOk(er) - t.equal(clean(e), '') - t.matchSnapshot(o) -}) diff --git a/typescript-vendor/tap/test/run/epipe-stdout.js b/typescript-vendor/tap/test/run/epipe-stdout.js deleted file mode 100644 index 88231ade4..000000000 --- a/typescript-vendor/tap/test/run/epipe-stdout.js +++ /dev/null @@ -1,22 +0,0 @@ -const { - run, - t, -} = require('./') - -t.plan(2) -const c = run(['-', '-C'], { stdio: 'pipe' }, (er, o, e) => { - t.equal(er, null) - t.equal(o, str) -}) -// comes in two chunks, because it's going through a parser -const str = 'TAP version 13\n1..9\nok\n' -let seen = '' -c.stdin.write(str) -c.stdout.on('data', chunk => { - seen += chunk - if (seen.length >= str.length) { - c.stdout.destroy() - c.stdin.write('ok\nok\nok\nok\n') - c.stdin.write('ok\nok\nok\nok\n') - } -}) diff --git a/typescript-vendor/tap/test/run/executables.js b/typescript-vendor/tap/test/run/executables.js deleted file mode 100644 index f63cfb7bb..000000000 --- a/typescript-vendor/tap/test/run/executables.js +++ /dev/null @@ -1,30 +0,0 @@ -const { - tmpfile, - run, - dir, - t, -} = require('./') -const fs = require('fs') - -t.test('executables', { - todo: process.platform === 'win32' ? - 'port the shell scripts to equivalent CMD files' : false -}, t => { - const ok = tmpfile(t, 'exe/ok.sh', `#!/bin/sh - echo 1..1 - echo ok 1 File with executable bit should be executed - `) - fs.chmodSync(ok, 0o755) - const notok = tmpfile(t, 'exe/notok.sh', `!#/bin/sh - echo 1..1 - echo not ok 1 File without executable bit should not be run - exit 1 - `) - fs.chmodSync(notok, 0o644) - run(['exe', '-C'], { cwd: dir }, (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o) - t.equal(e, '') - t.end() - }) -}) diff --git a/typescript-vendor/tap/test/run/files.js b/typescript-vendor/tap/test/run/files.js deleted file mode 100644 index c44111347..000000000 --- a/typescript-vendor/tap/test/run/files.js +++ /dev/null @@ -1,41 +0,0 @@ -const { - tmpfile, - run, - tap, - t, - clean, -} = require('./') -const yaml = require('tap-yaml') - -const one = tmpfile(t, 'one.js', ` - const t = require(${tap}) - t.pass('one') -`) - -const two = tmpfile(t, 'two.js', ` - const t = require(${tap}) - t.pass('two') -`) - -const three = tmpfile(t, 'two.js', ` - const t = require(${tap}) - t.pass('three') -`) - -t.test('--files work like explicit positional argument', t => { - t.plan(3) - run([`--files=${one}`, `--files=${two}`], {}, (er, o, e) => { - t.notOk(er) - t.equal(clean(e), '') - t.matchSnapshot(o) - }) -}) - -t.test('--files do not override explicit positional argument', t => { - t.plan(3) - run([`--files=${one}`, `--files=${two}`, three], {}, (er, o, e) => { - t.notOk(er) - t.equal(clean(e), '') - t.matchSnapshot(o) - }) -}) diff --git a/typescript-vendor/tap/test/run/flow.js b/typescript-vendor/tap/test/run/flow.js deleted file mode 100644 index 48e681b31..000000000 --- a/typescript-vendor/tap/test/run/flow.js +++ /dev/null @@ -1,45 +0,0 @@ -const { - tmpfile, - run, - tap, - t, -} = require('./') - - -t.test('flow', t => { - const ok = tmpfile(t, 'flow/ok.js', ` - // @flow - const t = require(${tap}) - function square(n: number): number { - return n * n; - } - t.pass('this is fine') - `) - - const args = [ok, '--flow'] - - run(args, {}, (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o) - t.end() - }) -}) - -t.test('flow manually', t => { - const ok = tmpfile(t, 'flow/ok2.js', ` - // @flow - const t = require(${tap}) - function square(n: number): number { - return n * n; - } - t.pass('this is fine') - `) - - const args = [ok, '--node-arg=--require', '--node-arg=flow-remove-types/register'] - - run(args, {}, (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o) - t.end() - }) -}) \ No newline at end of file diff --git a/typescript-vendor/tap/test/run/index.js b/typescript-vendor/tap/test/run/index.js deleted file mode 100644 index b0126574c..000000000 --- a/typescript-vendor/tap/test/run/index.js +++ /dev/null @@ -1,117 +0,0 @@ -'use strict' -const fs = require('fs') -const mkdirp = require('mkdirp') -const rimraf = require('rimraf') -// const rimraf = { sync: () => {} } -const path = require('path') -const cp = require('child_process') -const execFile = cp.execFile -const node = process.execPath -const bin = require.resolve('../../bin/run.js') -const tap = JSON.stringify(path.join(__dirname, '../..') + '/') -const t = require('../../') - -const dir = path.join(__dirname, `../../cli-tests-${process.pid}`) - -// set this forcibly so it doesn't interfere with other tests. -delete process.env.TAP_DIAG -delete process.env.TAP_BAIL -delete process.env.TAP_COLORS -delete process.env.TAP_TIMEOUT - -const winSkip = process.platform === 'win32' ? 'known windows failure' : false -const oldSkip = /^v10\./.test(process.version) ? 'known node v10 failure': false - -const cleanStacks = require('../clean-stacks.js') -// also clean up NYC output a bit, because the line lengths -// in the text report can vary on different platforms. -const clean = string => cleanStacks(string) - .replace(/uncovered line( #)?s/i, 'Uncovered Lines') - .replace(/ +\|/g, ' |') - .replace(/\| +/g, '| ') - .replace(/-+\|/g, '-|') - .replace(/\|-+/g, '|-') - // two that show up in config dump snapshots - .replace(/snapshot: (true|false)\n/, '') - .replace(/cli-tests-[0-9]+/g, 'cli-tests') - .split('\n').filter(l => !/ExperimentalWarning/.test(l)).join('\n') - -t.cleanSnapshot = clean - -const run = (args, options, cb) => { - if (options && options.env) - options.env = Object.keys(process.env).reduce((env, k) => { - if (env[k] === undefined) - env[k] = process.env[k] - return env - }, options.env) - - return execFile(node, [bin, '--no-coverage'].concat(args), options, cb) -} - -const tmpfile = (t, filename, content) => { - const parts = filename.split('/') - // make any necessary dirs - if (parts.length > 1) - mkdirp.sync(path.join(dir, parts.slice(0, -1).join('/'))) - if (t.tmpfiles) - t.tmpfiles.push(path.join(dir, parts[0])) - else { - t.tmpfiles = [path.join(dir, parts[0])] - if (process.env._TAP_TEST_NO_TEARDOWN_TMP !== '1') - t.teardown(() => t.tmpfiles.forEach(f => rimraf.sync(f))) - } - filename = path.join(dir, filename) - fs.writeFileSync(filename, content) - return path.relative('', filename) -} - -const escapeNYC = () => { - // This function is too intimate with nyc internals, could get broken by internal - // changes to nyc. Probably need an official istanbuljs API to unwrap. - const nycWrap = require.resolve('nyc/lib/wrap.js') - const preloadList = require('node-preload') - const idx = preloadList.indexOf(nycWrap) - if (idx === -1) { - return - } - - preloadList.splice(idx, 1) - - const processOnSpawn = require('process-on-spawn') - const nycEnvs = [ - 'NYC_CONFIG', - 'NYC_CWD', - 'NYC_PROCESS_ID', - 'BABEL_DISABLE_CACHE' - ] - processOnSpawn.addListener(({ env }) => { - for (const key of nycEnvs) { - delete env[key] - } - }) -} - -if (module === require.main) - t.pass('this is fine') -else { - mkdirp.sync(dir) - if (process.env._TAP_TEST_NO_TEARDOWN_TMP !== '1') { - t.teardown(() => rimraf.sync(dir)) - process.on('exit', () => rimraf.sync(dir)) - } -} - -module.exports = { - escapeNYC, - tmpfile, - run, - bin, - tap, - node, - clean, - dir, - t, - winSkip, - oldSkip, -} diff --git a/typescript-vendor/tap/test/run/invalid-option.js b/typescript-vendor/tap/test/run/invalid-option.js deleted file mode 100644 index 9689170e6..000000000 --- a/typescript-vendor/tap/test/run/invalid-option.js +++ /dev/null @@ -1,10 +0,0 @@ -const { - run, - t, -} = require('./') - -t.test('print a nicer message on invalid argument errors', t => { - t.plan(1) - run(['-R'], {}, (er, o, e) => - t.matchSnapshot(e)) -}) diff --git a/typescript-vendor/tap/test/run/jsx.js b/typescript-vendor/tap/test/run/jsx.js deleted file mode 100644 index 5d83855a0..000000000 --- a/typescript-vendor/tap/test/run/jsx.js +++ /dev/null @@ -1,31 +0,0 @@ -process.env.TAP_JSX = '1' - -const { - tmpfile, - run, - tap, - t, -} = require('./') - -t.test('jsx', t => { - const ok = tmpfile(t, 'jsx/ok.jsx', ` - const React = require('react') - const t = require(${tap}) - const div = (
Hello
) - t.pass('this is fine') - `) - run([ok], {}, (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o) - t.end() - }) -}) - -t.test('running jsx thingie directly raises an error', t => { - const jsx = require.resolve('../../bin/jsx.js') - const {execFile} = require('child_process') - execFile(process.execPath, [jsx], (er, o, e) => { - t.match(er, { code: 1 }) - t.end() - }) -}) diff --git a/typescript-vendor/tap/test/run/libtap-settings.js b/typescript-vendor/tap/test/run/libtap-settings.js deleted file mode 100644 index 21b711998..000000000 --- a/typescript-vendor/tap/test/run/libtap-settings.js +++ /dev/null @@ -1,132 +0,0 @@ -const { - tmpfile, - run, - tap, - t, - clean, -} = require('./') - -const { resolve } = require('path') - -const path = t.testdir({ - settings: { - 'ok.js': ` - module.exports = { - snapshotFile: (cwd, main, argv) => 'some-path/' + main + argv + '.test.cjs' - } - `, - 'ok-empty.js': ` - module.exports = {} - `, - 'unknown-field.js': ` - module.exports = { - foo: 'bar' - } - `, - 'wrong-type-field.js': ` - module.exports = { - snapshotFile: 75 - } - `, - 'export-function.js': ` - module.exports = () => {} - `, - 'export-array.js': ` - module.exports = [1, 2, 3] - `, - 'export-false.js': ` - module.exports = false - `, - 'export-null.js': ` - module.exports = null - `, - }, - 'test.js': ` - const t = require(${tap}) - t.comment(t.snapshotFile) - `, -}) - -const testFile = resolve(path, 'test.js') -const settings = n => `--libtap-settings=settings/${n}.js` - -t.test('print out a different snapshot file location', t => { - run([settings('ok'), testFile], {cwd: path}, (er, o, e) => { - t.notOk(er) - t.equal(clean(e), '') - t.matchSnapshot(o) - t.end() - }) -}) - -t.test('print out the normal snapshot file location', t => { - run([settings('ok-empty'), testFile], {cwd: path}, (er, o, e) => { - t.notOk(er) - t.equal(clean(e), '') - t.matchSnapshot(o) - t.end() - }) -}) - -t.test('fails if module not found', t => { - run([settings('does-not-exist'), testFile], {cwd: path}, (er, o, e) => { - t.match(er, { code: 1 }) - t.match(clean(e), 'Error: ') // specific msg different across node versions - t.equal(o, '') - t.end() - }) -}) - -t.test('adding an unknown field is invalid', t => { - run([settings('unknown-field'), testFile], {cwd: path}, (er, o, e) => { - t.match(er, { code: 1 }) - t.match(clean(e), 'Error: Unrecognized libtap setting: foo') - t.equal(o, '') - t.end() - }) -}) - -t.test('fields must be same type as libtap defines', t => { - run([settings('wrong-type-field'), testFile], {cwd: path}, (er, o, e) => { - t.match(er, { code: 1 }) - t.match(clean(e), 'Error: Invalid type for libtap setting snapshotFile. Expected function, received number.') - t.equal(o, '') - t.end() - }) -}) - -t.test('exporting function is invalid', t => { - run([settings('export-function'), testFile], {cwd: path}, (er, o, e) => { - t.match(er, { code: 1 }) - t.match(clean(e), 'Error: invalid libtap settings: function') - t.equal(o, '') - t.end() - }) -}) - -t.test('exporting array is invalid', t => { - run([settings('export-array'), testFile], {cwd: path}, (er, o, e) => { - t.match(er, { code: 1 }) - t.match(clean(e), 'Error: invalid libtap settings: array') - t.equal(o, '') - t.end() - }) -}) - -t.test('exporting false is invalid', t => { - run([settings('export-false'), testFile], {cwd: path}, (er, o, e) => { - t.match(er, { code: 1 }) - t.match(clean(e), 'Error: invalid libtap settings: boolean') - t.equal(o, '') - t.end() - }) -}) - -t.test('exporting null is invalid', t => { - run([settings('export-null'), testFile], {cwd: path}, (er, o, e) => { - t.match(er, { code: 1 }) - t.match(clean(e), 'Error: invalid libtap settings: null') - t.equal(o, '') - t.end() - }) -}) diff --git a/typescript-vendor/tap/test/run/nocolor-env.js b/typescript-vendor/tap/test/run/nocolor-env.js deleted file mode 100644 index 806a14353..000000000 --- a/typescript-vendor/tap/test/run/nocolor-env.js +++ /dev/null @@ -1,25 +0,0 @@ -process.env.TAP_COLORS = '1' - -const { - tmpfile, - run, - tap, - t, - clean, -} = require('./') - -const ok = tmpfile(t, 'ok.js', ` - const t = require(${tap}) - t.equal(process.env.TAP_COLORS, '0') -`) - -t.plan(3) -run([ok], { - env: { - NO_COLOR: '1' - } -}, (er, o, e) => { - t.notOk(er) - t.equal(clean(e), '') - t.matchSnapshot(o) -}) diff --git a/typescript-vendor/tap/test/run/nonparallel.js b/typescript-vendor/tap/test/run/nonparallel.js deleted file mode 100644 index 25ea236cc..000000000 --- a/typescript-vendor/tap/test/run/nonparallel.js +++ /dev/null @@ -1,29 +0,0 @@ -const { - tmpfile, - run, - tap, - dir, - t, -} = require('./') - -tmpfile(t, '1.js', ` -console.log('start one') -const t = require(${tap}) -setTimeout(() => { - t.pass('fine in 1') - t.end() -}, 250) -`) -tmpfile(t, '2.js', ` -console.log('start two') -const t = require(${tap}) -t.pass('fine in 2') -t.end() -`) - -t.plan(3) -run(['1.js', '2.js', '-j1'], { cwd: dir }, (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o, 'output') - t.matchSnapshot(e, 'stderr') -}) diff --git a/typescript-vendor/tap/test/run/output-file.js b/typescript-vendor/tap/test/run/output-file.js deleted file mode 100644 index 7d5fd67f0..000000000 --- a/typescript-vendor/tap/test/run/output-file.js +++ /dev/null @@ -1,91 +0,0 @@ -const { - tmpfile, - run, - bin, - tap, - node, - dir, - t, - winSkip, -} = require('./') - -const path = require('path') -const fs = require('fs') - -const tapdata = `TAP version 13 -1..1 -ok 1 - totally fine result from stdin -` - -t.test('output-file', t => { - const ok = tmpfile(t, 'ok.js', `require(${tap}).pass('this is fine')`) - t.test('ok.js', t => { - run([ok, `--output-file=${dir}/output.tap`], (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o, 'output') - t.matchSnapshot(e, 'stderr') - t.matchSnapshot(fs.readFileSync(`${dir}/output.tap`, 'utf8'), - 'output file') - t.end() - }) - }) - t.test('stdin', t => { - run(['-', `-o${dir}/output.tap`], (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o, 'output') - t.matchSnapshot(e, 'stderr') - t.matchSnapshot(fs.readFileSync(`${dir}/output.tap`, 'utf8'), - 'output file') - t.end() - }).stdin.end(tapdata) - }) - t.test('file and stdin together', t => { - run(['-', ok, `-o${dir}/output.tap`], (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o, 'output') - t.matchSnapshot(e, 'stderr') - t.matchSnapshot(fs.readFileSync(`${dir}/output.tap`, 'utf8'), - 'output file') - t.end() - }).stdin.end(tapdata) - }) - t.end() -}) - -t.test('output-file', t => { - const d = `${dir}/output/${path.basename(dir)}` - const ok = tmpfile(t, 'ok.js', `require(${tap}).pass('this is fine')`) - t.test('ok.js', t => { - run([ok, `--output-dir=${dir}/output`], (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o, 'output') - t.matchSnapshot(e, 'stderr') - t.matchSnapshot(fs.readFileSync(`${d}/ok.js.tap`, 'utf8'), - 'output file') - t.end() - }) - }) - t.test('stdin', t => { - run(['-', `-d${dir}/output`], (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o, 'output') - t.matchSnapshot(e, 'stderr') - t.matchSnapshot(fs.readFileSync(`${dir}/output/stdin.tap`, 'utf8'), - 'output file') - t.end() - }).stdin.end(tapdata) - }) - t.test('file and stdin together', t => { - run(['-', ok, `-d${dir}/output`], (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o, 'output') - t.matchSnapshot(e, 'stderr') - t.matchSnapshot(fs.readFileSync(`${d}/ok.js.tap`, 'utf8'), - 'ok.js output file') - t.matchSnapshot(fs.readFileSync(`${dir}/output/stdin.tap`, 'utf8'), - 'stdin output file') - t.end() - }).stdin.end(tapdata) - }) - t.end() -}) diff --git a/typescript-vendor/tap/test/run/parallel.js b/typescript-vendor/tap/test/run/parallel.js deleted file mode 100644 index 7f9e120b9..000000000 --- a/typescript-vendor/tap/test/run/parallel.js +++ /dev/null @@ -1,77 +0,0 @@ -const { - tmpfile, - run, - tap, - dir, - t, -} = require('./') - -const timeout = process.env.CI ? 1000 : 100 - -// should see start, start, end, end, in the output -tmpfile(t, 'p/y/1.js', `'use strict' - console.error('start') - setTimeout(() => console.error('end'), ${timeout}) - const t = require(${tap}) - t.pass('one') -`) - -tmpfile(t, 'p/y/2.js', `'use strict' - console.error('start') - setTimeout(() => console.error('end'), ${timeout}) - const t = require(${tap}) - t.pass('2') -`) - -tmpfile(t, 'p/tap-parallel-not-ok', '') -tmpfile(t, 'p/y/tap-parallel-ok', '') - -tmpfile(t, 'q/b/f1.js', `'use strict' - require(${tap}).pass('a/b') - setTimeout(() => console.error('f1'), ${timeout}) -`) - -tmpfile(t, 'q/b/f2.js', `'use strict' - require(${tap}).pass('c/d') - console.error('f2') -`) - -tmpfile(t, 'q/tap-parallel-ok', '') -tmpfile(t, 'q/b/tap-parallel-not-ok', '') - -tmpfile(t, 'r/y/1.js', `'use strict' - console.error('ry1') - setTimeout(() => console.error('ry1'), ${timeout}) - const t = require(${tap}) - t.pass('one') -`) - -tmpfile(t, 'r/y/2.js', `'use strict' - console.error('ry2') - setTimeout(() => console.error('ry2'), ${timeout}) - const t = require(${tap}) - t.pass('2') -`) - -tmpfile(t, 'r/tap-parallel-not-ok', '') - -tmpfile(t, 'z/y/1.js', `'use strict' - console.error('start') - setTimeout(() => console.error('end'), ${timeout}) - const t = require(${tap}) - t.pass('one') -`) - -tmpfile(t, 'z/y/2.js', `'use strict' - console.error('start') - setTimeout(() => console.error('end'), ${timeout}) - const t = require(${tap}) - t.pass('2') -`) - -t.plan(3) -run(['p/y/*.js', 'q', 'q/b/f1.js', 'r/y', 'z', '-j2'], { cwd: dir }, (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o, 'output') - t.matchSnapshot(e, 'stderr') -}) diff --git a/typescript-vendor/tap/test/run/rcfile-extensions.js b/typescript-vendor/tap/test/run/rcfile-extensions.js deleted file mode 100644 index 759c18305..000000000 --- a/typescript-vendor/tap/test/run/rcfile-extensions.js +++ /dev/null @@ -1,27 +0,0 @@ -const { - run, - dir, - t, -} = require('./') - -const fs = require('fs') -t.test('finds rc file with .yaml and .yml', t => { - const files = [ - '.taprc', - '.taprc.yml', - '.taprc.yaml', - ] - - t.plan(files.length) - for (const file of files) { - t.test(file, t => { - const cwd = t.testdir({ - [file]: 'check-coverage: false', - }) - run(['--dump-config'], { cwd }, (er, o, e) => { - t.match(o, /check-coverage: false/) - t.end() - }) - }) - } -}) diff --git a/typescript-vendor/tap/test/run/reporters.js b/typescript-vendor/tap/test/run/reporters.js deleted file mode 100644 index 4b3feb1ce..000000000 --- a/typescript-vendor/tap/test/run/reporters.js +++ /dev/null @@ -1,70 +0,0 @@ -const { - tmpfile, - run, - clean, - tap, - t, -} = require('./') -const path = require('path') - -const ok = tmpfile(t, 'ok.js', ` -require(${tap}).test(t => { - t.pass('this is fine') - t.end() -}) -`) - -const reactReporter = tmpfile(t, 'reporter.js', ` -module.exports = class ReactyReporter extends require('treport').Base {} -`) - -const check = t => (er, o, e) => { - t.error(er) - o = clean(o) - .replace(/^[\S\s]*SUMMARY RESULTS[\S\s]*$/,'treport output') - .replace(/^[\S\s]*✓[\S\s]*$/, 'spec output') - t.matchSnapshot(o, 'stdout') - t.equal(clean(e), '', 'stderr') - t.end() -} - -const opt = {env: {TAP_COLORS: 0, PATH: process.env.PATH}} - -t.test('builtin reporter', t => - run([ok, '-Rbase'], opt, check(t))) - -t.test('tmr builtin reporter', t => - run([ok, '-Rspec'], opt, check(t))) - -// `-Rtap-parser` will use the version libtap installed -t.test('cli reporter', t => - run([ok, '-Rtap-parser', '-r-t', '-r-f'], opt, check(t))) - -t.test('stream reporter', t => - run([ok, '-Rtap-mocha-reporter', '-rspec'], { - ...opt, - env: { - ...opt.env, - PATH: '' - } - }, check(t))) - -t.test('react component', t => - run([ok, '-R', './' + reactReporter], opt, check(t))) - -t.test('failures', t => { - const nonfunc = tmpfile(t, 'nonfunc.js', `module.exports = "hello"`) - const func = tmpfile(t, 'func.js', `module.exports = function () {}`) - const {makeReporter} = require('../../bin/run.js') - - t.throws(() => makeReporter(t, {reporter: 'not a reporter actually'}), - { message: `Cannot find module 'not a reporter actually'` }) - - t.throws(() => makeReporter(t, {reporter: path.resolve(nonfunc)}), - { message: 'Invalid reporter: non-class exported by ' }) - - t.throws(() => makeReporter(t, {reporter: path.resolve(func)}), - { message: 'Invalid reporter: not a stream or react component ' }) - - t.end() -}) diff --git a/typescript-vendor/tap/test/run/save-file.js b/typescript-vendor/tap/test/run/save-file.js deleted file mode 100644 index 50aa02105..000000000 --- a/typescript-vendor/tap/test/run/save-file.js +++ /dev/null @@ -1,89 +0,0 @@ -const { - tmpfile, - run, - bin, - tap, - node, - dir, - t, - winSkip, - oldSkip, - clean, -} = require('./') - -const path = require('path') -const fs = require('fs') - -const xy1 = tmpfile(t, 'x/y/1.js', ` - const t = require(${tap}) - t.pass('one') -`) - -const ab2 = tmpfile(t, 'a/b/2.js', ` - const t = require(${tap}) - t.pass('2') -`) - -const abf1 = tmpfile(t, 'a/b/f1.js', `//f1.js - require(${tap}).fail('a/b') -`) - -const abf2 = tmpfile(t, 'z.js', `//z.js - require(${tap}).fail('c/d') -`) - -const savefile = path.resolve(tmpfile(t, 'fails.txt', '')) -const opt = { cwd: dir, env: {} } - -t.test('with bailout, should save all untested', t => { - run(['a', 'x', 'z.js', '-s', savefile, '-b'], opt, (er, o, e) => { - t.match(er, { code: 1 }) - t.matchSnapshot(o, 'stdout', { skip: winSkip || oldSkip }) - t.equal(clean(e), '') - t.matchSnapshot(fs.readFileSync(savefile, 'utf8'), 'savefile') - t.end() - }) -}) - -t.test('without bailout, run untested, save failures', t => { - run(['a', 'x', 'z.js', '-s', savefile], opt, (er, o, e) => { - t.match(er, { code: 1 }) - t.matchSnapshot(o, 'stdout', { skip: winSkip || oldSkip }) - t.equal(clean(e), '') - t.matchSnapshot(fs.readFileSync(savefile, 'utf8'), 'savefile') - t.end() - }) -}) - -t.test('make fails pass', t => { - fs.writeFileSync(abf1, ` - require(${tap}).pass('fine now') - `) - fs.writeFileSync(abf2, ` - require(${tap}).pass('fine now too') - `) - t.end() -}) - -t.test('pass, empty save file', t => { - run(['a', 'x', 'z.js', '-s', savefile], opt, (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o, 'stdout') - t.equal(clean(e), '') - try { - console.log(fs.readFileSync(savefile, 'utf8')) - } catch (e) {} - t.throws(() => fs.statSync(savefile), 'save file is gone') - t.end() - }) -}) - -t.test('empty save file, run all tests', t => { - run(['a', 'x', 'z.js', '-s', savefile], opt, (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o, 'stdout') - t.equal(clean(e), '') - t.throws(() => fs.statSync(savefile), 'save file is gone') - t.end() - }) -}) diff --git a/typescript-vendor/tap/test/run/setup-tap-env.js b/typescript-vendor/tap/test/run/setup-tap-env.js deleted file mode 100644 index 8e3ed7bbf..000000000 --- a/typescript-vendor/tap/test/run/setup-tap-env.js +++ /dev/null @@ -1,24 +0,0 @@ -const setup = require('../../bin/run.js').setupTapEnv -const t = require('../../lib/tap.js') - -const cases = [ - [{timeout: 999}, {TAP_TIMEOUT: 999}], - [{color: false}, {TAP_COLORS: '0'}], - [{color: true}, {TAP_COLORS: '1'}], - [{snapshot: true}, {TAP_SNAPSHOT: '1'}], - [{bail: true}, {TAP_BAIL: '1'}], - [{invert: true}, {TAP_GREP_INVERT: '1'}], - [{grep: [/a/, /b/]}, {TAP_GREP: '/a/\n/b/'}], - [{only: true}, {TAP_ONLY: '1'}], -] - -const env = {...process.env} -cases.forEach(c => { - c[0].grep = c[0].grep || [] - setup(c[0]) - t.match(process.env, c[1]) - for (let k in c[1]) { - process.env[k] = env[k] - } -}) -t.end() diff --git a/typescript-vendor/tap/test/run/stdin.js b/typescript-vendor/tap/test/run/stdin.js deleted file mode 100644 index 86a80d6e8..000000000 --- a/typescript-vendor/tap/test/run/stdin.js +++ /dev/null @@ -1,55 +0,0 @@ -const { - tmpfile, - run, - tap, - t, -} = require('./') - -const fs = require('fs') -const tapcode = 'TAP version 13\n1..1\nok\n' - -t.test('with output file', t => { - const c = run(['-', '-c', '-Rspec', '-ofoo.txt', '--cov'], { env: { - TAP: '0' - }}, (er, o, e) => { - t.equal(er, null) - t.equal(e, '') - t.match(o, /✓|√/) - t.equal(fs.readFileSync('foo.txt', 'utf8'), tapcode) - fs.unlinkSync('foo.txt') - t.end() - }) - c.stdin.end(tapcode) -}) - -t.test('no output file', t => { - const c = run(['-', '--only', '-gx', '-iC', '-Rclassic'], { env: { - TAP: '0' - }}, (er, o, e) => { - t.equal(er, null) - t.equal(e, '') - t.match(o, /total \.+ 1\/1/) - t.throws(() => fs.statSync('foo.txt')) - t.end() - }) - c.stdin.end(tapcode) -}) - -t.test('with file', t => { - const foo = tmpfile(t, 'foo.test.js', ` - 'use strict' - require(${tap}).test('child', t => { - t.pass('this is fine') - t.end() - }) - `) - const args = ['-', foo, '-CRclassic', '-ofoo.txt'] - const c = run(args, { env: { TAP: 0, TAP_BUFFER: 1 }}, (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(fs.readFileSync('foo.txt', 'utf8')) - t.match(o, /foo.test.js \.+ 1\/1.*\n\/dev\/stdin \.+ 1\/1\n/) - fs.unlinkSync('foo.txt') - t.end() - }) - c.stdin.end(tapcode) -}) diff --git a/typescript-vendor/tap/test/run/test-regex.js b/typescript-vendor/tap/test/run/test-regex.js deleted file mode 100644 index 3750b6c3f..000000000 --- a/typescript-vendor/tap/test/run/test-regex.js +++ /dev/null @@ -1,36 +0,0 @@ -const { - tmpfile, - bin, - tap, - node, - dir, - t, - run, -} = require('./') - -t.test('no args, pull in default files, not exclusions', t => { - tmpfile(t, 'file.spec.js', ` - const t = require(${tap}) - t.pass('this is fine') - `) - tmpfile(t, 'tests.cjs', ` - const t = require(${tap}) - t.pass('this is also fine') - `) - tmpfile(t, 'node_modules/bad.test.js', ` - const t = require(${tap}) - t.fail('should not run this') - `) - run([], { cwd: dir }, (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o, 'output') - t.end() - }) -}) - -t.test('error out if loading files fails', t => { - run([], { cwd: '/dev' }, (er, o, e) => { - t.match(er, { code: 1 }) - t.end() - }) -}) diff --git a/typescript-vendor/tap/test/run/ts.js b/typescript-vendor/tap/test/run/ts.js deleted file mode 100644 index 3e171ecb2..000000000 --- a/typescript-vendor/tap/test/run/ts.js +++ /dev/null @@ -1,131 +0,0 @@ -const { - tmpfile, - run, - tap, - t, -} = require('./') - -t.beforeEach(() => { - delete process.env.TAP_TS - delete process.env.TAP_JSX -}) - -t.test('via env', t => { - t.test('ts', t => { - process.env.TAP_TS = '1' - const ok = tmpfile(t, 'ts/ok.ts', ` - import * as t from ${tap} - t.pass('this is fine') - `) - run([ok], {}, (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o) - t.end() - }) - }) - - t.test('no ts', t => { - const ok = tmpfile(t, 'ts/ok.ts', ` - import * as t from ${tap} - t.pass('this is fine') - `) - run([ok], {}, (er, o, e) => { - t.ok(er) - t.matchSnapshot(o) - t.end() - }) - }) - - t.test('ts, but no tsx', t => { - process.env.TAP_TS = '1' - const ok = tmpfile(t, 'tsx/ok.tsx', ` - import * as React from 'react' - import * as t from ${tap} - const div = (
Hello
) - t.pass('this is fine') - `) - run([ok], {}, (er, o, e) => { - t.ok(er) - t.matchSnapshot(o) - t.end() - }) - }) - - t.test('tsx', t => { - process.env.TAP_JSX = '1' - process.env.TAP_TS = '1' - const ok = tmpfile(t, 'tsx/ok.tsx', ` - import * as React from 'react' - import * as t from ${tap} - const div = (
Hello
) - t.pass('this is fine') - `) - run([ok], {}, (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o) - t.end() - }) - }) - - t.end() -}) - -t.test('via cli args', t => { - t.test('ts', t => { - const ok = tmpfile(t, 'ts/ok.ts', ` - import * as t from ${tap} - t.pass('this is fine') - `) - run([ok, '--ts'], {}, (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o) - t.end() - }) - }) - - t.test('ts, but no tsx', t => { - const ok = tmpfile(t, 'tsx/ok.tsx', ` - import * as React from 'react' - import * as t from ${tap} - const div = (
Hello
) - t.pass('this is fine') - `) - run([ok, '--ts'], {}, (er, o, e) => { - t.ok(er) - t.matchSnapshot(o) - t.end() - }) - }) - - t.test('tsx', t => { - const ok = tmpfile(t, 'tsx/ok.tsx', ` - import * as React from 'react' - import * as t from ${tap} - const div = (
Hello
) - t.pass('this is fine') - `) - run([ok, '--ts', '--jsx'], {}, (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o) - t.end() - }) - }) - - t.end() -}) - -t.test('ts manually', t => { - const ok = tmpfile(t, 'mixed/ok.js', ` - require('./foo.ts') - `) - const foots = tmpfile(t, 'mixed/foo.ts', ` - import * as t from ${tap} - t.pass('this is fine') - `) - const args = [ok, foots, '--no-ts', '--node-arg=--require', '--node-arg=ts-node/register'] - run(args, {}, (er, o, e) => { - t.equal(er, null) - t.matchSnapshot(o) - t.end() - }) -}) diff --git a/typescript-vendor/tap/test/run/watermarks.js b/typescript-vendor/tap/test/run/watermarks.js deleted file mode 100644 index 4285b50a7..000000000 --- a/typescript-vendor/tap/test/run/watermarks.js +++ /dev/null @@ -1,128 +0,0 @@ -const { - escapeNYC, - tmpfile, - node, - dir, - bin, - tap, - t, -} = require('./') - -const { execFile } = require('child_process') -const {dirname, basename} = require('path') - -const clean = t.cleanSnapshot -t.cleanSnapshot = str => clean(str).replace(/[0-9\.]+m?s/g, '{TIME}') - -escapeNYC() - -const escapePath = `${dirname(process.execPath)}:${process.env.PATH}` -const esc = tmpfile(t, 'runtest.sh', -`#!/bin/bash -export PATH=${escapePath} -"${node}" "${bin}" "\$@" \\ - --cov \\ - --nyc-arg=--temp-dir="${dir}/.nyc_output" \\ - --nyc-arg=--cache=false -`) - -const escape = (args, cb) => { - const options = { - cwd: dir, - env: Object.keys(process.env).filter( - k => !/^TAP|NYC|SW_ORIG|PWD/.test(k) - ).reduce((env, k) => { - if (!env.hasOwnProperty(k)) - env[k] = process.env[k] - return env - }, {}), - } - const a = [basename(esc)].concat( - args.map(a => !/^-/.test(a) ? basename(a) : a) - ) - return execFile('bash', a, options, cb) -} - -const branch = tmpfile(t, 'branch.js', ` -const ok = n => { - if (n) { - console.log('truthy') - if (n > 5) - console.log('gt 5') - } else { - console.log('falsey') - } -} -const notCalled = () => {} -module.exports = ok -`) - -const test = tmpfile(t, 't.js', ` -const ok = require('./branch.js') -ok(1) -ok(6) -const t = require(${tap}) -t.pass('this is fine') -`) - -const args = [ - test, - '-Rtap', - '-c', - '--cov', - '--nyc-arg=--include=' + branch, - '--coverage-report=text', -] - -t.test('default watermarks, all set at 100, red', t => - escape(['--no-check-coverage', ...args, '-c'], (er, o, e) => { - t.matchSnapshot(o, 'stdout') - t.matchSnapshot(e, 'stderr') - t.end() - })) - -t.test('unmet, red', t => - escape([ - '--no-check-coverage', - ...args, - '-c', - '--branches=76', - '--statements=88', - '--functions=51', - '--lines=88', - ], (er, o, e) => { - t.matchSnapshot(o, 'stdout') - t.matchSnapshot(e, 'stderr') - t.end() - })) - - -t.test('less than halfway to 100, yellow', t => - escape([ - '--no-check-coverage', - ...args, - '-c', - '--branches=51', - '--statements=76', - '--functions=1', - '--lines=76', - ], (er, o, e) => { - t.matchSnapshot(o, 'stdout') - t.matchSnapshot(e, 'stderr') - t.end() - })) - -t.test('more than halfway to 100, green', t => - escape([ - '--no-check-coverage', - ...args, - '-c', - '--branches=49', - '--statements=71', - '--functions=0', - '--lines=71' - ], (er, o, e) => { - t.matchSnapshot(o, 'stdout') - t.matchSnapshot(e, 'stderr') - t.end() - })) diff --git a/typescript-vendor/tap/test/settings/clean-for-snapshot.js b/typescript-vendor/tap/test/settings/clean-for-snapshot.js deleted file mode 100644 index 3dc4c0783..000000000 --- a/typescript-vendor/tap/test/settings/clean-for-snapshot.js +++ /dev/null @@ -1,20 +0,0 @@ -'use strict' - -// nothing to see here -if (module === require.main) - console.log('TAP version 13\n1..1\nok - 1\n') - -module.exports = settings => ({ - ...settings, - output: !!settings.output, - stackUtils: { - ...settings.stackUtils, - internals: [] - }, - mkdirpNeeded: false, - mkdirRecursive: 'function(path, cb)', - mkdirRecursiveSync: 'function(path)', - rimrafNeeded: false, - rmdirRecursive: 'function(path, cb)', - rmdirRecursiveSync: 'function(path)', -}) diff --git a/typescript-vendor/tap/test/settings/default.js b/typescript-vendor/tap/test/settings/default.js deleted file mode 100644 index 15381f895..000000000 --- a/typescript-vendor/tap/test/settings/default.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict' - -const t = require('../..') -const settings = require('../../settings.js') -const cleanForSnapshot = require('./clean-for-snapshot.js') - -t.ok(Array.isArray(settings.stackUtils.internals), 'Array.isArray(settings.stackUtils.internals)') -t.not(settings.stackUtils.internals.length, 0) - -t.matchSnapshot(cleanForSnapshot(settings)) diff --git a/typescript-vendor/tap/test/settings/long-stack.js b/typescript-vendor/tap/test/settings/long-stack.js deleted file mode 100644 index 89b7ec2f2..000000000 --- a/typescript-vendor/tap/test/settings/long-stack.js +++ /dev/null @@ -1,12 +0,0 @@ -'use strict' - -process.env.TAP_DEV_LONGSTACK = 1 - -const t = require('../..') -const settings = require('../../settings.js') -const cleanForSnapshot = require('./clean-for-snapshot.js') - -t.ok(Array.isArray(settings.stackUtils.internals), 'Array.isArray(settings.stackUtils.internals)') -t.not(settings.stackUtils.internals.length, 0) - -t.matchSnapshot(cleanForSnapshot(settings)) diff --git a/typescript-vendor/tap/test/settings/overrides.js b/typescript-vendor/tap/test/settings/overrides.js deleted file mode 100644 index 2fc65dda3..000000000 --- a/typescript-vendor/tap/test/settings/overrides.js +++ /dev/null @@ -1,51 +0,0 @@ -const t = require('../..') -const { resolve } = require('path') - -t.afterEach(() => delete process.env.TAP_LIBTAP_SETTINGS) - -t.test('override the snapshot location', t => { - process.env.TAP_LIBTAP_SETTINGS = resolve(t.testdir({ - 'settings.js': ` - module.exports = { - snapshotFile: (cwd, main, argv) => 'some-path/' + main + argv + '.snap' - } - ` - }), 'settings.js') - const settings = t.mock('../../settings.js') - t.equal(settings.snapshotFile('a', 'b', 'c'), 'some-path/bc.snap') - t.end() -}) - -t.test('no fields, thats ok', t => { - process.env.TAP_LIBTAP_SETTINGS = resolve(t.testdir({ - 'settings.js': ` - module.exports = {} - ` - }), 'settings.js') - const settings = t.mock('../../settings.js') - t.strictSame(settings, require('../../settings.js')) - t.end() -}) - -t.test('wrong export tests', t => { - const cases = Object.entries({ - function: '() => {}', - boolean: 'false', - array: '[]', - null: 'null', - 'unknown field': `{ foo: 'bar' }`, - 'wrong field type': '{ snapshotFile: 1234 }', - }) - t.plan(cases.length) - for (const [name, code] of cases) { - t.test(`export ${name}`, t => { - process.env.TAP_LIBTAP_SETTINGS = resolve(t.testdir({ - 'settings.js': ` - module.exports = ${code} - ` - }), 'settings.js') - t.throws(() => t.mock('../../settings.js')) - t.end() - }) - } -}) diff --git a/typescript-vendor/tap/test/synonyms.js b/typescript-vendor/tap/test/synonyms.js deleted file mode 100644 index 0ed6d7f26..000000000 --- a/typescript-vendor/tap/test/synonyms.js +++ /dev/null @@ -1,5 +0,0 @@ -'use strict' -const t = require('../') -const synonyms = require('../lib/synonyms.js') - -t.matchSnapshot(synonyms) diff --git a/typescript-vendor/tap/test/tap.js b/typescript-vendor/tap/test/tap.js deleted file mode 100644 index af0999c54..000000000 --- a/typescript-vendor/tap/test/tap.js +++ /dev/null @@ -1,57 +0,0 @@ -'use strict' -const tap = require('../') -const mocha = require('../lib/mocha.js') -const synonyms = require('../lib/synonyms.js') - -function testSynonyms(settings) { - let warnings; - - const {emitWarning} = process; - process.emitWarning = (msg, ...args) => { - warnings.push(msg) - } - - for (const [id, args] of Object.entries(settings)) { - tap.test(`${id} synonyms`, t => { - for (const fn of Object.values(synonyms[id])) { - warnings = [] - t[fn](...[].concat(args)) - if (id === fn) { - t.equal(warnings.length, 0, `no deprecation for ${fn}`) - } else { - t.same( - warnings, - [`${fn}() is deprecated, use ${id}() instead`], - `deprecation for ${fn}` - ) - } - } - - t.end() - }) - } -} - -tap.test('check exports', async t => { - t.type(tap, 'object') - t.equal(tap.mocha, mocha) - t.equal(tap.mochaGlobals, mocha.global) - t.equal(tap.synonyms, synonyms) -}) - -testSynonyms({ - ok: true, - notOk: false, - error: null, - throws: () => { throw new Error('test') }, - doesNotThrow: () => {}, - equal: [1, 1], - not: [2 + 2, 5], - same: [1, '1'], - notSame: [1, '2'], - strictSame: [{a: 1}, {a: 1}], - strictNotSame: [{a: 1}, {a: 2}], - match: [{a: 1, b: 1}, {a: Number}], - notMatch: [{a: 1, b: 1}, {a: String}], - type: ['test', 'string'] -}) diff --git a/typescript-vendor/tap/test/test-esm.js b/typescript-vendor/tap/test/test-esm.js deleted file mode 100644 index 7f83a92f1..000000000 --- a/typescript-vendor/tap/test/test-esm.js +++ /dev/null @@ -1,22 +0,0 @@ -let t; - -try { - t = require('tap') -} catch (e) { - t = require('../') - t.equal(e.code, 'MODULE_NOT_FOUND') - t.end() - return -} - -const {join} = require('path') - -const testPath = join(__dirname, './test.mjs') - -t.test('test esm entry point', t => { - const arg = [testPath] - if (/^v10\./.test(process.version)) - arg.unshift('--experimental-modules') - t.spawn('node', arg) - t.end() -}) diff --git a/typescript-vendor/tap/test/test.mjs b/typescript-vendor/tap/test/test.mjs deleted file mode 100644 index dba9cfe5b..000000000 --- a/typescript-vendor/tap/test/test.mjs +++ /dev/null @@ -1,25 +0,0 @@ -import * as module from 'module' -const { createRequire } = module - -import * as tap from 'tap' - -if (typeof createRequire !== 'function') { - console.log(`TAP version 13 -1..0 # SKIP - no createRequire function available ${process.version} -`) - process.exit(0) -} - -const require = createRequire(import.meta.url); - -const cjs = require('tap'); - -const t = cjs - -t.test('tap', async t => { - t.matchSnapshot(Object.keys(tap).sort()) - - for (const key of Object.keys(tap)) { - t.equal(tap[key], key === 'default' ? cjs : cjs[key], key) - } -}) diff --git a/typescript-vendor/tap/test/versions.js b/typescript-vendor/tap/test/versions.js deleted file mode 100644 index 41ce6ddc1..000000000 --- a/typescript-vendor/tap/test/versions.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict' - -const path = require('path') -const t = require('..') - -const versions = require('libtap/versions') -const ourVersions = { - libtap: require(path.join(path.dirname(require.resolve('libtap/versions')), 'package.json')).version, - tapParser: require('tap-parser/package.json').version, - tapYaml: require('tap-yaml/package.json').version, - tcompare: require('tcompare/package.json').version -} - -// Verify we're using the same version of selected libraries as libtap -t.same(versions, ourVersions) diff --git a/typescript-vendor/tap/test/watch.js b/typescript-vendor/tap/test/watch.js deleted file mode 100644 index 060d0eca4..000000000 --- a/typescript-vendor/tap/test/watch.js +++ /dev/null @@ -1,249 +0,0 @@ -// just load this to assign the runner test's output cleaner -require('./run/index.js') -const mkdirp = require('mkdirp') - -// spawn mock -// log what's being run, and then write out the index -const EE = require('events') -const spawnLog = [] -const spawnTrack = new EE() -const fakeSpawn = (file, args, options) => { - spawnLog.push([file, args, options]) - const ee = spawnTrack.current = new EE() - ee.file = file - ee.args = args - ee.options = options - spawnTrack.emit('spawn', ee) - ee.on('close', () => spawnTrack.current = null) - const to = setTimeout(() => { - try { - fs.unlinkSync('node_modules/.cache/tap/watch-' + process.pid) - } catch (_) {} - ee.emit('close', null, null) - }) - ee.kill = signal => { - clearTimeout(to) - try { - fs.unlinkSync('node_modules/.cache/tap/watch-' + process.pid) - } catch (_) {} - ee.emit('close', null, signal) - } - return ee -} -require('child_process').spawn = fakeSpawn - -// chokidar mock -const watcher = new EE() -const triggerWatchEvent = (event, file) => { - if (watcher.filelist.includes(file)) { - process.nextTick(() => { - watcher.emit(event, file) - watcher.emit('all', event, file) - }) - } -} -watcher.add = file => { - watcher.filelist.push(file) - process.nextTick(() => triggerWatchEvent('add', file)) -} -watcher.close = () => watcher.filelist = [] -require('chokidar').watch = filelist => { - watcher.filelist = [] - process.nextTick(() => filelist.forEach(f => watcher.add(f))) - return watcher -} - -const changeFile = file => triggerWatchEvent('change', file) - -const {Watch} = require('../lib/watch.js') -const bin = require.resolve('../bin/run.js') - -const dir = 'watch-test' -const index = { - "processes": { - "2d3328f2-7787-42c1-b4cb-fb0370e3ba4a": { - "parent": "a1160d95-3d31-4828-a0e0-abdfaa94e816", - "externalId": "4.test.js", - "children": [] - }, - "64951cad-dda0-4e8f-931f-709eb25c7c1f": { - "parent": "a1160d95-3d31-4828-a0e0-abdfaa94e816", - "externalId": "2.test.js", - "children": [] - }, - "7cb8d2d0-cad5-4b49-86a8-d7d693c48d89": { - "parent": "a1160d95-3d31-4828-a0e0-abdfaa94e816", - "externalId": "1.test.js", - "children": [] - }, - "a1160d95-3d31-4828-a0e0-abdfaa94e816": { - "parent": null, - "children": [ - "2d3328f2-7787-42c1-b4cb-fb0370e3ba4a", - "64951cad-dda0-4e8f-931f-709eb25c7c1f", - "7cb8d2d0-cad5-4b49-86a8-d7d693c48d89", - "ef4091b9-a036-474e-ba54-0dccf3b5c3ee" - ] - }, - "ef4091b9-a036-474e-ba54-0dccf3b5c3ee": { - "parent": "a1160d95-3d31-4828-a0e0-abdfaa94e816", - "externalId": "3.test.js", - "children": [] - } - }, - "files": { - "ko.js": [ - "2d3328f2-7787-42c1-b4cb-fb0370e3ba4a" - ], - "ok.js": [ - "64951cad-dda0-4e8f-931f-709eb25c7c1f", - "7cb8d2d0-cad5-4b49-86a8-d7d693c48d89", - "ef4091b9-a036-474e-ba54-0dccf3b5c3ee" - ] - }, - "externalIds": { - "4.test.js": { - "root": "2d3328f2-7787-42c1-b4cb-fb0370e3ba4a", - "children": [] - }, - "2.test.js": { - "root": "64951cad-dda0-4e8f-931f-709eb25c7c1f", - "children": [] - }, - "1.test.js": { - "root": "7cb8d2d0-cad5-4b49-86a8-d7d693c48d89", - "children": [] - }, - "3.test.js": { - "root": "ef4091b9-a036-474e-ba54-0dccf3b5c3ee", - "children": [] - } - } -} - -const rimraf = require('rimraf').sync -const fs = require('fs') -const pidir = dir + '/.nyc_output/processinfo' -mkdirp.sync(pidir) -const read = file => fs.existsSync(file) && fs.readFileSync(file, 'utf8') -const saveFile = 'node_modules/.cache/tap/watch-' + process.pid - -const options = { - watch: true, - coverage: true, - _: [ - '1.test.js', - '2.test.js', - '3.test.js', - '4.test.js', - ] -} - -options._.parsed = [...options._, '--watch'] - -const originalCwd = process.cwd() -process.chdir(dir) -const indexFile = '.nyc_output/processinfo/index.json' -fs.writeFileSync(indexFile, JSON.stringify(index)) - -const t = require('../') -t.teardown(() => { - process.chdir(originalCwd) - if (process.env._TAP_TEST_NO_TEARDOWN_TMP !== '1') - rimraf(dir) -}) - -t.throws(() => new Watch({}), { - message: '--watch requires coverage to be enabled' -}) - -t.test('run tests on changes', t => { - t.test('initial test', t => { - spawnTrack.once('spawn', proc => { - t.match(proc, { - file: process.execPath, - args: [ - bin, - '1.test.js', - '2.test.js', - '3.test.js', - '4.test.js', - '--watch', - '--no-watch' - ], - options: { stdio: 'inherit' }, - }) - t.matchSnapshot(read(saveFile), 'spawn initial test run') - t.matchSnapshot(Buffer.concat(out).toString(), 'logs') - out.length = 0 - // wait for all the files to be added to the watcher - proc.once('close', () => setTimeout(() => t.end())) - }) - }) - - const out = [] - const w = new Watch(options) - w.setEncoding('utf8') - w.on('data', c => out.push(c)) - - t.test('change a file', t => { - // initial test done, change a file - spawnTrack.once('spawn', proc => { - t.matchSnapshot(read(saveFile), 'spawn test run on change') - t.matchSnapshot(out.join(''), 'logs') - out.length = 0 - // don't wait for this one to close, that's the point. - t.end() - }) - changeFile('ko.js') - }) - - t.test('change a file mid-test', t => { - changeFile('1.test.js') - spawnTrack.once('spawn', proc => { - t.matchSnapshot(read(saveFile), 'spawn queued test') - t.matchSnapshot(out.join(''), 'logs') - out.length = 0 - - // add a new covered file to the equation - const newfile = 'new.js' - index.files[newfile] = [index.files['ok.js'].pop()] - fs.writeFileSync(indexFile, JSON.stringify(index)) - // set timeout to give it time to add the new file - proc.once('close', () => setTimeout(() => t.end())) - }) - }) - - t.test('new file added', t => { - t.matchSnapshot(out.join(''), 'logs') - out.length = 0 - spawnTrack.once('spawn', proc => { - t.matchSnapshot(read(saveFile), 'spawn test for new file') - t.matchSnapshot(out.join(''), 'log after spawn') - out.length = 0 - process.nextTick(() => w.kill('SIGTERM')) - proc.once('close', (code, signal) => { - t.same({code, signal}, {code: null, signal: 'SIGTERM'}) - // give the watcher cleanup a tick to remove proc - process.nextTick(() => t.end()) - }) - }) - changeFile('new.js') - }) - - t.test('killing if no proc is a noop', t => { - w.kill('no op') - t.end() - }) - - t.test('pause/resume', t => { - w.pause() - t.equal(w.watcher, null) - w.pause() - w.resume() - t.not(w.watcher, null) - t.end() - }) - - t.end() -}) diff --git a/typescript/api-parity/package.json b/typescript/api-parity/package.json index c354ba6ba..76dde7247 100644 --- a/typescript/api-parity/package.json +++ b/typescript/api-parity/package.json @@ -5,7 +5,7 @@ "type": "module", "license": "MIT", "scripts": { - "test": "vitest run", + "test": "vitest", "test:watch": "vitest", "typecheck": "tsc --noEmit" }, diff --git a/typescript/api-parity/tests/activity.test.ts b/typescript/api-parity/tests/activity.test.ts index bc683f317..30926e331 100644 --- a/typescript/api-parity/tests/activity.test.ts +++ b/typescript/api-parity/tests/activity.test.ts @@ -1,5 +1,5 @@ import { describe } from "vitest"; -import { api } from "./setup" +import { api } from "./setup"; import { it } from "vitest"; describe("GET /activity", () => { diff --git a/typescript/api-parity/tests/config.test.ts b/typescript/api-parity/tests/config.test.ts index cfb9d4f7f..4b9fdfe34 100644 --- a/typescript/api-parity/tests/config.test.ts +++ b/typescript/api-parity/tests/config.test.ts @@ -1,5 +1,5 @@ import { describe } from "vitest"; -import { api } from "./setup" +import { api } from "./setup"; import { it } from "vitest"; describe("GET /config/game-support", () => { diff --git a/typescript/api-parity/tests/games.test.ts b/typescript/api-parity/tests/games.test.ts index 2818c6bb2..19b567759 100644 --- a/typescript/api-parity/tests/games.test.ts +++ b/typescript/api-parity/tests/games.test.ts @@ -1,5 +1,5 @@ import { describe } from "vitest"; -import { api } from "./setup" +import { api } from "./setup"; import { it } from "vitest"; describe("GET /games", () => { diff --git a/typescript/api-parity/tests/imports.test.ts b/typescript/api-parity/tests/imports.test.ts index 9af805e23..878979a22 100644 --- a/typescript/api-parity/tests/imports.test.ts +++ b/typescript/api-parity/tests/imports.test.ts @@ -1,5 +1,5 @@ import { describe } from "vitest"; -import { api } from "./setup" +import { api } from "./setup"; import { it } from "vitest"; describe("GET /imports/:importID", () => { diff --git a/typescript/api-parity/tests/scores.test.ts b/typescript/api-parity/tests/scores.test.ts index 3e6fe43b9..094f3e066 100644 --- a/typescript/api-parity/tests/scores.test.ts +++ b/typescript/api-parity/tests/scores.test.ts @@ -1,5 +1,5 @@ import { describe } from "vitest"; -import { api } from "./setup" +import { api } from "./setup"; import { it } from "vitest"; describe("GET /scores/:scoreID", () => { diff --git a/typescript/api-parity/tests/search.test.ts b/typescript/api-parity/tests/search.test.ts index 67dbc82db..764694a10 100644 --- a/typescript/api-parity/tests/search.test.ts +++ b/typescript/api-parity/tests/search.test.ts @@ -1,5 +1,5 @@ import { describe } from "vitest"; -import { api } from "./setup" +import { api } from "./setup"; import { it } from "vitest"; describe("GET /search", () => { diff --git a/typescript/api-parity/tests/sessions.test.ts b/typescript/api-parity/tests/sessions.test.ts index 13a75714a..0fdca9112 100644 --- a/typescript/api-parity/tests/sessions.test.ts +++ b/typescript/api-parity/tests/sessions.test.ts @@ -1,5 +1,5 @@ import { describe } from "vitest"; -import { api } from "./setup" +import { api } from "./setup"; import { it } from "vitest"; describe("GET /sessions/:sessionID", () => { diff --git a/typescript/api-parity/tests/status.test.ts b/typescript/api-parity/tests/status.test.ts index 94c567488..8de1dfb50 100644 --- a/typescript/api-parity/tests/status.test.ts +++ b/typescript/api-parity/tests/status.test.ts @@ -1,5 +1,5 @@ import { describe } from "vitest"; -import { api } from "./setup" +import { api } from "./setup"; import { it } from "vitest"; describe("GET /status", () => { diff --git a/typescript/api-parity/tests/users.test.ts b/typescript/api-parity/tests/users.test.ts index 5e87d9faf..471b62ef9 100644 --- a/typescript/api-parity/tests/users.test.ts +++ b/typescript/api-parity/tests/users.test.ts @@ -1,5 +1,5 @@ import { describe } from "vitest"; -import { api } from "./setup" +import { api } from "./setup"; import { it } from "vitest"; describe("GET /users", () => { diff --git a/typescript/bliss/eslint.config.mjs b/typescript/bliss/eslint.config.mjs new file mode 100644 index 000000000..50b35e344 --- /dev/null +++ b/typescript/bliss/eslint.config.mjs @@ -0,0 +1,9 @@ +import configTachi from "eslint-config-tachi"; + +export default [ + ...configTachi.base, + configTachi.node, + { + ignores: ["js/**", "node_modules/**"], + }, +]; diff --git a/typescript/bliss/package.json b/typescript/bliss/package.json index 59049e1fc..55d82fb03 100644 --- a/typescript/bliss/package.json +++ b/typescript/bliss/package.json @@ -36,7 +36,7 @@ "dependencies": { "pino": "catalog:", "pino-pretty": "catalog:", - "zod": "^4.3.6" + "zod": "catalog:" }, "devDependencies": { "@typescript/native-preview": "catalog:", diff --git a/typescript/bliss/src/actions.ts b/typescript/bliss/src/actions.ts index 5e74a1c7b..b10b69932 100644 --- a/typescript/bliss/src/actions.ts +++ b/typescript/bliss/src/actions.ts @@ -1,7 +1,7 @@ import { AppendLogCtx, log as baseLogger } from "#log.js"; -import z, { ZodObject } from "zod"; +import { type ZodObject } from "zod"; -export type ActionResult = "GOOD" | "BAD" | "THROW"; +export type ActionResult = "BAD" | "GOOD" | "THROW"; /** * Secret input and output fields (keys starting with "!") are stripped before logging. @@ -67,10 +67,10 @@ export function MakeActionGuts({ kind, fn: actionBodyFn, }: { - db: any; appName: string; - kind: string; + db: any; fn: (taker: ActionTaker | AnonActionTaker, input: object) => Promise; + kind: string; }): unknown { return async (taker: ActionTaker | AnonActionTaker, input: Record) => { const ts_start = new Date(); diff --git a/typescript/bliss/src/index.ts b/typescript/bliss/src/index.ts index 7441d24fc..d2b0acd7b 100644 --- a/typescript/bliss/src/index.ts +++ b/typescript/bliss/src/index.ts @@ -1,4 +1,3 @@ -export * from "./log"; export { AcctInfo, ActionResult, @@ -7,3 +6,4 @@ export { ExpectedErr, MakeActionGuts, } from "./actions"; +export * from "./log"; diff --git a/typescript/bot/src/config.ts b/typescript/bot/src/config.ts index 609c012ee..515f37c5e 100644 --- a/typescript/bot/src/config.ts +++ b/typescript/bot/src/config.ts @@ -1,5 +1,6 @@ import type { GameGroup, integer, TachiServerCoreConfig } from "tachi-common"; +import { log } from "bliss/log"; import { config } from "dotenv"; import { p } from "prudence"; @@ -9,29 +10,6 @@ import { FormatPrError } from "./utils/prudence"; // Initialise .env. config(); -// the real log tries to bind to discord, and is dependent on the options -// below. -const log = console; - -export interface BotConfig { - TACHI_SERVER_LOCATION: string; - HTTP_SERVER: { - URL: string; - }; - OAUTH: { - CLIENT_ID: string; - CLIENT_SECRET: string; - }; - DISCORD: { - ADMIN_USERS: Array; - APPROVED_ROLE?: string; - GAME_CHANNELS: Partial>; - LIMBO_CHANNEL?: string; - SERVER_ID: string; - TOKEN: string; - }; -} - function ParseGameChannels(raw: string | undefined): Partial> { if (!raw) { return {}; @@ -62,57 +40,17 @@ function ParseGameChannels(raw: string | undefined): Partial>; } -function ParseBotConfig(): BotConfig { - const err = p( - process.env, - { - TACHI_SERVER_LOCATION: "string", - HTTP_SERVER_URL: "string", - OAUTH_CLIENT_ID: "string", - OAUTH_CLIENT_SECRET: "string", - DISCORD_TOKEN: "string", - DISCORD_SERVER_ID: "string", - DISCORD_GAME_CHANNELS: "*string", - DISCORD_ADMIN_USERS: "*string", - DISCORD_APPROVED_ROLE: "*string", - DISCORD_LIMBO_CHANNEL: "*string", - }, - {}, - { allowExcessKeys: true }, - ); - - if (err) { - log.error(FormatPrError(err, "Invalid environment. Cannot safely boot.")); - throw err; - } - - const gameChannels = ParseGameChannels(process.env.DISCORD_GAME_CHANNELS); - - const adminUsers = process.env.DISCORD_ADMIN_USERS - ? process.env.DISCORD_ADMIN_USERS.split(",").filter(Boolean) - : []; - - return { - TACHI_SERVER_LOCATION: process.env.TACHI_SERVER_LOCATION!, - HTTP_SERVER: { - URL: process.env.HTTP_SERVER_URL!, - }, - OAUTH: { - CLIENT_ID: process.env.OAUTH_CLIENT_ID!, - CLIENT_SECRET: process.env.OAUTH_CLIENT_SECRET!, - }, - DISCORD: { - TOKEN: process.env.DISCORD_TOKEN!, - SERVER_ID: process.env.DISCORD_SERVER_ID!, - GAME_CHANNELS: gameChannels, - ADMIN_USERS: adminUsers, - APPROVED_ROLE: process.env.DISCORD_APPROVED_ROLE, - LIMBO_CHANNEL: process.env.DISCORD_LIMBO_CHANNEL, - }, - }; -} - export interface ProcessEnvironment { + TACHI_SERVER_LOCATION: string; + HTTP_SERVER_URL: string; + OAUTH_CLIENT_ID: string; + OAUTH_CLIENT_SECRET: string; + DISCORD_TOKEN: string; + DISCORD_SERVER_ID: string; + DISCORD_GAME_CHANNELS: Partial>; + DISCORD_ADMIN_USERS: string[]; + DISCORD_APPROVED_ROLE: string | undefined; + DISCORD_LIMBO_CHANNEL: string | undefined; NODE_ENV: "dev" | "production" | "staging" | "test"; POSTGRES_URL: string; PORT: integer; @@ -132,6 +70,17 @@ function ParseEnvVars() { PORT: (self) => p.isPositiveInteger(Number(self)) === true || "Should be a string representing a whole integer port.", + + TACHI_SERVER_LOCATION: "string", + HTTP_SERVER_URL: "string", + OAUTH_CLIENT_ID: "string", + OAUTH_CLIENT_SECRET: "string", + DISCORD_TOKEN: "string", + DISCORD_SERVER_ID: "string", + DISCORD_GAME_CHANNELS: "*string", + DISCORD_ADMIN_USERS: "*string", + DISCORD_APPROVED_ROLE: "*string", + DISCORD_LIMBO_CHANNEL: "*string", }, {}, { allowExcessKeys: true }, @@ -143,14 +92,24 @@ function ParseEnvVars() { throw err; } - return { - NODE_ENV: process.env.NODE_ENV, - POSTGRES_URL: process.env.POSTGRES_URL, + const Env: ProcessEnvironment = { + NODE_ENV: process.env.NODE_ENV as "dev" | "production" | "staging" | "test", + POSTGRES_URL: process.env.POSTGRES_URL!, PORT: Number(process.env.PORT), - } as ProcessEnvironment; -} + TACHI_SERVER_LOCATION: process.env.TACHI_SERVER_LOCATION!, + HTTP_SERVER_URL: process.env.HTTP_SERVER_URL!, + OAUTH_CLIENT_ID: process.env.OAUTH_CLIENT_ID!, + OAUTH_CLIENT_SECRET: process.env.OAUTH_CLIENT_SECRET!, + DISCORD_TOKEN: process.env.DISCORD_TOKEN!, + DISCORD_SERVER_ID: process.env.DISCORD_SERVER_ID!, + DISCORD_GAME_CHANNELS: ParseGameChannels(process.env.DISCORD_GAME_CHANNELS), + DISCORD_ADMIN_USERS: process.env.DISCORD_ADMIN_USERS?.split(",") ?? [], + DISCORD_APPROVED_ROLE: process.env.DISCORD_APPROVED_ROLE, + DISCORD_LIMBO_CHANNEL: process.env.DISCORD_LIMBO_CHANNEL, + }; -export const BotConfig: BotConfig = ParseBotConfig(); + return Env; +} // The Tachi Server exports all of the information about it. This saves us having to // sync more metadata across instances. @@ -160,14 +119,10 @@ async function GetServerConfig() { // This *should* be solved with top-level-await, but good luck actually getting // typescript to output the right stuff here. - const res = await fetch(`${BotConfig.TACHI_SERVER_LOCATION}/api/v1/config`).then((res) => - res.json(), - ); + const res = await fetch(`${Env.TACHI_SERVER_LOCATION}/api/v1/config`).then((res) => res.json()); if (!res.success) { - log.error( - `Failed to fetch server info from ${BotConfig.TACHI_SERVER_LOCATION}. Can't run.`, - ); + log.error(`Failed to fetch server info from ${Env.TACHI_SERVER_LOCATION}. Can't run.`); process.exit(1); } @@ -181,7 +136,7 @@ export const Env = ParseEnvVars(); // General warnings for config misuse. // This warns people if their parent server supports games that they aren't acknowledging. for (const game of ServerConfig.GAMES) { - if (!Object.prototype.hasOwnProperty.call(BotConfig.DISCORD.GAME_CHANNELS, game)) { + if (!Object.prototype.hasOwnProperty.call(Env.DISCORD_GAME_CHANNELS, game)) { log.warn( `${ServerConfig.NAME} declares support for ${game}, but no channel is mapped to it. Set DISCORD_GAME_CHANNELS to include it.`, ); diff --git a/typescript/bot/src/main.ts b/typescript/bot/src/main.ts index e95fb5848..364e2f880 100644 --- a/typescript/bot/src/main.ts +++ b/typescript/bot/src/main.ts @@ -2,7 +2,7 @@ import { log } from "#utils/log.js"; import { GetLimboChannel } from "#utils/misc"; import { Client, type CommandInteraction, Intents, type SelectMenuInteraction } from "discord.js"; -import { BotConfig, Env, ServerConfig } from "./config"; +import { Env, ServerConfig } from "./config"; import { handleIsCommand } from "./interaction-handlers/handle-is-command"; import { GetUserAndTokenForDiscordID } from "./query/user-map"; import { app } from "./server/server"; @@ -25,7 +25,7 @@ export const client = new Client({ }); client.on("guildMemberAdd", async (_member) => { - if (BotConfig.DISCORD.APPROVED_ROLE && BotConfig.DISCORD.LIMBO_CHANNEL) { + if (Env.DISCORD_APPROVED_ROLE && Env.DISCORD_LIMBO_CHANNEL) { const channel = GetLimboChannel(client); await channel.send( @@ -62,7 +62,7 @@ client.on("interactionCreate", async (interaction) => { * If a user tries to do anything without auth, Tell them to authenticate. */ async function RequireUserAuth(interaction: CommandInteraction | SelectMenuInteraction) { - const oAuthLink = `${BotConfig.TACHI_SERVER_LOCATION}/oauth/request-auth?clientID=${BotConfig.OAUTH.CLIENT_ID}&context=${interaction.user.id}`; + const oAuthLink = `${Env.TACHI_SERVER_LOCATION}/oauth/request-auth?clientID=${Env.OAUTH_CLIENT_ID}&context=${interaction.user.id}`; const dmChannel = await interaction.user.createDM(); @@ -79,7 +79,7 @@ void (async () => { log.info(`Booting Tachi Bot ${VERSION_PRETTY}.`); // Login to discord. - await client.login(BotConfig.DISCORD.TOKEN); + await client.login(Env.DISCORD_TOKEN); log.info(`Logged in successfully to ${client.guilds.cache.size} guilds.`); diff --git a/typescript/bot/src/server/middleware.ts b/typescript/bot/src/server/middleware.ts index c95ba1e76..38046362b 100644 --- a/typescript/bot/src/server/middleware.ts +++ b/typescript/bot/src/server/middleware.ts @@ -2,7 +2,7 @@ import type { RequestHandler } from "express"; import { log } from "#utils/log"; -import { BotConfig } from "../config"; +import { Env } from "../config"; /** * Middleware that checks that a webhook request has Authorization set to @@ -32,7 +32,7 @@ export const ValidateWebhookRequest: RequestHandler = (req, res, next) => { }); } - if (value !== BotConfig.OAUTH.CLIENT_SECRET) { + if (value !== Env.OAUTH_CLIENT_SECRET) { log.warn(`Recieved invalid auth value from ${req.ip}. Has the client secret been changed?`); return res.status(403).json({ success: false, diff --git a/typescript/bot/src/server/server.ts b/typescript/bot/src/server/server.ts index 84ee12f36..215e77aed 100644 --- a/typescript/bot/src/server/server.ts +++ b/typescript/bot/src/server/server.ts @@ -6,7 +6,7 @@ import express, { type Express } from "express"; import path from "path"; import { ACTION_Register } from "../actions/register"; -import { BotConfig, Env } from "../config"; +import { Env } from "../config"; import { RequestTypes, TachiServerV1Get, TachiServerV1Request } from "../utils/fetch-tachi"; import { VERSION_PRETTY } from "../version"; import { HandleClassUpdateV1 } from "../webhook-handlers/class-update"; @@ -64,10 +64,10 @@ app.get("/oauth/callback", async (req, res) => { null, { code: req.query.code, - client_id: BotConfig.OAUTH.CLIENT_ID, - client_secret: BotConfig.OAUTH.CLIENT_SECRET, + client_id: Env.OAUTH_CLIENT_ID, + client_secret: Env.OAUTH_CLIENT_SECRET, grant_type: "authorization_code", - redirect_uri: `${BotConfig.HTTP_SERVER.URL}/oauth/callback`, + redirect_uri: `${Env.HTTP_SERVER_URL}/oauth/callback`, }, ); diff --git a/typescript/bot/src/slash-commands/commands.ts b/typescript/bot/src/slash-commands/commands.ts index 452f7fb00..2bc41b5d3 100644 --- a/typescript/bot/src/slash-commands/commands.ts +++ b/typescript/bot/src/slash-commands/commands.ts @@ -1,6 +1,6 @@ import type { SlashCommand } from "./types"; -import { BotConfig, ServerConfig } from "../config"; +import { Env, ServerConfig } from "../config"; import faq from "./commands/faq"; import invite from "./commands/invite"; import letmein from "./commands/letmein"; @@ -16,7 +16,7 @@ export const SLASH_COMMANDS: Map = new Map( }), ); -if (BotConfig.DISCORD.APPROVED_ROLE) { +if (Env.DISCORD_APPROVED_ROLE) { SLASH_COMMANDS.set("letmein", letmein); } diff --git a/typescript/bot/src/slash-commands/commands/faq.ts b/typescript/bot/src/slash-commands/commands/faq.ts index 2b4b3f7a6..9bd052fc4 100644 --- a/typescript/bot/src/slash-commands/commands/faq.ts +++ b/typescript/bot/src/slash-commands/commands/faq.ts @@ -3,7 +3,7 @@ import deepmerge from "deepmerge"; import type { SlashCommand } from "../types"; -import { BotConfig, ServerConfig } from "../../config"; +import { Env, ServerConfig } from "../../config"; import { CreateEmbed } from "../../utils/embeds"; const NEUTRAL_FAQ_ENTRIES: Record = { @@ -117,12 +117,12 @@ const KAMAI_FAQ_ENTRIES: Record = { })`, sdvx_1259: `Automation Paradise is song ID 1259. It is not a real song, and it is not supported by ${ServerConfig.NAME}. Related autopara-only songs are not supported, either.`, invites: `To invite your friends, go to your profile and select 'Invites'. From there, you can create an invite code. -Your friend can then go to ${BotConfig.TACHI_SERVER_LOCATION} and sign up with the code! You can also invite them to the discord.`, +Your friend can then go to ${Env.TACHI_SERVER_LOCATION} and sign up with the code! You can also invite them to the discord.`, }; const BOKU_FAQ_ENTRIES: Record = { usc_hard_mode: `Hard Mode windows are not supported on ${ServerConfig.NAME}. Your scores **will be ignored** if they are played on non-standard windows.`, - ir_login: `You **must** put your API Key in the password field for the Bokutachi IR, **NOT your real password!**. See instructions here: ${BotConfig.TACHI_SERVER_LOCATION}/import/beatoraja-ir. + ir_login: `You **must** put your API Key in the password field for the Bokutachi IR, **NOT your real password!**. See instructions here: ${Env.TACHI_SERVER_LOCATION}/import/beatoraja-ir. (This is because putting your real password in there is a security nightmare.)`, lr2: `Support for LR2 (the LR2Hook) are **not officially maintained** and therefore **not managed by bokutachi developers**. We urge you to use a more modern BMS client like lr2oraja, unless you have very good reason to stay on LR2. diff --git a/typescript/bot/src/slash-commands/commands/letmein.ts b/typescript/bot/src/slash-commands/commands/letmein.ts index 548c93298..e8d4edde6 100644 --- a/typescript/bot/src/slash-commands/commands/letmein.ts +++ b/typescript/bot/src/slash-commands/commands/letmein.ts @@ -1,4 +1,4 @@ -import { BotConfig } from "#config"; +import { Env } from "#config"; import { SlashCommandBuilder } from "@discordjs/builders"; import { ExpectedErr } from "bliss"; import { GuildMember } from "discord.js"; @@ -13,7 +13,7 @@ const command: SlashCommand = { .setDescription("Let yourself into the server.") .toJSON(), exec: async (interaction) => { - if (!BotConfig.DISCORD.APPROVED_ROLE) { + if (!Env.DISCORD_APPROVED_ROLE) { return null; // no-op } @@ -26,7 +26,7 @@ const command: SlashCommand = { { ip: null }, { discord_user_id: interaction.user.id, - role_id: BotConfig.DISCORD.APPROVED_ROLE, + role_id: Env.DISCORD_APPROVED_ROLE, "!member": interaction.member, }, ); diff --git a/typescript/bot/src/slash-commands/commands/sync.ts b/typescript/bot/src/slash-commands/commands/sync.ts index 8f443df1b..d6c8c4dd5 100644 --- a/typescript/bot/src/slash-commands/commands/sync.ts +++ b/typescript/bot/src/slash-commands/commands/sync.ts @@ -1,4 +1,4 @@ -import { BotConfig, ServerConfig } from "#config"; +import { Env, ServerConfig } from "#config"; import { SlashCommandBuilder } from "@discordjs/builders"; import { ExpectedErr } from "bliss"; @@ -66,7 +66,7 @@ const command: SlashCommand = { .addField("Errors", result.error_count.toString(), true) .addField( "Your Profile", - `${BotConfig.TACHI_SERVER_LOCATION}/u/${result.user_id}/games/${result.game}`, + `${Env.TACHI_SERVER_LOCATION}/u/${result.user_id}/games/${result.game}`, ); } catch (e) { if (ExpectedErr.is(e)) { diff --git a/typescript/bot/src/slash-commands/register.ts b/typescript/bot/src/slash-commands/register.ts index bb4041e9c..262b77155 100644 --- a/typescript/bot/src/slash-commands/register.ts +++ b/typescript/bot/src/slash-commands/register.ts @@ -4,12 +4,12 @@ import { log } from "#utils/log"; import { REST } from "@discordjs/rest"; import { Routes } from "discord-api-types/v9"; -import { BotConfig, Env } from "../config"; +import { Env } from "../config"; import { SLASH_COMMANDS } from "./commands"; const rest = new REST({ version: "9", -}).setToken(BotConfig.DISCORD.TOKEN); +}).setToken(Env.DISCORD_TOKEN); /** * Register our slash commands. If in prod, these @@ -34,10 +34,7 @@ export async function RegisterSlashCommands(client: Client): Promise { log.info("Registering guild slash commands."); await rest.put( - Routes.applicationGuildCommands( - client.application!.id, - BotConfig.DISCORD.SERVER_ID, - ), + Routes.applicationGuildCommands(client.application!.id, Env.DISCORD_SERVER_ID), { body: commandsArray.map((command) => command.info), }, diff --git a/typescript/bot/src/test-utils/db.ts b/typescript/bot/src/test-utils/db.ts index b067fb919..4c0562bc0 100644 --- a/typescript/bot/src/test-utils/db.ts +++ b/typescript/bot/src/test-utils/db.ts @@ -23,7 +23,7 @@ export async function createTestAccount( ): Promise { const { id } = await db .insertInto("account") - .values({ username }) + .values({ username, about: "Test user for tests." }) .returning("id") .executeTakeFirstOrThrow(); diff --git a/typescript/bot/src/utils/api-requests.ts b/typescript/bot/src/utils/api-requests.ts index 9f23deb67..aa2ddb630 100644 --- a/typescript/bot/src/utils/api-requests.ts +++ b/typescript/bot/src/utils/api-requests.ts @@ -12,7 +12,7 @@ import type { UserDocument, } from "tachi-common"; -import { BotConfig } from "#config"; +import { Env } from "#config"; import { log } from "#utils/log"; import type { ImportDeferred, ImportPollStatus, UGPTStats } from "./return-types"; @@ -161,7 +161,7 @@ export async function PerformScoreImport( throw new Error(`Failed to import scores. Your authentication with this service has expired, and a bug on their end prevents us from automatically renewing it. -Please go to ${BotConfig.TACHI_SERVER_LOCATION}/u/me/integrations/services to un-link and re-link.`); +Please go to ${Env.TACHI_SERVER_LOCATION}/u/me/integrations/services to un-link and re-link.`); } throw new Error(`Failed to import scores. ${pollRes.description}.`); diff --git a/typescript/bot/src/utils/embeds.ts b/typescript/bot/src/utils/embeds.ts index 1bc4a1782..a2c9d0672 100644 --- a/typescript/bot/src/utils/embeds.ts +++ b/typescript/bot/src/utils/embeds.ts @@ -2,7 +2,7 @@ import type { ImportDocument, integer, UserDocument } from "tachi-common"; import { MessageEmbed } from "discord.js"; -import { BotConfig, ServerConfig } from "../config"; +import { Env, ServerConfig } from "../config"; import { PrependTachiUrl } from "./fetch-tachi"; import { FormatDate, Pluralise } from "./misc"; @@ -30,7 +30,7 @@ export function CreateImportEmbed(importDoc: ImportDocument) { .addField("Errors", importDoc.errors.length.toString(), true) .addField( "Your Profile", - `${BotConfig.TACHI_SERVER_LOCATION}/u/${importDoc.userID}/games/${importDoc.game}`, + `${Env.TACHI_SERVER_LOCATION}/u/${importDoc.userID}/games/${importDoc.game}`, ); } @@ -40,5 +40,5 @@ export function CreateUserEmbed(userDoc: UserDocument) { .setThumbnail(PrependTachiUrl(`/users/${userDoc.id}/pfp`)) .setDescription(userDoc.status ?? "No status...") .addField("Join Date", FormatDate(userDoc.joinDate)) - .setURL(`${BotConfig.TACHI_SERVER_LOCATION}/u/${userDoc.username}`); + .setURL(`${Env.TACHI_SERVER_LOCATION}/u/${userDoc.username}`); } diff --git a/typescript/bot/src/utils/fetch-tachi.ts b/typescript/bot/src/utils/fetch-tachi.ts index 03e54f50e..e3a3ad760 100644 --- a/typescript/bot/src/utils/fetch-tachi.ts +++ b/typescript/bot/src/utils/fetch-tachi.ts @@ -4,7 +4,7 @@ import { log } from "#utils/log"; import fetch from "node-fetch"; import { URLSearchParams } from "url"; -import { BotConfig } from "../config"; +import { Env } from "../config"; import { VERSION_STR } from "../version"; type ApiResponseBase = { @@ -129,7 +129,7 @@ export function PrependTachiUrl(url: string, version: "1" = "1"): string { url = `/${url}`; } - return `${BotConfig.TACHI_SERVER_LOCATION}/api/v${version}${url}`; + return `${Env.TACHI_SERVER_LOCATION}/api/v${version}${url}`; } /** diff --git a/typescript/bot/src/utils/misc.ts b/typescript/bot/src/utils/misc.ts index 1d791bba3..418e536b0 100644 --- a/typescript/bot/src/utils/misc.ts +++ b/typescript/bot/src/utils/misc.ts @@ -15,7 +15,7 @@ import { type Playtype, } from "tachi-common"; -import { BotConfig } from "../config"; +import { Env } from "../config"; export function Sleep(ms: number) { return new Promise((resolve) => { @@ -99,7 +99,7 @@ export function FormatClass( * Given a game, return the discord channel it's associated with. */ export function GetGameChannel(client: Client, game: GameGroup) { - const gameChannelID = BotConfig.DISCORD.GAME_CHANNELS[game]; + const gameChannelID = Env.DISCORD_GAME_CHANNELS[game]; if (!gameChannelID) { throw new Error( @@ -123,7 +123,7 @@ export function GetGameChannel(client: Client, game: GameGroup) { } export function GetLimboChannel(client: Client) { - const limboCID = BotConfig.DISCORD.LIMBO_CHANNEL; + const limboCID = Env.DISCORD_LIMBO_CHANNEL; if (!limboCID) { throw new Error(`Attempted to get channel for #limbo, but none was registered.`); @@ -147,12 +147,12 @@ export function GetLimboChannel(client: Client) { */ export function CreateChartLink(chart: ChartDocument, game: GameGroup) { if (chart.isPrimary) { - return `${BotConfig.TACHI_SERVER_LOCATION}/games/${game}/${chart.playtype}/songs/${ + return `${Env.TACHI_SERVER_LOCATION}/games/${game}/${chart.playtype}/songs/${ chart.songID }/${encodeURIComponent(chart.difficulty)}`; } - return `${BotConfig.TACHI_SERVER_LOCATION}/games/${game}/${chart.playtype}/songs/${chart.songID}/${chart.chartID}`; + return `${Env.TACHI_SERVER_LOCATION}/games/${game}/${chart.playtype}/songs/${chart.songID}/${chart.chartID}`; } export function ConvertInputIntoGenerousRegex(input: string) { diff --git a/typescript/bot/src/webhook-handlers/class-update.ts b/typescript/bot/src/webhook-handlers/class-update.ts index c260fee88..5f0399d59 100644 --- a/typescript/bot/src/webhook-handlers/class-update.ts +++ b/typescript/bot/src/webhook-handlers/class-update.ts @@ -10,7 +10,7 @@ import { type WebhookEventClassUpdateV1, } from "tachi-common"; -import { BotConfig } from "../config"; +import { Env } from "../config"; import { client } from "../main"; import { GetUGPTStats, GetUserInfo } from "../utils/api-requests"; import { CreateEmbed } from "../utils/embeds"; @@ -62,9 +62,7 @@ export async function HandleClassUpdateV1( .setTitle( `${userDoc.username} just achieved ${newClass} in ${FormatGameGroup(game, playtype)}!`, ) - .setURL( - `${BotConfig.TACHI_SERVER_LOCATION}/users/${userDoc.username}/games/${game}/${playtype}`, - ) + .setURL(`${Env.TACHI_SERVER_LOCATION}/users/${userDoc.username}/games/${game}/${playtype}`) .setThumbnail(PrependTachiUrl(`/users/${userDoc.id}/pfp`)); if (event.old !== null) { diff --git a/typescript/bot/src/webhook-handlers/goal-achieved.ts b/typescript/bot/src/webhook-handlers/goal-achieved.ts index 75ebb1f96..85a61b310 100644 --- a/typescript/bot/src/webhook-handlers/goal-achieved.ts +++ b/typescript/bot/src/webhook-handlers/goal-achieved.ts @@ -1,4 +1,4 @@ -import { BotConfig } from "#config"; +import { Env } from "#config"; import { PrependTachiUrl } from "#utils/fetch-tachi.js"; import { log } from "#utils/log"; import { @@ -53,7 +53,7 @@ export async function HandleGoalAchievedV1( )}!`, ) .setThumbnail(PrependTachiUrl(`/users/${userDoc.id}/pfp`)) - .setURL(`${BotConfig.TACHI_SERVER_LOCATION}/u/${userDoc.username}`) + .setURL(`${Env.TACHI_SERVER_LOCATION}/u/${userDoc.username}`) .addFields( event.goals.map((e) => { const goal = goalMap.get(e.goalID)!; diff --git a/typescript/bot/src/webhook-handlers/quest-achieved.ts b/typescript/bot/src/webhook-handlers/quest-achieved.ts index 11043b779..ef7edbba7 100644 --- a/typescript/bot/src/webhook-handlers/quest-achieved.ts +++ b/typescript/bot/src/webhook-handlers/quest-achieved.ts @@ -1,4 +1,4 @@ -import { BotConfig } from "#config"; +import { Env } from "#config"; import { PrependTachiUrl } from "#utils/fetch-tachi.js"; import { log } from "#utils/log"; import { GetGameGroupConfig, type integer, type WebhookEventQuestAchievedV1 } from "tachi-common"; @@ -33,7 +33,7 @@ export async function HandleQuestAchievedV1( const embed = CreateEmbed(userDoc.id) .setThumbnail(PrependTachiUrl(`/users/${userDoc.id}/pfp`)) - .setURL(`${BotConfig.TACHI_SERVER_LOCATION}/u/${userDoc.username}`) + .setURL(`${Env.TACHI_SERVER_LOCATION}/u/${userDoc.username}`) .setTitle( `${userDoc.username} just completed the ${quest.name}${shouldShowPlaytype} quest!`, ); diff --git a/typescript/client/vite.config.ts b/typescript/client/vite.config.ts index c9fd4337b..7c577c68a 100644 --- a/typescript/client/vite.config.ts +++ b/typescript/client/vite.config.ts @@ -16,10 +16,11 @@ export default defineConfig({ css: { preprocessorOptions: { scss: { - additionalData: - process.env.VITE_TCHIC_MODE === "boku" - ? "$primary: #527acc;" - : "$primary: #e61c6e;", + additionalData: + process.env.VITE_TCHIC_MODE === "boku" + ? "$primary: #527acc;" + : "$primary: #e61c6e;", + silenceDeprecations: ["import", "global-builtin", "color-functions", "if-function"], }, }, }, diff --git a/typescript/common/package.json b/typescript/common/package.json index eace8dcfd..3c95739af 100644 --- a/typescript/common/package.json +++ b/typescript/common/package.json @@ -12,7 +12,7 @@ } }, "scripts": { - "test": "tap", + "test": "vitest", "build": "tsgo -p tsconfig.build.json", "prepublishOnly": "tsgo -p tsconfig.build.json", "lint": "eslint .", @@ -43,9 +43,7 @@ }, "devDependencies": { "@typescript/native-preview": "catalog:", - "@types/tap": "catalog:", "eslint-config-tachi": "workspace:*", - "tap": "workspace:*", "typescript": "catalog:" }, "nyc": { diff --git a/typescript/common/src/index.ts b/typescript/common/src/index.ts index a4dc3b523..4a90fad20 100644 --- a/typescript/common/src/index.ts +++ b/typescript/common/src/index.ts @@ -8,6 +8,7 @@ export * from "./constants/grade-boundaries"; export * as StaticConfig from "./constants/import-types"; export * from "./constants/permissions"; export * as Schemas from "./lib/schemas"; +export * from "./lib/zod-schemas"; export * from "./seeds/types"; export * from "./types"; export * from "./utils/util"; diff --git a/typescript/common/src/lib/zod-schemas.ts b/typescript/common/src/lib/zod-schemas.ts new file mode 100644 index 000000000..e35468479 --- /dev/null +++ b/typescript/common/src/lib/zod-schemas.ts @@ -0,0 +1,4 @@ +import { ALL_PERMISSIONS } from "#constants/permissions.js"; +import z from "zod"; + +export const zodPermission = z.enum(Object.keys(ALL_PERMISSIONS)); diff --git a/typescript/db-cli/src/index.ts b/typescript/db-cli/src/index.ts index c02f82ab5..2a8446875 100755 --- a/typescript/db-cli/src/index.ts +++ b/typescript/db-cli/src/index.ts @@ -15,11 +15,11 @@ import { applyMigrations, getMigrationInfo, revertLastMigration } from "tachi-db const DEFAULT_MIGRATIONS_DIR = "/tachi/db/migrations"; function getConnectionString(opts: { databaseUrl?: string }): string { - const url = opts.databaseUrl ?? process.env.DATABASE_URL; + const url = opts.databaseUrl ?? process.env.POSTGRES_URL; if (!url) { console.error( - "[migrate] No database URL provided. Set DATABASE_URL, or pass --database-url.", + "[migrate] No database URL provided. Set POSTGRES_URL, or pass --database-url.", ); process.exit(1); } @@ -88,7 +88,7 @@ const program = new Command(); program .name("tachi-migrate") .description("Database migration CLI, modelled after sqlx migrate.") - .option("--database-url ", "Postgres connection string (overrides DATABASE_URL env var)"); + .option("--database-url ", "Postgres connection string (overrides POSTGRES_URL env var)"); // --------------------------------------------------------------------------- // database subcommands @@ -98,7 +98,7 @@ const databaseCmd = program.command("database").description("Create or drop the databaseCmd .command("create") - .description("Create the database specified by DATABASE_URL.") + .description("Create the database specified by POSTGRES_URL.") .action(async () => { const parentOpts = program.opts<{ databaseUrl?: string }>(); const connectionString = getConnectionString(parentOpts); @@ -122,7 +122,7 @@ databaseCmd databaseCmd .command("drop") - .description("Drop the database specified by DATABASE_URL.") + .description("Drop the database specified by POSTGRES_URL.") .action(async () => { const parentOpts = program.opts<{ databaseUrl?: string }>(); const connectionString = getConnectionString(parentOpts); diff --git a/typescript/db/src/generated/index.ts b/typescript/db/src/generated/index.ts index 4f1abcb13..284a47fe9 100644 --- a/typescript/db/src/generated/index.ts +++ b/typescript/db/src/generated/index.ts @@ -35,7 +35,6 @@ export { type priv_password_reset_token_token, type default as PrivPasswordReset export { type song_id, type default as SongTable, type Song, type NewSong, type SongUpdate } from './public/Song'; export { type account_username_change_row_id, type default as AccountUsernameChangeTable, type AccountUsernameChange, type NewAccountUsernameChange, type AccountUsernameChangeUpdate } from './public/AccountUsernameChange'; export { type default as MigrationTable, type Migration, type NewMigration, type MigrationUpdate } from './public/Migration'; -export { type account_badge_badge, type default as AccountBadgeTable, type AccountBadge, type NewAccountBadge, type AccountBadgeUpdate } from './public/AccountBadge'; export { type action_row_id, type default as ActionTable, type Action, type NewAction, type ActionUpdate } from './public/Action'; export { type priv_svc_kai_auth_token_service, type default as PrivSvcKaiAuthTokenTable, type PrivSvcKaiAuthToken, type NewPrivSvcKaiAuthToken, type PrivSvcKaiAuthTokenUpdate } from './public/PrivSvcKaiAuthToken'; export { type chart_id, type default as ChartTable, type Chart, type NewChart, type ChartUpdate } from './public/Chart'; diff --git a/typescript/db/src/generated/public/Account.ts b/typescript/db/src/generated/public/Account.ts index 7cc6eb5e8..ffa7282a3 100644 --- a/typescript/db/src/generated/public/Account.ts +++ b/typescript/db/src/generated/public/Account.ts @@ -27,9 +27,15 @@ export default interface AccountTable { sm_twitch: ColumnType; + bd_alpha: ColumnType; + + bd_beta: ColumnType; + + bd_dev_team: ColumnType; + joined: ColumnType; - about: ColumnType; + about: ColumnType; status: ColumnType; @@ -40,6 +46,8 @@ export default interface AccountTable { last_seen: ColumnType; auth_level: ColumnType; + + is_supporter: ColumnType; } export type Account = Selectable; diff --git a/typescript/db/src/generated/public/PrivApiClient.ts b/typescript/db/src/generated/public/PrivApiClient.ts index ad14019e7..92d3cb1ca 100644 --- a/typescript/db/src/generated/public/PrivApiClient.ts +++ b/typescript/db/src/generated/public/PrivApiClient.ts @@ -36,6 +36,12 @@ export default interface PrivApiClientTable { api_key_template: ColumnType; api_key_filename: ColumnType; + + webhook_uri: ColumnType; + + redirect_uri: ColumnType; + + is_builtin: ColumnType; } export type PrivApiClient = Selectable; diff --git a/typescript/db/src/generated/public/PublicSchema.ts b/typescript/db/src/generated/public/PublicSchema.ts index 9c4da698a..a5279e1d4 100644 --- a/typescript/db/src/generated/public/PublicSchema.ts +++ b/typescript/db/src/generated/public/PublicSchema.ts @@ -35,7 +35,6 @@ import type { default as PrivPasswordResetTokenTable } from './PrivPasswordReset import type { default as SongTable } from './Song'; import type { default as AccountUsernameChangeTable } from './AccountUsernameChange'; import type { default as MigrationTable } from './Migration'; -import type { default as AccountBadgeTable } from './AccountBadge'; import type { default as ActionTable } from './Action'; import type { default as PrivSvcKaiAuthTokenTable } from './PrivSvcKaiAuthToken'; import type { default as ChartTable } from './Chart'; @@ -139,8 +138,6 @@ export default interface PublicSchema { _migration: MigrationTable; - account_badge: AccountBadgeTable; - action: ActionTable; priv_svc_kai_auth_token: PrivSvcKaiAuthTokenTable; diff --git a/typescript/eslint-config/index.js b/typescript/eslint-config/index.js index 478ce1eca..658e8d4ee 100644 --- a/typescript/eslint-config/index.js +++ b/typescript/eslint-config/index.js @@ -13,6 +13,9 @@ const base = tseslint.config( eslint.configs.recommended, ...tseslint.configs.recommended, pluginPerfectionist.configs["recommended-natural"], + { + ignores: ["**/*.oldtest.ts"], + }, { name: "zenith/base", files: ["**/*.{ts,tsx}"], diff --git a/typescript/github-bot/.gitignore b/typescript/github-bot/.gitignore index c1fa1ef44..bc42fd64f 100644 --- a/typescript/github-bot/.gitignore +++ b/typescript/github-bot/.gitignore @@ -1,2 +1 @@ -js -!example/.env \ No newline at end of file +js \ No newline at end of file diff --git a/typescript/github-bot/tsconfig.tsbuildinfo b/typescript/github-bot/tsconfig.tsbuildinfo deleted file mode 100644 index 968290f7a..000000000 --- a/typescript/github-bot/tsconfig.tsbuildinfo +++ /dev/null @@ -1 +0,0 @@ -{"version":"7.0.0-dev.20260309.1","root":[159,336],"fileNames":["lib.es5.d.ts","lib.es2015.d.ts","lib.es2016.d.ts","lib.es2017.d.ts","lib.es2018.d.ts","lib.es2019.d.ts","lib.es2020.d.ts","lib.dom.d.ts","lib.es2015.core.d.ts","lib.es2015.collection.d.ts","lib.es2015.generator.d.ts","lib.es2015.iterable.d.ts","lib.es2015.promise.d.ts","lib.es2015.proxy.d.ts","lib.es2015.reflect.d.ts","lib.es2015.symbol.d.ts","lib.es2015.symbol.wellknown.d.ts","lib.es2016.array.include.d.ts","lib.es2016.intl.d.ts","lib.es2017.arraybuffer.d.ts","lib.es2017.date.d.ts","lib.es2017.object.d.ts","lib.es2017.sharedmemory.d.ts","lib.es2017.string.d.ts","lib.es2017.intl.d.ts","lib.es2017.typedarrays.d.ts","lib.es2018.asyncgenerator.d.ts","lib.es2018.asynciterable.d.ts","lib.es2018.intl.d.ts","lib.es2018.promise.d.ts","lib.es2018.regexp.d.ts","lib.es2019.array.d.ts","lib.es2019.object.d.ts","lib.es2019.string.d.ts","lib.es2019.symbol.d.ts","lib.es2019.intl.d.ts","lib.es2020.bigint.d.ts","lib.es2020.date.d.ts","lib.es2020.promise.d.ts","lib.es2020.sharedmemory.d.ts","lib.es2020.string.d.ts","lib.es2020.symbol.wellknown.d.ts","lib.es2020.intl.d.ts","lib.es2020.number.d.ts","lib.decorators.d.ts","lib.decorators.legacy.d.ts","../node_modules/@types/node/compatibility/disposable.d.ts","../node_modules/@types/node/compatibility/indexable.d.ts","../node_modules/@types/node/compatibility/iterators.d.ts","../node_modules/@types/node/compatibility/index.d.ts","../node_modules/@types/node/globals.typedarray.d.ts","../node_modules/@types/node/buffer.buffer.d.ts","../node_modules/@types/node/globals.d.ts","../node_modules/@types/node/web-globals/abortcontroller.d.ts","../node_modules/@types/node/web-globals/domexception.d.ts","../node_modules/@types/node/web-globals/events.d.ts","../node_modules/buffer/index.d.ts","../node_modules/undici-types/header.d.ts","../node_modules/undici-types/readable.d.ts","../node_modules/undici-types/file.d.ts","../node_modules/undici-types/fetch.d.ts","../node_modules/undici-types/formdata.d.ts","../node_modules/undici-types/connector.d.ts","../node_modules/undici-types/client.d.ts","../node_modules/undici-types/errors.d.ts","../node_modules/undici-types/dispatcher.d.ts","../node_modules/undici-types/global-dispatcher.d.ts","../node_modules/undici-types/global-origin.d.ts","../node_modules/undici-types/pool-stats.d.ts","../node_modules/undici-types/pool.d.ts","../node_modules/undici-types/handlers.d.ts","../node_modules/undici-types/balanced-pool.d.ts","../node_modules/undici-types/agent.d.ts","../node_modules/undici-types/mock-interceptor.d.ts","../node_modules/undici-types/mock-agent.d.ts","../node_modules/undici-types/mock-client.d.ts","../node_modules/undici-types/mock-pool.d.ts","../node_modules/undici-types/mock-errors.d.ts","../node_modules/undici-types/proxy-agent.d.ts","../node_modules/undici-types/env-http-proxy-agent.d.ts","../node_modules/undici-types/retry-handler.d.ts","../node_modules/undici-types/retry-agent.d.ts","../node_modules/undici-types/api.d.ts","../node_modules/undici-types/interceptors.d.ts","../node_modules/undici-types/util.d.ts","../node_modules/undici-types/cookies.d.ts","../node_modules/undici-types/patch.d.ts","../node_modules/undici-types/websocket.d.ts","../node_modules/undici-types/eventsource.d.ts","../node_modules/undici-types/filereader.d.ts","../node_modules/undici-types/diagnostics-channel.d.ts","../node_modules/undici-types/content-type.d.ts","../node_modules/undici-types/cache.d.ts","../node_modules/undici-types/index.d.ts","../node_modules/@types/node/web-globals/fetch.d.ts","../node_modules/@types/node/assert.d.ts","../node_modules/@types/node/assert/strict.d.ts","../node_modules/@types/node/async_hooks.d.ts","../node_modules/@types/node/buffer.d.ts","../node_modules/@types/node/child_process.d.ts","../node_modules/@types/node/cluster.d.ts","../node_modules/@types/node/console.d.ts","../node_modules/@types/node/constants.d.ts","../node_modules/@types/node/crypto.d.ts","../node_modules/@types/node/dgram.d.ts","../node_modules/@types/node/diagnostics_channel.d.ts","../node_modules/@types/node/dns.d.ts","../node_modules/@types/node/dns/promises.d.ts","../node_modules/@types/node/domain.d.ts","../node_modules/@types/node/events.d.ts","../node_modules/@types/node/fs.d.ts","../node_modules/@types/node/fs/promises.d.ts","../node_modules/@types/node/http.d.ts","../node_modules/@types/node/http2.d.ts","../node_modules/@types/node/https.d.ts","../node_modules/@types/node/inspector.generated.d.ts","../node_modules/@types/node/module.d.ts","../node_modules/@types/node/net.d.ts","../node_modules/@types/node/os.d.ts","../node_modules/@types/node/path.d.ts","../node_modules/@types/node/perf_hooks.d.ts","../node_modules/@types/node/process.d.ts","../node_modules/@types/node/punycode.d.ts","../node_modules/@types/node/querystring.d.ts","../node_modules/@types/node/readline.d.ts","../node_modules/@types/node/readline/promises.d.ts","../node_modules/@types/node/repl.d.ts","../node_modules/@types/node/sea.d.ts","../node_modules/@types/node/stream.d.ts","../node_modules/@types/node/stream/promises.d.ts","../node_modules/@types/node/stream/consumers.d.ts","../node_modules/@types/node/stream/web.d.ts","../node_modules/@types/node/string_decoder.d.ts","../node_modules/@types/node/test.d.ts","../node_modules/@types/node/timers.d.ts","../node_modules/@types/node/timers/promises.d.ts","../node_modules/@types/node/tls.d.ts","../node_modules/@types/node/trace_events.d.ts","../node_modules/@types/node/tty.d.ts","../node_modules/@types/node/url.d.ts","../node_modules/@types/node/util.d.ts","../node_modules/@types/node/v8.d.ts","../node_modules/@types/node/vm.d.ts","../node_modules/@types/node/wasi.d.ts","../node_modules/@types/node/worker_threads.d.ts","../node_modules/@types/node/zlib.d.ts","../node_modules/@types/node/index.d.ts","./node_modules/dotenv/lib/main.d.ts","../node_modules/prudence/js/error.d.ts","../node_modules/@types/range-parser/index.d.ts","../node_modules/@types/qs/index.d.ts","../node_modules/@types/express-serve-static-core/index.d.ts","../node_modules/prudence/js/types.d.ts","../node_modules/prudence/js/validator.d.ts","../node_modules/prudence/js/static.d.ts","../node_modules/prudence/js/util.d.ts","../node_modules/prudence/js/assert.d.ts","../node_modules/prudence/js/main.d.ts","./src/config.ts","../node_modules/before-after-hook/index.d.ts","../node_modules/@octokit/request/node_modules/@octokit/types/dist-types/RequestMethod.d.ts","../node_modules/@octokit/request/node_modules/@octokit/types/dist-types/Url.d.ts","../node_modules/@octokit/request/node_modules/@octokit/types/dist-types/Fetch.d.ts","../node_modules/@octokit/request/node_modules/@octokit/types/dist-types/RequestRequestOptions.d.ts","../node_modules/@octokit/request/node_modules/@octokit/types/dist-types/RequestHeaders.d.ts","../node_modules/@octokit/request/node_modules/@octokit/types/dist-types/RequestParameters.d.ts","../node_modules/@octokit/request/node_modules/@octokit/types/dist-types/EndpointOptions.d.ts","../node_modules/@octokit/request/node_modules/@octokit/types/dist-types/ResponseHeaders.d.ts","../node_modules/@octokit/request/node_modules/@octokit/types/dist-types/OctokitResponse.d.ts","../node_modules/@octokit/request/node_modules/@octokit/types/dist-types/EndpointDefaults.d.ts","../node_modules/@octokit/request/node_modules/@octokit/types/dist-types/RequestOptions.d.ts","../node_modules/@octokit/request/node_modules/@octokit/types/dist-types/Route.d.ts","../node_modules/@octokit/request/node_modules/@octokit/types/node_modules/@octokit/openapi-types/types.d.ts","../node_modules/@octokit/request/node_modules/@octokit/types/dist-types/generated/Endpoints.d.ts","../node_modules/@octokit/request/node_modules/@octokit/types/dist-types/EndpointInterface.d.ts","../node_modules/@octokit/request/node_modules/@octokit/types/dist-types/RequestInterface.d.ts","../node_modules/@octokit/request/node_modules/@octokit/types/dist-types/AuthInterface.d.ts","../node_modules/@octokit/request/node_modules/@octokit/types/dist-types/RequestError.d.ts","../node_modules/@octokit/request/node_modules/@octokit/types/dist-types/StrategyInterface.d.ts","../node_modules/@octokit/request/node_modules/@octokit/types/dist-types/VERSION.d.ts","../node_modules/@octokit/request/node_modules/@octokit/types/dist-types/GetResponseTypeFromEndpointMethod.d.ts","../node_modules/@octokit/request/node_modules/@octokit/types/dist-types/index.d.ts","../node_modules/@octokit/request/dist-types/index.d.ts","../node_modules/@octokit/graphql/dist-types/types.d.ts","../node_modules/@octokit/graphql/dist-types/error.d.ts","../node_modules/@octokit/graphql/dist-types/index.d.ts","../node_modules/@octokit/request-error/dist-types/types.d.ts","../node_modules/@octokit/request-error/dist-types/index.d.ts","../node_modules/@octokit/core/dist-types/types.d.ts","../node_modules/@octokit/core/dist-types/index.d.ts","../node_modules/@octokit/oauth-methods/dist-types/version.d.ts","../node_modules/@octokit/oauth-authorization-url/dist-types/types.d.ts","../node_modules/@octokit/oauth-authorization-url/dist-types/index.d.ts","../node_modules/@octokit/oauth-methods/dist-types/get-web-flow-authorization-url.d.ts","../node_modules/@octokit/oauth-methods/dist-types/types.d.ts","../node_modules/@octokit/oauth-methods/dist-types/exchange-web-flow-code.d.ts","../node_modules/@octokit/oauth-methods/dist-types/create-device-code.d.ts","../node_modules/@octokit/oauth-methods/dist-types/exchange-device-code.d.ts","../node_modules/@octokit/oauth-methods/dist-types/check-token.d.ts","../node_modules/@octokit/oauth-methods/dist-types/refresh-token.d.ts","../node_modules/@octokit/oauth-methods/dist-types/scope-token.d.ts","../node_modules/@octokit/oauth-methods/dist-types/reset-token.d.ts","../node_modules/@octokit/oauth-methods/dist-types/delete-token.d.ts","../node_modules/@octokit/oauth-methods/dist-types/delete-authorization.d.ts","../node_modules/@octokit/oauth-methods/dist-types/index.d.ts","../node_modules/@octokit/auth-oauth-device/dist-types/types.d.ts","../node_modules/@octokit/auth-oauth-device/dist-types/index.d.ts","../node_modules/@octokit/auth-oauth-user/dist-types/types.d.ts","../node_modules/@octokit/auth-oauth-user/dist-types/requires-basic-auth.d.ts","../node_modules/@octokit/auth-oauth-user/dist-types/index.d.ts","../node_modules/@octokit/auth-oauth-app/dist-types/types.d.ts","../node_modules/@octokit/auth-oauth-app/dist-types/index.d.ts","../node_modules/@octokit/oauth-app/dist-types/oauth-app-octokit.d.ts","../node_modules/@octokit/oauth-app/dist-types/types.d.ts","../node_modules/@octokit/oauth-app/dist-types/methods/get-user-octokit.d.ts","../node_modules/@octokit/oauth-app/dist-types/methods/get-web-flow-authorization-url.d.ts","../node_modules/@octokit/oauth-app/dist-types/methods/create-token.d.ts","../node_modules/@octokit/oauth-app/dist-types/methods/check-token.d.ts","../node_modules/@octokit/oauth-app/dist-types/methods/reset-token.d.ts","../node_modules/@octokit/oauth-app/dist-types/methods/refresh-token.d.ts","../node_modules/@octokit/oauth-app/dist-types/methods/scope-token.d.ts","../node_modules/@octokit/oauth-app/dist-types/methods/delete-token.d.ts","../node_modules/@octokit/oauth-app/dist-types/methods/delete-authorization.d.ts","../node_modules/@octokit/oauth-app/dist-types/middleware/types.d.ts","../node_modules/@octokit/oauth-app/dist-types/middleware/handle-request.d.ts","../node_modules/@octokit/oauth-app/dist-types/middleware/unknown-route-response.d.ts","../node_modules/@octokit/oauth-app/dist-types/middleware/node/index.d.ts","../node_modules/@octokit/oauth-app/dist-types/middleware/node/send-response.d.ts","../node_modules/@octokit/oauth-app/dist-types/middleware/web-worker/index.d.ts","../node_modules/@types/aws-lambda/common/api-gateway.d.ts","../node_modules/@types/aws-lambda/common/cloudfront.d.ts","../node_modules/@types/aws-lambda/handler.d.ts","../node_modules/@types/aws-lambda/trigger/alb.d.ts","../node_modules/@types/aws-lambda/trigger/api-gateway-proxy.d.ts","../node_modules/@types/aws-lambda/trigger/api-gateway-authorizer.d.ts","../node_modules/@types/aws-lambda/trigger/appsync-resolver.d.ts","../node_modules/@types/aws-lambda/trigger/autoscaling.d.ts","../node_modules/@types/aws-lambda/trigger/cloudformation-custom-resource.d.ts","../node_modules/@types/aws-lambda/trigger/cdk-custom-resource.d.ts","../node_modules/@types/aws-lambda/trigger/cloudfront-request.d.ts","../node_modules/@types/aws-lambda/trigger/cloudfront-response.d.ts","../node_modules/@types/aws-lambda/trigger/cloudwatch-alarm.d.ts","../node_modules/@types/aws-lambda/trigger/eventbridge.d.ts","../node_modules/@types/aws-lambda/trigger/cloudwatch-events.d.ts","../node_modules/@types/aws-lambda/trigger/cloudwatch-logs.d.ts","../node_modules/@types/aws-lambda/trigger/codebuild-cloudwatch-state.d.ts","../node_modules/@types/aws-lambda/trigger/codecommit.d.ts","../node_modules/@types/aws-lambda/trigger/codepipeline.d.ts","../node_modules/@types/aws-lambda/trigger/codepipeline-cloudwatch-action.d.ts","../node_modules/@types/aws-lambda/trigger/codepipeline-cloudwatch-pipeline.d.ts","../node_modules/@types/aws-lambda/trigger/codepipeline-cloudwatch-stage.d.ts","../node_modules/@types/aws-lambda/trigger/codepipeline-cloudwatch.d.ts","../node_modules/@types/aws-lambda/trigger/cognito-user-pool-trigger/_common.d.ts","../node_modules/@types/aws-lambda/trigger/cognito-user-pool-trigger/create-auth-challenge.d.ts","../node_modules/@types/aws-lambda/trigger/cognito-user-pool-trigger/custom-email-sender.d.ts","../node_modules/@types/aws-lambda/trigger/cognito-user-pool-trigger/custom-message.d.ts","../node_modules/@types/aws-lambda/trigger/cognito-user-pool-trigger/custom-sms-sender.d.ts","../node_modules/@types/aws-lambda/trigger/cognito-user-pool-trigger/define-auth-challenge.d.ts","../node_modules/@types/aws-lambda/trigger/cognito-user-pool-trigger/inbound-federation.d.ts","../node_modules/@types/aws-lambda/trigger/cognito-user-pool-trigger/post-authentication.d.ts","../node_modules/@types/aws-lambda/trigger/cognito-user-pool-trigger/post-confirmation.d.ts","../node_modules/@types/aws-lambda/trigger/cognito-user-pool-trigger/pre-authentication.d.ts","../node_modules/@types/aws-lambda/trigger/cognito-user-pool-trigger/pre-signup.d.ts","../node_modules/@types/aws-lambda/trigger/cognito-user-pool-trigger/pre-token-generation.d.ts","../node_modules/@types/aws-lambda/trigger/cognito-user-pool-trigger/pre-token-generation-v2.d.ts","../node_modules/@types/aws-lambda/trigger/cognito-user-pool-trigger/pre-token-generation-v3.d.ts","../node_modules/@types/aws-lambda/trigger/cognito-user-pool-trigger/user-migration.d.ts","../node_modules/@types/aws-lambda/trigger/cognito-user-pool-trigger/verify-auth-challenge-response.d.ts","../node_modules/@types/aws-lambda/trigger/cognito-user-pool-trigger/index.d.ts","../node_modules/@types/aws-lambda/trigger/connect-contact-flow.d.ts","../node_modules/@types/aws-lambda/trigger/dynamodb-stream.d.ts","../node_modules/@types/aws-lambda/trigger/guard-duty-event-notification.d.ts","../node_modules/@types/aws-lambda/trigger/iot.d.ts","../node_modules/@types/aws-lambda/trigger/iot-authorizer.d.ts","../node_modules/@types/aws-lambda/trigger/kinesis-firehose-transformation.d.ts","../node_modules/@types/aws-lambda/trigger/kinesis-stream.d.ts","../node_modules/@types/aws-lambda/trigger/lambda-function-url.d.ts","../node_modules/@types/aws-lambda/trigger/lex.d.ts","../node_modules/@types/aws-lambda/trigger/lex-v2.d.ts","../node_modules/@types/aws-lambda/trigger/amplify-resolver.d.ts","../node_modules/@types/aws-lambda/trigger/msk.d.ts","../node_modules/@types/aws-lambda/trigger/s3.d.ts","../node_modules/@types/aws-lambda/trigger/s3-batch.d.ts","../node_modules/@types/aws-lambda/trigger/s3-event-notification.d.ts","../node_modules/@types/aws-lambda/trigger/secretsmanager.d.ts","../node_modules/@types/aws-lambda/trigger/self-managed-kafka.d.ts","../node_modules/@types/aws-lambda/trigger/ses.d.ts","../node_modules/@types/aws-lambda/trigger/sns.d.ts","../node_modules/@types/aws-lambda/trigger/sqs.d.ts","../node_modules/@types/aws-lambda/trigger/transfer-family-authorizer.d.ts","../node_modules/@types/aws-lambda/index.d.ts","../node_modules/@octokit/oauth-app/dist-types/middleware/aws-lambda/api-gateway-v2.d.ts","../node_modules/@octokit/oauth-app/dist-types/index.d.ts","../node_modules/@octokit/openapi-webhooks-types/types.d.ts","../node_modules/@octokit/webhooks/dist-types/generated/webhook-identifiers.d.ts","../node_modules/@octokit/webhooks/dist-types/create-logger.d.ts","../node_modules/@octokit/webhooks/dist-types/generated/webhook-names.d.ts","../node_modules/@octokit/webhooks/dist-types/types.d.ts","../node_modules/@octokit/webhooks/dist-types/event-handler/index.d.ts","../node_modules/@octokit/webhooks/dist-types/middleware/types.d.ts","../node_modules/@octokit/webhooks/dist-types/middleware/node/index.d.ts","../node_modules/@octokit/webhooks/dist-types/middleware/web/index.d.ts","../node_modules/@octokit/webhooks/dist-types/index.d.ts","../node_modules/@octokit/types/dist-types/RequestMethod.d.ts","../node_modules/@octokit/types/dist-types/Url.d.ts","../node_modules/@octokit/types/dist-types/Fetch.d.ts","../node_modules/@octokit/types/dist-types/RequestRequestOptions.d.ts","../node_modules/@octokit/types/dist-types/RequestHeaders.d.ts","../node_modules/@octokit/types/dist-types/RequestParameters.d.ts","../node_modules/@octokit/types/dist-types/EndpointOptions.d.ts","../node_modules/@octokit/types/dist-types/ResponseHeaders.d.ts","../node_modules/@octokit/types/dist-types/OctokitResponse.d.ts","../node_modules/@octokit/types/dist-types/EndpointDefaults.d.ts","../node_modules/@octokit/types/dist-types/RequestOptions.d.ts","../node_modules/@octokit/types/dist-types/Route.d.ts","../node_modules/@octokit/openapi-types/types.d.ts","../node_modules/@octokit/types/dist-types/generated/Endpoints.d.ts","../node_modules/@octokit/types/dist-types/EndpointInterface.d.ts","../node_modules/@octokit/types/dist-types/RequestInterface.d.ts","../node_modules/@octokit/types/dist-types/AuthInterface.d.ts","../node_modules/@octokit/types/dist-types/RequestError.d.ts","../node_modules/@octokit/types/dist-types/StrategyInterface.d.ts","../node_modules/@octokit/types/dist-types/VERSION.d.ts","../node_modules/@octokit/types/dist-types/GetResponseTypeFromEndpointMethod.d.ts","../node_modules/@octokit/types/dist-types/index.d.ts","../node_modules/@octokit/app/dist-types/types.d.ts","../node_modules/@octokit/app/dist-types/middleware/node/index.d.ts","../node_modules/@octokit/app/dist-types/index.d.ts","../node_modules/@types/http-errors/index.d.ts","../node_modules/@types/serve-static/index.d.ts","../node_modules/@types/connect/index.d.ts","../node_modules/@types/body-parser/index.d.ts","../node_modules/@types/express/index.d.ts","./node_modules/node-fetch/@types/index.d.ts","../node_modules/@octokit/webhooks-types/schema.d.ts","./src/main.ts","./node_modules/@types/node-fetch/node_modules/form-data/index.d.ts","./node_modules/@types/node-fetch/externals.d.ts","./node_modules/@types/node-fetch/index.d.ts","../node_modules/@types/bcryptjs/index.d.ts","../node_modules/@types/bson/index.d.ts","../node_modules/@types/bunyan/index.d.ts","../node_modules/@types/express-session/index.d.ts","../node_modules/@types/redis/index.d.ts","../node_modules/@types/connect-redis/index.d.ts","../node_modules/@types/cookiejar/index.d.ts","../node_modules/@types/ms/index.d.ts","../node_modules/@types/debug/index.d.ts","../node_modules/@types/estree/index.d.ts","../node_modules/@types/json-schema/index.d.ts","../node_modules/@types/eslint/use-at-your-own-risk.d.ts","../node_modules/@types/eslint/index.d.ts","../node_modules/@eslint/core/dist/cjs/types.d.cts","../node_modules/eslint/lib/types/use-at-your-own-risk.d.ts","../node_modules/eslint/lib/types/index.d.ts","../node_modules/@types/eslint__js/index.d.ts","../node_modules/@types/express-rate-limit/index.d.ts","../node_modules/@types/glob/index.d.ts","../node_modules/@types/google-protobuf/index.d.ts","../node_modules/@types/unist/index.d.ts","../node_modules/@types/hast/index.d.ts","../node_modules/@types/history/DOMUtils.d.ts","../node_modules/@types/history/createBrowserHistory.d.ts","../node_modules/@types/history/createHashHistory.d.ts","../node_modules/@types/history/createMemoryHistory.d.ts","../node_modules/@types/history/LocationUtils.d.ts","../node_modules/@types/history/PathUtils.d.ts","../node_modules/@types/history/index.d.ts","../node_modules/@types/humanize-duration/index.d.ts","../node_modules/@types/lodash/common/common.d.ts","../node_modules/@types/lodash/common/array.d.ts","../node_modules/@types/lodash/common/collection.d.ts","../node_modules/@types/lodash/common/date.d.ts","../node_modules/@types/lodash/common/function.d.ts","../node_modules/@types/lodash/common/lang.d.ts","../node_modules/@types/lodash/common/math.d.ts","../node_modules/@types/lodash/common/number.d.ts","../node_modules/@types/lodash/common/object.d.ts","../node_modules/@types/lodash/common/seq.d.ts","../node_modules/@types/lodash/common/string.d.ts","../node_modules/@types/lodash/common/util.d.ts","../node_modules/@types/lodash/index.d.ts","../node_modules/@types/lodash.get/index.d.ts","../node_modules/@types/luxon/src/zone.d.ts","../node_modules/@types/luxon/src/misc.d.ts","../node_modules/@types/luxon/src/duration.d.ts","../node_modules/@types/luxon/src/interval.d.ts","../node_modules/@types/luxon/src/datetime.d.ts","../node_modules/@types/luxon/src/info.d.ts","../node_modules/@types/luxon/src/settings.d.ts","../node_modules/@types/luxon/src/luxon.d.ts","../node_modules/@types/luxon/index.d.ts","../node_modules/@types/mdast/index.d.ts","../node_modules/@types/mdurl/encode.d.ts","../node_modules/@types/mdurl/decode.d.ts","../node_modules/@types/mdurl/parse.d.ts","../node_modules/@types/mdurl/format.d.ts","../node_modules/@types/mdurl/index.d.ts","../node_modules/@types/methods/index.d.ts","../node_modules/@types/minimist/index.d.ts","../node_modules/@types/mkdirp/index.d.ts","../node_modules/@types/mongodb/index.d.ts","../node_modules/@types/multer/index.d.ts","../node_modules/@types/natural-compare/index.d.ts","../node_modules/@types/nodemailer/lib/dkim/index.d.ts","../node_modules/@types/nodemailer/lib/mailer/mail-message.d.ts","../node_modules/@types/nodemailer/lib/xoauth2/index.d.ts","../node_modules/@types/nodemailer/lib/mailer/index.d.ts","../node_modules/@types/nodemailer/lib/mime-node/index.d.ts","../node_modules/@types/nodemailer/lib/smtp-connection/index.d.ts","../node_modules/@types/nodemailer/lib/shared/index.d.ts","../node_modules/@types/nodemailer/lib/json-transport/index.d.ts","../node_modules/@types/nodemailer/lib/sendmail-transport/index.d.ts","../node_modules/@types/nodemailer/lib/ses-transport/index.d.ts","../node_modules/@types/nodemailer/lib/smtp-pool/index.d.ts","../node_modules/@types/nodemailer/lib/smtp-transport/index.d.ts","../node_modules/@types/nodemailer/lib/stream-transport/index.d.ts","../node_modules/@types/nodemailer/index.d.ts","../node_modules/@types/normalize-package-data/index.d.ts","../node_modules/@types/parse-json/index.d.ts","../node_modules/pg-types/index.d.ts","../node_modules/pg-protocol/dist/messages.d.ts","../node_modules/pg-protocol/dist/serializer.d.ts","../node_modules/pg-protocol/dist/parser.d.ts","../node_modules/pg-protocol/dist/index.d.ts","../node_modules/@types/pg/lib/type-overrides.d.ts","../node_modules/@types/pg/index.d.ts","../node_modules/@types/prop-types/index.d.ts","../node_modules/@types/rate-limit-redis/node_modules/@types/express-rate-limit/index.d.ts","../node_modules/@types/rate-limit-redis/index.d.ts","../node_modules/@types/react/global.d.ts","../node_modules/csstype/index.d.ts","../node_modules/@types/react/index.d.ts","../node_modules/@types/react-dom/index.d.ts","../node_modules/@types/react-google-recaptcha/index.d.ts","../node_modules/@types/react-router/index.d.ts","../node_modules/@types/react-router-bootstrap/lib/LinkContainer.d.ts","../node_modules/@types/react-router-bootstrap/index.d.ts","../node_modules/@types/react-router-dom/index.d.ts","../node_modules/@types/react-transition-group/config.d.ts","../node_modules/@types/react-transition-group/Transition.d.ts","../node_modules/@types/react-transition-group/CSSTransition.d.ts","../node_modules/@types/react-transition-group/SwitchTransition.d.ts","../node_modules/@types/react-transition-group/TransitionGroup.d.ts","../node_modules/@types/react-transition-group/index.d.ts","../node_modules/@types/rimraf/index.d.ts","../node_modules/@types/safe-json-stringify/index.d.ts","../node_modules/@types/scheduler/index.d.ts","../node_modules/@types/semver/functions/inc.d.ts","../node_modules/@types/semver/classes/semver.d.ts","../node_modules/@types/semver/functions/parse.d.ts","../node_modules/@types/semver/functions/valid.d.ts","../node_modules/@types/semver/functions/clean.d.ts","../node_modules/@types/semver/functions/diff.d.ts","../node_modules/@types/semver/functions/major.d.ts","../node_modules/@types/semver/functions/minor.d.ts","../node_modules/@types/semver/functions/patch.d.ts","../node_modules/@types/semver/functions/prerelease.d.ts","../node_modules/@types/semver/functions/compare.d.ts","../node_modules/@types/semver/functions/rcompare.d.ts","../node_modules/@types/semver/functions/compare-loose.d.ts","../node_modules/@types/semver/functions/compare-build.d.ts","../node_modules/@types/semver/functions/sort.d.ts","../node_modules/@types/semver/functions/rsort.d.ts","../node_modules/@types/semver/functions/gt.d.ts","../node_modules/@types/semver/functions/lt.d.ts","../node_modules/@types/semver/functions/eq.d.ts","../node_modules/@types/semver/functions/neq.d.ts","../node_modules/@types/semver/functions/gte.d.ts","../node_modules/@types/semver/functions/lte.d.ts","../node_modules/@types/semver/functions/cmp.d.ts","../node_modules/@types/semver/functions/coerce.d.ts","../node_modules/@types/semver/classes/comparator.d.ts","../node_modules/@types/semver/classes/range.d.ts","../node_modules/@types/semver/functions/satisfies.d.ts","../node_modules/@types/semver/ranges/max-satisfying.d.ts","../node_modules/@types/semver/ranges/min-satisfying.d.ts","../node_modules/@types/semver/ranges/to-comparators.d.ts","../node_modules/@types/semver/ranges/min-version.d.ts","../node_modules/@types/semver/ranges/valid.d.ts","../node_modules/@types/semver/ranges/outside.d.ts","../node_modules/@types/semver/ranges/gtr.d.ts","../node_modules/@types/semver/ranges/ltr.d.ts","../node_modules/@types/semver/ranges/intersects.d.ts","../node_modules/@types/semver/ranges/simplify.d.ts","../node_modules/@types/semver/ranges/subset.d.ts","../node_modules/@types/semver/internals/identifiers.d.ts","../node_modules/@types/semver/index.d.ts","../node_modules/@types/superagent/lib/agent-base.d.ts","../node_modules/@types/superagent/lib/node/response.d.ts","../node_modules/@types/superagent/types.d.ts","../node_modules/@types/superagent/lib/node/agent.d.ts","../node_modules/@types/superagent/lib/request-base.d.ts","../node_modules/@types/superagent/node_modules/form-data/index.d.ts","../node_modules/@types/superagent/lib/node/http2wrapper.d.ts","../node_modules/@types/superagent/lib/node/index.d.ts","../node_modules/@types/superagent/index.d.ts","../node_modules/@types/supertest/index.d.ts","../node_modules/@types/tap/index.d.ts","../node_modules/@types/triple-beam/index.d.ts","../node_modules/@types/warning/index.d.ts","../node_modules/@types/ws/index.d.ts","../node_modules/@types/yoga-layout/index.d.ts"],"fileInfos":[{"version":"a1aa1a5e065d48ef5c7bb99e38412f96","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"d4306fb2e47f74835e8674ffac07d76f","impliedNodeFormat":99},{"version":"e437c5c1302869326c3bb93da85bbbcf","impliedNodeFormat":99},{"version":"e4324975a566567b21d350615f1fc6ac","impliedNodeFormat":99},{"version":"333b1b9a2a9ac3b8497dba5c63b5ba50","impliedNodeFormat":99},{"version":"6cffacd662b6eb5fa7a36aa2ea366bfa","impliedNodeFormat":99},{"version":"b4c34f9c23304dbef2d23698637ed638","impliedNodeFormat":99},{"version":"aae8996e8b5684814785a42cbbefcd79","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"01ac052ec4a79e87229f90466a9645f8","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"edba5df642941aa062a62f6328c6df3d","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"6344b55f26a4e81d9608777dbfb877dd","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"3c0ed28e53d3695b363e256ec1c023fd","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"4c2761daba7f17141c25baa0821ac5da","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"b87656acabd63e69379ff6ffcfe52fc7","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"597469522da047a5af5222cc6989f405","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"bb3a710cbcda0533bb127712927cbe37","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"55d97a8c6fbf34a30450a7b1e5f7a298","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"0ee05eb59426d33e374226d8dcfa708b","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"e347c14030993906efcfbb88915b6a05","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"b0231263857c9b6a03641acdc9280ceb","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"3b15c4a83b598cacb4067676e6f0abed","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"b417d97b7934cef63b1889abec0bbfbf","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"09a6cf4032ebba60ce22a501e663f881","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"7a42de379b489e8f7b647455bebfc576","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"e22cc07e3f3cc242ba52fa3f8ea1fc58","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"2c45da767a1bfbb220848df1bc4029e4","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"b44c3e0fbaf2130cdcf6ac38b120ffa1","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"b612fb5cf8e5d964b92063a75207632a","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"02705151a5e1551b9162a9ed8ab763f7","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"41025e398be9215d32e4337335da8f0b","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"52684c2b1f353a5538e4f275182a54cd","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"6dedb6a4f90d1df3a6fbe5693e44886c","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"ca3f36fe3562c07e0f0d71c2bebd3f6d","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"409974d6129befbb8226ddd1c6558568","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"4d9cfde2a1ae1b4925f1f9bc10848e5d","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"7e1daecc66dd564144e3bb1a0266b5fd","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"a8e1d9bb35fd0637f2f9fd2b2a54f2ec","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"4f168501772a6543182765bfd5f2fbfe","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"a19c80aad1b2162103496f5ba293a732","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"b69afa63cd5d059851c78adb2856ee09","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"ae2fc5d954e9b0f5feee3d481b953c27","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"1cfd3091a071d8b6feec15277643bafe","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"692bcd75364db0f65d428801c7884466","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"a0d87491913d843139e0c993650a3235","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"f64453cbf9671f28158677fa5c43967a","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"33f317af5428801f944a478d2c1e38e5","affectsGlobalScope":true,"impliedNodeFormat":99},{"version":"90630acdc4134173bcf216f187bd3010","affectsGlobalScope":true,"impliedNodeFormat":1},{"version":"913b52a39743d1c11984e4e124b1f3a8","affectsGlobalScope":true,"impliedNodeFormat":1},{"version":"2514e9a2749c0fc18d2f478e9e1e641e","affectsGlobalScope":true,"impliedNodeFormat":1},"fe51d7a9bcdbcce1e65bbcf39b212298",{"version":"849a1873c4e917b0887368c1be897faa","affectsGlobalScope":true,"impliedNodeFormat":1},{"version":"c46c3108bde06d3b1fc6f802eb09eeeb","affectsGlobalScope":true,"impliedNodeFormat":1},{"version":"1332add604574cf529fb33b23304b296","affectsGlobalScope":true,"impliedNodeFormat":1},{"version":"9af0a7e3ef1c42cd066b9f8d365cc1ba","affectsGlobalScope":true,"impliedNodeFormat":1},{"version":"9eca652586205dc5b07f9ba57b1c8500","affectsGlobalScope":true,"impliedNodeFormat":1},{"version":"ec5f75939754ce94652189ec7d0d3058","affectsGlobalScope":true,"impliedNodeFormat":1},"e2f0114efc512fd95bdc12c81d5e8d6a","628b774b54637325e286752c79c12432","bb1f9e3091b88e9d0e93b7198c736af3","514fd35e5eb35bb2e5cbb908af0a0aa2","ed57ae88565308729f2327f26d684976","dcd932d6b0038f2e250e30dc10125b22","9508c917bd7e458745e222a82dd24ef0","fabf86f455f96b90538cc26320ab2765","14550b4cd9f433e0addd8b6d67614d23","cf0ac6aebc25cb780674653aaeee688e","35fef7bac8048f583e4ce6eaa0c2a4c0","6752dc653fc7a333027249fd851fdb62","b9ad7e689b46dfba362c1bf174e69018","8b0a2930dfabdf27c69c6bb1eeabcdc8","4513b1ed15523d247040689f37fa9db2","5c0c499eed773a750903d9497beafacd","2377da227d1bac82ff7b3f2081ddf8d3","beef985b474fefeb80d27fb2c8778371","c79fc9d9f09ae598a374ae7c0b5284a4","f2caa3cebb1e6be855519004830a6be0","8ebc9f2a77c900e710801214c5cb994c","ec617a0e0577dd6a3210c3b067ecb325","1db5d06e485bd82d6d5f6e36925a8714","24b864518967840216c779b5e5f00975","8719cd8047700bfb6046798390053823","f2840afb502c94db92dab0fb8d27812f","a7e7d08b372210c203745d3eac61a411","531183cc80535e0e94226d720e5eb038","f627eb958ca52c85e42f04dce4661f86","9a43fc665bce9012a3d5fe1b574ff4dc","13f4b4da6546a34719fd6bde15fb63dd","a589216508844bfc00e62fc2d97fda45","865aea1c3209e31b076cb6fe780e769e","12e64aaf26af0f5c8f1b263dd66e3feb","928971ebbcdf5b093ef37669b33bddf6","b744265d8ad12b7d4d5c5dc35d18d44e","eff32168b8348b822afeed9cbf61afa7","9aafd1f29b4d8861c1c6c34bf83c0721",{"version":"a127795c4dad633990c87aa0b86831e5","affectsGlobalScope":true,"impliedNodeFormat":1},"4ef82d390af85e719a46d566e229dd83","7deced3ef46e082f97d49bb71622526a","fa7acb320bbf7da8f76e431437143034",{"version":"ee57e9bfec6167b488ced231a7bf233d","affectsGlobalScope":true,"impliedNodeFormat":1},"7aa99080b86233d5b21416907d7a75a6","7d6a8df1becce5c270da2093e7835920",{"version":"c37fea1584ced70492ff5748ed570abd","affectsGlobalScope":true,"impliedNodeFormat":1},"b2b472cd0bf0f8d5f022e00ca1c401f4",{"version":"e22c8fb0fc65df14299c6c94cf729b3c","affectsGlobalScope":true,"impliedNodeFormat":1},"a84efb87b57f78ea0b0577edaacd8b33","814e0c2cc2a92531b6dc5d2a1767a6e1","79ac55b2d493df83277d681e4d64a992","7ab3b7243211fa08af307609be231d6c","7fd0f028e135bdf15ff757781a2f4ba9",{"version":"cab5b406d3defff17c931e41fa98aeb1","affectsGlobalScope":true,"impliedNodeFormat":1},"aa7dc1db3d208fb91418794a18539d4c","e9b8fc3c9291fc5701e219c0407e973d","b8f5d7bb61117f48a99b0cecaa75d186","687b6f8f042393a27ce6c8fd32972129","1d3b57f542db6054f952f8076b3c6a3a","4e0ba29e69c1176d4b909ad8a85a2695",{"version":"e5d25a206975fb43b72d246ed86fd7fe","affectsGlobalScope":true,"impliedNodeFormat":1},"88822a7d78686871bb9c15cedd3f45f2","86308ea691fd06825dd9a3ce0f1e901a","44782881f424302151e0de8fdaa39575",{"version":"f0ef68e9c53d126a7b3bfc1ae9f2c7c4","affectsGlobalScope":true,"impliedNodeFormat":1},{"version":"71259a408a4178e5f0795188b4d802f1","affectsGlobalScope":true,"impliedNodeFormat":1},"eb80d821e5e931dcccee2697f3e249e2","961969874f5ee12ed3429d3d0c82de02","9a62058bc2d476e807cc6594eed72adb","40cbc4bb3b225b03c54255154dada07d","6534825a8bda3eeecde9bca9e3a99712","09a582ab2a73806ab5887ea413b3d55a","a0592261111f47230eb35ee52c12e635","c8206d568b54e5ea06131963eecd576a","15878f4f50c2e562c0768eb735c2aed5",{"version":"b636ba40489f78f02a4b71322994828a","affectsGlobalScope":true,"impliedNodeFormat":1},"7b844f3801adf7e09ea25ec5f39f9a86","46c937d243665015f13587ac0db55909",{"version":"1f29cf27395029939dd41fce4f0c5653","affectsGlobalScope":true,"impliedNodeFormat":1},"f026c594d60114f10583261f6499df38","1ac553606580aeec204cce24c38b2e3f","3c29bf7066a67e7151f563fc1c4e56cb","00d49834780046c2d6a7da3e50d83cca",{"version":"377c78771ee061f2b4e59d5eb5a28782","affectsGlobalScope":true,"impliedNodeFormat":1},{"version":"7293552290431bff8ef5f13e6f7fcbfc","affectsGlobalScope":true,"impliedNodeFormat":1},"9453ee3aca6a51ae747057fbc6388cf2","6c491c1508bd3e1df0f45f38b07243c2","1f99b8445612afcfaae0f8591d8d18ac",{"version":"bbd4e0ea8a847a4b60ba7d5741985c9d","affectsGlobalScope":true,"impliedNodeFormat":1},"f57ef54941c62bf28f3beafb86025735","7b6c16d46b33570aaef9d0fdc4f02f14","951dc84d8b38b2dc11ed0cbf81c6497e","16a3bf4e99e3275960702bbfcbbfa9d8","cd5c183c2bad150213a0cccaafe937e2","d6ea403bc9232d6fe7e7622a88f42faf",{"version":"7fa578df98e59f9f70329e3e06ae7784","affectsGlobalScope":true,"impliedNodeFormat":1},"2a85fc47377fe0593e11df0805c37b24","0bf60b6ce895c720124a6b0aba87990f","f5da2e5790c2d5acd98acdce0b2c6a60","0997be57498f2b766cc3561c7df58f06","4f8a359db312d088f12df2b4333c70b2","c5bea81fda0ed705abe31dfb992bebbf",{"version":"99a260c66b14e11e17475ecdbf8ed8da","impliedNodeFormat":99},{"version":"e099f0b39e59d74ddaffe15de4ff6885","impliedNodeFormat":99},"c3730b45ade650ec37e4bf1afad7fd96","5dad4f65b10a678446760718390886c5","cbd4224276f17d2e284e5421c87e3bfe","bc8748e58b7f2aaa1f44b61e626acf01","b654cd0a34544ca86375d4d012aeb071","fd9e9c9350c65781b9ed5056a208b0e0","cf28b76066e38bc1f5d048f3f60627b3","7cab08e97ca5e8a423b3a0e9a4abfecd","1417aa0e4db0867e09f092c607161a92","5ff4c9807ed7aea2c006b1d38ad00f6f","d6dddbf2419c9b3d99e50b8edabf4ef9","bad6da77d0a8d94ef9779efed416f3ca","95fabefe7f6ec2a26aade94548677dcf","4e120498a6ce75cb512630dc9047b304","eb7b9d6f3233a8791ff2fab9c5daa278","1e06c1f9bf7499bb9fe057a2a93bd6d8","36887ccdaa6480c8440a9741e0ca548c","024064dbb654d6f3e0dc9f7895e8b2d2","9bbc8d14f8aa956a38d73c24047ffcae","d84eea03b4f0d80ccd75d7100935a72a","12a941a3d304dc1710fba6558bfb859d","6e9b4b0de53b7aeafbf6a49e833c0ba3",{"version":"f831ee0eac3ac5211db87c71c04a22b4","impliedNodeFormat":99},{"version":"fe725612f42f9a89b2a5c2a838c83e23","impliedNodeFormat":99},{"version":"16887a74430ae0fecbcab8120af887da","impliedNodeFormat":99},{"version":"f635af062ce4bb632344ca69feb441e8","impliedNodeFormat":99},{"version":"23045a3dc670001afed713b1d8baa390","impliedNodeFormat":99},{"version":"9a68af8d3adbcae7ee07d98d3e3b29e7","impliedNodeFormat":99},{"version":"c3e811a043a75d05a8d4277b293ea608","impliedNodeFormat":99},{"version":"dc09a88fbb8d3a1f578f081bf90cd408","impliedNodeFormat":99},{"version":"5bec30c121296e0aee7ac869d67754e0","impliedNodeFormat":99},{"version":"6a4c0e1d07859cfe0bc64c3646ef6127","impliedNodeFormat":99},{"version":"315d7b5a803ca70321da2819a61d6380","impliedNodeFormat":99},{"version":"2ba2d2db53c8b457811d087dd35f036c","impliedNodeFormat":99},{"version":"5b82ef0b0dd08dd956c20362c8e0b457","impliedNodeFormat":99},{"version":"10e13747234dd2299da6a1062bdcbabe","impliedNodeFormat":99},{"version":"a8f9b1ae3d0333407985e7866272eed4","impliedNodeFormat":99},{"version":"fcb951fc2c494445a253e4350f63bd49","impliedNodeFormat":99},{"version":"1bdb36e8bd07e6df75aa383078291c33","impliedNodeFormat":99},{"version":"028a014fd293309853fc36e0da6911bf","impliedNodeFormat":99},{"version":"bf4e0e44e072c110554d2b658bfa4304","impliedNodeFormat":99},{"version":"325f3cf830ee7f59c8f7b6e319ba2a89","impliedNodeFormat":99},{"version":"d142d228c347091f117241f110006e20","impliedNodeFormat":99},{"version":"1a539e8db552dfb616c71def3f6ab953","impliedNodeFormat":99},{"version":"e4cab15ed6f9ab7575311d281ba80842","impliedNodeFormat":99},{"version":"4bd1d558726c9ecd0babd9f709edcee6","impliedNodeFormat":99},{"version":"7113c56d0b223b109378588e54392c6b","impliedNodeFormat":99},{"version":"d327d6949651856bed4b6371246ceb67","impliedNodeFormat":99},{"version":"94b76c23eaa734e99ebaca1f568a7a5e","impliedNodeFormat":99},{"version":"2823d9aa39fe2002433376d7f368bc44","impliedNodeFormat":99},{"version":"d53a4de03b849c276c1fa1b2f29e4a7e","impliedNodeFormat":99},{"version":"4fb6b12b88d5582294210340ed78dd8d","impliedNodeFormat":99},{"version":"a6970464eeac7e7be238822ed550c835","impliedNodeFormat":99},{"version":"0d40cfd5a45b7bb2e51159c3224211eb","impliedNodeFormat":99},{"version":"f6fbc1f5a15a1bc069641222133dacc1","impliedNodeFormat":99},{"version":"11fd9a1a54fe04c83434e807c7d80d9a","impliedNodeFormat":99},{"version":"a7808327515c59846b63c80da6daf895","impliedNodeFormat":99},{"version":"a9adbef28f77c39f2f7fb6b52fbbae1a","impliedNodeFormat":99},{"version":"b75efe5a53cab8a5375e22db1d484c64","impliedNodeFormat":99},{"version":"19f519c47b7d1068dced08783a7c61f3","impliedNodeFormat":99},{"version":"deac05e7f6e3791f3e60f19dcd1f7bcf","impliedNodeFormat":99},{"version":"e28888b8c85e484c2ab1a2fb047df76d","impliedNodeFormat":99},{"version":"fec6282dabade7c5a98e4c454313b6e4","impliedNodeFormat":99},{"version":"79e4760a70d1655535ae8de31bca9be9","impliedNodeFormat":99},{"version":"e844c60d7ca4c6c605690533531e2483","impliedNodeFormat":99},{"version":"d14dc646e81ac4e9f4ac880eb2046527","impliedNodeFormat":99},{"version":"0a4c02e4ea2b10e80b17c2cff20c7212","impliedNodeFormat":99},{"version":"21672701c98b0f9f75be0c4ec8b0dc52","impliedNodeFormat":99},{"version":"17430fa07ebc46bbef0d2945e8ff49df","impliedNodeFormat":99},"e16ee2cba8fccd4888dda67d8313af8f","09236d421d18c90c9cf2b1a62df88e93",{"version":"8c9f31c32a5525bd747cf424bc17f778","affectsGlobalScope":true,"impliedNodeFormat":1},"9da7817000afeeeea3222d0ff2a686c6","2cfba9984aed78da242b0a21e3f38a2a","e93eb137b979617ec06b22a362976ce6","c0ec98811b4559ddda1f61ba53e1d2d4","9de6d4b67fc1b39b6dda81b6f93579dd","f150286488a8ca9e5402dd15c8957340","31ce40f173f27100522a719437fcfd9c","01253493709f6c42fb5b70e764ef90cc","7aa0667e23415be0a645dfcf2b47c82e","4efac62cab7941afad9cadf1d94be7f3","e0f557cdd6695b87547a877b69ca38b9","70ad1c3b71a769b2a1a584c774788db7","642f6536f2a6b5bdbafd4e25f407d2c7","1db38078e8dbd37f16947069fd0093c6","fe812c7b8986cf1c50ce352e9e3f8c89","a9b6a70f9e138db0cacf973b904f9ce8","138603e1e6787901dc8761a2803a6310","8cdf819bc802d165541b23781a845948","23133f69c05e8d7c456e1a9d68c1fffc","89bcd59d3d48cb11935ff1cfa15070f3","6502191f732e6a899b71d18f2cb7a669","83a6c12fe0d568638a6e8db98c137c49","844210e48517107ab0fa5a7e40edbba4","3c2f892a82b351392b98c312e067fa15","cc262de9275b1c5fa529688f33ceae7f","7acde96d9deadd2884a93ae4bba972da","767f37418b3ec86d2e6fe3e3219c64f7","b7f217cb2b3f9082360e5684f55f8cc1","bffc4e97a99bff31a3686a5c688581f0","8ce2d57bab6ef91e5eddacbf7d7f0181","0331baa726704b161af0efe64f980e41","3e47b1dab9282fd8fd4922d3f0655314","0e52a45f8d24b10b300a582bbc389238","38586984d045a69166f13015d71501b2","256153f676e34a933916962fe97f4749","da107fb4df9e8676ccef0f7b37ea53ce","0991cffb042883e18ab62d233b31d3aa","428c7c416f9ef4b684745ecd18800cdf","2732b197815df32818230d9e433fe682","6f29a9c10351aafe84986db22f67df8a","b41b7744429d74640dd2a1cb87b3199e","09182295d359b15a9facd08f8fd51966","6c7c40d6161213505b0b2a0991aa708b","c61dcede83e20904c8751b2d08beb296","6a1ef55dc603c92ba031491cf46c78a6","6d6c5e4f921002d2a37f4e7750513f8c","d9d2d04eb9d7c7a35ea9249d96ab66e9","c92e37bef68312b0809b209dd35bc2ee","46d5eb0382f3ca27ce2af109ac132709","1f9ff9d0706bcf42c48445798e0aa736","18c29f2c9116baa565528501a8d7a367","0fba992bccaca17b160c8f20986c8b30","a4e850cd58d40f96cca03c145e523ea7","1f18f87f73dd7802d6ac6d69f9791f58","cd88c0466b9ef15bdbc49bfe4acd11d5","7842952684833f73af52e689186b8804","04092ab5516d57c53e4b6d4385217184","1372fea4f73fc1f594cf3ffa58c56484","c8693ee5630c780142bb6a834f71cb9d",{"version":"ff7b34d3bfaead19a0d75872ebedba4f","impliedNodeFormat":99},{"version":"201f2c3d40dee89503e517fb9887e9f0","impliedNodeFormat":99},"6c95598cec8d7aaba78ec3ebe84be9b0",{"version":"38f6b55c849e878fae4ebc53fb18461e","impliedNodeFormat":99},{"version":"1a336cf0cbae00db9c4ccf44ed5be976","impliedNodeFormat":99},{"version":"13aa555f1050fa61fe2b821fe174449f","impliedNodeFormat":99},{"version":"16d45554490fa16b89ccfb15d2871dca","impliedNodeFormat":99},{"version":"a907e572b9a87770da89e1392cd8c4c7","impliedNodeFormat":99},{"version":"17b655ee27d57250c96f2d58b33d6c05","impliedNodeFormat":99},{"version":"430d21bcdec94f2f192115f164d7e56b","impliedNodeFormat":99},{"version":"0d5677f4d70c67c75c2fd97f894893b9","impliedNodeFormat":99},{"version":"ba6e8f93b27e2afb78fba423405bf83e","impliedNodeFormat":99},"c3730b45ade650ec37e4bf1afad7fd96","5dad4f65b10a678446760718390886c5","cbd4224276f17d2e284e5421c87e3bfe","bc8748e58b7f2aaa1f44b61e626acf01","b654cd0a34544ca86375d4d012aeb071","fd9e9c9350c65781b9ed5056a208b0e0","3a902ca438e0f97432b898f1f59d6204","7cab08e97ca5e8a423b3a0e9a4abfecd","1417aa0e4db0867e09f092c607161a92","5823eac455edb0f76985681f92388fac","d6dddbf2419c9b3d99e50b8edabf4ef9","bad6da77d0a8d94ef9779efed416f3ca","e4131bdc4134dd8aaa7477947ccdf1f1","f40b745d21d12632761ba29507edb0b2","b15d3273a51291e657e486de13614ca1","69c7eed71fe7a3fd96ba3c7705f83eb3","36887ccdaa6480c8440a9741e0ca548c","024064dbb654d6f3e0dc9f7895e8b2d2","9bbc8d14f8aa956a38d73c24047ffcae","aa36a6938d39c02839914ddfc6fc04a0","12a941a3d304dc1710fba6558bfb859d","6e9b4b0de53b7aeafbf6a49e833c0ba3",{"version":"e909274bfa2cfbaddf45b40ec0f8cf4e","impliedNodeFormat":99},{"version":"365900d6b15d19b30577f3c3b48aba40","impliedNodeFormat":99},{"version":"33e782a4fe9e854fcf9287f13dbbb999","impliedNodeFormat":99},"f8059a9ddf1aeefd103fce9c853541b9","f100181a3e9ff7f5b23eed47dd66a1de","fbb1086d78fad2e81eefa859d53b6ffd","fa614d3a843db2f4f4f804155cf537a6","c4437a75ed05801f47848d00286b7f3e",{"version":"1072db148929666545fd845600a6bed8","impliedNodeFormat":99},"6a90d39afd71a0c079ff0d7ec999862c",{"version":"4c5afeeac7367afc2dfe7c5526ab294d","impliedNodeFormat":99},"80934c477870fc97980d6296f5e7f063","98e86452fc67f68abc49450eb5f01d73","9916f4848c2282b9cb588a41f6b7d823","42a13551ef0ce9e68aef58b6a4ac31fa","29c297b3336e6ee3c2b9aa7c7dbdf00d","db4704d82c740ed3bd067c9bae72d092",{"version":"5a297566b9863f5dfd9c8d4850ed8af4","affectsGlobalScope":true,"impliedNodeFormat":1},"81ffe24ac8999f2e25fff992831b7324","02575b4ec4091570b193cc6ca8e6821e","fdf29cba6a2cc2f1f28f95f337ae66a4","58e9b716b50fdc8506289bd37edd9a55","8f4109203208f26263f439ab576f3820","4501167a04f84205017251407e6ed43b","295c0cb8891e7424d3a27dc56dc1b430","e1d3f27d8738af5fa721eed17a4e4d69","a6965f10e458de7e18e283070aca6c05","9d9ae7679668928510f3c7d0f678969c","1c17807e0ad03afd162d77cb40ad5309","7749a56d15eb26058bcfef426d94139b","9b7ad984483d8ffb801e212338bc8f2c",{"version":"1bd9c33fa4dc9f5db21e3499cce058e6","affectsGlobalScope":true,"impliedNodeFormat":1},"112adfdfe0121f630c2187e89145ee2c","da3da10502293a0e1bf580ef6c26486c","f9dccda118924be25899c703ae07bf83","9befaa4adb42627db765639b7b5ba193",{"version":"8c6ffdee98d8e8323825d4ec4d2c6ee7","affectsGlobalScope":true,"impliedNodeFormat":1},"3ef6bce1299faf411f33ab1bae59a884","086accdf702b13dc96a5cc430c0c5fe4","2746dbce860e8644ce715860fb7f880b","7dec06c9860e0c2431b1f63823cd58f3","c15e75315b24033b53bf1716c810bc55","2a4d28da0f4f4a73c6a6ca0b70432196","2441a7d236246e3191870102259d866c","182d0810dc5bdb879a67cc73a54ab2e3","1131df6a1351dcd15a42b4f3dc2c26bc","bc4823928adf48deb7c79ccc4775af03","1b3158a1a6d317b1c468286c3cf11a52","54dd1676bc6db5ce3e64a4eef95409c9","32fb98b3fa25d2c284822d8b409a5a70","8c895bc59b75fb4c3c7a83990ea8e5e3","9abb48a6060724febe2476521e93fe94","34e8d5bbd90cfb7d60388aad23d2e702","a62d8dafef1e7f21c6440685faa5ce88","acffc5529515007024548e136a409ff2","088e36755fde5ceaad9cb5dea73f3ab3",{"version":"29fc9434962e9a75367715ae674704df","affectsGlobalScope":true,"impliedNodeFormat":1},"0cb91525c248fbd47796f4b66bf6d1ce","52d0b2118c50f027c3e705fb3a5b3a76","6835b3cccbe7b95c93e13a5d2188af82","ac4f57a25ce44013001d94a713084cbe","e64c6ae74fe4629d2dbd4cbc0327523c","ea87a90387d7680c978dd4af6a7451c8","7c397e72523aa26bbf2d48cf765213d2","9292df5c013e4ea9da035de7faa497e6","36ccb3844d98882c63be347b7c67c0a3","37c3bfc01246d2b6cd575bddb0ea9d1b","079f4855e44a9ca9b014e5a860bfb72f","cc495e570859464081995c5735f3badc","9f2f4a4f07a3455f4d52a26f7a4b8fcb","c7eb90a6d4e18b33d0eeba6074e872d4","2baf2a8f6a612cd1295f79f3e873a2a4","ad276fb55297e6d29fe65d795d7e7ce4","e2a8e187d8a7db24931ea2c655d52c80","0db97d91db6e499a5df8e99257afe07c","af4e50cb99ba66919d9a70c28bfc5e24","07841866bdfb5333a0eeb445e8052006",{"version":"ff4d71935967e02fbe90430738a46c4e","affectsGlobalScope":true,"impliedNodeFormat":1},"2f1bb71d08da4fe6737ce0fd504fab4f","d9baefe45beff3fcb99e0e879c4b3a1a","a0e5e2be58876b4545cb3be195801e6e","1aa0fb5fa5d8275b5db27bc931aa1975","3449ea63993a991312e0e444ece2382b","7e5c546175ec5fae4c0e494ad24c5b90","bc6d97e46a1dafb8e8d1b601ccaf7e1a","579e6d11933f5644dc142c0a4e79c645","2a9d6baa2c42865fc9f5e7516d0910de","9ca582829e8eba4129943a1a941c6bbe","2aef22a88fd7dd975ba87f7c8e099fc3","836ca98928668baf27ac19645c07198e","757a8e678340c7a0aa01c699d2084933","48c676e5241e9876faafba68ef7d6d26","c3201c3dc232ea9f79fd9034b036ba94","8829f5982af596616a839da1807c12a2","5c4f67e441d334f46ff34670f61b22cd","25d40df8bff69a330a687d98bdbc2eb6","87175b4aa2f6ef2d25834a1969a45b51","be8b3bcdd2ba7d7c8c87946a20626c5b","b52b31c86a05afbf7cc827a8889b69da","cd8070bba6df1e7a7b65224ab106d7d0","ce15efa1c092340d16d533b69f4fb1a9","6b72f2919a3ebe1223b418b9bc920d7e","52b842fca322a4b30f1f7b8cab78ab88","292f07f2059da3d1699f18623544d2a8","bd29402f5bac2be501b52a3a0ec997a1",{"version":"7376cd32b4c24f5e3471d538cce0a6cc","affectsGlobalScope":true,"impliedNodeFormat":1},"afd868916e9e1d1fc494f6573e112e3f",{"version":"7b395e75f006f1b735d397f8db9f19de","affectsGlobalScope":true,"impliedNodeFormat":1},"da1fd4bfd6803838104ca05c0ed7fadc","55775a298812b43e9114dabd88a8918d","48500b61fc86a5b4dbba2c94adf5214d","08438df3f7f27a87056551849edd15cd","6aec5e4ab841d50c79df02ede12103ef","cec011fd991c3267563c5309daf65ea2","aa13fb67f56469e7590006108066e0be","e30cd47fea0096ba312924f04657fcfd","f12c7bdfa7c2ad0fe84247f800b9956c","d40cee60c3c264a4c1cd9c1013dbaac7","bec0d6e171cab6b12d98455bf99217c6","92fe312f908112181c00a3d36883a58e","3720c6074ec2bfeba0a1d638ab26a0bb","a041df5c00a7c9e6343c519782e99329","0303d7f5e0d019fa88ca2945794fa8d2","9dad91c6969461f75aef8fc683a9376e","c33ae00968f45dfe2ef2bea7966cf6c5","18c8c018e7e8a2f4ae676f8661110c6d","24d9b69b67823f208423e5796b79c23b","bf799332c55c6eb4a6d9d652d6dda457","3948daef404f94c35fb3ceca755a27d6","cd2ad42b2eff27f2277cced9b91b270e","304539db53f7fe3cc4bfd5878b7223fe","9c56184330b09c7c690ed42d3bcff59d","450ca6229325f8123bea072e71c866ed","88d22ddd44a72912036f38833360b8f1","edeec51072253363d7345541bb78d745","32d5973e5b42693387462b0998810615","db80558b0bf2653338bcf91434263fda","0136b37037b7ce74caaad4abcdd15237","e6179eee2131e249ccd9f2dd960b92b5","c089839d8abcdfe1168ebde55894cad4","097a7aae20edb8e5de5f45463d6fa417","c1df21c2195796f767d0e03cf9ac00d1","1362b703c4f9be29bd918a71a07b7e6c","f71afe5e74b683a5260d9b7dbd741104","adcc06f04eaa91f672f07e00c5f63eec","6ea7a8edac19a93df999f41b0058f2a2","563051f5836b5e450d5c9868552c9685","e41fb55415372b37046b46fd27a3e8b9","649b0e8e352b40125538627774ddccd6","b1c43dab0c298c8661b08e49779c32a0","239019e12520a280526af43974a0aeca","ec8beec2c9e2259c26be90574cb94989","57d6b728909ea2c50bf50e0e19c99b8c","93f871c7865a245efe8b0b2405a09c9e","b9b7a66404c8da7d1fc634a7a69a989e","0312a7938ed343c86f23d1178be1a62f","0d34e16363c4b2e88f9be19c0eda14a3","b4646a5c02912f7b2e8ce18a2f960bb7","4ba759be6e6d6510d41817c11f60c6d2","482e3e94bb83169ff0a2f1cd516c9394","9e07e42545fb1aba960b46e3d676c245","bd8fbc7e2e26a4686f8b4c45386a46db","ad1145336b61b23d724d3923b953ae82","3fb91489cde0479c146410f814c27749","589e3a40397dc48971ac6612dca9896b","86d78487119720cc47028100d7bb39d1","91535037303b09ae267a5ee85f9efd85","39f77cd7e5d49be8457371af1c217c22","80934c477870fc97980d6296f5e7f063","928a0d860e4d81f663a40bb1a5b4b1fd","e2eef8c06c88bf82bd89bd640527a835","0238452f4fa45cb934637e05d02d1db8","ff00d59cbd64d6d0ba3e1d83e11ca21b",{"version":"a62fe363e41eb53f7404361a8232f476","affectsGlobalScope":true,"impliedNodeFormat":1},"ec4eccca7dc09c8eac03e58d5704c74c","4e3963a0487d205b3f1ca917119aa4ac","19c7641b62556c1c081bd997e2f32a2f","de3e3ebddccbc9fe1e96a37b45aff49d"],"fileIdsList":[[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,337,338,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,148,158,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,159,328,333,334,335,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,350],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,190,293,303,326,327,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,326,328,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,190,325,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,210,211,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,182,207,210,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,206,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,182,205,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,208,209,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,182,205,207,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,160,183,186,189,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,182,188,190,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,182,184,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,183,184,185,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,182,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,292,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,205,214,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,212,214,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,190,210,214,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,214,224,291,293,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,214,224,293,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,224,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,190,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,190,212,213,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,192,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,182,195,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,182,193,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,191,194,195,196,197,198,199,200,201,202,203,204,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,182,187,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,166,167,169,172,176,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,161,162,165,166,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,166,170,171,172,174,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,161,162,166,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,162,168,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,166,169,172,174,175,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,161,162,164,165,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,162,164,165,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,163,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,177,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,164,165,169,173,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,178,179,180,181,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,309,310,312,315,319,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,304,305,308,309,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,309,313,314,315,317,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,304,305,309,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,305,311,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,309,312,315,317,318,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,304,305,307,308,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,305,307,308,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,306,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,320,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,307,308,312,316,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,304,305,306,307,308,309,310,311,312,313,314,315,317,318,319,320,321,322,323,324,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,298,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,297,298,299,301,302,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,300,303,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,296,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,188,294,295,296,297,299,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,232,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,232,236,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,230,232,234,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,230,232,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,232,238,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,231,232,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,243,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,232,249,250,251,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,232,253,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,232,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,232,235,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,232,234,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,232,243,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,331,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,333,343,344],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,347],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,349,350,351],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,352],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,355],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,333,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,150,151,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,151,152,330,332,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,360],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,368],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,362,368],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,363,364,365,366,367],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,382],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,370,372,373,374,375,376,377,378,379,380,381,382],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,370,371,373,374,375,376,377,378,379,380,381,382],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,371,372,373,374,375,376,377,378,379,380,381,382],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,370,371,372,374,375,376,377,378,379,380,381,382],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,370,371,372,373,375,376,377,378,379,380,381,382],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,370,371,372,373,374,376,377,378,379,380,381,382],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,370,371,372,373,374,375,377,378,379,380,381,382],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,370,371,372,373,374,375,376,378,379,380,381,382],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,370,371,372,373,374,375,376,377,379,380,381,382],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,370,371,372,373,374,375,376,377,378,380,381,382],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,370,371,372,373,374,375,376,377,378,379,381,382],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,370,371,372,373,374,375,376,377,378,379,380,382],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,370,371,372,373,374,375,376,377,378,379,380,381],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,391],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,384,386,387,392],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,385,388],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,384,385],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,386,388],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,384,385,386,387,388,389,390],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,384],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,398],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,394,395,396,397],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,341,345],[52,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[47,48,49,52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[50,51,52,53,54,55,56,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,136,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,137,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,138,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,139,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,140,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,141,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,142,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,143,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,144,145,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,145,146,345],[52,94,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,94,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,146,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,345,406,408,412,413,414,415,416,417],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,345,406,408,409,411,418],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,345,405,406,407,409,410,411,418],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,345,408,409],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,345,408],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,345,410],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,345,407,409,411],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,345,406,408,409,410,411,418],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,345,406,407,408,409,410,411,418],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,345,411],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,345,421,422,425,426],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,427],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,344,345,429],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,433],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,437],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,368,433,436],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,368,433],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,433,441],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,440,441,442,443,444],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,428,431,432],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,345,358],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,450,488],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,450,473,488],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,449,488],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,488],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,450],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,450,474,488],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,449,450,451,452,453,454,455,456,457,458,459,460,461,462,463,464,465,466,467,468,469,470,471,472,473,474,475,476,477,478,479,480,481,482,483,484,485,486,487],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,474,488],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,329,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,496],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,346,399,489,491,497],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,399,490,491,492,493,494,495],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,490,491],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,490],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,497],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345,349,353,354],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,345,422,423,424],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,345,422],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,153,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,153,154,155,156,157,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,149,152,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,152,153,345],[52,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,149,153,345],[52,66,70,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,66,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,61,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,63,66,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,61,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,345],[52,58,59,62,65,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,66,73,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,58,64,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,66,87,88,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,62,66,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,345],[52,87,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,345],[52,60,61,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,345],[52,60,61,62,63,64,65,66,67,68,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,88,89,90,91,92,93,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,66,81,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,66,73,74,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,64,66,74,75,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,65,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,58,61,66,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,66,70,74,75,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,70,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,64,66,69,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,58,63,66,73,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,345],[52,61,66,87,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,345]],"options":{"allowImportingTsExtensions":true,"composite":true,"declaration":true,"declarationMap":true,"jsx":4,"module":100,"noFallthroughCasesInSwitch":true,"outDir":"./js","rootDir":"./src","skipLibCheck":true,"strict":true,"sourceMap":true,"target":2,"useDefineForClassFields":true},"referencedMap":[[338,1],[339,2],[337,3],[148,3],[334,3],[159,4],[336,5],[353,6],[328,7],[327,8],[326,9],[212,10],[211,11],[207,12],[206,13],[210,14],[209,1],[208,15],[190,16],[189,17],[185,18],[186,19],[184,20],[293,21],[218,22],[217,23],[223,22],[222,22],[215,24],[216,22],[220,22],[219,22],[221,22],[292,25],[225,26],[227,26],[228,27],[224,1],[226,27],[229,26],[213,28],[214,29],[193,30],[192,1],[199,31],[197,20],[204,20],[203,20],[198,31],[196,31],[194,32],[205,33],[200,31],[202,31],[201,31],[195,1],[191,1],[316,1],[294,1],[188,34],[187,20],[183,20],[177,35],[170,36],[175,37],[167,38],[163,1],[181,1],[169,39],[178,1],[165,1],[176,40],[161,1],[171,41],[166,42],[164,43],[168,1],[172,1],[179,44],[162,1],[180,1],[174,45],[182,46],[173,1],[320,47],[313,48],[318,49],[310,50],[306,1],[324,1],[312,51],[321,1],[308,1],[319,52],[304,1],[314,53],[309,54],[307,55],[311,1],[315,1],[322,56],[305,1],[323,1],[317,57],[325,58],[335,1],[296,1],[299,59],[295,59],[297,1],[303,60],[301,61],[300,62],[302,61],[298,63],[230,1],[231,1],[232,1],[291,64],[233,65],[280,66],[235,67],[234,68],[236,65],[237,65],[239,69],[238,65],[240,70],[241,70],[242,65],[244,71],[245,65],[246,71],[247,65],[249,65],[250,65],[251,65],[252,72],[248,65],[253,1],[254,73],[255,73],[256,73],[257,73],[258,73],[259,73],[269,74],[260,73],[261,73],[262,73],[263,73],[265,73],[266,73],[264,73],[267,73],[268,73],[270,65],[271,65],[243,65],[272,71],[274,75],[273,65],[275,65],[276,65],[277,76],[279,65],[278,65],[281,65],[283,65],[284,77],[282,65],[285,65],[286,65],[287,65],[288,65],[289,65],[290,65],[340,1],[332,78],[341,3],[342,3],[345,79],[331,3],[346,1],[348,80],[352,81],[351,82],[356,83],[349,1],[357,84],[152,85],[343,84],[333,86],[358,3],[359,1],[361,87],[362,1],[366,88],[367,88],[363,89],[364,89],[365,89],[368,90],[329,1],[369,1],[350,1],[383,91],[371,92],[372,93],[370,94],[373,95],[374,96],[375,97],[376,98],[377,99],[378,100],[379,101],[380,102],[381,103],[382,104],[392,105],[388,106],[386,107],[389,108],[387,109],[391,110],[385,1],[390,111],[384,1],[393,87],[395,1],[394,1],[397,112],[398,113],[396,112],[399,1],[400,1],[401,3],[402,114],[347,1],[403,84],[404,1],[96,115],[97,116],[98,117],[52,118],[99,119],[100,120],[101,121],[47,1],[50,122],[48,1],[49,1],[102,123],[103,124],[104,125],[105,126],[106,127],[107,128],[108,129],[109,130],[110,131],[111,132],[112,133],[53,1],[51,1],[113,134],[114,135],[115,136],[147,137],[116,138],[117,139],[118,140],[119,141],[120,142],[121,143],[122,144],[123,145],[124,146],[125,147],[126,148],[127,149],[128,150],[129,151],[131,152],[130,153],[132,154],[133,155],[134,156],[135,157],[136,158],[137,159],[138,160],[139,161],[140,162],[141,163],[142,164],[143,165],[144,166],[54,1],[55,1],[56,1],[95,167],[145,168],[146,169],[418,170],[405,3],[412,171],[408,172],[406,173],[409,174],[413,171],[414,171],[411,175],[410,176],[415,177],[416,178],[417,171],[407,179],[419,1],[420,1],[427,180],[426,181],[428,1],[151,1],[150,1],[430,182],[429,84],[434,183],[435,183],[438,184],[437,183],[439,185],[436,186],[442,187],[443,183],[441,183],[444,187],[440,1],[445,188],[431,1],[433,189],[344,3],[446,190],[447,1],[448,1],[473,191],[474,192],[450,193],[453,194],[471,191],[472,191],[462,191],[461,195],[459,191],[454,191],[467,191],[465,191],[469,191],[449,191],[466,191],[470,191],[455,191],[456,191],[468,191],[451,191],[457,191],[458,191],[460,191],[464,191],[475,196],[463,191],[452,191],[488,197],[487,1],[482,196],[484,198],[483,196],[476,196],[477,196],[479,196],[481,196],[485,198],[486,198],[478,198],[480,198],[330,199],[497,200],[489,1],[492,201],[495,3],[496,202],[490,200],[493,203],[494,3],[491,204],[498,205],[499,3],[500,1],[360,1],[501,1],[502,3],[503,1],[45,1],[46,1],[8,1],[10,1],[9,1],[2,1],[11,1],[12,1],[13,1],[14,1],[15,1],[16,1],[17,1],[18,1],[3,1],[19,1],[20,1],[4,1],[21,1],[25,1],[22,1],[23,1],[24,1],[26,1],[27,1],[28,1],[5,1],[29,1],[30,1],[31,1],[32,1],[6,1],[36,1],[33,1],[34,1],[35,1],[37,1],[7,1],[38,1],[43,1],[44,1],[39,1],[40,1],[41,1],[42,1],[1,1],[160,1],[57,1],[432,1],[355,206],[354,83],[425,207],[422,3],[424,208],[423,1],[421,1],[157,209],[149,1],[158,210],[155,209],[153,211],[156,212],[154,213],[73,214],[83,215],[72,214],[93,216],[64,217],[63,1],[92,3],[86,218],[91,217],[66,219],[80,220],[65,221],[89,222],[61,223],[60,3],[90,224],[62,225],[67,215],[68,1],[71,215],[58,1],[94,226],[84,227],[75,228],[76,229],[78,230],[74,231],[77,232],[87,3],[69,233],[70,234],[79,235],[59,1],[82,227],[81,215],[85,1],[88,236]],"affectedFilesPendingEmit":[[159,51],[336,51]],"emitSignatures":[159,336]} \ No newline at end of file diff --git a/typescript/server/.env b/typescript/server/.env new file mode 100644 index 000000000..4147b36e6 --- /dev/null +++ b/typescript/server/.env @@ -0,0 +1,9 @@ +NODE_ENV="dev" +REDIS_URL="tachi-redis" +MONGO_URL="tachi-mongo" +PORT=8080 +LOG_LEVEL="debug" +MSGPACKR_NATIVE_ACCELERATION_DISABLED=true + +POSTGRES_URL="postgresql://tachi:tachi@tachi-postgres/tachi_dev" +MIGRATIONS_DIR="/tachi/db/migrations" \ No newline at end of file diff --git a/typescript/server/.gitignore b/typescript/server/.gitignore index 46a36aada..b59db80bf 100644 --- a/typescript/server/.gitignore +++ b/typescript/server/.gitignore @@ -1,5 +1,4 @@ # Test stuffs -.env .setup_env failed-tests.txt diff --git a/typescript/server/README.md b/typescript/server/README.md index 9393caae0..0e69bb716 100644 --- a/typescript/server/README.md +++ b/typescript/server/README.md @@ -8,9 +8,3 @@ Tachi-Server is the backend for Tachi. - Opens a public API on `/api`. - Supports various IR frameworks under `/ir` - Powers a ridiculously overengineered score import framework. - - -## Setup - -Make sure you've ran the bootstrap script in `../_scripts`. You can then start the server with -`pnpm start` (in this directory) or `pnpm start-server` at the top level. \ No newline at end of file diff --git a/typescript/server/buf.gen.yaml b/typescript/server/buf.gen.yaml new file mode 100644 index 000000000..d59c8dbe4 --- /dev/null +++ b/typescript/server/buf.gen.yaml @@ -0,0 +1,10 @@ +version: v2 +inputs: + - directory: src/proto/myt +plugins: + - local: protoc-gen-es + out: src/proto/generated + opt: target=ts + - local: protoc-gen-connect-es + out: src/proto/generated + opt: target=ts diff --git a/typescript/server/example/.env b/typescript/server/example/.env deleted file mode 100644 index 7bc8d3d02..000000000 --- a/typescript/server/example/.env +++ /dev/null @@ -1,6 +0,0 @@ -NODE_ENV="dev" -REDIS_URL="redis" -MONGO_URL="mongo" -PORT=8080 -LOG_LEVEL="debug" -MSGPACKR_NATIVE_ACCELERATION_DISABLED=true \ No newline at end of file diff --git a/typescript/server/example/conf.json5 b/typescript/server/example/conf.json5 deleted file mode 100644 index 4d0992aec..000000000 --- a/typescript/server/example/conf.json5 +++ /dev/null @@ -1,98 +0,0 @@ -// Config file that the CI deployment uses. This is moved to $pwd/conf.json5 before CI tests are ran. - -{ - MONGO_DATABASE_NAME: "localdb", - CAPTCHA_SECRET_KEY: "unused", - SESSION_SECRET: "unused", - FLO_API_URL: "https://flo.example.com", - EAG_API_URL: "https://eag.example.com", - MIN_API_URL: "https://min.example.com", - MYT_API_HOST: "myt.example.com", - FLO_OAUTH2_INFO: { - CLIENT_ID: "DUMMY_CLIENT_ID", - CLIENT_SECRET: "DUMMY_CLIENT_SECRET", - REDIRECT_URI: "https://example.com", - }, - EAG_OAUTH2_INFO: { - CLIENT_ID: "DUMMY_CLIENT_ID", - CLIENT_SECRET: "DUMMY_CLIENT_SECRET", - REDIRECT_URI: "https://example.com", - }, - ARC_AUTH_TOKEN: "unused", - MYT_AUTH_TOKEN: "unused", - OUR_URL: "https://127.0.0.1:8080", - CLIENT_DEV_SERVER: "http://127.0.0.1:3000", - ENABLE_SERVER_HTTPS: true, - OPTIONS_ALWAYS_SUCCEEDS: true, - CDN_CONFIG: { - WEB_LOCATION: "https://127.0.0.1:8080/cdn", - SAVE_LOCATION: { - TYPE: "LOCAL_FILESYSTEM", - LOCATION: "./local-cdn", - SERVE_OWN_CDN: true, - }, - }, - TACHI_CONFIG: { - TYPE: "omni", - NAME: "Tachi Local Dev", - GAMES: [ - "iidx", - "museca", - "sdvx", - "bms", - "chunithm", - "usc", - "wacca", - "popn", - "jubeat", - "pms", - "gitadora", - "maimai", - "maimaidx", - "itg", - "ongeki", - "ddr" - ], - IMPORT_TYPES: [ - "file/eamusement-iidx-csv", - "file/batch-manual", - "file/solid-state-squad", - "file/pli-iidx-csv", - "file/mypagescraper-records-csv", - "file/mypagescraper-player-csv", - "ir/direct-manual", - "ir/barbatos", - "ir/fervidex", - "ir/fervidex-static", - "ir/beatoraja", - "ir/usc", - "ir/kshook-sv6c", - "ir/lr2hook", - "api/eag-iidx", - "api/eag-sdvx", - "api/flo-iidx", - "api/flo-sdvx", - "api/min-sdvx", - "api/cg-dev-sdvx", - "api/cg-dev-popn", - "api/cg-dev-museca", - "api/cg-dev-jubeat", - "api/cg-nag-sdvx", - "api/cg-nag-popn", - "api/cg-nag-jubeat", - "api/cg-nag-museca", - "api/cg-gan-sdvx", - "api/cg-gan-popn", - "api/cg-gan-museca", - "api/cg-gan-jubeat", - "api/myt-chunithm", - "api/myt-maimaidx", - "api/myt-ongeki", - "api/myt-wacca", - ], - }, - SEEDS_CONFIG: { - TYPE: "LOCAL_FILES", - PATH: "../seeds/collections", - }, -} diff --git a/typescript/server/nodemon.json b/typescript/server/nodemon.json deleted file mode 100644 index 40bfac496..000000000 --- a/typescript/server/nodemon.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "watch": ["src"], - "ext": "ts,json", - "ignore": ["src/**/*.spec.ts"], - "exec": "ts-node ./src/main.ts" -} diff --git a/typescript/server/package.json b/typescript/server/package.json index 45f6a7c69..a9c771776 100644 --- a/typescript/server/package.json +++ b/typescript/server/package.json @@ -6,9 +6,7 @@ "type": "module", "scripts": { "dev": "bun --watch src/main.ts", - "test": "tap", - "snap": "TAP_SNAPSHOT=1 tap", - "watchtest": "tap --watch", + "test": "vitest", "build": "tsgo -b tsconfig.build.json -v", "typecheck": "tsgo --noEmit", "lint": "eslint ./src", @@ -21,8 +19,8 @@ "@types/google-protobuf": "catalog:", "@types/pg": "catalog:", "@types/supertest": "catalog:", - "@types/tap": "catalog:", "@typescript/native-preview": "catalog:", + "@vitest/coverage-v8": "catalog:", "eslint-config-tachi": "workspace:*", "glob": "catalog:", "google-protobuf": "catalog:", @@ -30,9 +28,9 @@ "node-mocks-http": "catalog:", "nodemon": "catalog:", "supertest": "catalog:", - "tap": "workspace:*", "ts-protoc-gen": "catalog:", - "tsconfig-paths": "catalog:" + "tsconfig-paths": "catalog:", + "vitest": "catalog:" }, "dependencies": { "@aws-sdk/client-s3": "catalog:", @@ -59,6 +57,7 @@ "@types/semver": "catalog:", "@valuabletouch/winston-seq": "catalog:", "bcryptjs": "catalog:", + "bliss": "workspace:*", "bms-table-loader": "catalog:", "bullmq": "catalog:", "bunyan": "catalog:", @@ -77,6 +76,7 @@ "html-entities": "catalog:", "json5": "catalog:", "kysely": "catalog:", + "lodash": "^4.17.23", "mkdirp": "catalog:", "mongodb": "catalog:", "monk": "catalog:", @@ -86,7 +86,6 @@ "node-html-parser": "catalog:", "nodemailer": "catalog:", "pg": "catalog:", - "bliss": "workspace:*", "prudence": "catalog:", "rate-limit-redis": "catalog:", "redis": "catalog:", @@ -115,7 +114,7 @@ "src/*/!(test-utils)/**/*.ts" ], "exclude": [ - "src/**/*.test.ts", + "src/**/*.oldtest.ts", "src/external/mongo/schemas.ts" ] }, diff --git a/typescript/server/src/actions/install-builtin-client.ts b/typescript/server/src/actions/install-builtin-client.ts new file mode 100644 index 000000000..c5875fbbb --- /dev/null +++ b/typescript/server/src/actions/install-builtin-client.ts @@ -0,0 +1,67 @@ +import { MakeAction } from "#lib/actions/actions.js"; +import DB from "#services/pg/db.js"; +import { Random20Hex } from "#utils/misc.js"; +import { IsUserAdmin } from "#utils/user.js"; +import { ExpectedErr } from "bliss"; + +export const ACTION_InstallBuiltinClient = MakeAction( + "INSTALL_BUILTIN_CLIENT", + async ( + taker, + { clientID, name, permissions, apiKeyFilename, apiKeyTemplate, redirectUri, webhookUri }, + ) => { + // verify taker is admin + if (!(await IsUserAdmin(taker.acct.id))) { + throw new ExpectedErr(403, "You are not authorized to perform this action."); + } + + // I honestly think this is unused for builtin clients + // as they don't need to auth with anyone! + const clientSecret = `CS${Random20Hex()}`; + + // create client + await DB.insertInto("priv_api_client") + .values({ + client_id: clientID, + client_secret: clientSecret, + name: name, + author: taker.acct.id, + pm_customise_profile: permissions.customise_profile, + pm_customise_score: permissions.customise_score, + pm_customise_session: permissions.customise_session, + pm_delete_score: permissions.delete_score, + pm_manage_rivals: permissions.manage_rivals, + pm_manage_targets: permissions.manage_targets, + pm_submit_score: permissions.submit_score, + pm_manage_challenges: permissions.manage_challenges, + api_key_filename: apiKeyFilename, + api_key_template: apiKeyTemplate, + webhook_uri: webhookUri, + redirect_uri: redirectUri, + is_builtin: true, + }) + .onConflict((oc) => + oc.doUpdateSet({ + client_secret: clientSecret, + name: name, + author: taker.acct.id, + pm_customise_profile: permissions.customise_profile, + pm_customise_score: permissions.customise_score, + pm_customise_session: permissions.customise_session, + pm_delete_score: permissions.delete_score, + pm_manage_rivals: permissions.manage_rivals, + pm_manage_targets: permissions.manage_targets, + pm_submit_score: permissions.submit_score, + pm_manage_challenges: permissions.manage_challenges, + api_key_filename: apiKeyFilename, + api_key_template: apiKeyTemplate, + webhook_uri: webhookUri, + redirect_uri: redirectUri, + is_builtin: true, + }), + ) + .execute(); + + return {}; + }, +); diff --git a/typescript/server/src/game-implementations/games/arcaea.test.ts b/typescript/server/src/game-implementations/games/arcaea.oldtest.ts similarity index 97% rename from typescript/server/src/game-implementations/games/arcaea.test.ts rename to typescript/server/src/game-implementations/games/arcaea.oldtest.ts index 4b8d0e7e3..d346f3131 100644 --- a/typescript/server/src/game-implementations/games/arcaea.test.ts +++ b/typescript/server/src/game-implementations/games/arcaea.oldtest.ts @@ -2,7 +2,7 @@ import type { DeepPartial } from "#utils/types"; import { log } from "#lib/log/log.js"; import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc"; import ResetDBState from "#test-utils/resets"; import { TestSnapshot } from "#test-utils/single-process-snapshot"; @@ -146,8 +146,8 @@ t.test("Arcaea Implementation", (t) => { t.beforeEach(ResetDBState); t.test("Should join best lamp", async (t) => { - await db.scores.insert(mockScore); - await db.scores.insert( + await MONGODB_KILL.scores.insert(mockScore); + await MONGODB_KILL.scores.insert( dmf(mockScore, { scoreID: "bestLamp", scoreData: { diff --git a/typescript/server/src/game-implementations/games/bms-pms.test.ts b/typescript/server/src/game-implementations/games/bms-pms.oldtest.ts similarity index 97% rename from typescript/server/src/game-implementations/games/bms-pms.test.ts rename to typescript/server/src/game-implementations/games/bms-pms.oldtest.ts index b42f0cd88..17d05517f 100644 --- a/typescript/server/src/game-implementations/games/bms-pms.test.ts +++ b/typescript/server/src/game-implementations/games/bms-pms.oldtest.ts @@ -1,6 +1,6 @@ import { log } from "#lib/log/log.js"; import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc"; import ResetDBState from "#test-utils/resets"; import { TestSnapshot } from "#test-utils/single-process-snapshot"; @@ -297,10 +297,10 @@ for (const [game, playtype, impl] of [ t.test("PB Mergers", (t) => { t.beforeEach(ResetDBState); - t.beforeEach(() => db.scores.insert(mockScore)); + t.beforeEach(() => MONGODB_KILL.scores.insert(mockScore)); t.test("Should join best lamp", async (t) => { - await db.scores.insert( + await MONGODB_KILL.scores.insert( dmf(mockScore, { scoreID: "bestLamp", scoreData: { @@ -326,7 +326,7 @@ for (const [game, playtype, impl] of [ }); t.test("Should join best BP", async (t) => { - await db.scores.insert([ + await MONGODB_KILL.scores.insert([ dmf(mockScore, { scoreID: "whateverBP", scoreData: { optional: { bp: 100 } }, diff --git a/typescript/server/src/game-implementations/games/chunithm.test.ts b/typescript/server/src/game-implementations/games/chunithm.oldtest.ts similarity index 96% rename from typescript/server/src/game-implementations/games/chunithm.test.ts rename to typescript/server/src/game-implementations/games/chunithm.oldtest.ts index d160328ae..230c5822c 100644 --- a/typescript/server/src/game-implementations/games/chunithm.test.ts +++ b/typescript/server/src/game-implementations/games/chunithm.oldtest.ts @@ -1,6 +1,6 @@ import { log } from "#lib/log/log.js"; import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc"; import ResetDBState from "#test-utils/resets"; import { CHUNITHMBBKKChart, TestingChunithmScorePB } from "#test-utils/test-data"; @@ -83,7 +83,7 @@ t.test("CHUNITHM Implementation", (t) => { const mockPBs = async (ratings: Array) => { await Promise.all( ratings.map((rating, idx) => - db["personal-bests"].insert({ + MONGODB_KILL["personal-bests"].insert({ ...TestingChunithmScorePB, chartID: `TEST${idx}`, calculatedData: { @@ -192,8 +192,8 @@ t.test("CHUNITHM Implementation", (t) => { t.beforeEach(ResetDBState); t.test("Should join best lamp", async (t) => { - await db.scores.insert(mockScore); - await db.scores.insert( + await MONGODB_KILL.scores.insert(mockScore); + await MONGODB_KILL.scores.insert( dmf(mockScore, { scoreID: "bestNoteLamp", scoreData: { @@ -203,7 +203,7 @@ t.test("CHUNITHM Implementation", (t) => { }, }), ); - await db.scores.insert( + await MONGODB_KILL.scores.insert( dmf(mockScore, { scoreID: "bestClearLamp", scoreData: { diff --git a/typescript/server/src/game-implementations/games/ddr.test.ts b/typescript/server/src/game-implementations/games/ddr.oldtest.ts similarity index 100% rename from typescript/server/src/game-implementations/games/ddr.test.ts rename to typescript/server/src/game-implementations/games/ddr.oldtest.ts diff --git a/typescript/server/src/game-implementations/games/ddr.ts b/typescript/server/src/game-implementations/games/ddr.ts index a4cc381b4..1e473a6fc 100644 --- a/typescript/server/src/game-implementations/games/ddr.ts +++ b/typescript/server/src/game-implementations/games/ddr.ts @@ -6,7 +6,7 @@ import type { ScoreValidator, } from "#game-implementations/types"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { DDRFlare } from "rg-stats"; import { type ChartDocument, @@ -157,7 +157,7 @@ const DDR_CALCULATE_FLARE_SKILL: GPTNewProfileCalcs<"ddr:DP" | "ddr:SP"> = async playtype, userID, ) => { - const sc: Array = await db["personal-bests"].aggregate([ + const sc: Array = await MONGODB_KILL["personal-bests"].aggregate([ { $match: { userID, diff --git a/typescript/server/src/game-implementations/games/gitadora.test.ts b/typescript/server/src/game-implementations/games/gitadora.oldtest.ts similarity index 97% rename from typescript/server/src/game-implementations/games/gitadora.test.ts rename to typescript/server/src/game-implementations/games/gitadora.oldtest.ts index 9f180bf66..250969b02 100644 --- a/typescript/server/src/game-implementations/games/gitadora.test.ts +++ b/typescript/server/src/game-implementations/games/gitadora.oldtest.ts @@ -1,6 +1,6 @@ import { log } from "#lib/log/log.js"; import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc"; import ResetDBState from "#test-utils/resets"; import { TestingGitadoraChart } from "#test-utils/test-data"; @@ -170,8 +170,8 @@ for (const [playtype, impl] of [ t.beforeEach(ResetDBState); t.test("Should join best lamp", async (t) => { - await db.scores.insert(mockScore); - await db.scores.insert( + await MONGODB_KILL.scores.insert(mockScore); + await MONGODB_KILL.scores.insert( dmf(mockScore, { scoreID: "bestLamp", scoreData: { diff --git a/typescript/server/src/game-implementations/games/iidx.test.ts b/typescript/server/src/game-implementations/games/iidx.oldtest.ts similarity index 98% rename from typescript/server/src/game-implementations/games/iidx.test.ts rename to typescript/server/src/game-implementations/games/iidx.oldtest.ts index 090f47438..e5df78085 100644 --- a/typescript/server/src/game-implementations/games/iidx.test.ts +++ b/typescript/server/src/game-implementations/games/iidx.oldtest.ts @@ -1,6 +1,6 @@ import { log } from "#lib/log/log.js"; import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { dmf, mkFakePBIIDXSP, mkFakeScoreIIDXSP } from "#test-utils/misc"; import ResetDBState from "#test-utils/resets"; import { TestSnapshot } from "#test-utils/single-process-snapshot"; @@ -399,7 +399,7 @@ t.test("IIDX Implementation", (t) => { for (const gptStr of ["iidx:SP", "iidx:DP"] as const) { t.test("Should join best lamp", async (t) => { - await db.scores.insert( + await MONGODB_KILL.scores.insert( mkFakeScoreIIDXSP({ scoreID: "bestLamp", scoreData: { @@ -428,7 +428,7 @@ t.test("IIDX Implementation", (t) => { }); t.test("Should join best BP", async (t) => { - await db.scores.insert([ + await MONGODB_KILL.scores.insert([ mkFakeScoreIIDXSP({ scoreID: "whateverBP", scoreData: { optional: { bp: 100 } }, diff --git a/typescript/server/src/game-implementations/games/jubeat.test.ts b/typescript/server/src/game-implementations/games/jubeat.oldtest.ts similarity index 96% rename from typescript/server/src/game-implementations/games/jubeat.test.ts rename to typescript/server/src/game-implementations/games/jubeat.oldtest.ts index 0d6f991ae..baf3d8f63 100644 --- a/typescript/server/src/game-implementations/games/jubeat.test.ts +++ b/typescript/server/src/game-implementations/games/jubeat.oldtest.ts @@ -2,7 +2,7 @@ import type { ProvidedMetrics, ScoreData } from "tachi-common"; import { log } from "#lib/log/log.js"; import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc"; import ResetDBState from "#test-utils/resets"; import { TestSnapshot } from "#test-utils/single-process-snapshot"; @@ -204,8 +204,8 @@ t.test("Jubeat Implementation", (t) => { t.beforeEach(ResetDBState); t.test("Should join best lamp", async (t) => { - await db.scores.insert(mockScore); - await db.scores.insert( + await MONGODB_KILL.scores.insert(mockScore); + await MONGODB_KILL.scores.insert( dmf(mockScore, { scoreID: "bestLamp", scoreData: { @@ -234,8 +234,8 @@ t.test("Jubeat Implementation", (t) => { }); t.test("Should join best score", async (t) => { - await db.scores.insert(mockScore); - await db.scores.insert( + await MONGODB_KILL.scores.insert(mockScore); + await MONGODB_KILL.scores.insert( dmf(mockScore, { scoreID: "bestScore", scoreData: { diff --git a/typescript/server/src/game-implementations/games/jubeat.ts b/typescript/server/src/game-implementations/games/jubeat.ts index 9eded3232..d0dc72c01 100644 --- a/typescript/server/src/game-implementations/games/jubeat.ts +++ b/typescript/server/src/game-implementations/games/jubeat.ts @@ -3,7 +3,7 @@ import type { GPTServerImplementation } from "#game-implementations/types"; import { CreatePBMergeFor } from "#game-implementations/utils/pb-merge"; import { ProfileSumBestN } from "#game-implementations/utils/profile-calc"; import { SessionAvgBest10For } from "#game-implementations/utils/session-calc"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { IsNullish } from "#utils/misc"; import { p } from "prudence"; import { Jubility } from "rg-stats"; @@ -27,7 +27,7 @@ async function GetBestJubilityOnSongs( playtype: Playtype, limit: integer, ): Promise> { - const r: Array<{ doc: PBScoreDocument }> = await db["personal-bests"].aggregate([ + const r: Array<{ doc: PBScoreDocument }> = await MONGODB_KILL["personal-bests"].aggregate([ { $match: { game, @@ -107,14 +107,14 @@ async function GetBestJubilityOnSongs( const CURRENT_JUBEAT_HOT_VERSION: Versions["jubeat:Single"] = "ave"; export async function GetPBsForJubility(userID: integer) { - const hotSongs = await db.songs.jubeat.find( + const hotSongs = await MONGODB_KILL.songs.jubeat.find( { "data.displayVersion": CURRENT_JUBEAT_HOT_VERSION }, { projection: { id: 1 } }, ); const hotSongIDs = hotSongs.map((e) => e.id); - const coldSongs = await db.songs.jubeat.find( + const coldSongs = await MONGODB_KILL.songs.jubeat.find( { "data.displayVersion": { $ne: CURRENT_JUBEAT_HOT_VERSION } }, { projection: { id: 1 } }, ); diff --git a/typescript/server/src/game-implementations/games/maimai.test.ts b/typescript/server/src/game-implementations/games/maimai.oldtest.ts similarity index 97% rename from typescript/server/src/game-implementations/games/maimai.test.ts rename to typescript/server/src/game-implementations/games/maimai.oldtest.ts index 3df144221..7beb858de 100644 --- a/typescript/server/src/game-implementations/games/maimai.test.ts +++ b/typescript/server/src/game-implementations/games/maimai.oldtest.ts @@ -2,7 +2,7 @@ import type { integer, ProvidedMetrics, ScoreData } from "tachi-common"; import { log } from "#lib/log/log.js"; import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc"; import ResetDBState from "#test-utils/resets"; import { TestSnapshot } from "#test-utils/single-process-snapshot"; @@ -167,8 +167,8 @@ t.test("maimai Implementation", (t) => { t.beforeEach(ResetDBState); t.test("Should join best lamp", async (t) => { - await db.scores.insert(mockScore); - await db.scores.insert( + await MONGODB_KILL.scores.insert(mockScore); + await MONGODB_KILL.scores.insert( dmf(mockScore, { scoreID: "bestLamp", scoreData: { diff --git a/typescript/server/src/game-implementations/games/maimaidx.test.ts b/typescript/server/src/game-implementations/games/maimaidx.oldtest.ts similarity index 96% rename from typescript/server/src/game-implementations/games/maimaidx.test.ts rename to typescript/server/src/game-implementations/games/maimaidx.oldtest.ts index a08932203..f0a45e7c1 100644 --- a/typescript/server/src/game-implementations/games/maimaidx.test.ts +++ b/typescript/server/src/game-implementations/games/maimaidx.oldtest.ts @@ -2,7 +2,7 @@ import type { ProvidedMetrics, ScoreData } from "tachi-common"; import { log } from "#lib/log/log.js"; import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc"; import ResetDBState from "#test-utils/resets"; import { TestingMaimaiDXChart } from "#test-utils/test-data"; @@ -134,8 +134,8 @@ t.test("Maimai DX Implementation", (t) => { t.beforeEach(ResetDBState); t.test("Should join best lamp", async (t) => { - await db.scores.insert(mockScore); - await db.scores.insert( + await MONGODB_KILL.scores.insert(mockScore); + await MONGODB_KILL.scores.insert( dmf(mockScore, { scoreID: "bestLamp", scoreData: { diff --git a/typescript/server/src/game-implementations/games/museca.test.ts b/typescript/server/src/game-implementations/games/museca.oldtest.ts similarity index 95% rename from typescript/server/src/game-implementations/games/museca.test.ts rename to typescript/server/src/game-implementations/games/museca.oldtest.ts index 9e278a9e3..d09d17800 100644 --- a/typescript/server/src/game-implementations/games/museca.test.ts +++ b/typescript/server/src/game-implementations/games/museca.oldtest.ts @@ -1,6 +1,6 @@ import { log } from "#lib/log/log.js"; import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc"; import ResetDBState from "#test-utils/resets"; import { TestingMusecaChart } from "#test-utils/test-data"; @@ -100,8 +100,8 @@ t.test("MUSECA Implementation", (t) => { t.beforeEach(ResetDBState); t.test("Should join best lamp", async (t) => { - await db.scores.insert(mockScore); - await db.scores.insert( + await MONGODB_KILL.scores.insert(mockScore); + await MONGODB_KILL.scores.insert( dmf(mockScore, { scoreID: "bestLamp", scoreData: { diff --git a/typescript/server/src/game-implementations/games/ongeki.test.ts b/typescript/server/src/game-implementations/games/ongeki.oldtest.ts similarity index 94% rename from typescript/server/src/game-implementations/games/ongeki.test.ts rename to typescript/server/src/game-implementations/games/ongeki.oldtest.ts index 9615cdfa9..33280eb2e 100644 --- a/typescript/server/src/game-implementations/games/ongeki.test.ts +++ b/typescript/server/src/game-implementations/games/ongeki.oldtest.ts @@ -2,7 +2,7 @@ import { log } from "#lib/log/log.js"; import { CreatePBDoc, UpdateChartRanking } from "#lib/score-import/framework/pb/create-pb-doc"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc"; import ResetDBState from "#test-utils/resets"; import { TestingOngekiChart, TestingOngekiScorePB } from "#test-utils/test-data"; @@ -136,7 +136,7 @@ t.test("ONGEKI Implementation", (t: any) => { ? { rating: 0, scoreRating: rating, starRating: 0 } : { rating: 0, scoreRating: 0, starRating: rating }; - return db["personal-bests"].insert({ + return MONGODB_KILL["personal-bests"].insert({ ...TestingOngekiScorePB, chartID: `TEST${field}${idx}`, calculatedData: { @@ -282,8 +282,8 @@ t.test("ONGEKI Implementation", (t: any) => { t.beforeEach(ResetDBState); t.test("Should join best lamp", async (t: any) => { - await db.scores.insert(mockScore); - await db.scores.insert( + await MONGODB_KILL.scores.insert(mockScore); + await MONGODB_KILL.scores.insert( dmf(mockScore, { scoreID: "bestLamp", scoreData: { @@ -314,8 +314,8 @@ t.test("ONGEKI Implementation", (t: any) => { }); t.test("Should join platinum score", async (t: any) => { - await db.scores.insert(mockScore); - await db.scores.insert( + await MONGODB_KILL.scores.insert(mockScore); + await MONGODB_KILL.scores.insert( dmf(mockScore, { scoreID: "bestPlatinum", scoreData: { @@ -348,7 +348,7 @@ t.test("ONGEKI Implementation", (t: any) => { t.beforeEach(ResetDBState); t.test("Should tiebreak according to platinum score", async (t: any) => { - await db["personal-bests"].insert({ + await MONGODB_KILL["personal-bests"].insert({ ...TestingOngekiScorePB, scoreData: { ...TestingOngekiScorePB.scoreData, @@ -358,7 +358,7 @@ t.test("ONGEKI Implementation", (t: any) => { userID: 1, }); - await db["personal-bests"].insert({ + await MONGODB_KILL["personal-bests"].insert({ ...TestingOngekiScorePB, scoreData: { ...TestingOngekiScorePB.scoreData, @@ -368,7 +368,7 @@ t.test("ONGEKI Implementation", (t: any) => { userID: 2, }); - await db["personal-bests"].insert({ + await MONGODB_KILL["personal-bests"].insert({ ...TestingOngekiScorePB, scoreData: { ...TestingOngekiScorePB.scoreData, @@ -380,7 +380,7 @@ t.test("ONGEKI Implementation", (t: any) => { await UpdateChartRanking("ongeki", "Single", TestingOngekiChart.chartID); - const pbs = (await db["personal-bests"].find({ + const pbs = (await MONGODB_KILL["personal-bests"].find({ chartID: TestingOngekiChart.chartID, })) as Array>; @@ -401,7 +401,7 @@ t.test("ONGEKI Implementation", (t: any) => { }); t.test("Should not tiebreak if tech scores differ", async (t: any) => { - await db["personal-bests"].insert({ + await MONGODB_KILL["personal-bests"].insert({ ...TestingOngekiScorePB, scoreData: { ...TestingOngekiScorePB.scoreData, @@ -411,7 +411,7 @@ t.test("ONGEKI Implementation", (t: any) => { userID: 1, }); - await db["personal-bests"].insert({ + await MONGODB_KILL["personal-bests"].insert({ ...TestingOngekiScorePB, scoreData: { ...TestingOngekiScorePB.scoreData, @@ -423,7 +423,7 @@ t.test("ONGEKI Implementation", (t: any) => { await UpdateChartRanking("ongeki", "Single", TestingOngekiChart.chartID); - const [pb1, pb2] = (await db["personal-bests"].find( + const [pb1, pb2] = (await MONGODB_KILL["personal-bests"].find( { chartID: TestingOngekiChart.chartID, }, diff --git a/typescript/server/src/game-implementations/games/popn.test.ts b/typescript/server/src/game-implementations/games/popn.oldtest.ts similarity index 97% rename from typescript/server/src/game-implementations/games/popn.test.ts rename to typescript/server/src/game-implementations/games/popn.oldtest.ts index 0f0a9f892..b09a63d4b 100644 --- a/typescript/server/src/game-implementations/games/popn.test.ts +++ b/typescript/server/src/game-implementations/games/popn.oldtest.ts @@ -2,7 +2,7 @@ import type { ProvidedMetrics, ScoreData } from "tachi-common"; import { log } from "#lib/log/log.js"; import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc"; import ResetDBState from "#test-utils/resets"; import { TestingPopnChart } from "#test-utils/test-data"; @@ -159,8 +159,8 @@ t.test("Pop'n Implementation", (t) => { t.beforeEach(ResetDBState); t.test("Should join best clear medal", async (t) => { - await db.scores.insert(mockScore); - await db.scores.insert( + await MONGODB_KILL.scores.insert(mockScore); + await MONGODB_KILL.scores.insert( dmf(mockScore, { scoreID: "bestClearMedal", scoreData: { diff --git a/typescript/server/src/game-implementations/games/sdvx.test.ts b/typescript/server/src/game-implementations/games/sdvx.oldtest.ts similarity index 96% rename from typescript/server/src/game-implementations/games/sdvx.test.ts rename to typescript/server/src/game-implementations/games/sdvx.oldtest.ts index 1f4afbe7c..2a06b605b 100644 --- a/typescript/server/src/game-implementations/games/sdvx.test.ts +++ b/typescript/server/src/game-implementations/games/sdvx.oldtest.ts @@ -1,6 +1,6 @@ import { log } from "#lib/log/log.js"; import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc"; import ResetDBState from "#test-utils/resets"; import { TestSnapshot } from "#test-utils/single-process-snapshot"; @@ -172,8 +172,8 @@ t.test("SDVX Implementation", (t) => { t.beforeEach(ResetDBState); t.test("Should join best lamp", async (t) => { - await db.scores.insert(mockScore); - await db.scores.insert( + await MONGODB_KILL.scores.insert(mockScore); + await MONGODB_KILL.scores.insert( dmf(mockScore, { scoreID: "bestLamp", scoreData: { @@ -197,8 +197,8 @@ t.test("SDVX Implementation", (t) => { }); t.test("Should join best EX SCore", async (t) => { - await db.scores.insert(mockScore); - await db.scores.insert( + await MONGODB_KILL.scores.insert(mockScore); + await MONGODB_KILL.scores.insert( dmf(mockScore, { scoreID: "bestLamp", scoreData: { diff --git a/typescript/server/src/game-implementations/games/usc.test.ts b/typescript/server/src/game-implementations/games/usc.oldtest.ts similarity index 97% rename from typescript/server/src/game-implementations/games/usc.test.ts rename to typescript/server/src/game-implementations/games/usc.oldtest.ts index c4b1570f4..3b8e3bf4c 100644 --- a/typescript/server/src/game-implementations/games/usc.test.ts +++ b/typescript/server/src/game-implementations/games/usc.oldtest.ts @@ -1,6 +1,6 @@ import { log } from "#lib/log/log.js"; import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc"; import ResetDBState from "#test-utils/resets"; import { TestingUSCChart } from "#test-utils/test-data"; @@ -166,8 +166,8 @@ for (const [playtype, impl] of [ t.beforeEach(ResetDBState); t.test("Should join best lamp", async (t) => { - await db.scores.insert(mockScore); - await db.scores.insert( + await MONGODB_KILL.scores.insert(mockScore); + await MONGODB_KILL.scores.insert( dmf(mockScore, { scoreID: "bestLamp", scoreData: { diff --git a/typescript/server/src/game-implementations/games/wacca.test.ts b/typescript/server/src/game-implementations/games/wacca.oldtest.ts similarity index 97% rename from typescript/server/src/game-implementations/games/wacca.test.ts rename to typescript/server/src/game-implementations/games/wacca.oldtest.ts index bfe060998..dcc231455 100644 --- a/typescript/server/src/game-implementations/games/wacca.test.ts +++ b/typescript/server/src/game-implementations/games/wacca.oldtest.ts @@ -3,7 +3,7 @@ import type { ProvidedMetrics, ScoreData, ScoreDocument } from "tachi-common"; import { log } from "#lib/log/log.js"; import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc"; import ResetDBState from "#test-utils/resets"; import { TestSnapshot } from "#test-utils/single-process-snapshot"; @@ -144,8 +144,8 @@ t.test("WACCA Implementation", (t) => { t.beforeEach(ResetDBState); t.test("Should join best lamp", async (t) => { - await db.scores.insert(mockScore); - await db.scores.insert( + await MONGODB_KILL.scores.insert(mockScore); + await MONGODB_KILL.scores.insert( dmf(mockScore, { scoreID: "bestLamp", scoreData: { diff --git a/typescript/server/src/game-implementations/utils/pb-merge.ts b/typescript/server/src/game-implementations/utils/pb-merge.ts index eb3bfc510..9e14d4f9f 100644 --- a/typescript/server/src/game-implementations/utils/pb-merge.ts +++ b/typescript/server/src/game-implementations/utils/pb-merge.ts @@ -10,7 +10,7 @@ import type { } from "tachi-common"; import type { ExtractEnumMetricNames } from "tachi-common/types/metrics"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; // insane typemagic to get mongodb-safe names for this GPT's metrics. type MetricKeys = Exclude< @@ -55,7 +55,7 @@ export function CreatePBMergeFor( applicator: (base: PBScoreDocumentNoRank, score: ScoreDocument) => void, ): PBMergeFunction { return async (userID, chartID, asOfTimestamp, base) => { - const bestScoreFor = (await db.scores.findOne( + const bestScoreFor = (await MONGODB_KILL.scores.findOne( HandleAsOf( { userID, diff --git a/typescript/server/src/game-implementations/utils/profile-calc.ts b/typescript/server/src/game-implementations/utils/profile-calc.ts index b9fc29447..0c8226902 100644 --- a/typescript/server/src/game-implementations/utils/profile-calc.ts +++ b/typescript/server/src/game-implementations/utils/profile-calc.ts @@ -7,7 +7,7 @@ import type { ScoreRatingAlgorithms, } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; /** * Curries a function that returns the sum of N best ratings on `key`. @@ -29,7 +29,7 @@ function CalcN( multiplier = 1, ) { return async (game: GameGroup, playtype: Playtype, userID: integer) => { - const sc = await db["personal-bests"].find( + const sc = await MONGODB_KILL["personal-bests"].find( { game, playtype, @@ -100,7 +100,7 @@ export async function GetBestRatingOnSongs( ratingProp: "skill", limit: integer, ): Promise> { - const r: Array<{ doc: PBScoreDocument }> = await db["personal-bests"].aggregate([ + const r: Array<{ doc: PBScoreDocument }> = await MONGODB_KILL["personal-bests"].aggregate([ { $match: { game, diff --git a/typescript/server/src/lib/actions/actions.ts b/typescript/server/src/lib/actions/actions.ts index 0ecb9ec6b..24c0a0c7d 100644 --- a/typescript/server/src/lib/actions/actions.ts +++ b/typescript/server/src/lib/actions/actions.ts @@ -1,6 +1,7 @@ -import db from "#services/pg/db"; +import DB from "#services/pg/db"; import { type ActionTaker, type AnonActionTaker, MakeActionGuts } from "bliss"; import { type ActionSignature } from "bliss/actions"; +import { zodPermission } from "tachi-common"; import { z } from "zod"; const APP_NAME = "TACHI_SERVER"; @@ -13,6 +14,29 @@ export const ActionSignatures = { input: z.object({}), output: z.object({}), }, + INSTALL_BUILTIN_CLIENT: { + input: z.object({ + clientID: z.string(), + name: z.string(), + webhookUri: z.url().nullable(), + redirectUri: z.url().nullable(), + permissions: z + .object({ + customise_profile: z.boolean(), + customise_score: z.boolean(), + customise_session: z.boolean(), + delete_score: z.boolean(), + manage_rivals: z.boolean(), + manage_targets: z.boolean(), + submit_score: z.boolean(), + manage_challenges: z.boolean(), + }) + .partial(), + apiKeyTemplate: z.string().nullable(), + apiKeyFilename: z.string().nullable(), + }), + output: z.object({}), + }, } satisfies Record; export const AnonActionSignatures = { @@ -30,7 +54,7 @@ export function MakeAnonAction( fn: AnonActionFn, ): AnonActionFn { return MakeActionGuts({ - db, + db: DB, appName: APP_NAME, kind, // @ts-expect-error we're being creative with the types here @@ -44,7 +68,7 @@ export function MakeAnonAction( */ export function MakeAction(kind: A, fn: ActionFn): ActionFn { return MakeActionGuts({ - db, + db: DB, appName: APP_NAME, kind, // @ts-expect-error we're being creative with the types here diff --git a/typescript/server/src/lib/activity/activity.ts b/typescript/server/src/lib/activity/activity.ts index 5920dbc63..b7c6e926d 100644 --- a/typescript/server/src/lib/activity/activity.ts +++ b/typescript/server/src/lib/activity/activity.ts @@ -16,7 +16,7 @@ import type { UserDocument, } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetRecentlyAchievedGoals, GetRecentlyAchievedQuests, @@ -79,7 +79,7 @@ export async function GetRecentActivity( timeStarted: { $lt: startFrom === null ? Infinity : startFrom }, }; - const recentSessions = await db.sessions.find(initialSessionQuery, { + const recentSessions = await MONGODB_KILL.sessions.find(initialSessionQuery, { sort: { timeStarted: -1, }, @@ -108,7 +108,7 @@ export async function GetRecentActivity( { goals, goalSubs }, { quests, questSubs }, ] = await Promise.all([ - db["class-achievements"].find( + MONGODB_KILL["class-achievements"].find( { ...baseQuery, timeAchieved: timeConstraint, @@ -119,7 +119,7 @@ export async function GetRecentActivity( }, }, ), - db.scores.find( + MONGODB_KILL.scores.find( { ...baseQuery, highlight: true, diff --git a/typescript/server/src/lib/builtin-clients/builtin-clients.ts b/typescript/server/src/lib/builtin-clients/builtin-clients.ts index fe7148a7e..50b68e1fe 100644 --- a/typescript/server/src/lib/builtin-clients/builtin-clients.ts +++ b/typescript/server/src/lib/builtin-clients/builtin-clients.ts @@ -1,13 +1,11 @@ -import type { PrudenceError } from "prudence"; - +import { ACTION_InstallBuiltinClient } from "#actions/install-builtin-client.js"; import { log } from "#lib/log/log.js"; import { ServerConfig, TachiConfig } from "#lib/setup/config"; -import { type TachiAPIClientDocument, UserAuthLevels } from "tachi-common"; +import _ from "lodash"; +import { type TachiAPIClientDocument } from "tachi-common"; /* eslint-disable no-await-in-loop */ -import db from "#services/mongo/db"; -import { DatabaseSchemas } from "#services/mongo/schemas"; -import { Random20Hex } from "#utils/misc"; -import { FormatPrError } from "#utils/prudence"; +import { GetClientByID } from "#utils/queries/api-clients.js"; +import { GetFirstAdmin } from "#utils/user.js"; import fjsh from "fast-json-stable-hash"; type DefaultClients = Array>; @@ -307,56 +305,48 @@ export async function LoadDefaultClients() { } async function LoadClients(clients: DefaultClients) { - const firstAdmin = await db.users.findOne({ - authLevel: UserAuthLevels.ADMIN, - }); - - if (!firstAdmin) { - log.warn(`There are no admins on this instance of tachi-server. We cannot create default API Clients! -Chances are, you're seeing this message because you just bootstrapped Tachi. -You'll need to set a user's authLevel to ${UserAuthLevels.ADMIN}. -If you have no users, go create an account using the frontend, then run pnpm make-user-admin 1.`); - return; - } + const firstAdmin = await GetFirstAdmin(); for (const client of clients) { - const exists = await db["api-clients"].findOne( + const exists = await GetClientByID(client.clientID); + + if (exists) { + const existsOmitted = _.omit(exists, "author", "clientSecret"); + + // Skip if nothing has changed. + if (fjsh.hash(existsOmitted, "sha256") === fjsh.hash(client, "sha256")) { + continue; + } + } + + await ACTION_InstallBuiltinClient( { - clientID: client.clientID, - }, - { - projection: { - clientSecret: 0, - author: 0, + ip: null, + acct: { + id: firstAdmin.id, + username: firstAdmin.username, }, }, + { + clientID: client.clientID, + name: client.name, + permissions: { + customise_profile: false, + customise_score: false, + customise_session: false, + delete_score: false, + manage_rivals: false, + manage_targets: false, + submit_score: false, + manage_challenges: false, + }, + apiKeyFilename: client.apiKeyFilename, + apiKeyTemplate: client.apiKeyTemplate, + redirectUri: client.redirectUri, + webhookUri: client.webhookUri, + }, ); - // Skip if nothing has changed. - if (fjsh.hash(exists, "sha256") === fjsh.hash(client, "sha256")) { - continue; - } - - const realClient: TachiAPIClientDocument = { - ...client, - clientSecret: `CS${Random20Hex()}`, - author: 1, - }; - - try { - DatabaseSchemas["api-clients"](realClient); - } catch (err) { - log.error(`Invalid API Client ${client.name}: ${FormatPrError(err as PrudenceError)}.`); - continue; - } - - // No replaceOne support in monk -- have to do this. - await db["api-clients"].remove({ - clientID: client.clientID, - }); - - await db["api-clients"].insert(realClient); - log.info(`Loaded/Modified new built-in client ${client.name}.`); } } diff --git a/typescript/server/src/lib/cdn/cdn.test.ts b/typescript/server/src/lib/cdn/cdn.oldtest.ts similarity index 100% rename from typescript/server/src/lib/cdn/cdn.test.ts rename to typescript/server/src/lib/cdn/cdn.oldtest.ts diff --git a/typescript/server/src/lib/db-formats/api-client.ts b/typescript/server/src/lib/db-formats/api-client.ts new file mode 100644 index 000000000..0ade35fd3 --- /dev/null +++ b/typescript/server/src/lib/db-formats/api-client.ts @@ -0,0 +1,64 @@ +import { type Selection } from "kysely"; +import { type APIPermissions, type TachiAPIClientDocument } from "tachi-common"; +import { type Database } from "tachi-db"; + +export const SELECT_API_CLIENT = [ + "priv_api_client.client_id", + "priv_api_client.client_secret", + "priv_api_client.name", + "priv_api_client.author", + "priv_api_client.pm_customise_profile", + "priv_api_client.pm_customise_score", + "priv_api_client.pm_customise_session", + "priv_api_client.pm_delete_score", + "priv_api_client.pm_manage_rivals", + "priv_api_client.pm_manage_targets", + "priv_api_client.pm_submit_score", + "priv_api_client.pm_manage_challenges", + "priv_api_client.api_key_filename", + "priv_api_client.api_key_template", + "priv_api_client.webhook_uri", + "priv_api_client.redirect_uri", +] as const; + +export function ToAPIClientDocument( + row: Selection, +): TachiAPIClientDocument { + const requestedPermissions: Array = []; + if (row.pm_customise_profile) { + requestedPermissions.push("customise_profile"); + } + if (row.pm_customise_score) { + requestedPermissions.push("customise_score"); + } + if (row.pm_customise_session) { + requestedPermissions.push("customise_session"); + } + if (row.pm_delete_score) { + requestedPermissions.push("delete_score"); + } + if (row.pm_manage_rivals) { + requestedPermissions.push("manage_rivals"); + } + if (row.pm_manage_targets) { + requestedPermissions.push("manage_targets"); + } + if (row.pm_submit_score) { + requestedPermissions.push("submit_score"); + } + if (row.pm_manage_challenges) { + requestedPermissions.push("manage_challenges"); + } + + return { + clientID: row.client_id, + clientSecret: row.client_secret, + name: row.name, + author: row.author, + apiKeyFilename: row.api_key_filename, + apiKeyTemplate: row.api_key_template, + redirectUri: row.redirect_uri, + requestedPermissions, + webhookUri: row.webhook_uri, + }; +} diff --git a/typescript/server/src/lib/db-formats/user-settings.ts b/typescript/server/src/lib/db-formats/user-settings.ts new file mode 100644 index 000000000..002c5a640 --- /dev/null +++ b/typescript/server/src/lib/db-formats/user-settings.ts @@ -0,0 +1,29 @@ +import { type Selection } from "kysely"; +import { type integer, type UserSettingsDocument } from "tachi-common"; +import { type Database } from "tachi-db"; + +export const SELECT_USER_SETTINGS = [ + "account_settings.user_id", + "account_settings.pf_invisible", + "account_settings.pf_developer_mode", + "account_settings.pf_advanced_mode", + "account_settings.pf_contentious_content", + "account_settings.pf_deletable_scores", +] as const; + +export function ToUserSettingsDocument( + following: Array, + row: Selection, +): UserSettingsDocument { + return { + userID: row.user_id, + following, + preferences: { + invisible: row.pf_invisible, + developerMode: row.pf_developer_mode, + advancedMode: row.pf_advanced_mode, + contentiousContent: row.pf_contentious_content, + deletableScores: row.pf_deletable_scores, + }, + }; +} diff --git a/typescript/server/src/lib/db-formats/user.ts b/typescript/server/src/lib/db-formats/user.ts new file mode 100644 index 000000000..3c943a72e --- /dev/null +++ b/typescript/server/src/lib/db-formats/user.ts @@ -0,0 +1,67 @@ +import { AuthLevelToInt } from "#utils/conversion.js"; +import { ISO8601ToUnixMilliseconds } from "#utils/time.js"; +import { type Selection } from "kysely"; +import { type UserBadges, type UserDocument } from "tachi-common"; +import { type Database } from "tachi-db"; + +export const SELECT_USER = [ + "account.id", + "account.bd_alpha", + "account.bd_beta", + "account.bd_dev_team", + "account.about", + "account.auth_level", + "account.custom_banner_location", + "account.custom_pfp_location", + "account.joined", + "account.last_seen", + "account.sm_discord", + "account.sm_twitter", + "account.sm_github", + "account.sm_steam", + "account.sm_youtube", + "account.sm_twitch", + "account.status", + "account.username", + "account.normalized_username", + "account.is_supporter", +] as const; + +export function ToUserDocument( + row: Selection, +): UserDocument { + const badges: Array = []; + + if (row.bd_alpha) { + badges.push("alpha"); + } + if (row.bd_beta) { + badges.push("beta"); + } + if (row.bd_dev_team) { + badges.push("dev-team"); + } + + return { + about: row.about, + authLevel: AuthLevelToInt(row.auth_level), + badges, + customBannerLocation: row.custom_banner_location, + customPfpLocation: row.custom_pfp_location, + id: row.id, + joinDate: ISO8601ToUnixMilliseconds(row.joined), + lastSeen: ISO8601ToUnixMilliseconds(row.last_seen), + socialMedia: { + discord: row.sm_discord, + twitter: row.sm_twitter, + github: row.sm_github, + steam: row.sm_steam, + youtube: row.sm_youtube, + twitch: row.sm_twitch, + }, + status: row.status, + username: row.username, + usernameLowercase: row.normalized_username, + isSupporter: row.is_supporter, + }; +} diff --git a/typescript/server/src/lib/game-settings/create-game-settings.test.ts b/typescript/server/src/lib/game-settings/create-game-settings.oldtest.ts similarity index 85% rename from typescript/server/src/lib/game-settings/create-game-settings.test.ts rename to typescript/server/src/lib/game-settings/create-game-settings.oldtest.ts index 522d9012f..ab0ab5bdc 100644 --- a/typescript/server/src/lib/game-settings/create-game-settings.test.ts +++ b/typescript/server/src/lib/game-settings/create-game-settings.oldtest.ts @@ -1,4 +1,4 @@ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import ResetDBState from "#test-utils/resets"; import t from "tap"; @@ -10,7 +10,7 @@ t.test("#CreateGameSettings", (t) => { t.test("Should create a new user's settings.", async (t) => { await CreateGameSettings(1, "bms", "7K"); - const data = await db["game-settings"].findOne({ + const data = await MONGODB_KILL["game-settings"].findOne({ userID: 1, game: "bms", playtype: "7K", diff --git a/typescript/server/src/lib/game-settings/create-game-settings.ts b/typescript/server/src/lib/game-settings/create-game-settings.ts index b5a035b50..a7fb68c50 100644 --- a/typescript/server/src/lib/game-settings/create-game-settings.ts +++ b/typescript/server/src/lib/game-settings/create-game-settings.ts @@ -1,13 +1,13 @@ import type { GameGroup, integer, Playtype } from "tachi-common"; import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; /** * Create GameSettings for a UGPT (which contains their preferences). */ export async function CreateGameSettings(userID: integer, game: GameGroup, playtype: Playtype) { - const exists = await db["game-settings"].findOne({ + const exists = await MONGODB_KILL["game-settings"].findOne({ userID, game, playtype, @@ -32,7 +32,7 @@ export async function CreateGameSettings(userID: integer, game: GameGroup, playt }; } - await db["game-settings"].insert({ + await MONGODB_KILL["game-settings"].insert({ userID, game, playtype, diff --git a/typescript/server/src/lib/game-specific/iidx-playlists.ts b/typescript/server/src/lib/game-specific/iidx-playlists.ts index 1db3e41b7..59e6f851d 100644 --- a/typescript/server/src/lib/game-specific/iidx-playlists.ts +++ b/typescript/server/src/lib/game-specific/iidx-playlists.ts @@ -1,7 +1,7 @@ import type { ChartDocument, integer, Playtypes } from "tachi-common"; import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { PoyashiBPI } from "rg-stats"; interface PlaylistEntry { @@ -110,7 +110,7 @@ export const CUSTOM_TACHI_IIDX_PLAYLISTS: Array = [ bounds.push([cutoffs[i], cutoffs[i + 1]] as [number, number]); } - const charts = (await db.charts.iidx.find({ + const charts = (await MONGODB_KILL.charts.iidx.find({ "data.kaidenAverage": { $ne: null }, playtype, })) as Array>; diff --git a/typescript/server/src/lib/imports/imports.test.ts b/typescript/server/src/lib/imports/imports.oldtest.ts similarity index 74% rename from typescript/server/src/lib/imports/imports.test.ts rename to typescript/server/src/lib/imports/imports.oldtest.ts index 46088762a..178998b89 100644 --- a/typescript/server/src/lib/imports/imports.test.ts +++ b/typescript/server/src/lib/imports/imports.oldtest.ts @@ -1,4 +1,4 @@ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { dmf, mkFakeImport } from "#test-utils/misc"; import ResetDBState from "#test-utils/resets"; import { TestingIIDXSPScore } from "#test-utils/test-data"; @@ -9,7 +9,7 @@ import { RevertImport } from "./imports"; t.test("#RevertImport", (t) => { t.beforeEach(ResetDBState); t.beforeEach(() => - db.scores.insert([ + MONGODB_KILL.scores.insert([ dmf(TestingIIDXSPScore, { scoreID: "score_1" }), dmf(TestingIIDXSPScore, { scoreID: "score_2" }), dmf(TestingIIDXSPScore, { scoreID: "score_3" }), @@ -21,16 +21,16 @@ t.test("#RevertImport", (t) => { scoreIDs: ["score_1", "score_2"], }); - await db.imports.insert(importDoc); + await MONGODB_KILL.imports.insert(importDoc); await RevertImport(importDoc); - const dbRes = await db.imports.findOne({ importID: "fake_import" }); + const dbRes = await MONGODB_KILL.imports.findOne({ importID: "fake_import" }); t.equal(dbRes, null, "Should have removed the import from the DB."); t.resolveMatch( - db.scores.findOne({ userID: 1, scoreID: "score_1" }), + MONGODB_KILL.scores.findOne({ userID: 1, scoreID: "score_1" }), // @ts-expect-error https://github.com/DefinitelyTyped/DefinitelyTyped/pull/60020 null, @@ -38,7 +38,7 @@ t.test("#RevertImport", (t) => { ); t.resolveMatch( - db.scores.findOne({ userID: 1, scoreID: "score_2" }), + MONGODB_KILL.scores.findOne({ userID: 1, scoreID: "score_2" }), // @ts-expect-error see above null, @@ -46,7 +46,7 @@ t.test("#RevertImport", (t) => { ); t.resolveMatch( - db.scores.findOne({ userID: 1, scoreID: "score_3" }), + MONGODB_KILL.scores.findOne({ userID: 1, scoreID: "score_3" }), // @ts-expect-error see above { scoreID: "score_3" }, diff --git a/typescript/server/src/lib/imports/imports.ts b/typescript/server/src/lib/imports/imports.ts index 700b21f5b..4becb5621 100644 --- a/typescript/server/src/lib/imports/imports.ts +++ b/typescript/server/src/lib/imports/imports.ts @@ -6,7 +6,7 @@ import { UnsetOngoingImportLock, } from "#lib/score-import/framework/import-locks/lock"; import { DeleteMultipleScores } from "#lib/score-mutation/delete-scores"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; interface OngoingImportError { tag: "ONGOING_IMPORT"; @@ -43,7 +43,7 @@ export async function RevertImport(importDoc: ImportDocument): Promise>; @@ -85,7 +85,7 @@ async function HandleTableRemovals( // remove this table info from all of the charts that no longer // exist in the table. - await db.charts.bms.update( + await MONGODB_KILL.charts.bms.update( { chartID: { $in: toRemove }, }, @@ -115,7 +115,7 @@ async function ImportTableLevels( .filter((e) => e.checksum.type === "md5") .map((e) => e.checksum.value); - await db.charts.bms.update( + await MONGODB_KILL.charts.bms.update( { "data.hashMD5": { $in: md5s }, }, @@ -127,7 +127,7 @@ async function ImportTableLevels( { multi: true }, ); - await db.charts.bms.update( + await MONGODB_KILL.charts.bms.update( { "data.hashSHA256": { $in: sha256s }, }, @@ -154,7 +154,8 @@ async function ImportTableLevels( } } - let chart: ChartDocument<"bms:7K" | "bms:14K"> | null = await db.charts.bms.findOne(query); + let chart: ChartDocument<"bms:7K" | "bms:14K"> | null = + await MONGODB_KILL.charts.bms.findOne(query); if (!chart) { // didn't find it in the DB? @@ -195,7 +196,7 @@ async function ImportTableLevels( return a.level.localeCompare(b.level); }); - await db.charts.bms.update( + await MONGODB_KILL.charts.bms.update( { chartID: chart.chartID, }, @@ -206,7 +207,7 @@ async function ImportTableLevels( }, ); - await db.songs.bms.update( + await MONGODB_KILL.songs.bms.update( { id: chart.songID, }, diff --git a/typescript/server/src/lib/jobs/ugs-snapshot.ts b/typescript/server/src/lib/jobs/ugs-snapshot.ts index b3f6c8b6e..6af12cc73 100644 --- a/typescript/server/src/lib/jobs/ugs-snapshot.ts +++ b/typescript/server/src/lib/jobs/ugs-snapshot.ts @@ -1,7 +1,7 @@ import type { UserGameStats, UserGameStatsSnapshotDocument } from "tachi-common"; import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetMillisecondsSince } from "#utils/misc"; import { GetAllRankings } from "#utils/user"; @@ -16,7 +16,9 @@ let batchWrite: Array = []; export async function UGSSnapshot() { const timeStart = process.hrtime.bigint(); - const alreadyExists = await db["game-stats-snapshots"].findOne({ timestamp: currentTime }); + const alreadyExists = await MONGODB_KILL["game-stats-snapshots"].findOne({ + timestamp: currentTime, + }); if (alreadyExists) { log.warn( @@ -31,7 +33,7 @@ export async function UGSSnapshot() { log.info(`Snapshotting UserGameStats.`); try { - await db["game-stats"] + await MONGODB_KILL["game-stats"] .find({}) // @ts-expect-error faulty TS types @@ -41,7 +43,11 @@ export async function UGSSnapshot() { log.debug(`Snapshotting ${ugs.userID} ${ugs.playtype} ${ugs.game}.`); const [playcount, rankings] = await Promise.all([ - db.scores.count({ userID: ugs.userID, playtype: ugs.playtype, game: ugs.game }), + MONGODB_KILL.scores.count({ + userID: ugs.userID, + playtype: ugs.playtype, + game: ugs.game, + }), GetAllRankings(ugs), ]); @@ -56,7 +62,7 @@ export async function UGSSnapshot() { if (batchWrite.length >= 500) { log.debug(`Flushed batch.`); - await db["game-stats-snapshots"].insert(batchWrite); + await MONGODB_KILL["game-stats-snapshots"].insert(batchWrite); batchWrite = []; } @@ -65,7 +71,7 @@ export async function UGSSnapshot() { }); if (batchWrite.length) { - await db["game-stats-snapshots"].insert(batchWrite); + await MONGODB_KILL["game-stats-snapshots"].insert(batchWrite); } log.info( @@ -84,7 +90,7 @@ export async function UGSSnapshot() { log.info(`Removing all snapshots at this timestamp (${currentTime}).`); - await db["game-stats-snapshots"].remove({ timestamp: currentTime }); + await MONGODB_KILL["game-stats-snapshots"].remove({ timestamp: currentTime }); log.info(`Removed.`); diff --git a/typescript/server/src/lib/jobs/update-bpi-data.ts b/typescript/server/src/lib/jobs/update-bpi-data.ts index d9be2b2bd..8ee85b637 100644 --- a/typescript/server/src/lib/jobs/update-bpi-data.ts +++ b/typescript/server/src/lib/jobs/update-bpi-data.ts @@ -9,7 +9,7 @@ import type { import { log } from "#lib/log/log.js"; import { BacksyncCollection, PullDatabaseSeeds } from "#lib/seeds/repo"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { RecalcAllScores } from "#utils/calculations/recalc-scores"; import fetch from "#utils/fetch"; import { WrapScriptPromise } from "#utils/misc"; @@ -173,7 +173,7 @@ export async function UpdatePoyashiData() { await repo.Destroy(); - await BacksyncCollection("charts-iidx", db.charts.iidx, "Update BPI Data"); + await BacksyncCollection("charts-iidx", MONGODB_KILL.charts.iidx, "Update BPI Data"); } if (require.main === module) { diff --git a/typescript/server/src/lib/jobs/update-dp-tiers.ts b/typescript/server/src/lib/jobs/update-dp-tiers.ts index f193631eb..f15255f2a 100644 --- a/typescript/server/src/lib/jobs/update-dp-tiers.ts +++ b/typescript/server/src/lib/jobs/update-dp-tiers.ts @@ -1,7 +1,7 @@ import { log } from "#lib/log/log.js"; import { BacksyncCollection } from "#lib/seeds/repo"; /* eslint-disable no-await-in-loop */ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { RecalcAllScores } from "#utils/calculations/recalc-scores"; import fetch from "#utils/fetch"; import { WrapScriptPromise } from "#utils/misc"; @@ -56,7 +56,7 @@ export async function UpdateDPTiers() { continue; } - const result = await db.charts.iidx.update( + const result = await MONGODB_KILL.charts.iidx.update( { versions: "29", playtype: "DP", @@ -92,7 +92,7 @@ export async function UpdateDPTiers() { log.info(`Recalced those scores.`); - await BacksyncCollection("charts-iidx", db.charts.iidx, "Update DP Tierlist"); + await BacksyncCollection("charts-iidx", MONGODB_KILL.charts.iidx, "Update DP Tierlist"); } log.info("Done."); diff --git a/typescript/server/src/lib/jobs/update-sp12-data.ts b/typescript/server/src/lib/jobs/update-sp12-data.ts index 9d24f82b5..8ca37e72f 100644 --- a/typescript/server/src/lib/jobs/update-sp12-data.ts +++ b/typescript/server/src/lib/jobs/update-sp12-data.ts @@ -4,7 +4,7 @@ import type { ChartDocument, Difficulties, integer } from "tachi-common"; import { log } from "#lib/log/log.js"; import { BacksyncCollection } from "#lib/seeds/repo"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { RecalcAllScores } from "#utils/calculations/recalc-scores"; import { WrapScriptPromise } from "#utils/misc"; import { FindChartWithPTDF } from "#utils/queries/charts"; @@ -172,7 +172,7 @@ async function FetchSP12Data() { updatedChartIDs.push(chart.chartID); - await db.charts.iidx.update( + await MONGODB_KILL.charts.iidx.update( { chartID: chart.chartID, }, @@ -203,7 +203,7 @@ async function FetchSP12Data() { log.info(`Finished recalcing scores.`); - await BacksyncCollection("charts-iidx", db.charts.iidx, "Update SP12 Tierlist"); + await BacksyncCollection("charts-iidx", MONGODB_KILL.charts.iidx, "Update SP12 Tierlist"); } process.exit(0); diff --git a/typescript/server/src/lib/notifications/notification-wrappers.ts b/typescript/server/src/lib/notifications/notification-wrappers.ts index 1b55646a4..04e239bd0 100644 --- a/typescript/server/src/lib/notifications/notification-wrappers.ts +++ b/typescript/server/src/lib/notifications/notification-wrappers.ts @@ -1,4 +1,4 @@ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { FormatGameGroup, type GameGroup, @@ -23,7 +23,7 @@ export async function SendSetRivalNotification( game: GameGroup, playtype: Playtype, ) { - const alreadyBeenPinged = await db.notifications.findOne({ + const alreadyBeenPinged = await MONGODB_KILL.notifications.findOne({ sentTo: toUserID, "body.type": "RIVALED_BY", "body.content": { @@ -60,13 +60,13 @@ export async function SendSiteAnnouncementNotification( if (maybeGame && maybePlaytype) { toUserIDs = ( - await db["game-stats"].find( + await MONGODB_KILL["game-stats"].find( { game: maybeGame, playtype: maybePlaytype }, { projection: { userID: 1 } }, ) ).map((e) => e.userID); } else { - toUserIDs = (await db.users.find({}, { projection: { id: 1 } })).map((e) => e.id); + toUserIDs = (await MONGODB_KILL.users.find({}, { projection: { id: 1 } })).map((e) => e.id); } return BulkSendNotification(title, toUserIDs, { diff --git a/typescript/server/src/lib/notifications/notifications.test.ts b/typescript/server/src/lib/notifications/notifications.oldtest.ts similarity index 84% rename from typescript/server/src/lib/notifications/notifications.test.ts rename to typescript/server/src/lib/notifications/notifications.oldtest.ts index 43e7cfcf8..b4f63550d 100644 --- a/typescript/server/src/lib/notifications/notifications.test.ts +++ b/typescript/server/src/lib/notifications/notifications.oldtest.ts @@ -1,4 +1,4 @@ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import ResetDBState from "#test-utils/resets"; import t from "tap"; @@ -13,7 +13,7 @@ t.test("#SendNotification", (t) => { content: { questID: "foo", game: "iidx", playtype: "SP" }, }); - const dbRes = await db.notifications.findOne({ + const dbRes = await MONGODB_KILL.notifications.findOne({ sentTo: 1, }); @@ -42,7 +42,7 @@ t.test("#BulkSendNotification", (t) => { content: { questID: "foo", game: "iidx", playtype: "SP" }, }); - const dbRes = await db.notifications.findOne({ + const dbRes = await MONGODB_KILL.notifications.findOne({ sentTo: 1, }); @@ -56,7 +56,7 @@ t.test("#BulkSendNotification", (t) => { }, }); - const dbRes2 = await db.notifications.findOne({ + const dbRes2 = await MONGODB_KILL.notifications.findOne({ sentTo: 2, }); diff --git a/typescript/server/src/lib/notifications/notifications.ts b/typescript/server/src/lib/notifications/notifications.ts index fb5964533..dd4d03373 100644 --- a/typescript/server/src/lib/notifications/notifications.ts +++ b/typescript/server/src/lib/notifications/notifications.ts @@ -1,6 +1,6 @@ import type { integer, NotificationBody, NotificationDocument } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { Random20Hex } from "#utils/misc"; function ConstructNotificationDoc( @@ -28,7 +28,7 @@ function ConstructNotificationDoc( export function SendNotification(title: string, toUserID: integer, body: NotificationBody) { const notification = ConstructNotificationDoc(title, toUserID, body); - return db.notifications.insert(notification); + return MONGODB_KILL.notifications.insert(notification); } /** @@ -42,5 +42,5 @@ export function BulkSendNotification( ) { const notifications = toUserIDs.map((e) => ConstructNotificationDoc(title, e, body)); - return db.notifications.insert(notifications); + return MONGODB_KILL.notifications.insert(notifications); } diff --git a/typescript/server/src/lib/orphan-queue/orphan-queue.ts b/typescript/server/src/lib/orphan-queue/orphan-queue.ts index f295ea07b..933c553c5 100644 --- a/typescript/server/src/lib/orphan-queue/orphan-queue.ts +++ b/typescript/server/src/lib/orphan-queue/orphan-queue.ts @@ -9,7 +9,7 @@ import type { } from "tachi-common"; import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetNextCounterValue } from "#utils/db"; import { DedupeArr } from "#utils/misc"; @@ -37,7 +37,7 @@ export async function HandleOrphanQueue( ) { log.debug(`Received orphanqueue request for ${chartName}.`); - const orphanChart = await db["orphan-chart-queue"].findOne( + const orphanChart = await MONGODB_KILL["orphan-chart-queue"].findOne( { gptString, ...orphanMatchCriteria }, { projectID: true, @@ -47,7 +47,7 @@ export async function HandleOrphanQueue( if (!orphanChart) { log.debug(`Received unknown chart ${chartName}, orphaning.`); - await db["orphan-chart-queue"].insert({ + await MONGODB_KILL["orphan-chart-queue"].insert({ gptString, chartDoc, songDoc, @@ -77,10 +77,10 @@ export async function HandleOrphanQueue( songDoc.id = songID; chartDoc.songID = songID; - await db.anySongs[game].insert(songDoc); - await db.anyCharts[game].insert(chartDoc); + await MONGODB_KILL.anySongs[game].insert(songDoc); + await MONGODB_KILL.anyCharts[game].insert(chartDoc); - await db["orphan-chart-queue"].remove({ + await MONGODB_KILL["orphan-chart-queue"].remove({ _id: orphanChart._id, }); @@ -90,7 +90,7 @@ export async function HandleOrphanQueue( // otherwise, update the state of this orphan. log.debug(`UserID ${userID} played ${chartName}, which is now at ${playcount} plays.`); - await db["orphan-chart-queue"].update( + await MONGODB_KILL["orphan-chart-queue"].update( { _id: orphanChart._id, }, @@ -115,7 +115,7 @@ export async function DeorphanIfInQueue( game: GPTStringToGame[GPT], orphanMatchCriteria: FilterQuery>, ): Promise | null> { - const orphanChart = await db["orphan-chart-queue"].findOne( + const orphanChart = await MONGODB_KILL["orphan-chart-queue"].findOne( { gptString, ...orphanMatchCriteria }, { projectID: true, @@ -136,9 +136,9 @@ export async function DeorphanIfInQueue( songDoc.id = songID; chartDoc.songID = songID; - await db.anySongs[game].insert(songDoc); - await db.anyCharts[game].insert(chartDoc); - await db["orphan-chart-queue"].remove({ + await MONGODB_KILL.anySongs[game].insert(songDoc); + await MONGODB_KILL.anyCharts[game].insert(chartDoc); + await MONGODB_KILL["orphan-chart-queue"].remove({ _id: orphanChart._id, }); diff --git a/typescript/server/src/lib/rivals/rivals.ts b/typescript/server/src/lib/rivals/rivals.ts index 3c3c5827a..4e3d11549 100644 --- a/typescript/server/src/lib/rivals/rivals.ts +++ b/typescript/server/src/lib/rivals/rivals.ts @@ -4,7 +4,7 @@ import { SetRivalsFailReasons } from "#lib/constants/err-codes"; import { log } from "#lib/log/log.js"; import { SendSetRivalNotification } from "#lib/notifications/notification-wrappers"; import { ServerConfig } from "#lib/setup/config"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { ArrayDiff } from "#utils/misc"; import { GetUsersWithIDs, GetUserWithIDGuaranteed } from "#utils/user"; import { @@ -21,7 +21,7 @@ import { * Throws if the user hasn't played the GPT in question. */ export async function GetRivalIDs(userID: integer, game: GameGroup, playtype: Playtype) { - const gameSettings = await db["game-settings"].findOne( + const gameSettings = await MONGODB_KILL["game-settings"].findOne( { userID, game, @@ -63,7 +63,7 @@ export async function GetEveryonesRivalIDs( game: GameGroup, playtype: Playtype, ): Promise>> { - const allGameSettings = await db["game-settings"].find( + const allGameSettings = await MONGODB_KILL["game-settings"].find( { game, playtype, @@ -104,7 +104,7 @@ export async function SetRivals( return SetRivalsFailReasons.RIVALED_SELF; } - const playedGPTCount = await db["game-settings"].count({ + const playedGPTCount = await MONGODB_KILL["game-settings"].count({ userID: { $in: newRivals }, game, playtype, @@ -115,7 +115,7 @@ export async function SetRivals( return SetRivalsFailReasons.RIVALS_HAVENT_PLAYED_GPT; } - const currentGameSettings = await db["game-settings"].findOne({ + const currentGameSettings = await MONGODB_KILL["game-settings"].findOne({ userID, game, playtype, @@ -145,7 +145,7 @@ export async function SetRivals( newSubs.map((toUserID) => SendSetRivalNotification(toUserID, user, game, playtype)), ); - await db["game-settings"].update( + await MONGODB_KILL["game-settings"].update( { userID, game, @@ -206,7 +206,7 @@ export async function RemoveRival( * Get all of the userIDs of people who rival the userID for this GPT. */ export async function GetChallengerIDs(userID: integer, game: GameGroup, playtype: Playtype) { - const result = await db["game-settings"].find( + const result = await MONGODB_KILL["game-settings"].find( { game, playtype, @@ -252,7 +252,7 @@ export async function UpdatePlayersRivalRankings( const rivalIDs = await GetRivalIDs(userID, game, playtype); // get all of this user's chartIDs so we know what to update - const userPBs = (await db["personal-bests"].find( + const userPBs = (await MONGODB_KILL["personal-bests"].find( { userID, game, playtype }, { projection: { chartID: 1, [`scoreData.${gptConfig.defaultMetric}`]: 1 } }, )) as Array<{ chartID: string; scoreData: { percent: number } }>; @@ -262,7 +262,7 @@ export async function UpdatePlayersRivalRankings( await Promise.all( userPBs.map(async (pb) => { const rivalRank = - (await db["personal-bests"].count({ + (await MONGODB_KILL["personal-bests"].count({ chartID: pb.chartID, userID: { $in: rivalIDs }, [`scoreData.${gptConfig.defaultMetric}`]: { $gt: pb.scoreData.percent }, @@ -285,5 +285,5 @@ export async function UpdatePlayersRivalRankings( return; } - await db["personal-bests"].bulkWrite(bwrite, { ordered: false }); + await MONGODB_KILL["personal-bests"].bulkWrite(bwrite, { ordered: false }); } diff --git a/typescript/server/src/lib/score-import/framework/common/string-asserts.test.ts b/typescript/server/src/lib/score-import/framework/common/string-asserts.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/framework/common/string-asserts.test.ts rename to typescript/server/src/lib/score-import/framework/common/string-asserts.oldtest.ts diff --git a/typescript/server/src/lib/score-import/framework/goals/goals.test.ts b/typescript/server/src/lib/score-import/framework/goals/goals.oldtest.ts similarity index 83% rename from typescript/server/src/lib/score-import/framework/goals/goals.test.ts rename to typescript/server/src/lib/score-import/framework/goals/goals.oldtest.ts index 914a625c0..9f137ee89 100644 --- a/typescript/server/src/lib/score-import/framework/goals/goals.test.ts +++ b/typescript/server/src/lib/score-import/framework/goals/goals.oldtest.ts @@ -1,7 +1,7 @@ import type { GoalDocument, GoalSubscriptionDocument } from "tachi-common"; import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { dmf } from "#test-utils/misc.js"; import ResetDBState from "#test-utils/resets"; import { @@ -51,10 +51,10 @@ t.test("#UpdateGoalsForUser", (t) => { }; t.test("Should correctly update goals when user achieves goal.", async (t) => { - await db.goals.insert(baseGoalDocument); + await MONGODB_KILL.goals.insert(baseGoalDocument); - await db["goal-subs"].insert(baseGoalSubscriptionDocument); - await db["personal-bests"].insert(TestingIIDXSPScorePB); + await MONGODB_KILL["goal-subs"].insert(baseGoalSubscriptionDocument); + await MONGODB_KILL["personal-bests"].insert(TestingIIDXSPScorePB); const ugMap = new Map([["FAKE_GOAL_ID", baseGoalSubscriptionDocument]]); @@ -80,7 +80,7 @@ t.test("#UpdateGoalsForUser", (t) => { }, ]); - const r = await db["goal-subs"].findOne({ goalID: "FAKE_GOAL_ID", userID: 1 }); + const r = await MONGODB_KILL["goal-subs"].findOne({ goalID: "FAKE_GOAL_ID", userID: 1 }); t.hasStrict( r, @@ -100,19 +100,21 @@ t.test("#UpdateGoalsForUser", (t) => { t.test("Should correctly update goals when user does not achieve goal.", async (t) => { const goal = dmf(baseGoalDocument, { criteria: { value: 2 } }); - await db.goals.insert(goal); + await MONGODB_KILL.goals.insert(goal); const goalSub = deepmerge(baseGoalSubscriptionDocument, { outOf: 2, outOfHuman: "2", }) as unknown as GoalSubscriptionDocument; - await db["goal-subs"].insert(goalSub); + await MONGODB_KILL["goal-subs"].insert(goalSub); // we dont delete _id here because updategoalsforuser // depends on usergoal _id - await db["personal-bests"].insert(dmf(TestingIIDXSPScorePB, { scoreData: { score: 1 } })); + await MONGODB_KILL["personal-bests"].insert( + dmf(TestingIIDXSPScorePB, { scoreData: { score: 1 } }), + ); const ugMap = new Map([["FAKE_GOAL_ID", goalSub]]); @@ -138,7 +140,7 @@ t.test("#UpdateGoalsForUser", (t) => { }, ]); - const r = await db["goal-subs"].findOne({ goalID: "FAKE_GOAL_ID", userID: 1 }); + const r = await MONGODB_KILL["goal-subs"].findOne({ goalID: "FAKE_GOAL_ID", userID: 1 }); t.hasStrict( r, @@ -196,14 +198,14 @@ t.todo("#GetAndUpdateUsersGoals", (t) => { t.test("#ProcessGoal", (t) => { t.beforeEach(ResetDBState); t.beforeEach(async () => { - await db.goals.insert(HC511Goal); + await MONGODB_KILL.goals.insert(HC511Goal); }); t.test("Should process the users goal if a score has changed.", async (t) => { - await db["goal-subs"].insert(HC511UserGoal); + await MONGODB_KILL["goal-subs"].insert(HC511UserGoal); // score is EX HARD CLEAR by default. - await db["personal-bests"].insert(TestingIIDXSPScorePB); + await MONGODB_KILL["personal-bests"].insert(TestingIIDXSPScorePB); const res = await ProcessGoal(HC511Goal, HC511UserGoal, 1, log); @@ -251,7 +253,7 @@ t.test("#ProcessGoal", (t) => { userID: 1, }; - await db["goal-subs"].insert(achievedGoalSub); + await MONGODB_KILL["goal-subs"].insert(achievedGoalSub); const res = await ProcessGoal(HC511Goal, achievedGoalSub, 1, log); @@ -285,7 +287,7 @@ t.test("#ProcessGoal", (t) => { }); t.test("Should return undefined if there's no score.", async (t) => { - await db["goal-subs"].insert(HC511UserGoal); + await MONGODB_KILL["goal-subs"].insert(HC511UserGoal); const res = await ProcessGoal(HC511Goal, HC511UserGoal, 1, log); @@ -295,17 +297,20 @@ t.test("#ProcessGoal", (t) => { }); t.test("Should return undefined if the progress has not changed.", async (t) => { - await db["goal-subs"].insert(HC511UserGoal); - await db["personal-bests"].insert(TestingIIDXSPScorePB); + await MONGODB_KILL["goal-subs"].insert(HC511UserGoal); + await MONGODB_KILL["personal-bests"].insert(TestingIIDXSPScorePB); const firstUpdate = await ProcessGoal(HC511Goal, HC511UserGoal, 1, log); // ignore this one t.not(firstUpdate, undefined, "Should NOT return undefined."); - await db["goal-subs"].bulkWrite([firstUpdate!.bwrite]); + await MONGODB_KILL["goal-subs"].bulkWrite([firstUpdate!.bwrite]); - const goalSub = await db["goal-subs"].findOne({ userID: 1, goalID: HC511Goal.goalID }); + const goalSub = await MONGODB_KILL["goal-subs"].findOne({ + userID: 1, + goalID: HC511Goal.goalID, + }); const secondUpdate = await ProcessGoal(HC511Goal, goalSub!, 1, log); diff --git a/typescript/server/src/lib/score-import/framework/goals/goals.ts b/typescript/server/src/lib/score-import/framework/goals/goals.ts index 6230bc727..b0e79ab71 100644 --- a/typescript/server/src/lib/score-import/framework/goals/goals.ts +++ b/typescript/server/src/lib/score-import/framework/goals/goals.ts @@ -3,7 +3,7 @@ import type { GameGroup, GoalDocument, GoalSubscriptionDocument, integer } from import { EvaluateGoalForUser, GetRelevantGoals } from "#lib/targets/goals"; import { EmitWebhookEvent } from "#lib/webhooks/webhooks"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; /** * Update a user's progress on all of their set goals. @@ -90,7 +90,7 @@ export async function UpdateGoalsForUser( }); } - await db["goal-subs"].bulkWrite(bulkWrite, { ordered: false }); + await MONGODB_KILL["goal-subs"].bulkWrite(bulkWrite, { ordered: false }); return importInfo; } @@ -209,14 +209,14 @@ export async function ProcessGoal( } export async function UpdateGoalsInFolder(folderID: string, log: KtLogger) { - const goals = await db.goals.find({ + const goals = await MONGODB_KILL.goals.find({ "charts.type": "folder", "charts.data": folderID, }); log.info(`Updating ${goals.length} goals for ${folderID}`); - const goalSubs = await db["goal-subs"].find({ + const goalSubs = await MONGODB_KILL["goal-subs"].find({ goalID: { $in: goals.map((e) => e.goalID) }, }); diff --git a/typescript/server/src/lib/score-import/framework/import-locks/lock.ts b/typescript/server/src/lib/score-import/framework/import-locks/lock.ts index 5c0f718fd..023ed3810 100644 --- a/typescript/server/src/lib/score-import/framework/import-locks/lock.ts +++ b/typescript/server/src/lib/score-import/framework/import-locks/lock.ts @@ -2,7 +2,7 @@ import type { integer } from "tachi-common"; import { ONE_DAY, ONE_HOUR } from "#lib/constants/time"; import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; /** * If a user has no ongoing import, enable the import lock and return true. @@ -13,19 +13,19 @@ import db from "#services/mongo/db"; * has a lock. */ export async function CheckAndSetOngoingImportLock(userID: integer) { - const lockExists = await db["import-locks"].findOne({ + const lockExists = await MONGODB_KILL["import-locks"].findOne({ userID, }); if (!lockExists) { - await db["import-locks"].insert({ + await MONGODB_KILL["import-locks"].insert({ userID, locked: false, lockedAt: null, }); } else if (lockExists.locked && lockExists.lockedAt! + ONE_DAY < Date.now()) { log.warn(`Removed import lock for ${userID} as it is ostensibly stuck.`); - await db["import-locks"].update( + await MONGODB_KILL["import-locks"].update( { userID, }, @@ -38,7 +38,7 @@ export async function CheckAndSetOngoingImportLock(userID: integer) { ); } - const lockWasSet = await db["import-locks"].findOneAndUpdate( + const lockWasSet = await MONGODB_KILL["import-locks"].findOneAndUpdate( { userID, locked: false, @@ -68,7 +68,7 @@ export async function CheckAndSetOngoingImportLock(userID: integer) { * Disable a users import lock. */ export function UnsetOngoingImportLock(userID: integer) { - return db["import-locks"].findOneAndUpdate( + return MONGODB_KILL["import-locks"].findOneAndUpdate( { userID, locked: true, diff --git a/typescript/server/src/lib/score-import/framework/orphans/orphans.test.ts b/typescript/server/src/lib/score-import/framework/orphans/orphans.oldtest.ts similarity index 86% rename from typescript/server/src/lib/score-import/framework/orphans/orphans.test.ts rename to typescript/server/src/lib/score-import/framework/orphans/orphans.oldtest.ts index f9c1a0799..775d77a21 100644 --- a/typescript/server/src/lib/score-import/framework/orphans/orphans.test.ts +++ b/typescript/server/src/lib/score-import/framework/orphans/orphans.oldtest.ts @@ -1,7 +1,7 @@ import type { BatchManualScore } from "tachi-common"; import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import ResetDBState from "#test-utils/resets"; import deepmerge from "deepmerge"; import fjsh from "fast-json-stable-hash"; @@ -56,7 +56,7 @@ t.test("#OrphanScore", (t) => { "Should be a checksum of the orphan's contents.", ); - const dbCheck = await db["orphan-scores"].findOne({ + const dbCheck = await MONGODB_KILL["orphan-scores"].findOne({ orphanID: res.orphanID, }); @@ -101,7 +101,7 @@ t.test("#OrphanScore", (t) => { t.equal(res2.success, false); t.equal(res1.orphanID, res2.orphanID); - const dbCheck = await db["orphan-scores"].count(); + const dbCheck = await MONGODB_KILL["orphan-scores"].count(); t.equal(dbCheck, 1); }); @@ -163,7 +163,9 @@ t.test("#ReprocessOrphan", (t) => { "Should successfully import the score.", ); - const orphan = await db["orphan-scores"].findOne({ orphanID: orphanDoc.orphanID }); + const orphan = await MONGODB_KILL["orphan-scores"].findOne({ + orphanID: orphanDoc.orphanID, + }); t.equal(orphan, null, "Should remove the orphan document."); @@ -171,7 +173,7 @@ t.test("#ReprocessOrphan", (t) => { }); t.test("Should delete the orphan doc and return null if the score is invalid.", async (t) => { - await db["orphan-scores"].insert(orphanDoc); + await MONGODB_KILL["orphan-scores"].insert(orphanDoc); const res = await ReprocessOrphan( deepmerge(orphanDoc, { data: { @@ -184,7 +186,9 @@ t.test("#ReprocessOrphan", (t) => { t.equal(res, null); - const orphan = await db["orphan-scores"].findOne({ orphanID: orphanDoc.orphanID }); + const orphan = await MONGODB_KILL["orphan-scores"].findOne({ + orphanID: orphanDoc.orphanID, + }); t.equal(orphan, null, "Should remove the orphan document."); @@ -194,7 +198,7 @@ t.test("#ReprocessOrphan", (t) => { t.test( "Should keep the orphan doc and return false if no parents could be found.", async (t) => { - await db["orphan-scores"].insert(orphanDoc); + await MONGODB_KILL["orphan-scores"].insert(orphanDoc); const res = await ReprocessOrphan( deepmerge(orphanDoc, { data: { @@ -207,7 +211,9 @@ t.test("#ReprocessOrphan", (t) => { t.equal(res, false); - const orphan = await db["orphan-scores"].findOne({ orphanID: orphanDoc.orphanID }); + const orphan = await MONGODB_KILL["orphan-scores"].findOne({ + orphanID: orphanDoc.orphanID, + }); t.not(orphan, null, "Should not remove the orphan document."); @@ -218,12 +224,12 @@ t.test("#ReprocessOrphan", (t) => { t.test( "Should keep the orphan doc and return false if an internal failure was triggered.", async (t) => { - await db["orphan-scores"].insert(orphanDoc); + await MONGODB_KILL["orphan-scores"].insert(orphanDoc); const GAZER_SHA256 = "195fe1be5c3e74fccd04dc426e05f8a9cfa8a1059c339d0a23e99f63661f0b7d"; // force an internal failure by removing all bms songs and then // leaving a valid bms chart to match with. - await db.songs.bms.remove({}); + await MONGODB_KILL.songs.bms.remove({}); const res = await ReprocessOrphan( deepmerge(orphanDoc, { @@ -242,7 +248,9 @@ t.test("#ReprocessOrphan", (t) => { t.equal(res, false); - const orphan = await db["orphan-scores"].findOne({ orphanID: orphanDoc.orphanID }); + const orphan = await MONGODB_KILL["orphan-scores"].findOne({ + orphanID: orphanDoc.orphanID, + }); t.not(orphan, null, "Should not remove the orphan document."); diff --git a/typescript/server/src/lib/score-import/framework/orphans/orphans.ts b/typescript/server/src/lib/score-import/framework/orphans/orphans.ts index 1560b2160..15c736151 100644 --- a/typescript/server/src/lib/score-import/framework/orphans/orphans.ts +++ b/typescript/server/src/lib/score-import/framework/orphans/orphans.ts @@ -2,7 +2,7 @@ import type { KtLogger } from "#lib/log/log.js"; import type { FilterQuery } from "mongodb"; import type { GameGroup, ImportTypes, integer } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetBlacklist } from "#utils/queries/blacklist"; import { GetUserWithID } from "#utils/user"; import fjsh from "fast-json-stable-hash"; @@ -53,7 +53,7 @@ export async function OrphanScore( throw new Error(`Failed to orphan score. ${(err as Error).message}`); } - const exists = await db["orphan-scores"].findOne({ orphanID }); + const exists = await MONGODB_KILL["orphan-scores"].findOne({ orphanID }); if (exists) { log.debug(`Skipped orphaning score ${orphanID} because it already exists.`); @@ -70,7 +70,7 @@ export async function OrphanScore( log.debug(orphanScoreDoc, `Inserting orphanScoreDoc...`); - await db["orphan-scores"].insert(orphanScoreDoc); + await MONGODB_KILL["orphan-scores"].insert(orphanScoreDoc); return { success: true, orphanID }; } @@ -136,7 +136,7 @@ export async function ReprocessOrphan( // @danger - This could go terribly, if there's a mistake in the converterFN we might accidentally // remove a users score. - await db["orphan-scores"].remove({ orphanID: orphan.orphanID }); + await MONGODB_KILL["orphan-scores"].remove({ orphanID: orphan.orphanID }); return null; } @@ -155,7 +155,7 @@ export async function ReprocessOrphan( ); } catch (err) { if (IsConverterFailure(err) && err.failureType === "InvalidScore") { - await db["orphan-scores"].remove({ orphanID: orphan.orphanID }); + await MONGODB_KILL["orphan-scores"].remove({ orphanID: orphan.orphanID }); return null; } @@ -165,7 +165,7 @@ export async function ReprocessOrphan( } if (converterReturns === null || !converterReturns.success) { - await db["orphan-scores"].remove({ orphanID: orphan.orphanID }); + await MONGODB_KILL["orphan-scores"].remove({ orphanID: orphan.orphanID }); return null; } @@ -175,7 +175,7 @@ export async function ReprocessOrphan( log.error( `Orphan ${orphan.orphanID} belongs to ${orphan.userID}, but that user no longer exists in the database. Going to skip this and remove the orphan.`, ); - await db["orphan-scores"].remove({ orphanID: orphan.orphanID }); + await MONGODB_KILL["orphan-scores"].remove({ orphanID: orphan.orphanID }); return null; } @@ -189,12 +189,12 @@ export async function ReprocessOrphan( undefined, ); - await db["orphan-scores"].remove({ orphanID: orphan.orphanID }); + await MONGODB_KILL["orphan-scores"].remove({ orphanID: orphan.orphanID }); return converterReturns; } export async function DeorphanScores(query: FilterQuery, log: KtLogger) { - const orphans = await db["orphan-scores"].find(query); + const orphans = await MONGODB_KILL["orphan-scores"].find(query); // ScoreIDs are essentially userID dependent, so this is fine. const blacklist = await GetBlacklist(); diff --git a/typescript/server/src/lib/score-import/framework/pb/create-pb-doc.test.ts b/typescript/server/src/lib/score-import/framework/pb/create-pb-doc.oldtest.ts similarity index 93% rename from typescript/server/src/lib/score-import/framework/pb/create-pb-doc.test.ts rename to typescript/server/src/lib/score-import/framework/pb/create-pb-doc.oldtest.ts index 05b1d64a7..a54f4e324 100644 --- a/typescript/server/src/lib/score-import/framework/pb/create-pb-doc.test.ts +++ b/typescript/server/src/lib/score-import/framework/pb/create-pb-doc.oldtest.ts @@ -1,5 +1,5 @@ import { type KtLogger, log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import ResetDBState from "#test-utils/resets"; import { BMSGazerChart, @@ -56,8 +56,8 @@ t.test("#CreatePBDoc", (t) => { t.test( "(IIDX) Should use the Server Impl Merge FNs to also join the BP PB if necessary.", async (t) => { - await db.scores.remove({}); - await db.scores.insert([ + await MONGODB_KILL.scores.remove({}); + await MONGODB_KILL.scores.insert([ IIDXScore, deepmerge(IIDXScore, { scoreData: { @@ -130,8 +130,8 @@ t.test("#CreatePBDoc", (t) => { scoreID: "LAMP_PB_ID", }); - await db.scores.remove({}); - await db.scores.insert([IIDXScore, d]); + await MONGODB_KILL.scores.remove({}); + await MONGODB_KILL.scores.insert([IIDXScore, d]); const res = await CreatePBDoc("iidx:SP", 1, Testing511SPA, log); @@ -151,7 +151,7 @@ t.test("#CreatePBDoc", (t) => { let warnCalled = false; - await db.scores.remove({}); + await MONGODB_KILL.scores.remove({}); const res = await CreatePBDoc("iidx:SP", 1, Testing511SPA, fakeLogger); @@ -163,8 +163,8 @@ t.test("#CreatePBDoc", (t) => { }); t.test("(BMS) Should inherit BP from the best BP score.", async (t) => { - await db.scores.remove({}); - await db.scores.insert([ + await MONGODB_KILL.scores.remove({}); + await MONGODB_KILL.scores.insert([ TestingBMS7KScore, deepmerge(TestingBMS7KScore, { scoreData: { @@ -215,8 +215,8 @@ t.test("#CreatePBDoc", (t) => { }); t.test("(BMS) Should inherit graph data from the best lamp.", async (t) => { - await db.scores.remove({}); - await db.scores.insert([ + await MONGODB_KILL.scores.remove({}); + await MONGODB_KILL.scores.insert([ TestingBMS7KScore, deepmerge(TestingBMS7KScore, { scoreData: { diff --git a/typescript/server/src/lib/score-import/framework/pb/create-pb-doc.ts b/typescript/server/src/lib/score-import/framework/pb/create-pb-doc.ts index a3672bb8d..4a0b2b0d4 100644 --- a/typescript/server/src/lib/score-import/framework/pb/create-pb-doc.ts +++ b/typescript/server/src/lib/score-import/framework/pb/create-pb-doc.ts @@ -3,7 +3,7 @@ import type { BulkWriteUpdateOneOperation, FilterQuery, SortOptionObject } from import { GPT_SERVER_IMPLEMENTATIONS } from "#game-implementations/game-implementations"; import { GetEveryonesRivalIDs } from "#lib/rivals/rivals"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { DeleteUndefinedProps } from "#utils/misc"; import { type ChartDocument, @@ -49,7 +49,7 @@ export async function CreatePBDoc( const gptConfig = GetGPTConfig(gpt); - const defaultMetricPB = await db.scores.findOne(query, { + const defaultMetricPB = await MONGODB_KILL.scores.findOne(query, { sort: { [`scoreData.${gptConfig.defaultMetric}`]: -1, }, @@ -177,7 +177,7 @@ export async function UpdateChartRanking(game: GameGroup, playtype: Playtype, ch return; } - await db["personal-bests"].bulkWrite(bwrite, { ordered: false }); + await MONGODB_KILL["personal-bests"].bulkWrite(bwrite, { ordered: false }); } async function GetSortedPBs(game: GameGroup, playtype: Playtype, chartID: string) { @@ -199,7 +199,7 @@ async function GetSortedPBs(game: GameGroup, playtype: Playtype, chartID: string }; } - return db["personal-bests"].aggregate([ + return MONGODB_KILL["personal-bests"].aggregate([ { $match: { chartID, diff --git a/typescript/server/src/lib/score-import/framework/pb/process-pbs.test.ts b/typescript/server/src/lib/score-import/framework/pb/process-pbs.oldtest.ts similarity index 83% rename from typescript/server/src/lib/score-import/framework/pb/process-pbs.test.ts rename to typescript/server/src/lib/score-import/framework/pb/process-pbs.oldtest.ts index f1e13bf69..ed76cf23e 100644 --- a/typescript/server/src/lib/score-import/framework/pb/process-pbs.test.ts +++ b/typescript/server/src/lib/score-import/framework/pb/process-pbs.oldtest.ts @@ -1,5 +1,5 @@ import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import ResetDBState from "#test-utils/resets"; import { Testing511SPA, TestingIIDXSPScore } from "#test-utils/test-data"; import crypto from "crypto"; @@ -12,12 +12,12 @@ t.test("#ProcessPBs", (t) => { t.beforeEach(ResetDBState); t.test("Should successfully insert a pb into the score-pb database", async (t) => { - await db["personal-bests"].remove({}); + await MONGODB_KILL["personal-bests"].remove({}); // scores on 511 SPA are pre-loaded into the database await ProcessPBs("iidx", "SP", 1, new Set([Testing511SPA.chartID]), log); - const pbs = await db["personal-bests"].find({}); + const pbs = await MONGODB_KILL["personal-bests"].find({}); t.equal(pbs.length, 1, "Should match the amount of PBs inserted into the DB."); @@ -25,9 +25,9 @@ t.test("#ProcessPBs", (t) => { }); t.test("Should successfully insert multiple pbs into the score-pb database", async (t) => { - await db["personal-bests"].remove({}); + await MONGODB_KILL["personal-bests"].remove({}); - await db.charts.iidx.insert([ + await MONGODB_KILL.charts.iidx.insert([ // @ts-expect-error lol deepmerge(Testing511SPA, { chartID: "test1", @@ -45,7 +45,7 @@ t.test("#ProcessPBs", (t) => { }), ]); - await db.scores.insert([ + await MONGODB_KILL.scores.insert([ // @ts-expect-error lol deepmerge(TestingIIDXSPScore, { chartID: "test1", @@ -73,7 +73,7 @@ t.test("#ProcessPBs", (t) => { log, ); - const pbs = await db["personal-bests"].find({}); + const pbs = await MONGODB_KILL["personal-bests"].find({}); t.equal(pbs.length, 4, "Should match the amount of PBs inserted into the DB."); diff --git a/typescript/server/src/lib/score-import/framework/pb/process-pbs.ts b/typescript/server/src/lib/score-import/framework/pb/process-pbs.ts index df069d6ad..0d96a9014 100644 --- a/typescript/server/src/lib/score-import/framework/pb/process-pbs.ts +++ b/typescript/server/src/lib/score-import/framework/pb/process-pbs.ts @@ -1,6 +1,6 @@ import type { KtLogger } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetChartForIDGuaranteed } from "#utils/db"; import { type GameGroup, GetGPTString, type integer, type Playtype } from "tachi-common"; @@ -53,7 +53,7 @@ export async function ProcessPBs( // this *is* bad behaviour, but I don't have a nice way to fix it. // This should be fixed in the future to avoid crashes between these two // calls - but that is unlikely. - await db["personal-bests"].bulkWrite( + await MONGODB_KILL["personal-bests"].bulkWrite( pbDocs.map((e) => ({ updateOne: { filter: { chartID: e.chartID, userID: e.userID }, diff --git a/typescript/server/src/lib/score-import/framework/profile-calculated-data/classes.test.ts b/typescript/server/src/lib/score-import/framework/profile-calculated-data/classes.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/framework/profile-calculated-data/classes.test.ts rename to typescript/server/src/lib/score-import/framework/profile-calculated-data/classes.oldtest.ts diff --git a/typescript/server/src/lib/score-import/framework/profile-calculated-data/classes.ts b/typescript/server/src/lib/score-import/framework/profile-calculated-data/classes.ts index fffe64e99..95882b1ce 100644 --- a/typescript/server/src/lib/score-import/framework/profile-calculated-data/classes.ts +++ b/typescript/server/src/lib/score-import/framework/profile-calculated-data/classes.ts @@ -1,7 +1,7 @@ import type { KtLogger } from "#lib/log/log.js"; import { EmitWebhookEvent } from "#lib/webhooks/webhooks"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { ReturnClassIfGreater } from "#utils/class"; import deepmerge from "deepmerge"; import { @@ -163,7 +163,7 @@ export async function ProcessClassDeltas( } } - await db["class-achievements"].insert(achievementOps); + await MONGODB_KILL["class-achievements"].insert(achievementOps); return deltas; } diff --git a/typescript/server/src/lib/score-import/framework/quests/quests.test.ts b/typescript/server/src/lib/score-import/framework/quests/quests.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/framework/quests/quests.test.ts rename to typescript/server/src/lib/score-import/framework/quests/quests.oldtest.ts diff --git a/typescript/server/src/lib/score-import/framework/quests/quests.ts b/typescript/server/src/lib/score-import/framework/quests/quests.ts index 0cb3b731b..c87bb7305 100644 --- a/typescript/server/src/lib/score-import/framework/quests/quests.ts +++ b/typescript/server/src/lib/score-import/framework/quests/quests.ts @@ -12,7 +12,7 @@ import type { import { EvaluateQuestProgress } from "#lib/targets/quests"; import { EmitWebhookEvent } from "#lib/webhooks/webhooks"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; export async function UpdateUsersQuests( importGoalInfo: Array, @@ -114,7 +114,7 @@ export async function UpdateQuestsForUser( ); if (bwrite.length !== 0) { - await db["quest-subs"].bulkWrite(bwrite, { ordered: false }); + await MONGODB_KILL["quest-subs"].bulkWrite(bwrite, { ordered: false }); } return importQuestInfo; @@ -127,7 +127,7 @@ async function GetRelevantQuests( userID: integer, log: KtLogger, ) { - const questSubs = await db["quest-subs"].find({ + const questSubs = await MONGODB_KILL["quest-subs"].find({ game, playtype: { $in: playtypes }, userID, @@ -135,7 +135,7 @@ async function GetRelevantQuests( log.debug(`Found ${questSubs.length} quest-subs.`); - const quests = await db.quests.find({ + const quests = await MONGODB_KILL.quests.find({ questID: { $in: questSubs.map((e) => e.questID) }, "questData.goals.goalID": { $in: goalIDs }, }); diff --git a/typescript/server/src/lib/score-import/framework/score-import.test.ts b/typescript/server/src/lib/score-import/framework/score-import.oldtest.ts similarity index 88% rename from typescript/server/src/lib/score-import/framework/score-import.test.ts rename to typescript/server/src/lib/score-import/framework/score-import.oldtest.ts index bddf694aa..e3aab1052 100644 --- a/typescript/server/src/lib/score-import/framework/score-import.test.ts +++ b/typescript/server/src/lib/score-import/framework/score-import.oldtest.ts @@ -1,5 +1,5 @@ import { CDNRetrieve } from "#lib/cdn/cdn"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import ResetDBState from "#test-utils/resets"; import { FakeSmallBatchManual } from "#test-utils/test-data"; import t from "tap"; @@ -22,7 +22,7 @@ t.test("#MakeScoreImport", (t) => { importID: "mockImportID", }); - const dbRes = await db.imports.findOne( + const dbRes = await MONGODB_KILL.imports.findOne( { importID: "mockImportID" }, { projection: { _id: 0 } }, ); @@ -57,7 +57,9 @@ t.test("#MakeScoreImport", (t) => { t.fail(err as any); } - const importTrack = await db["import-trackers"].findOne({ importID: "mockImportID" }); + const importTrack = await MONGODB_KILL["import-trackers"].findOne({ + importID: "mockImportID", + }); t.equal( importTrack, diff --git a/typescript/server/src/lib/score-import/framework/score-importing/hydrate-score.test.ts b/typescript/server/src/lib/score-import/framework/score-importing/hydrate-score.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/framework/score-importing/hydrate-score.test.ts rename to typescript/server/src/lib/score-import/framework/score-importing/hydrate-score.oldtest.ts diff --git a/typescript/server/src/lib/score-import/framework/score-importing/insert-score.test.ts b/typescript/server/src/lib/score-import/framework/score-importing/insert-score.oldtest.ts similarity index 93% rename from typescript/server/src/lib/score-import/framework/score-importing/insert-score.test.ts rename to typescript/server/src/lib/score-import/framework/score-importing/insert-score.oldtest.ts index e5c46103e..2666dc247 100644 --- a/typescript/server/src/lib/score-import/framework/score-importing/insert-score.test.ts +++ b/typescript/server/src/lib/score-import/framework/score-importing/insert-score.oldtest.ts @@ -1,6 +1,6 @@ import type { ScoreDocument } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import ResetDBState from "#test-utils/resets"; import t from "tap"; @@ -12,7 +12,7 @@ t.test("#QueueScoreInsert, #InsertQueue", async (t) => { // empty scoreDB after t.afterEach(async () => { - await db.scores.remove({}); + await MONGODB_KILL.scores.remove({}); }); t.test("Single Queue Test", async (t) => { @@ -33,7 +33,7 @@ t.test("#QueueScoreInsert, #InsertQueue", async (t) => { t.equal(flushSize, 1, "QueueScoreInsert should append the score to the queue."); - const dbRes = await db.scores.find({ + const dbRes = await MONGODB_KILL.scores.find({ scoreID: "foo", }); @@ -75,7 +75,7 @@ t.test("#QueueScoreInsert, #InsertQueue", async (t) => { t.equal(flushRes, 0, "The queue should now be empty."); - const dbRes = await db.scores.find({ + const dbRes = await MONGODB_KILL.scores.find({ scoreID: { $exists: true }, chartID: "test", }); @@ -112,7 +112,7 @@ t.test("#QueueScoreInsert, #InsertQueue", async (t) => { t.equal(flushRes, 1, "Should flush 1 score document"); - const dbRes = await db.scores.find({ + const dbRes = await MONGODB_KILL.scores.find({ chartID: "foo", }); diff --git a/typescript/server/src/lib/score-import/framework/score-importing/insert-score.ts b/typescript/server/src/lib/score-import/framework/score-importing/insert-score.ts index ed1c05a74..296227ff2 100644 --- a/typescript/server/src/lib/score-import/framework/score-importing/insert-score.ts +++ b/typescript/server/src/lib/score-import/framework/score-importing/insert-score.ts @@ -1,7 +1,7 @@ import type { integer, ScoreDocument } from "tachi-common"; import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; const MAX_PIPELINE_LENGTH = 500; @@ -64,7 +64,7 @@ export async function InsertQueue(userID: integer) { delete ScoreQueues[userID]; try { - await db.scores.insert(queuedScores); + await MONGODB_KILL.scores.insert(queuedScores); } catch (err) { log.warn( { err }, diff --git a/typescript/server/src/lib/score-import/framework/score-importing/score-id.test.ts b/typescript/server/src/lib/score-import/framework/score-importing/score-id.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/framework/score-importing/score-id.test.ts rename to typescript/server/src/lib/score-import/framework/score-importing/score-id.oldtest.ts diff --git a/typescript/server/src/lib/score-import/framework/score-importing/score-id.ts b/typescript/server/src/lib/score-import/framework/score-importing/score-id.ts index 8c62029ce..406ce383f 100644 --- a/typescript/server/src/lib/score-import/framework/score-importing/score-id.ts +++ b/typescript/server/src/lib/score-import/framework/score-importing/score-id.ts @@ -1,6 +1,6 @@ import type { KtLogger } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import fjsh from "fast-json-stable-hash"; import { GetGPTConfig, @@ -58,7 +58,7 @@ export function CreateScoreID( } export function GetWithScoreID(scoreID: string) { - return db.scores.findOne({ + return MONGODB_KILL.scores.findOne({ scoreID, }); } diff --git a/typescript/server/src/lib/score-import/framework/score-importing/score-import-error.test.ts b/typescript/server/src/lib/score-import/framework/score-importing/score-import-error.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/framework/score-importing/score-import-error.test.ts rename to typescript/server/src/lib/score-import/framework/score-importing/score-import-error.oldtest.ts diff --git a/typescript/server/src/lib/score-import/framework/score-importing/score-import-main.ts b/typescript/server/src/lib/score-import/framework/score-importing/score-import-main.ts index d325c44e0..db9f14ac1 100644 --- a/typescript/server/src/lib/score-import/framework/score-importing/score-import-main.ts +++ b/typescript/server/src/lib/score-import/framework/score-importing/score-import-main.ts @@ -1,7 +1,7 @@ import type { KtLogger } from "#lib/log/log"; import type { ScoreImportJob } from "#lib/score-import/worker/types"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetMillisecondsSince } from "#utils/misc"; import { GetUserWithID } from "#utils/user"; import { @@ -133,7 +133,7 @@ export default async function ScoreImportMain( ); } catch (err) { // Remove all scores from the database for this user which were imported after our timer started. - const r = await db.scores.remove({ + const r = await MONGODB_KILL.scores.remove({ userID: user.id, timeAdded: { $gte: startOfImportingScores }, }); @@ -222,11 +222,11 @@ export default async function ScoreImportMain( log.debug(logMessage); } - await db.imports.insert(ImportDocument); + await MONGODB_KILL.imports.insert(ImportDocument); // we don't await this because we don't // particularly care about waiting for it. - void db["import-timings"].insert({ + void MONGODB_KILL["import-timings"].insert({ importID, timestamp: Date.now(), total: ImportDocument.timeFinished - timeStarted, diff --git a/typescript/server/src/lib/score-import/framework/score-importing/score-importing.test.ts b/typescript/server/src/lib/score-import/framework/score-importing/score-importing.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/framework/score-importing/score-importing.test.ts rename to typescript/server/src/lib/score-import/framework/score-importing/score-importing.oldtest.ts diff --git a/typescript/server/src/lib/score-import/framework/score-importing/score-importing.ts b/typescript/server/src/lib/score-import/framework/score-importing/score-importing.ts index 72b2b018e..0bcc9ce66 100644 --- a/typescript/server/src/lib/score-import/framework/score-importing/score-importing.ts +++ b/typescript/server/src/lib/score-import/framework/score-importing/score-importing.ts @@ -1,7 +1,7 @@ import type { ScoreImportJob } from "#lib/score-import/worker/types"; import { AppendLogCtx, type KtLogger } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { ClassToObject } from "#utils/misc"; import { type ChartDocument, @@ -52,7 +52,7 @@ export async function ImportAllIterableData( // @optimisable: could filter harder with score.game and score.playtype // stuff. const blacklist = ( - await db["score-blacklist"].find({ + await MONGODB_KILL["score-blacklist"].find({ userID, }) ).map((e) => e.scoreID); @@ -345,7 +345,7 @@ async function HydrateCheckAndInsertScore( return null; } - const existingScore = await db.scores.findOne( + const existingScore = await MONGODB_KILL.scores.findOne( { scoreID, }, @@ -377,7 +377,7 @@ async function HydrateCheckAndInsertScore( let res; if (force) { - res = await db.scores.insert(score); + res = await MONGODB_KILL.scores.insert(score); } else { res = await QueueScoreInsert(score); } diff --git a/typescript/server/src/lib/score-import/framework/score-importing/validate-score.test.ts b/typescript/server/src/lib/score-import/framework/score-importing/validate-score.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/framework/score-importing/validate-score.test.ts rename to typescript/server/src/lib/score-import/framework/score-importing/validate-score.oldtest.ts diff --git a/typescript/server/src/lib/score-import/framework/sessions/name-generation.test.ts b/typescript/server/src/lib/score-import/framework/sessions/name-generation.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/framework/sessions/name-generation.test.ts rename to typescript/server/src/lib/score-import/framework/sessions/name-generation.oldtest.ts diff --git a/typescript/server/src/lib/score-import/framework/sessions/sessions.test.ts b/typescript/server/src/lib/score-import/framework/sessions/sessions.oldtest.ts similarity index 80% rename from typescript/server/src/lib/score-import/framework/sessions/sessions.test.ts rename to typescript/server/src/lib/score-import/framework/sessions/sessions.oldtest.ts index 38cd1b22a..38c51b545 100644 --- a/typescript/server/src/lib/score-import/framework/sessions/sessions.test.ts +++ b/typescript/server/src/lib/score-import/framework/sessions/sessions.oldtest.ts @@ -1,6 +1,6 @@ import type { ScoreDocument, SessionDocument, UserDocument } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import ResetDBState from "#test-utils/resets"; import { TestingIIDXSPScore, TestingIIDXSPScorePB } from "#test-utils/test-data"; import deepmerge from "deepmerge"; @@ -17,7 +17,7 @@ const log = CreateScoreLogger( t.test("#CreateSessions", (t) => { t.beforeEach(ResetDBState); - t.beforeEach(() => db.sessions.remove({})); + t.beforeEach(() => MONGODB_KILL.sessions.remove({})); t.test("Should compose sessions from one timestamped score provided.", async (t) => { const res = await CreateSessions(1, "iidx", { SP: [TestingIIDXSPScore] }, log); @@ -31,9 +31,17 @@ t.test("#CreateSessions", (t) => { t.equal(res.length, 1); - const session = await db.sessions.findOne({ userID: 1, game: "iidx", playtype: "SP" }); + const session = await MONGODB_KILL.sessions.findOne({ + userID: 1, + game: "iidx", + playtype: "SP", + }); - const sessionCount = await db.sessions.count({ userID: 1, game: "iidx", playtype: "SP" }); + const sessionCount = await MONGODB_KILL.sessions.count({ + userID: 1, + game: "iidx", + playtype: "SP", + }); t.equal(sessionCount, 1); @@ -66,7 +74,11 @@ t.test("#CreateSessions", (t) => { t.strictSame(res, []); - const sessionCount = await db.sessions.count({ userID: 1, game: "iidx", playtype: "SP" }); + const sessionCount = await MONGODB_KILL.sessions.count({ + userID: 1, + game: "iidx", + playtype: "SP", + }); t.equal(sessionCount, 0); @@ -83,11 +95,19 @@ t.test("#CreateSessions", (t) => { t.equal(res.length, 2); - const sessionCount = await db.sessions.count({ userID: 1, game: "iidx", playtype: "SP" }); + const sessionCount = await MONGODB_KILL.sessions.count({ + userID: 1, + game: "iidx", + playtype: "SP", + }); t.equal(sessionCount, 1); - const sessionCountDP = await db.sessions.count({ userID: 1, game: "iidx", playtype: "DP" }); + const sessionCountDP = await MONGODB_KILL.sessions.count({ + userID: 1, + game: "iidx", + playtype: "DP", + }); t.equal(sessionCountDP, 1); @@ -101,7 +121,7 @@ const start = 1619454485988; t.test("#LoadScoresIntoSessions", (t) => { t.beforeEach(ResetDBState); - t.beforeEach(() => db.sessions.remove({})); + t.beforeEach(() => MONGODB_KILL.sessions.remove({})); t.test("Should create sessions from scores.", async (t) => { const res = await LoadScoresIntoSessions( @@ -121,7 +141,11 @@ t.test("#LoadScoresIntoSessions", (t) => { }, ]); - const sessions = await db.sessions.find({ game: "iidx", playtype: "SP", userID: 1 }); + const sessions = await MONGODB_KILL.sessions.find({ + game: "iidx", + playtype: "SP", + userID: 1, + }); t.equal(sessions.length, 1); @@ -154,7 +178,11 @@ t.test("#LoadScoresIntoSessions", (t) => { }, ]); - const sessions = await db.sessions.find({ game: "iidx", playtype: "SP", userID: 1 }); + const sessions = await MONGODB_KILL.sessions.find({ + game: "iidx", + playtype: "SP", + userID: 1, + }); t.equal(sessions.length, 1); @@ -201,7 +229,11 @@ t.test("#LoadScoresIntoSessions", (t) => { }, ]); - const sessions = await db.sessions.find({ game: "iidx", playtype: "SP", userID: 1 }); + const sessions = await MONGODB_KILL.sessions.find({ + game: "iidx", + playtype: "SP", + userID: 1, + }); t.equal(sessions.length, 2); @@ -212,7 +244,7 @@ t.test("#LoadScoresIntoSessions", (t) => { }); t.test("Should append to existing sessions.", async (t) => { - await db.sessions.insert({ + await MONGODB_KILL.sessions.insert({ sessionID: "EXAMPLE_SESSION_ID", userID: 1, game: "iidx", @@ -239,7 +271,11 @@ t.test("#LoadScoresIntoSessions", (t) => { }, ]); - const sessions = await db.sessions.find({ game: "iidx", playtype: "SP", userID: 1 }); + const sessions = await MONGODB_KILL.sessions.find({ + game: "iidx", + playtype: "SP", + userID: 1, + }); t.equal(sessions.length, 1); @@ -256,7 +292,7 @@ t.test("#LoadScoresIntoSessions", (t) => { }); t.test("Should change the timeStarted of existing sessions if prepending.", async (t) => { - await db.sessions.insert({ + await MONGODB_KILL.sessions.insert({ sessionID: "EXAMPLE_SESSION_ID", userID: 1, game: "iidx", @@ -283,7 +319,11 @@ t.test("#LoadScoresIntoSessions", (t) => { }, ]); - const sessions = await db.sessions.find({ game: "iidx", playtype: "SP", userID: 1 }); + const sessions = await MONGODB_KILL.sessions.find({ + game: "iidx", + playtype: "SP", + userID: 1, + }); t.equal(sessions.length, 1); @@ -300,7 +340,7 @@ t.test("#LoadScoresIntoSessions", (t) => { }); t.test("Should calculate pbDifferences if a scorePB exists.", async (t) => { - await db["personal-bests"].insert(TestingIIDXSPScorePB); + await MONGODB_KILL["personal-bests"].insert(TestingIIDXSPScorePB); const res = await LoadScoresIntoSessions(1, [TestingIIDXSPScore], "iidx", "SP", log); @@ -311,7 +351,11 @@ t.test("#LoadScoresIntoSessions", (t) => { }, ]); - const sessions = await db.sessions.find({ game: "iidx", playtype: "SP", userID: 1 }); + const sessions = await MONGODB_KILL.sessions.find({ + game: "iidx", + playtype: "SP", + userID: 1, + }); t.equal(sessions.length, 1); diff --git a/typescript/server/src/lib/score-import/framework/sessions/sessions.ts b/typescript/server/src/lib/score-import/framework/sessions/sessions.ts index 7ef77e7c4..c7553a5d2 100644 --- a/typescript/server/src/lib/score-import/framework/sessions/sessions.ts +++ b/typescript/server/src/lib/score-import/framework/sessions/sessions.ts @@ -1,6 +1,6 @@ import { ONE_HOUR } from "#lib/constants/time"; import { AppendLogCtx, type KtLogger, log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetChartForIDGuaranteed } from "#utils/db"; import { GetScoresFromSession } from "#utils/session"; import crypto from "crypto"; @@ -99,7 +99,7 @@ function ScoreToSessionScoreInfo( export async function GetSessionScoreInfo( session: SessionDocument, ): Promise> { - const scores = await db.scores.find({ + const scores = await MONGODB_KILL.scores.find({ scoreID: { $in: session.scoreIDs }, }); @@ -253,7 +253,7 @@ export async function LoadScoresIntoSessions( // Find any sessions with +/-2hrs of this group. This is rather exhaustive, and could result in some issues // if this query returns more than one session. We could account for that by smushing sessions together. // This is not possible however, so this is now just a known tachi oddity. - const nearbySession = await db.sessions.findOne({ + const nearbySession = await MONGODB_KILL.sessions.findOne({ userID, game, playtype, @@ -276,7 +276,7 @@ export async function LoadScoresIntoSessions( infoReturn = { sessionID: session.sessionID, type: "Appended" }; - await db.sessions.update( + await MONGODB_KILL.sessions.update( { sessionID: session.sessionID, }, @@ -292,7 +292,7 @@ export async function LoadScoresIntoSessions( const session = CreateSession(userID, scoreIDs, groupScores, game, playtype); infoReturn = { sessionID: session.sessionID, type: "Created" }; - await db.sessions.insert(session); + await MONGODB_KILL.sessions.insert(session); } sessionInfoReturns.push(infoReturn); diff --git a/typescript/server/src/lib/score-import/framework/status-tracking/import-status-tracking.ts b/typescript/server/src/lib/score-import/framework/status-tracking/import-status-tracking.ts index ccb094841..07e36c21e 100644 --- a/typescript/server/src/lib/score-import/framework/status-tracking/import-status-tracking.ts +++ b/typescript/server/src/lib/score-import/framework/status-tracking/import-status-tracking.ts @@ -4,7 +4,7 @@ import type { ImportTypes } from "tachi-common"; import { CDNStoreOrOverwrite } from "#lib/cdn/cdn"; import { GetScoreImportInputURL } from "#lib/cdn/url-format"; import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import type ScoreImportFatalError from "../score-importing/score-import-error"; @@ -37,7 +37,7 @@ function SerialiseJobData(jobData: ScoreImportJobData): string { * awaited when you await this function, it happens in the background. */ export async function StartTrackingImport(jobData: ScoreImportJobData) { - await db["import-trackers"].insert({ + await MONGODB_KILL["import-trackers"].insert({ type: "ONGOING", importID: jobData.importID, importType: jobData.importType, @@ -63,7 +63,7 @@ export async function StartTrackingImport(jobData: ScoreImportJobData { t.beforeEach(ResetDBState); t.beforeEach(async () => { - await db["personal-bests"].insert(TestingIIDXSPScorePB); + await MONGODB_KILL["personal-bests"].insert(TestingIIDXSPScorePB); }); t.test( "Should create new UserGameStats and UserGameSettings if the user has none", async (t) => { - await db["game-stats"].remove({}); - await db["game-settings"].remove({}); + await MONGODB_KILL["game-stats"].remove({}); + await MONGODB_KILL["game-settings"].remove({}); const res = await UpdateUsersGamePlaytypeStats("iidx", "SP", 1, null, log); t.strictSame(res, [], "Should return an empty object"); - const gs = await db["game-stats"].findOne(); + const gs = await MONGODB_KILL["game-stats"].findOne(); t.hasStrict( gs, @@ -39,7 +39,7 @@ t.test("#UpdateUsersGamePlaytypeStats", (t) => { "Should insert an appropriate game-stats object", ); - const settings = await db["game-settings"].findOne(); + const settings = await MONGODB_KILL["game-settings"].findOne(); t.hasStrict(settings, { game: "iidx", @@ -53,9 +53,9 @@ t.test("#UpdateUsersGamePlaytypeStats", (t) => { ); t.test("Should update UserGameStats if the user has one", async (t) => { - await db["game-stats"].remove({}); + await MONGODB_KILL["game-stats"].remove({}); - await db["game-stats"].insert({ + await MONGODB_KILL["game-stats"].insert({ game: "iidx", playtype: "SP", userID: 1, @@ -66,7 +66,7 @@ t.test("#UpdateUsersGamePlaytypeStats", (t) => { // insert some mock scores const ratings = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - await db["personal-bests"].insert( + await MONGODB_KILL["personal-bests"].insert( ratings.map((e) => deepmerge(TestingIIDXSPScorePB, { chartID: crypto.randomBytes(20).toString("hex"), @@ -81,7 +81,7 @@ t.test("#UpdateUsersGamePlaytypeStats", (t) => { t.strictSame(res, [], "Should return an empty object"); - const gs = await db["game-stats"].findOne(); + const gs = await MONGODB_KILL["game-stats"].findOne(); t.hasStrict( gs, @@ -99,9 +99,9 @@ t.test("#UpdateUsersGamePlaytypeStats", (t) => { }); t.test("Should return class deltas", async (t) => { - await db["game-stats"].remove({}); + await MONGODB_KILL["game-stats"].remove({}); - await db["game-stats"].insert({ + await MONGODB_KILL["game-stats"].insert({ game: "iidx", playtype: "SP", userID: 1, @@ -132,7 +132,7 @@ t.test("#UpdateUsersGamePlaytypeStats", (t) => { "Should return the class delta", ); - const gs = await db["game-stats"].findOne(); + const gs = await MONGODB_KILL["game-stats"].findOne(); t.hasStrict( gs, @@ -152,9 +152,9 @@ t.test("#UpdateUsersGamePlaytypeStats", (t) => { }); t.test("Should return updated class deltas", async (t) => { - await db["game-stats"].remove({}); + await MONGODB_KILL["game-stats"].remove({}); - await db["game-stats"].insert({ + await MONGODB_KILL["game-stats"].insert({ game: "iidx", playtype: "SP", userID: 1, @@ -186,7 +186,7 @@ t.test("#UpdateUsersGamePlaytypeStats", (t) => { "Should return the updated class delta", ); - const gs = await db["game-stats"].findOne(); + const gs = await MONGODB_KILL["game-stats"].findOne(); t.hasStrict( gs, diff --git a/typescript/server/src/lib/score-import/framework/ugpt-stats/update-ugpt-stats.ts b/typescript/server/src/lib/score-import/framework/ugpt-stats/update-ugpt-stats.ts index f14639bfd..6679411d4 100644 --- a/typescript/server/src/lib/score-import/framework/ugpt-stats/update-ugpt-stats.ts +++ b/typescript/server/src/lib/score-import/framework/ugpt-stats/update-ugpt-stats.ts @@ -2,7 +2,7 @@ import type { KtLogger } from "#lib/log/log.js"; import type { ClassDelta, GameGroup, integer, Playtype, UserGameStats } from "tachi-common"; import { CreateGameSettings } from "#lib/game-settings/create-game-settings"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import type { ClassProvider } from "../calculated-data/types"; @@ -22,7 +22,7 @@ export async function UpdateUsersGamePlaytypeStats( // Attempt to find a users game stats if one already exists. If one doesn't exist, // this is this players first import for this game! - const userGameStats = await db["game-stats"].findOne({ + const userGameStats = await MONGODB_KILL["game-stats"].findOne({ game, playtype, userID, @@ -49,7 +49,7 @@ export async function UpdateUsersGamePlaytypeStats( updateClasses[`classes.${delta.set}`] = delta.new; } - await db["game-stats"].update( + await MONGODB_KILL["game-stats"].update( { game, playtype, @@ -63,7 +63,7 @@ export async function UpdateUsersGamePlaytypeStats( }, ); } else { - const hasAnyScores = await db.scores.findOne({ + const hasAnyScores = await MONGODB_KILL.scores.findOne({ game, playtype, userID, @@ -90,7 +90,7 @@ export async function UpdateUsersGamePlaytypeStats( }; log.info(`Created new gamestats for ${game} (${playtype})`); - await db["game-stats"].insert(newStats); + await MONGODB_KILL["game-stats"].insert(newStats); await CreateGameSettings(userID, game, playtype); } diff --git a/typescript/server/src/lib/score-import/import-types/api/myt-chunithm/converter.test.ts b/typescript/server/src/lib/score-import/import-types/api/myt-chunithm/converter.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/import-types/api/myt-chunithm/converter.test.ts rename to typescript/server/src/lib/score-import/import-types/api/myt-chunithm/converter.oldtest.ts diff --git a/typescript/server/src/lib/score-import/import-types/api/myt-chunithm/converter.ts b/typescript/server/src/lib/score-import/import-types/api/myt-chunithm/converter.ts index fcf3d481f..5aa7771ba 100644 --- a/typescript/server/src/lib/score-import/import-types/api/myt-chunithm/converter.ts +++ b/typescript/server/src/lib/score-import/import-types/api/myt-chunithm/converter.ts @@ -20,31 +20,31 @@ import type { ConverterFunction } from "../../common/types"; import type { MytChunithmScore } from "./types"; const DIFFICULTIES = { - [ChunithmLevel.CHUNITHM_LEVEL_UNSPECIFIED]: undefined, - [ChunithmLevel.CHUNITHM_LEVEL_BASIC]: "BASIC", - [ChunithmLevel.CHUNITHM_LEVEL_ADVANCED]: "ADVANCED", - [ChunithmLevel.CHUNITHM_LEVEL_EXPERT]: "EXPERT", - [ChunithmLevel.CHUNITHM_LEVEL_MASTER]: "MASTER", - [ChunithmLevel.CHUNITHM_LEVEL_ULTIMA]: "ULTIMA", - [ChunithmLevel.CHUNITHM_LEVEL_WORLDS_END]: "WORLD'S END", + [ChunithmLevel.UNSPECIFIED]: undefined, + [ChunithmLevel.BASIC]: "BASIC", + [ChunithmLevel.ADVANCED]: "ADVANCED", + [ChunithmLevel.EXPERT]: "EXPERT", + [ChunithmLevel.MASTER]: "MASTER", + [ChunithmLevel.ULTIMA]: "ULTIMA", + [ChunithmLevel.WORLDS_END]: "WORLD'S END", }; const CLEAR_LAMPS = { - [ChunithmClearStatus.CHUNITHM_CLEAR_STATUS_UNSPECIFIED]: undefined, - [ChunithmClearStatus.CHUNITHM_CLEAR_STATUS_FAILED]: "FAILED", - [ChunithmClearStatus.CHUNITHM_CLEAR_STATUS_CLEAR]: "CLEAR", - [ChunithmClearStatus.CHUNITHM_CLEAR_STATUS_HARD]: "HARD", - [ChunithmClearStatus.CHUNITHM_CLEAR_STATUS_ABSOLUTE]: "BRAVE", - [ChunithmClearStatus.CHUNITHM_CLEAR_STATUS_ABSOLUTE_PLUS]: "ABSOLUTE", - [ChunithmClearStatus.CHUNITHM_CLEAR_STATUS_CATASTROPHY]: "CATASTROPHY", + [ChunithmClearStatus.UNSPECIFIED]: undefined, + [ChunithmClearStatus.FAILED]: "FAILED", + [ChunithmClearStatus.CLEAR]: "CLEAR", + [ChunithmClearStatus.HARD]: "HARD", + [ChunithmClearStatus.ABSOLUTE]: "BRAVE", + [ChunithmClearStatus.ABSOLUTE_PLUS]: "ABSOLUTE", + [ChunithmClearStatus.CATASTROPHY]: "CATASTROPHY", } as const; const NOTE_LAMPS = { - [ChunithmComboStatus.CHUNITHM_COMBO_STATUS_UNSPECIFIED]: undefined, - [ChunithmComboStatus.CHUNITHM_COMBO_STATUS_NONE]: "NONE", - [ChunithmComboStatus.CHUNITHM_COMBO_STATUS_FULL_COMBO]: "FULL COMBO", - [ChunithmComboStatus.CHUNITHM_COMBO_STATUS_ALL_JUSTICE]: "ALL JUSTICE", - [ChunithmComboStatus.CHUNITHM_COMBO_STATUS_ALL_JUSTICE_CRITICAL]: "ALL JUSTICE CRITICAL", + [ChunithmComboStatus.UNSPECIFIED]: undefined, + [ChunithmComboStatus.NONE]: "NONE", + [ChunithmComboStatus.FULL_COMBO]: "FULL COMBO", + [ChunithmComboStatus.ALL_JUSTICE]: "ALL JUSTICE", + [ChunithmComboStatus.ALL_JUSTICE_CRITICAL]: "ALL JUSTICE CRITICAL", } as const; const ConvertAPIMytChunithm: ConverterFunction = async ( diff --git a/typescript/server/src/lib/score-import/import-types/api/myt-chunithm/parser.ts b/typescript/server/src/lib/score-import/import-types/api/myt-chunithm/parser.ts index 3cf5b8299..e728f22d9 100644 --- a/typescript/server/src/lib/score-import/import-types/api/myt-chunithm/parser.ts +++ b/typescript/server/src/lib/score-import/import-types/api/myt-chunithm/parser.ts @@ -3,24 +3,38 @@ import type { EmptyObject } from "#utils/types"; import type { integer } from "tachi-common"; import ScoreImportFatalError from "#lib/score-import/framework/score-importing/score-import-error"; -import { ChunithmUserClient } from "#proto/generated/chunithm/user_grpc_pb"; -import { GetPlaylogRequest, type GetPlaylogStreamItem } from "#proto/generated/chunithm/user_pb"; -import { credentials } from "@grpc/grpc-js"; +import { ChunithmUser, GetPlaylogRequestSchema } from "#proto/generated/chunithm/user_pb"; +import { create } from "@bufbuild/protobuf"; +import { ConnectError, createClient } from "@connectrpc/connect"; import type { ParserFunctionReturns } from "../../common/types"; import type { MytChunithmScore } from "./types"; -import { - FetchMytTitleAPIID, - GetMytHostname, - StreamRPCAsAsync, -} from "../../common/api-myt/traverse-api"; +import { CreateMytTransport, FetchMytTitleAPIID } from "../../common/api-myt/traverse-api"; -async function* getObjectsFromGrpcIterable( - iterable: AsyncIterable, -): AsyncIterable { - for await (const item of iterable) { - yield item.toObject(); +async function* streamPlaylog(userID: integer, log: KtLogger): AsyncIterable { + const profileApiId = await FetchMytTitleAPIID(userID, "chunithm", log); + const client = createClient(ChunithmUser, CreateMytTransport()); + const request = create(GetPlaylogRequestSchema, { profileApiId }); + + try { + for await (const item of client.getPlaylog(request)) { + yield item; + } + } catch (err) { + if (err instanceof ConnectError) { + log.error( + { err, code: err.code }, + `MYT gRPC error streaming Chunithm playlog for userID ${userID}`, + ); + } else { + log.error( + { err }, + `Unexpected MYT error streaming Chunithm playlog for userID ${userID}`, + ); + } + + throw new ScoreImportFatalError(500, `Failed to get scores from MYT.`); } } @@ -28,29 +42,8 @@ export default async function ParseMytChunithm( userID: integer, log: KtLogger, ): Promise> { - const profileApiId = await FetchMytTitleAPIID(userID, "chunithm", log); - const endpoint = GetMytHostname(); - const client = new ChunithmUserClient(endpoint, credentials.createSsl()); - const request = new GetPlaylogRequest(); - - request.setProfileApiId(profileApiId); - - let iterable; - - try { - const stream = StreamRPCAsAsync(client.getPlaylog.bind(client), request, log); - - iterable = getObjectsFromGrpcIterable(stream); - } catch (err) { - log.error( - `Unexpected MYT error while streaming Chunithm playlog items for userID ${userID}: ${err}`, - ); - - throw new ScoreImportFatalError(500, `Failed to get scores from MYT.`); - } - return { - iterable, + iterable: streamPlaylog(userID, log), context: {}, classProvider: null, game: "chunithm", diff --git a/typescript/server/src/lib/score-import/import-types/api/myt-chunithm/types.ts b/typescript/server/src/lib/score-import/import-types/api/myt-chunithm/types.ts index bde0d9235..2ce5de87d 100644 --- a/typescript/server/src/lib/score-import/import-types/api/myt-chunithm/types.ts +++ b/typescript/server/src/lib/score-import/import-types/api/myt-chunithm/types.ts @@ -1,3 +1,3 @@ import type { GetPlaylogStreamItem } from "#proto/generated/chunithm/user_pb"; -export type MytChunithmScore = GetPlaylogStreamItem.AsObject; +export type MytChunithmScore = GetPlaylogStreamItem; diff --git a/typescript/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.test.ts b/typescript/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.test.ts rename to typescript/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.oldtest.ts diff --git a/typescript/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.ts b/typescript/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.ts index dea20cc7a..4dce5edea 100644 --- a/typescript/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.ts +++ b/typescript/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.ts @@ -17,20 +17,20 @@ import type { ConverterFunction } from "../../common/types"; import type { MytMaimaiDxScore } from "./types"; const DIFFICULTIES = { - [MaimaiLevel.MAIMAI_LEVEL_UNSPECIFIED]: undefined, - [MaimaiLevel.MAIMAI_LEVEL_BASIC]: "Basic", - [MaimaiLevel.MAIMAI_LEVEL_ADVANCED]: "Advanced", - [MaimaiLevel.MAIMAI_LEVEL_EXPERT]: "Expert", - [MaimaiLevel.MAIMAI_LEVEL_MASTER]: "Master", - [MaimaiLevel.MAIMAI_LEVEL_REMASTER]: "Re:Master", - [MaimaiLevel.MAIMAI_LEVEL_UTAGE]: "Utage", + [MaimaiLevel.UNSPECIFIED]: undefined, + [MaimaiLevel.BASIC]: "Basic", + [MaimaiLevel.ADVANCED]: "Advanced", + [MaimaiLevel.EXPERT]: "Expert", + [MaimaiLevel.MASTER]: "Master", + [MaimaiLevel.REMASTER]: "Re:Master", + [MaimaiLevel.UTAGE]: "Utage", }; function getLamp( comboStatus: number, isClear: boolean, ): ScoreData<"maimaidx:Single">["lamp"] | undefined { - if (comboStatus === MaimaiComboStatus.MAIMAI_COMBO_STATUS_UNSPECIFIED) { + if (comboStatus === MaimaiComboStatus.UNSPECIFIED) { return undefined; } @@ -38,23 +38,23 @@ function getLamp( return "FAILED"; } - if (comboStatus === MaimaiComboStatus.MAIMAI_COMBO_STATUS_NONE) { + if (comboStatus === MaimaiComboStatus.NONE) { return "CLEAR"; } - if (comboStatus === MaimaiComboStatus.MAIMAI_COMBO_STATUS_FULL_COMBO) { + if (comboStatus === MaimaiComboStatus.FULL_COMBO) { return "FULL COMBO"; } - if (comboStatus === MaimaiComboStatus.MAIMAI_COMBO_STATUS_FULL_COMBO_PLUS) { + if (comboStatus === MaimaiComboStatus.FULL_COMBO_PLUS) { return "FULL COMBO+"; } - if (comboStatus === MaimaiComboStatus.MAIMAI_COMBO_STATUS_ALL_PERFECT) { + if (comboStatus === MaimaiComboStatus.ALL_PERFECT) { return "ALL PERFECT"; } - if (comboStatus === MaimaiComboStatus.MAIMAI_COMBO_STATUS_ALL_PERFECT_PLUS) { + if (comboStatus === MaimaiComboStatus.ALL_PERFECT_PLUS) { return "ALL PERFECT+"; } diff --git a/typescript/server/src/lib/score-import/import-types/api/myt-maimaidx/parser.ts b/typescript/server/src/lib/score-import/import-types/api/myt-maimaidx/parser.ts index be81064cb..23c6a34ae 100644 --- a/typescript/server/src/lib/score-import/import-types/api/myt-maimaidx/parser.ts +++ b/typescript/server/src/lib/score-import/import-types/api/myt-maimaidx/parser.ts @@ -3,24 +3,38 @@ import type { EmptyObject } from "#utils/types"; import type { integer } from "tachi-common"; import ScoreImportFatalError from "#lib/score-import/framework/score-importing/score-import-error"; -import { MaimaiUserClient } from "#proto/generated/maimai/user_grpc_pb"; -import { GetPlaylogRequest, type GetPlaylogStreamItem } from "#proto/generated/maimai/user_pb"; -import { credentials } from "@grpc/grpc-js"; +import { GetPlaylogRequestSchema, MaimaiUser } from "#proto/generated/maimai/user_pb"; +import { create } from "@bufbuild/protobuf"; +import { ConnectError, createClient } from "@connectrpc/connect"; import type { ParserFunctionReturns } from "../../common/types"; import type { MytMaimaiDxScore } from "./types"; -import { - FetchMytTitleAPIID, - GetMytHostname, - StreamRPCAsAsync, -} from "../../common/api-myt/traverse-api"; +import { CreateMytTransport, FetchMytTitleAPIID } from "../../common/api-myt/traverse-api"; -async function* getObjectsFromGrpcIterable( - iterable: AsyncIterable, -): AsyncIterable { - for await (const item of iterable) { - yield item.toObject(); +async function* streamPlaylog(userID: integer, log: KtLogger): AsyncIterable { + const profileApiId = await FetchMytTitleAPIID(userID, "maimaidx", log); + const client = createClient(MaimaiUser, CreateMytTransport()); + const request = create(GetPlaylogRequestSchema, { profileApiId }); + + try { + for await (const item of client.getPlaylog(request)) { + yield item; + } + } catch (err) { + if (err instanceof ConnectError) { + log.error( + { err, code: err.code }, + `MYT gRPC error streaming maimai DX playlog for userID ${userID}`, + ); + } else { + log.error( + { err }, + `Unexpected MYT error streaming maimai DX playlog for userID ${userID}`, + ); + } + + throw new ScoreImportFatalError(500, `Failed to get scores from MYT.`); } } @@ -28,29 +42,8 @@ export default async function ParseMytMaimaiDx( userID: integer, log: KtLogger, ): Promise> { - const profileApiId = await FetchMytTitleAPIID(userID, "maimaidx", log); - const endpoint = GetMytHostname(); - const client = new MaimaiUserClient(endpoint, credentials.createSsl()); - const request = new GetPlaylogRequest(); - - request.setProfileApiId(profileApiId); - - let iterable; - - try { - const stream = StreamRPCAsAsync(client.getPlaylog.bind(client), request, log); - - iterable = getObjectsFromGrpcIterable(stream); - } catch (err) { - log.error( - `Unexpected MYT error while streaming maimai DX playlog items for userID ${userID}: ${err}`, - ); - - throw new ScoreImportFatalError(500, `Failed to get scores from MYT.`); - } - return { - iterable, + iterable: streamPlaylog(userID, log), context: {}, classProvider: null, game: "maimaidx", diff --git a/typescript/server/src/lib/score-import/import-types/api/myt-maimaidx/types.ts b/typescript/server/src/lib/score-import/import-types/api/myt-maimaidx/types.ts index 5f20c3b3b..aa4793484 100644 --- a/typescript/server/src/lib/score-import/import-types/api/myt-maimaidx/types.ts +++ b/typescript/server/src/lib/score-import/import-types/api/myt-maimaidx/types.ts @@ -1,3 +1,3 @@ import type { GetPlaylogStreamItem } from "#proto/generated/maimai/user_pb"; -export type MytMaimaiDxScore = GetPlaylogStreamItem.AsObject; +export type MytMaimaiDxScore = GetPlaylogStreamItem; diff --git a/typescript/server/src/lib/score-import/import-types/api/myt-ongeki/converter.test.ts b/typescript/server/src/lib/score-import/import-types/api/myt-ongeki/converter.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/import-types/api/myt-ongeki/converter.test.ts rename to typescript/server/src/lib/score-import/import-types/api/myt-ongeki/converter.oldtest.ts diff --git a/typescript/server/src/lib/score-import/import-types/api/myt-ongeki/converter.ts b/typescript/server/src/lib/score-import/import-types/api/myt-ongeki/converter.ts index 02fd615f6..d669fe623 100644 --- a/typescript/server/src/lib/score-import/import-types/api/myt-ongeki/converter.ts +++ b/typescript/server/src/lib/score-import/import-types/api/myt-ongeki/converter.ts @@ -20,12 +20,12 @@ import type { ConverterFunction } from "../../common/types"; import type { MytOngekiScore } from "./types"; const DIFFICULTIES = { - [OngekiLevel.ONGEKI_LEVEL_UNSPECIFIED]: undefined, - [OngekiLevel.ONGEKI_LEVEL_BASIC]: "BASIC", - [OngekiLevel.ONGEKI_LEVEL_ADVANCED]: "ADVANCED", - [OngekiLevel.ONGEKI_LEVEL_EXPERT]: "EXPERT", - [OngekiLevel.ONGEKI_LEVEL_MASTER]: "MASTER", - [OngekiLevel.ONGEKI_LEVEL_LUNATIC]: "LUNATIC", + [OngekiLevel.UNSPECIFIED]: undefined, + [OngekiLevel.BASIC]: "BASIC", + [OngekiLevel.ADVANCED]: "ADVANCED", + [OngekiLevel.EXPERT]: "EXPERT", + [OngekiLevel.MASTER]: "MASTER", + [OngekiLevel.LUNATIC]: "LUNATIC", }; function getNoteLamp( @@ -34,8 +34,8 @@ function getNoteLamp( techScore: number, ): ScoreData<"ongeki:Single">["noteLamp"] | undefined { if ( - comboStatus === OngekiComboStatus.ONGEKI_COMBO_STATUS_UNSPECIFIED || - clearStatus === OngekiClearStatus.ONGEKI_CLEAR_STATUS_UNSPECIFIED + comboStatus === OngekiComboStatus.UNSPECIFIED || + clearStatus === OngekiClearStatus.UNSPECIFIED ) { return undefined; } @@ -44,22 +44,22 @@ function getNoteLamp( return "ALL BREAK+"; } - if (comboStatus === OngekiComboStatus.ONGEKI_COMBO_STATUS_ALL_BREAK) { + if (comboStatus === OngekiComboStatus.ALL_BREAK) { return "ALL BREAK"; } - if (comboStatus === OngekiComboStatus.ONGEKI_COMBO_STATUS_FULL_COMBO) { + if (comboStatus === OngekiComboStatus.FULL_COMBO) { return "FULL COMBO"; } if ( - clearStatus === OngekiClearStatus.ONGEKI_CLEAR_STATUS_OVER_DAMAGE || - clearStatus === OngekiClearStatus.ONGEKI_CLEAR_STATUS_CLEARED + clearStatus === OngekiClearStatus.OVER_DAMAGE || + clearStatus === OngekiClearStatus.CLEARED ) { return "CLEAR"; } - if (clearStatus === OngekiClearStatus.ONGEKI_CLEAR_STATUS_FAILED) { + if (clearStatus === OngekiClearStatus.FAILED) { return "LOSS"; } diff --git a/typescript/server/src/lib/score-import/import-types/api/myt-ongeki/parser.ts b/typescript/server/src/lib/score-import/import-types/api/myt-ongeki/parser.ts index 8d31cc3b0..7275f50f4 100644 --- a/typescript/server/src/lib/score-import/import-types/api/myt-ongeki/parser.ts +++ b/typescript/server/src/lib/score-import/import-types/api/myt-ongeki/parser.ts @@ -3,24 +3,38 @@ import type { EmptyObject } from "#utils/types"; import type { integer } from "tachi-common"; import ScoreImportFatalError from "#lib/score-import/framework/score-importing/score-import-error"; -import { OngekiUserClient } from "#proto/generated/ongeki/user_grpc_pb"; -import { GetPlaylogRequest, type GetPlaylogStreamItem } from "#proto/generated/ongeki/user_pb"; -import { credentials } from "@grpc/grpc-js"; +import { GetPlaylogRequestSchema, OngekiUser } from "#proto/generated/ongeki/user_pb"; +import { create } from "@bufbuild/protobuf"; +import { ConnectError, createClient } from "@connectrpc/connect"; import type { ParserFunctionReturns } from "../../common/types"; import type { MytOngekiScore } from "./types"; -import { - FetchMytTitleAPIID, - GetMytHostname, - StreamRPCAsAsync, -} from "../../common/api-myt/traverse-api"; +import { CreateMytTransport, FetchMytTitleAPIID } from "../../common/api-myt/traverse-api"; -async function* getObjectsFromGrpcIterable( - iterable: AsyncIterable, -): AsyncIterable { - for await (const item of iterable) { - yield item.toObject(); +async function* streamPlaylog(userID: integer, log: KtLogger): AsyncIterable { + const profileApiId = await FetchMytTitleAPIID(userID, "ongeki", log); + const client = createClient(OngekiUser, CreateMytTransport()); + const request = create(GetPlaylogRequestSchema, { profileApiId }); + + try { + for await (const item of client.getPlaylog(request)) { + yield item; + } + } catch (err) { + if (err instanceof ConnectError) { + log.error( + { err, code: err.code }, + `MYT gRPC error streaming Ongeki playlog for userID ${userID}`, + ); + } else { + log.error( + { err }, + `Unexpected MYT error streaming Ongeki playlog for userID ${userID}`, + ); + } + + throw new ScoreImportFatalError(500, `Failed to get scores from MYT.`); } } @@ -28,29 +42,8 @@ export default async function ParseMytOngeki( userID: integer, log: KtLogger, ): Promise> { - const profileApiId = await FetchMytTitleAPIID(userID, "ongeki", log); - const endpoint = GetMytHostname(); - const client = new OngekiUserClient(endpoint, credentials.createSsl()); - const request = new GetPlaylogRequest(); - - request.setProfileApiId(profileApiId); - - let iterable; - - try { - const stream = StreamRPCAsAsync(client.getPlaylog.bind(client), request, log); - - iterable = getObjectsFromGrpcIterable(stream); - } catch (err) { - log.error( - `Unexpected MYT error while streaming Ongeki playlog items for userID ${userID}: ${err}`, - ); - - throw new ScoreImportFatalError(500, `Failed to get scores from MYT.`); - } - return { - iterable, + iterable: streamPlaylog(userID, log), context: {}, classProvider: null, game: "ongeki", diff --git a/typescript/server/src/lib/score-import/import-types/api/myt-ongeki/types.ts b/typescript/server/src/lib/score-import/import-types/api/myt-ongeki/types.ts index 550ad8170..befde6cbb 100644 --- a/typescript/server/src/lib/score-import/import-types/api/myt-ongeki/types.ts +++ b/typescript/server/src/lib/score-import/import-types/api/myt-ongeki/types.ts @@ -1,3 +1,3 @@ import type { GetPlaylogStreamItem } from "#proto/generated/ongeki/user_pb"; -export type MytOngekiScore = GetPlaylogStreamItem.AsObject; +export type MytOngekiScore = GetPlaylogStreamItem; diff --git a/typescript/server/src/lib/score-import/import-types/api/myt-wacca/class-handler.ts b/typescript/server/src/lib/score-import/import-types/api/myt-wacca/class-handler.ts index da767d508..53c5d2303 100644 --- a/typescript/server/src/lib/score-import/import-types/api/myt-wacca/class-handler.ts +++ b/typescript/server/src/lib/score-import/import-types/api/myt-wacca/class-handler.ts @@ -1,50 +1,36 @@ import type { ClassProvider } from "#lib/score-import/framework/calculated-data/types"; -import type { WaccaUserClient } from "#proto/generated/wacca/user_grpc_pb"; import ScoreImportFatalError from "#lib/score-import/framework/score-importing/score-import-error"; import { WaccaVersion } from "#proto/generated/wacca/common_pb"; -import { DataRequest, type DataResponse } from "#proto/generated/wacca/user_pb"; +import { DataRequestSchema, WaccaUser } from "#proto/generated/wacca/user_pb"; +import { create } from "@bufbuild/protobuf"; +import { createClient, type Transport } from "@connectrpc/connect"; import { WaccaStageUps } from "tachi-common/config/game-support/wacca"; -import { UnaryRPCAsAsync } from "../../common/api-myt/traverse-api"; - export default async function CreateMytWACCAClassHandler( titleApiId: string, - userClient: WaccaUserClient, + transport: Transport, ): Promise { - const req = new DataRequest(); - - req.setApiId(titleApiId); - const dataRes: DataResponse = await UnaryRPCAsAsync(userClient.getData.bind(userClient), req); - const data = dataRes; + const client = createClient(WaccaUser, transport); + const req = create(DataRequestSchema, { apiId: titleApiId }); + const data = await client.getData(req); return (_gptString, _userID, _ratings, _logger) => { // Currently (May 2025) Reverse and Plus are supported on Myt. // We look for both Reverse and PLUS version data, PLUS being prioritized if exists. // If / when custom dans are added, this will need to change. - const versionDataMap = data.getVersionDataMap(); - let versionData: DataResponse.VersionData | undefined; + const versionData = + data.versionData[WaccaVersion.PLUS] ?? data.versionData[WaccaVersion.REVERSE]; - if (versionDataMap.has(WaccaVersion.WACCA_VERSION_PLUS)) { - versionData = versionDataMap.get(WaccaVersion.WACCA_VERSION_PLUS); - } else if (versionDataMap.has(WaccaVersion.WACCA_VERSION_REVERSE)) { - versionData = versionDataMap.get(WaccaVersion.WACCA_VERSION_REVERSE); - } - - // rank: - // 0 -> none - // 1 -> stage I - // 2 -> stage II - // ... - // 14 -> stage XIV - if (versionData === undefined || versionData.getRank() === 0) { + // rank: 0 = none, 1 = stage I, 2 = stage II, ... 14 = stage XIV + if (versionData === undefined || versionData.rank === 0) { return {}; } - const stageEnum = WaccaStageUps[versionData.getRank() - 1]; + const stageEnum = WaccaStageUps[versionData.rank - 1]; if (stageEnum === undefined) { - throw new ScoreImportFatalError(400, `Unknown stage up value ${versionData.getRank()}`); + throw new ScoreImportFatalError(400, `Unknown stage up value ${versionData.rank}`); } return { diff --git a/typescript/server/src/lib/score-import/import-types/api/myt-wacca/converter.test.ts b/typescript/server/src/lib/score-import/import-types/api/myt-wacca/converter.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/import-types/api/myt-wacca/converter.test.ts rename to typescript/server/src/lib/score-import/import-types/api/myt-wacca/converter.oldtest.ts diff --git a/typescript/server/src/lib/score-import/import-types/api/myt-wacca/converter.ts b/typescript/server/src/lib/score-import/import-types/api/myt-wacca/converter.ts index 1842979a2..62803900c 100644 --- a/typescript/server/src/lib/score-import/import-types/api/myt-wacca/converter.ts +++ b/typescript/server/src/lib/score-import/import-types/api/myt-wacca/converter.ts @@ -9,30 +9,23 @@ import { SongOrChartNotFoundFailure, } from "#lib/score-import/framework/common/converter-failures"; import { ParseDateFromString } from "#lib/score-import/framework/common/score-utils"; -import { - type WaccaClearStatus, - WaccaMusicDifficulty, - type WaccaMusicDifficultyMap, -} from "#proto/generated/wacca/common_pb"; +import { type WaccaClearStatus, WaccaMusicDifficulty } from "#proto/generated/wacca/common_pb"; import { FindChartOnInGameID } from "#utils/queries/charts"; import { FindSongOnID } from "#utils/queries/songs"; import type { ConverterFunction } from "../../common/types"; import type { MytWaccaScore } from "./types"; -const DIFFICULTIES: Record< - WaccaMusicDifficultyMap[keyof WaccaMusicDifficultyMap], - Difficulties["wacca:Single"] | undefined -> = { - [WaccaMusicDifficulty.WACCA_MUSIC_DIFFICULTY_UNSPECIFIED]: undefined, - [WaccaMusicDifficulty.WACCA_MUSIC_DIFFICULTY_NORMAL]: "NORMAL", - [WaccaMusicDifficulty.WACCA_MUSIC_DIFFICULTY_HARD]: "HARD", - [WaccaMusicDifficulty.WACCA_MUSIC_DIFFICULTY_EXPERT]: "EXPERT", - [WaccaMusicDifficulty.WACCA_MUSIC_DIFFICULTY_INFERNO]: "INFERNO", +const DIFFICULTIES: Partial> = { + [WaccaMusicDifficulty.UNSPECIFIED]: undefined, + [WaccaMusicDifficulty.NORMAL]: "NORMAL", + [WaccaMusicDifficulty.HARD]: "HARD", + [WaccaMusicDifficulty.EXPERT]: "EXPERT", + [WaccaMusicDifficulty.INFERNO]: "INFERNO", }; function convertClearStatus( - status: WaccaClearStatus.AsObject | undefined, + status: WaccaClearStatus | undefined, ): GetEnumValue<"wacca:Single", "lamp"> { if (status === undefined) { throw new InvalidScoreFailure(`Can't process a score without clearStatus`); diff --git a/typescript/server/src/lib/score-import/import-types/api/myt-wacca/parser.ts b/typescript/server/src/lib/score-import/import-types/api/myt-wacca/parser.ts index cee879128..7518a24fc 100644 --- a/typescript/server/src/lib/score-import/import-types/api/myt-wacca/parser.ts +++ b/typescript/server/src/lib/score-import/import-types/api/myt-wacca/parser.ts @@ -3,27 +3,37 @@ import type { EmptyObject } from "#utils/types"; import type { integer } from "tachi-common"; import ScoreImportFatalError from "#lib/score-import/framework/score-importing/score-import-error"; -import { WaccaUserClient } from "#proto/generated/wacca/user_grpc_pb"; -import { PlaylogRequest, type PlaylogStreamItem } from "#proto/generated/wacca/user_pb"; -import { credentials } from "@grpc/grpc-js"; +import { PlaylogRequestSchema, WaccaUser } from "#proto/generated/wacca/user_pb"; +import { create } from "@bufbuild/protobuf"; +import { ConnectError, createClient } from "@connectrpc/connect"; import type { ParserFunctionReturns } from "../../common/types"; import type { MytWaccaScore } from "./types"; -import { - FetchMytTitleAPIID, - GetMytHostname, - StreamRPCAsAsync, -} from "../../common/api-myt/traverse-api"; +import { CreateMytTransport, FetchMytTitleAPIID } from "../../common/api-myt/traverse-api"; import CreateMytWACCAClassHandler from "./class-handler"; -async function* getObjectsFromGrpcIterable( - iterable: AsyncIterable, -): AsyncIterable { - for await (const item of iterable) { - // Re: non-null assertion - in GRPC, all fields are always technically optional. - // Realistically, it's ok to crash if this is undefined - it should never be. - yield item.getInfo()!.toObject(); +async function* streamPlaylog(apiId: string, log: KtLogger): AsyncIterable { + const client = createClient(WaccaUser, CreateMytTransport()); + const request = create(PlaylogRequestSchema, { apiId }); + + try { + for await (const item of client.getPlaylog(request)) { + if (!item.info) { + log.warn(`Received WACCA playlog stream item with no info — skipping.`); + continue; + } + + yield item.info; + } + } catch (err) { + if (err instanceof ConnectError) { + log.error({ err, code: err.code }, `MYT gRPC error streaming WACCA playlog`); + } else { + log.error({ err }, `Unexpected MYT error streaming WACCA playlog`); + } + + throw new ScoreImportFatalError(500, `Failed to get scores from MYT.`); } } @@ -32,56 +42,18 @@ export default async function ParseMytWACCA( log: KtLogger, ): Promise> { const titleApiId = await FetchMytTitleAPIID(userID, "wacca", log); - const endpoint = GetMytHostname(); - let client; - - try { - client = new WaccaUserClient(endpoint, credentials.createSsl()); - } catch (err) { - // Note: I don't think this actually does anything on the network, so - // it shouldn't really fail. Still, wrap just in case. - - log.error( - { - userID, - err, - }, - `Unexpected MYT during WaccaUserClient creation for ${userID}: ${err}`, - ); - - throw new ScoreImportFatalError(500, `Failed to connect to MYT.`); - } - - const req = new PlaylogRequest(); - - req.setApiId(titleApiId); - - let iterable; - - try { - const stream = StreamRPCAsAsync(client.getPlaylog.bind(client), req, log); - - iterable = getObjectsFromGrpcIterable(stream); - } catch (err) { - log.error( - `Unexpected MYT error while streaming WACCA playlog items for userID ${userID}: ${err}`, - ); - - throw new ScoreImportFatalError(500, `Failed to get scores from MYT.`); - } let classProvider; try { - classProvider = await CreateMytWACCAClassHandler(titleApiId, client); + classProvider = await CreateMytWACCAClassHandler(titleApiId, CreateMytTransport()); } catch (err) { log.error(`Unexpected MYT error while fetching player data for userID ${userID}: ${err}`); - throw new ScoreImportFatalError(500, `Failed to fetch player data from MYT.`); } return { - iterable, + iterable: streamPlaylog(titleApiId, log), context: {}, classProvider, game: "wacca", diff --git a/typescript/server/src/lib/score-import/import-types/api/myt-wacca/types.ts b/typescript/server/src/lib/score-import/import-types/api/myt-wacca/types.ts index 814e22fe4..d5c7fe8e6 100644 --- a/typescript/server/src/lib/score-import/import-types/api/myt-wacca/types.ts +++ b/typescript/server/src/lib/score-import/import-types/api/myt-wacca/types.ts @@ -1,3 +1,3 @@ import type { PlaylogInfo } from "#proto/generated/wacca/user_pb"; -export type MytWaccaScore = PlaylogInfo.AsObject; +export type MytWaccaScore = PlaylogInfo; diff --git a/typescript/server/src/lib/score-import/import-types/common/api-cg/jb/converter.test.ts b/typescript/server/src/lib/score-import/import-types/common/api-cg/jb/converter.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/import-types/common/api-cg/jb/converter.test.ts rename to typescript/server/src/lib/score-import/import-types/common/api-cg/jb/converter.oldtest.ts diff --git a/typescript/server/src/lib/score-import/import-types/common/api-cg/museca/converter.test.ts b/typescript/server/src/lib/score-import/import-types/common/api-cg/museca/converter.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/import-types/common/api-cg/museca/converter.test.ts rename to typescript/server/src/lib/score-import/import-types/common/api-cg/museca/converter.oldtest.ts diff --git a/typescript/server/src/lib/score-import/import-types/common/api-cg/parsers.ts b/typescript/server/src/lib/score-import/import-types/common/api-cg/parsers.ts index 8ab066acd..a61030cda 100644 --- a/typescript/server/src/lib/score-import/import-types/common/api-cg/parsers.ts +++ b/typescript/server/src/lib/score-import/import-types/common/api-cg/parsers.ts @@ -1,7 +1,7 @@ import type { KtLogger } from "#lib/log/log.js"; import ScoreImportFatalError from "#lib/score-import/framework/score-importing/score-import-error"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import fetch from "node-fetch"; import { p, type PrudenceSchema } from "prudence"; import { FormatPrError, type integer } from "tachi-common"; @@ -107,7 +107,7 @@ const CG_SCHEMAS: Record = { */ export function CreateCGParser(cgGame: CGSupportedGames, service: CGServices) { return async (userID: integer, log: KtLogger): Promise> => { - const cardInfo = await db["cg-card-info"].findOne({ + const cardInfo = await MONGODB_KILL["cg-card-info"].findOne({ userID, service, }); diff --git a/typescript/server/src/lib/score-import/import-types/common/api-cg/popn/converter.test.ts b/typescript/server/src/lib/score-import/import-types/common/api-cg/popn/converter.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/import-types/common/api-cg/popn/converter.test.ts rename to typescript/server/src/lib/score-import/import-types/common/api-cg/popn/converter.oldtest.ts diff --git a/typescript/server/src/lib/score-import/import-types/common/api-cg/sdvx/converter.test.ts b/typescript/server/src/lib/score-import/import-types/common/api-cg/sdvx/converter.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/import-types/common/api-cg/sdvx/converter.test.ts rename to typescript/server/src/lib/score-import/import-types/common/api-cg/sdvx/converter.oldtest.ts diff --git a/typescript/server/src/lib/score-import/import-types/common/api-kai/iidx/class-handler.test.ts b/typescript/server/src/lib/score-import/import-types/common/api-kai/iidx/class-handler.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/import-types/common/api-kai/iidx/class-handler.test.ts rename to typescript/server/src/lib/score-import/import-types/common/api-kai/iidx/class-handler.oldtest.ts diff --git a/typescript/server/src/lib/score-import/import-types/common/api-kai/iidx/converter.test.ts b/typescript/server/src/lib/score-import/import-types/common/api-kai/iidx/converter.oldtest.ts similarity index 96% rename from typescript/server/src/lib/score-import/import-types/common/api-kai/iidx/converter.test.ts rename to typescript/server/src/lib/score-import/import-types/common/api-kai/iidx/converter.oldtest.ts index 90232a7ea..d10e44afa 100644 --- a/typescript/server/src/lib/score-import/import-types/common/api-kai/iidx/converter.test.ts +++ b/typescript/server/src/lib/score-import/import-types/common/api-kai/iidx/converter.oldtest.ts @@ -1,5 +1,5 @@ import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import ResetDBState from "#test-utils/resets"; import { LoadTachiIIDXData, Testing511Song, Testing511SPA } from "#test-utils/test-data"; import deepmerge from "deepmerge"; @@ -136,7 +136,7 @@ t.test("#ConvertAPIKaiIIDX", (t) => { }); t.test("Should throw InternalFailure on song-chart desync", async (t) => { - await db.songs.iidx.remove({}); + await MONGODB_KILL.songs.iidx.remove({}); t.rejects(() => ConvertAPIKaiIIDX(iidxScore, { service: "FLO" }, "api/flo-iidx", log), { message: /Song-Chart desync/u, diff --git a/typescript/server/src/lib/score-import/import-types/common/api-kai/iidx/parser.test.ts b/typescript/server/src/lib/score-import/import-types/common/api-kai/iidx/parser.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/import-types/common/api-kai/iidx/parser.test.ts rename to typescript/server/src/lib/score-import/import-types/common/api-kai/iidx/parser.oldtest.ts diff --git a/typescript/server/src/lib/score-import/import-types/common/api-kai/reauth.test.ts b/typescript/server/src/lib/score-import/import-types/common/api-kai/reauth.oldtest.ts similarity index 83% rename from typescript/server/src/lib/score-import/import-types/common/api-kai/reauth.test.ts rename to typescript/server/src/lib/score-import/import-types/common/api-kai/reauth.oldtest.ts index c7e6a8b4a..43940848c 100644 --- a/typescript/server/src/lib/score-import/import-types/common/api-kai/reauth.test.ts +++ b/typescript/server/src/lib/score-import/import-types/common/api-kai/reauth.oldtest.ts @@ -2,7 +2,7 @@ import type { KaiAuthDocument } from "tachi-common"; import { log } from "#lib/log/log.js"; import { ServerConfig } from "#lib/setup/config"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { MockJSONFetch } from "#test-utils/mock-fetch"; import ResetDBState from "#test-utils/resets"; import deepmerge from "deepmerge"; @@ -20,7 +20,7 @@ const authDoc: KaiAuthDocument = { t.test("#CreateKaiReauthFunction", (t) => { t.beforeEach(ResetDBState); // eslint-disable-next-line no-return-await - t.beforeEach(async () => await db["kai-auth-tokens"].remove({})); + t.beforeEach(async () => await MONGODB_KILL["kai-auth-tokens"].remove({})); if (!ServerConfig.FLO_OAUTH2_INFO) { throw new Error( @@ -36,7 +36,7 @@ t.test("#CreateKaiReauthFunction", (t) => { }, }); - await db["kai-auth-tokens"].insert(deepmerge(authDoc, {})); + await MONGODB_KILL["kai-auth-tokens"].insert(deepmerge(authDoc, {})); const reauthFn = CreateKaiReauthFunction("FLO", authDoc, log, mockFetch); @@ -46,7 +46,7 @@ t.test("#CreateKaiReauthFunction", (t) => { t.equal(data, "NEW_ACCESS_TOKEN"); - const dbChange = await db["kai-auth-tokens"].findOne({ + const dbChange = await MONGODB_KILL["kai-auth-tokens"].findOne({ userID: 1, service: "FLO", }); @@ -61,7 +61,7 @@ t.test("#CreateKaiReauthFunction", (t) => { // will fail const mockFetch = MockJSONFetch({}); - await db["kai-auth-tokens"].insert(deepmerge(authDoc, {})); + await MONGODB_KILL["kai-auth-tokens"].insert(deepmerge(authDoc, {})); const reauthFn = CreateKaiReauthFunction("FLO", authDoc, log, mockFetch); @@ -69,7 +69,7 @@ t.test("#CreateKaiReauthFunction", (t) => { message: "An error has occured while attempting reauthentication.", }); - const dbChange = await db["kai-auth-tokens"].findOne({ + const dbChange = await MONGODB_KILL["kai-auth-tokens"].findOne({ userID: 1, service: "FLO", }); @@ -95,7 +95,7 @@ t.test("#CreateKaiReauthFunction", (t) => { }, }); - await db["kai-auth-tokens"].insert(deepmerge(authDoc, {})); + await MONGODB_KILL["kai-auth-tokens"].insert(deepmerge(authDoc, {})); const reauthFn = CreateKaiReauthFunction("FLO", authDoc, log, mockFetch); @@ -103,7 +103,7 @@ t.test("#CreateKaiReauthFunction", (t) => { message: "An error has occured while attempting reauthentication.", }); - const dbChange = await db["kai-auth-tokens"].findOne({ + const dbChange = await MONGODB_KILL["kai-auth-tokens"].findOne({ userID: 1, service: "FLO", }); diff --git a/typescript/server/src/lib/score-import/import-types/common/api-kai/reauth.ts b/typescript/server/src/lib/score-import/import-types/common/api-kai/reauth.ts index df8ae1111..33c3101a1 100644 --- a/typescript/server/src/lib/score-import/import-types/common/api-kai/reauth.ts +++ b/typescript/server/src/lib/score-import/import-types/common/api-kai/reauth.ts @@ -3,7 +3,7 @@ import type { KaiAuthDocument } from "tachi-common"; import ScoreImportFatalError from "#lib/score-import/framework/score-importing/score-import-error"; import { ServerConfig } from "#lib/setup/config"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import nodeFetch from "#utils/fetch"; import { p } from "prudence"; @@ -109,7 +109,7 @@ export function CreateKaiReauthFunction( refresh_token: string; }; - await db["kai-auth-tokens"].update( + await MONGODB_KILL["kai-auth-tokens"].update( { userID: authDoc.userID, service: authDoc.service, diff --git a/typescript/server/src/lib/score-import/import-types/common/api-kai/sdvx/class-handler.test.ts b/typescript/server/src/lib/score-import/import-types/common/api-kai/sdvx/class-handler.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/import-types/common/api-kai/sdvx/class-handler.test.ts rename to typescript/server/src/lib/score-import/import-types/common/api-kai/sdvx/class-handler.oldtest.ts diff --git a/typescript/server/src/lib/score-import/import-types/common/api-kai/sdvx/converter.test.ts b/typescript/server/src/lib/score-import/import-types/common/api-kai/sdvx/converter.oldtest.ts similarity index 97% rename from typescript/server/src/lib/score-import/import-types/common/api-kai/sdvx/converter.test.ts rename to typescript/server/src/lib/score-import/import-types/common/api-kai/sdvx/converter.oldtest.ts index df40a6a6c..3fb727dd6 100644 --- a/typescript/server/src/lib/score-import/import-types/common/api-kai/sdvx/converter.test.ts +++ b/typescript/server/src/lib/score-import/import-types/common/api-kai/sdvx/converter.oldtest.ts @@ -1,5 +1,5 @@ import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import ResetDBState from "#test-utils/resets"; import { TestingAlbidaADV, TestingSDVXAlbidaSong } from "#test-utils/test-data"; import deepmerge from "deepmerge"; @@ -102,7 +102,7 @@ t.test("#ConvertAPIKaiSDVX", (t) => { }); t.test("Should throw InternalFailure on song-chart desync", async (t) => { - await db.songs.sdvx.remove({}); + await MONGODB_KILL.songs.sdvx.remove({}); t.rejects(() => ConvertAPIKaiSDVX(sdvxScore, { service: "FLO" }, "api/flo-sdvx", log), { message: /Song-Chart desync/u, diff --git a/typescript/server/src/lib/score-import/import-types/common/api-kai/sdvx/parser.test.ts b/typescript/server/src/lib/score-import/import-types/common/api-kai/sdvx/parser.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/import-types/common/api-kai/sdvx/parser.test.ts rename to typescript/server/src/lib/score-import/import-types/common/api-kai/sdvx/parser.oldtest.ts diff --git a/typescript/server/src/lib/score-import/import-types/common/api-kai/traverse-api.test.ts b/typescript/server/src/lib/score-import/import-types/common/api-kai/traverse-api.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/import-types/common/api-kai/traverse-api.test.ts rename to typescript/server/src/lib/score-import/import-types/common/api-kai/traverse-api.oldtest.ts diff --git a/typescript/server/src/lib/score-import/import-types/common/api-myt/traverse-api.ts b/typescript/server/src/lib/score-import/import-types/common/api-myt/traverse-api.ts index b89b706cb..b90464599 100644 --- a/typescript/server/src/lib/score-import/import-types/common/api-myt/traverse-api.ts +++ b/typescript/server/src/lib/score-import/import-types/common/api-myt/traverse-api.ts @@ -2,25 +2,22 @@ import type { KtLogger } from "#lib/log/log.js"; import ScoreImportFatalError from "#lib/score-import/framework/score-importing/score-import-error"; import { ServerConfig } from "#lib/setup/config"; -import { CardsClient } from "#proto/generated/cards/cards_grpc_pb"; -import { LookupRequest, type LookupResponse } from "#proto/generated/cards/cards_pb"; -import db from "#services/mongo/db"; +import { Cards, LookupRequestSchema } from "#proto/generated/cards/cards_pb"; +import MONGODB_KILL from "#services/mongo/db"; +import { create } from "@bufbuild/protobuf"; import { - type CallOptions, - type ClientReadableStream, - credentials, - Metadata, - type requestCallback, - type ServiceError, -} from "@grpc/grpc-js"; -import { Status } from "@grpc/grpc-js/build/src/constants"; + Code as ConnectCode, + ConnectError, + createClient, + type Interceptor, +} from "@connectrpc/connect"; +import { createGrpcTransport } from "@connectrpc/connect-node"; import { type GameGroup, GetGameGroupConfig, type integer } from "tachi-common"; import { GameToMytGame } from "./util"; -// Hardcode all requests to time out after 10m. -// 6000 scores seems to take more than 30 seconds. 5mins seems like a safe upper bound. -const GRPC_TIMEOUT_SECS = 10 * 60; +// Hardcode all requests to time out after 10 minutes. +const GRPC_TIMEOUT_MS = 10 * 60 * 1000; export function GetMytHostname(): string { const hostname = ServerConfig.MYT_API_HOST; @@ -35,97 +32,27 @@ export function GetMytHostname(): string { return hostname; } -function CreateAuthenticatedMetadata() { - const auth = new Metadata(); +function createBearerInterceptor(): Interceptor { const authToken = ServerConfig.MYT_AUTH_TOKEN; if (authToken === undefined) { throw new ScoreImportFatalError(500, `Tried to get MYT auth token, yet was not defined?`); } - auth.add("Authorization", `Bearer ${authToken}`); - return auth; + return (next) => async (req) => { + req.header.set("Authorization", `Bearer ${authToken}`); + return next(req); + }; } -function CreateDeadlineOptions() { - const deadline = new Date().setSeconds(new Date().getSeconds() + GRPC_TIMEOUT_SECS); - - return { deadline }; -} - -export function UnaryRPCAsAsync( - unaryCall: ( - argument: TIn, - metadata: Metadata, - options: CallOptions, - callback: requestCallback, - ) => void, - argument: TIn, -): Promise { - return new Promise((resolve, reject) => { - unaryCall( - argument, - CreateAuthenticatedMetadata(), - CreateDeadlineOptions(), - (error, value) => { - if (error) { - reject(error); - } - - resolve(value!); - }, - ); +export function CreateMytTransport() { + return createGrpcTransport({ + baseUrl: `https://${GetMytHostname()}`, + interceptors: [createBearerInterceptor()], + defaultTimeoutMs: GRPC_TIMEOUT_MS, }); } -export async function* StreamRPCAsAsync( - streamingCall: ( - argument: TIn, - metadata: Metadata, - options: CallOptions, - ) => ClientReadableStream, - argument: TIn, - log: KtLogger, -): AsyncIterable { - try { - const stream = streamingCall( - argument, - CreateAuthenticatedMetadata(), - CreateDeadlineOptions(), - ); - let err: Error | null = null; - - // If we don't have stream.on("error"), or if we throw the error within - // this callback, it's not caught properly and will crash the whole node - // process. Note: if there are multiple errors within one stream, some may - // be dropped... so be it. - stream.on("error", (e) => (err = e)); - // ClientReadableStream already implements AsyncIterable vs Readable, - // but we know that it will really be AsyncIterable. - for await (const streamItem of stream) { - yield streamItem as TOut; - } - - // TS doesn't realize that err can be set to an Error by stream.on, so it - // thinks the type of err is always null. Fix by casting. - const fixedErr = err as Error | null; - - if (fixedErr !== null) { - throw fixedErr; - } - } catch (err) { - log.error(`Error while streaming score data from MYT: ${err}`); - - // Avoid rethrowing the error as it may reveal things like the upstream - // server IP address. - throw new ScoreImportFatalError(500, `Error while streaming score data from MYT`); - } -} - -function errIsServiceError(err: Error): err is ServiceError { - return "code" in err && "details" in err && "metadata" in err; -} - /** * The Myt API is (currently) based on card access codes, which you can use to * get a "title_api_id" (see proto/cards/cards.proto). @@ -144,51 +71,38 @@ export async function FetchMytTitleAPIID( throw new ScoreImportFatalError(500, `Unsupported game ${game}`); } - const cardInfo = await db["myt-card-info"].findOne({ userID }); + const cardInfo = await MONGODB_KILL["myt-card-info"].findOne({ userID }); if (!cardInfo) { throw new ScoreImportFatalError(401, `This user has no card info set up for this service.`); } const { cardAccessCode } = cardInfo; - const hostname = GetMytHostname(); - - const client = new CardsClient(hostname, credentials.createSsl()); - const req = new LookupRequest(); - - req.setAccessCode(cardAccessCode); - req.addTitles(mytGame); + const client = createClient(Cards, CreateMytTransport()); + const req = create(LookupRequestSchema, { + accessCode: cardAccessCode, + titles: [mytGame], + }); try { - const response: LookupResponse = await UnaryRPCAsAsync(client.lookup.bind(client), req); + const response = await client.lookup(req); - for (const title of response.getTitlesList()) { - if (title.getTitleKind() === mytGame) { - return title.getTitleApiId(); + for (const title of response.titles) { + if (title.titleKind === mytGame) { + return title.titleApiId; } } } catch (e) { - const err = e as Error | ServiceError; - - if (errIsServiceError(err)) { - if (err.code === Status.NOT_FOUND) { - throw new ScoreImportFatalError(401, `Card not found on MYT: ${err.details}`); + if (e instanceof ConnectError) { + if (e.code === ConnectCode.NotFound) { + throw new ScoreImportFatalError(401, `Card not found on MYT: ${e.message}`); } - log.error( - { - err, - code: err.code, - details: err.details, - req: req.toObject(), - }, - `Received unexpected status from ${hostname}`, - ); - throw new ScoreImportFatalError(500, `Unexpected response from MYT - ${err.code}`); + log.error({ err: e, code: e.code }, `Received unexpected status from MYT`); + throw new ScoreImportFatalError(500, `Unexpected response from MYT - ${e.code}`); } - log.error({ err }, `Received invalid response`); - + log.error({ err: e }, `Received invalid response`); throw new ScoreImportFatalError(500, `Failed to look up card at MYT. Are they down?`); } diff --git a/typescript/server/src/lib/score-import/import-types/common/batch-manual/converter.test.ts b/typescript/server/src/lib/score-import/import-types/common/batch-manual/converter.oldtest.ts similarity index 99% rename from typescript/server/src/lib/score-import/import-types/common/batch-manual/converter.test.ts rename to typescript/server/src/lib/score-import/import-types/common/batch-manual/converter.oldtest.ts index 47de2228a..dca370685 100644 --- a/typescript/server/src/lib/score-import/import-types/common/batch-manual/converter.test.ts +++ b/typescript/server/src/lib/score-import/import-types/common/batch-manual/converter.oldtest.ts @@ -7,7 +7,7 @@ import type { import { log } from "#lib/log/log.js"; import { InvalidScoreFailure } from "#lib/score-import/framework/common/converter-failures"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { dmf } from "#test-utils/misc"; import ResetDBState from "#test-utils/resets"; import { @@ -140,7 +140,7 @@ t.test("#ResolveSongAndChart", (t) => { }); t.test("Should support ANY_INF if matchType is sdvxInGameID", async (t) => { - await db.charts.sdvx.insert( + await MONGODB_KILL.charts.sdvx.insert( deepmerge(TestingSDVXAlbidaChart, { chartID: "fake_xcd", data: {}, diff --git a/typescript/server/src/lib/score-import/import-types/common/batch-manual/converter.ts b/typescript/server/src/lib/score-import/import-types/common/batch-manual/converter.ts index 45f4c746c..84240547c 100644 --- a/typescript/server/src/lib/score-import/import-types/common/batch-manual/converter.ts +++ b/typescript/server/src/lib/score-import/import-types/common/batch-manual/converter.ts @@ -1,7 +1,7 @@ import type { KtLogger } from "#lib/log/log.js"; import ScoreImportFatalError from "#lib/score-import/framework/score-importing/score-import-error"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { FindBMSChartOnHash, FindChartWithPTDF, @@ -211,7 +211,7 @@ export async function ResolveSongAndChart( throw new InvalidScoreFailure(`Invalid playtype '${playtype}', expected 9B.`); } - const chart = await db.charts.popn.findOne({ + const chart = await MONGODB_KILL.charts.popn.findOne({ playtype, "data.hashSHA256": resolver.identifier, }); @@ -328,7 +328,7 @@ export async function ResolveSongAndChart( return null; } - const song = await db.anySongs[game].findOne({ id: chart.songID }); + const song = await MONGODB_KILL.anySongs[game].findOne({ id: chart.songID }); if (!song) { log.error(`Song-Chart desync on ${chart.songID}.`); @@ -346,14 +346,14 @@ export async function ResolveSongAndChart( let chart: ChartDocument | null | undefined; if (resolver.version) { - chart = await db.anyCharts[game].findOne({ + chart = await MONGODB_KILL.anyCharts[game].findOne({ "data.inGameID": identifier, playtype: resolver.playtype, difficulty, versions: resolver.version, }); } else { - chart = await db.anyCharts[game].findOne({ + chart = await MONGODB_KILL.anyCharts[game].findOne({ "data.inGameID": identifier, playtype: resolver.playtype, difficulty, @@ -365,7 +365,7 @@ export async function ResolveSongAndChart( return null; } - const song = await db.anySongs[game].findOne({ id: chart.songID }); + const song = await MONGODB_KILL.anySongs[game].findOne({ id: chart.songID }); if (!song) { log.error(`Song-Chart desync on ${chart.songID}.`); @@ -381,14 +381,14 @@ export async function ResolveSongAndChart( let chart; if (resolver.version) { - chart = await db.anyCharts[game].findOne({ + chart = await MONGODB_KILL.anyCharts[game].findOne({ "data.inGameStrID": resolver.identifier, playtype: resolver.playtype, difficulty, versions: resolver.version, }); } else { - chart = await db.anyCharts[game].findOne({ + chart = await MONGODB_KILL.anyCharts[game].findOne({ "data.inGameStrID": resolver.identifier, playtype: resolver.playtype, difficulty, @@ -400,7 +400,7 @@ export async function ResolveSongAndChart( return null; } - const song = await db.anySongs[game].findOne({ id: chart.songID }); + const song = await MONGODB_KILL.anySongs[game].findOne({ id: chart.songID }); if (!song) { log.error(`Song-Chart desync on ${chart.songID}.`); @@ -419,7 +419,7 @@ export async function ResolveSongAndChart( throw new InvalidScoreFailure(`Invalid playtype, expected Keyboard or Controller.`); } - const chart = await db.charts.usc.findOne({ + const chart = await MONGODB_KILL.charts.usc.findOne({ "data.hashSHA1": resolver.identifier, playtype, }); @@ -428,7 +428,7 @@ export async function ResolveSongAndChart( return null; } - const song = await db.anySongs[game].findOne({ id: chart.songID }); + const song = await MONGODB_KILL.anySongs[game].findOne({ id: chart.songID }); if (!song) { log.error(`Song-Chart desync on ${chart.songID}.`); @@ -445,7 +445,7 @@ export async function ResolveSongAndChart( const difficulty = AssertStrAsDifficulty(resolver.difficulty, game, resolver.playtype); - const song = await db.anySongs.ddr.findOne({ + const song = await MONGODB_KILL.anySongs.ddr.findOne({ "data.ddrSongHash": resolver.identifier, }); @@ -454,7 +454,7 @@ export async function ResolveSongAndChart( } // check that a chart with the song's id exists - const chartSync = await db.anyCharts.ddr.findOne({ + const chartSync = await MONGODB_KILL.anyCharts.ddr.findOne({ songID: song.id, }); @@ -466,14 +466,14 @@ export async function ResolveSongAndChart( let chart; if (resolver.version) { - chart = await db.anyCharts.ddr.findOne({ + chart = await MONGODB_KILL.anyCharts.ddr.findOne({ songID: song.id, playtype: resolver.playtype, difficulty, versions: resolver.version, }); } else { - chart = await db.anyCharts.ddr.findOne({ + chart = await MONGODB_KILL.anyCharts.ddr.findOne({ songID: song.id, playtype: resolver.playtype, difficulty, diff --git a/typescript/server/src/lib/score-import/import-types/common/batch-manual/parser.test.ts b/typescript/server/src/lib/score-import/import-types/common/batch-manual/parser.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/import-types/common/batch-manual/parser.test.ts rename to typescript/server/src/lib/score-import/import-types/common/batch-manual/parser.oldtest.ts diff --git a/typescript/server/src/lib/score-import/import-types/common/eamusement-iidx-csv/converter.test.ts b/typescript/server/src/lib/score-import/import-types/common/eamusement-iidx-csv/converter.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/import-types/common/eamusement-iidx-csv/converter.test.ts rename to typescript/server/src/lib/score-import/import-types/common/eamusement-iidx-csv/converter.oldtest.ts diff --git a/typescript/server/src/lib/score-import/import-types/common/eamusement-iidx-csv/parser.test.ts b/typescript/server/src/lib/score-import/import-types/common/eamusement-iidx-csv/parser.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/import-types/common/eamusement-iidx-csv/parser.test.ts rename to typescript/server/src/lib/score-import/import-types/common/eamusement-iidx-csv/parser.oldtest.ts diff --git a/typescript/server/src/lib/score-import/import-types/file/eamusement-sdvx-csv/converter.test.ts b/typescript/server/src/lib/score-import/import-types/file/eamusement-sdvx-csv/converter.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/import-types/file/eamusement-sdvx-csv/converter.test.ts rename to typescript/server/src/lib/score-import/import-types/file/eamusement-sdvx-csv/converter.oldtest.ts diff --git a/typescript/server/src/lib/score-import/import-types/file/eamusement-sdvx-csv/parser.test.ts b/typescript/server/src/lib/score-import/import-types/file/eamusement-sdvx-csv/parser.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/import-types/file/eamusement-sdvx-csv/parser.test.ts rename to typescript/server/src/lib/score-import/import-types/file/eamusement-sdvx-csv/parser.oldtest.ts diff --git a/typescript/server/src/lib/score-import/import-types/file/solid-state-squad/converter.test.ts b/typescript/server/src/lib/score-import/import-types/file/solid-state-squad/converter.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/import-types/file/solid-state-squad/converter.test.ts rename to typescript/server/src/lib/score-import/import-types/file/solid-state-squad/converter.oldtest.ts diff --git a/typescript/server/src/lib/score-import/import-types/file/solid-state-squad/parser.test.ts b/typescript/server/src/lib/score-import/import-types/file/solid-state-squad/parser.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/import-types/file/solid-state-squad/parser.test.ts rename to typescript/server/src/lib/score-import/import-types/file/solid-state-squad/parser.oldtest.ts diff --git a/typescript/server/src/lib/score-import/import-types/file/wacca-mypage-scraper/converter.test.ts b/typescript/server/src/lib/score-import/import-types/file/wacca-mypage-scraper/converter.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/import-types/file/wacca-mypage-scraper/converter.test.ts rename to typescript/server/src/lib/score-import/import-types/file/wacca-mypage-scraper/converter.oldtest.ts diff --git a/typescript/server/src/lib/score-import/import-types/file/wacca-mypage-scraper/parser.test.ts b/typescript/server/src/lib/score-import/import-types/file/wacca-mypage-scraper/parser.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/import-types/file/wacca-mypage-scraper/parser.test.ts rename to typescript/server/src/lib/score-import/import-types/file/wacca-mypage-scraper/parser.oldtest.ts diff --git a/typescript/server/src/lib/score-import/import-types/ir/barbatos/converter.test.ts b/typescript/server/src/lib/score-import/import-types/ir/barbatos/converter.oldtest.ts similarity index 96% rename from typescript/server/src/lib/score-import/import-types/ir/barbatos/converter.test.ts rename to typescript/server/src/lib/score-import/import-types/ir/barbatos/converter.oldtest.ts index 8b2900914..0e6c15cd4 100644 --- a/typescript/server/src/lib/score-import/import-types/ir/barbatos/converter.test.ts +++ b/typescript/server/src/lib/score-import/import-types/ir/barbatos/converter.oldtest.ts @@ -1,5 +1,5 @@ import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import ResetDBState from "#test-utils/resets"; import { MockBarbatosScore, MockBarbatosSDVX6Score } from "#test-utils/test-data"; import deepmerge from "deepmerge"; @@ -143,7 +143,7 @@ t.test("#ConverterIRBarbatos", (t) => { t.test("Should honor provided context.version, and match accordingly.", async (t) => { // remove vividwave from the set of charts that this chart appears in. - await db.charts.sdvx.update( + await MONGODB_KILL.charts.sdvx.update( { "data.inGameID": 1, }, @@ -172,7 +172,7 @@ t.test("#ConverterIRBarbatos", (t) => { t.test("Should throw InternalFailure if song-chart desync.", async (t) => { // force a song-chart desync - await db.songs.sdvx.remove({ id: 1 }); + await MONGODB_KILL.songs.sdvx.remove({ id: 1 }); t.rejects( () => diff --git a/typescript/server/src/lib/score-import/import-types/ir/barbatos/parser.test.ts b/typescript/server/src/lib/score-import/import-types/ir/barbatos/parser.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/import-types/ir/barbatos/parser.test.ts rename to typescript/server/src/lib/score-import/import-types/ir/barbatos/parser.oldtest.ts diff --git a/typescript/server/src/lib/score-import/import-types/ir/fervidex-static/class-handler.test.ts b/typescript/server/src/lib/score-import/import-types/ir/fervidex-static/class-handler.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/import-types/ir/fervidex-static/class-handler.test.ts rename to typescript/server/src/lib/score-import/import-types/ir/fervidex-static/class-handler.oldtest.ts diff --git a/typescript/server/src/lib/score-import/import-types/ir/fervidex-static/converter.test.ts b/typescript/server/src/lib/score-import/import-types/ir/fervidex-static/converter.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/import-types/ir/fervidex-static/converter.test.ts rename to typescript/server/src/lib/score-import/import-types/ir/fervidex-static/converter.oldtest.ts diff --git a/typescript/server/src/lib/score-import/import-types/ir/fervidex-static/parser.test.ts b/typescript/server/src/lib/score-import/import-types/ir/fervidex-static/parser.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/import-types/ir/fervidex-static/parser.test.ts rename to typescript/server/src/lib/score-import/import-types/ir/fervidex-static/parser.oldtest.ts diff --git a/typescript/server/src/lib/score-import/import-types/ir/fervidex/converter.test.ts b/typescript/server/src/lib/score-import/import-types/ir/fervidex/converter.oldtest.ts similarity index 96% rename from typescript/server/src/lib/score-import/import-types/ir/fervidex/converter.test.ts rename to typescript/server/src/lib/score-import/import-types/ir/fervidex/converter.oldtest.ts index 8f074491e..635fdd82e 100644 --- a/typescript/server/src/lib/score-import/import-types/ir/fervidex/converter.test.ts +++ b/typescript/server/src/lib/score-import/import-types/ir/fervidex/converter.oldtest.ts @@ -1,6 +1,6 @@ import { log } from "#lib/log/log.js"; import { CreateScoreID } from "#lib/score-import/framework/score-importing/score-id"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { dmf, mkFakeScoreIIDXSP } from "#test-utils/misc"; import ResetDBState from "#test-utils/resets"; import { Testing511Song, Testing511SPA, TestingIIDXSPDryScore } from "#test-utils/test-data"; @@ -184,7 +184,7 @@ t.test("#ConverterIRFervidex", (t) => { data: {}, }); - await db.charts.iidx.insert(Testing511DPA); + await MONGODB_KILL.charts.iidx.insert(Testing511DPA); const res = await ConverterIRFervidex( deepmerge(baseFervidexScore, { option: { style_2p: "R_RANDOM" }, chart: "dpa" }), @@ -245,7 +245,7 @@ t.test("#ConverterIRFervidex", (t) => { t.test("Should throw internal failure on chart-song desync", async (t) => { // this forces desync - await db.songs.iidx.remove({}); + await MONGODB_KILL.songs.iidx.remove({}); t.rejects( ConverterIRFervidex( @@ -295,7 +295,7 @@ t.test("#ConverterIRFervidex", (t) => { }); // add a score with an existing scoreID that is not highlighted. - await db.scores.insert(fakeScore); + await MONGODB_KILL.scores.insert(fakeScore); t.equal(fakeScore.highlight, false, "The score should not be highlighted."); @@ -315,7 +315,7 @@ t.test("#ConverterIRFervidex", (t) => { log, ); - const dbRes = await db.scores.findOne({ scoreID: fakeScore.scoreID }); + const dbRes = await MONGODB_KILL.scores.findOne({ scoreID: fakeScore.scoreID }); t.equal(dbRes?.highlight, true, "The score should now be highlighted."); diff --git a/typescript/server/src/lib/score-import/import-types/ir/fervidex/converter.ts b/typescript/server/src/lib/score-import/import-types/ir/fervidex/converter.ts index 4ae2d181b..dbe5c39aa 100644 --- a/typescript/server/src/lib/score-import/import-types/ir/fervidex/converter.ts +++ b/typescript/server/src/lib/score-import/import-types/ir/fervidex/converter.ts @@ -1,5 +1,5 @@ import { CreateScoreID } from "#lib/score-import/framework/score-importing/score-id"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { DeleteUndefinedProps, IsNullishOrEmptyStr } from "#utils/misc"; import { FindIIDXChartOnInGameIDVersion, FindIIDXChartWith2DXtraHash } from "#utils/queries/charts"; import { FindSongOnID } from "#utils/queries/songs"; @@ -269,7 +269,7 @@ export const ConverterIRFervidex: ConverterFunction { }); t.test("Should throw an InternalFailure on song-chart desync", async (t) => { - await db.songs.usc.remove({}); + await MONGODB_KILL.songs.usc.remove({}); t.rejects(dm({})); t.end(); diff --git a/typescript/server/src/lib/score-import/import-types/ir/usc/converter.ts b/typescript/server/src/lib/score-import/import-types/ir/usc/converter.ts index d06c29c3c..29ea9e8fd 100644 --- a/typescript/server/src/lib/score-import/import-types/ir/usc/converter.ts +++ b/typescript/server/src/lib/score-import/import-types/ir/usc/converter.ts @@ -8,7 +8,7 @@ import { USC_DEFAULT_PERFECT, USC_DEFAULT_SLAM, } from "#lib/constants/usc-ir"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { FindSongOnID } from "#utils/queries/songs"; import type { DryScore } from "../../../framework/common/types"; @@ -101,7 +101,7 @@ export const ConverterIRUSC: ConverterFunction = a throw new InvalidScoreFailure(`Autoplay was enabled - Score is invalid.`); } - const chartDoc = await db.charts.usc.findOne({ + const chartDoc = await MONGODB_KILL.charts.usc.findOne({ "data.hashSHA1": context.chartHash, playtype: context.playtype, }); diff --git a/typescript/server/src/lib/score-import/import-types/ir/usc/parser.test.ts b/typescript/server/src/lib/score-import/import-types/ir/usc/parser.oldtest.ts similarity index 100% rename from typescript/server/src/lib/score-import/import-types/ir/usc/parser.test.ts rename to typescript/server/src/lib/score-import/import-types/ir/usc/parser.oldtest.ts diff --git a/typescript/server/src/lib/score-mutation/delete-scores.test.ts b/typescript/server/src/lib/score-mutation/delete-scores.oldtest.ts similarity index 80% rename from typescript/server/src/lib/score-mutation/delete-scores.test.ts rename to typescript/server/src/lib/score-mutation/delete-scores.oldtest.ts index 08b01f040..6d93f61ff 100644 --- a/typescript/server/src/lib/score-mutation/delete-scores.test.ts +++ b/typescript/server/src/lib/score-mutation/delete-scores.oldtest.ts @@ -1,6 +1,6 @@ import type { ImportDocument, ScoreDocument, SessionDocument } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { mkFakeSDVXChart, mkFakeSDVXPB } from "#test-utils/misc"; import ResetDBState from "#test-utils/resets"; import { TestingIIDXSPScore, TestingSDVXScore } from "#test-utils/test-data"; @@ -49,22 +49,22 @@ t.test("#DeleteScore", (t) => { const score = deepmerge(TestingIIDXSPScore, { scoreID: "scoreid_1" }); const score2 = deepmerge(TestingIIDXSPScore, { scoreID: "scoreid_2" }); - await db.imports.insert(mockImportDocument); - await db.sessions.insert(mockSessionDocument); + await MONGODB_KILL.imports.insert(mockImportDocument); + await MONGODB_KILL.sessions.insert(mockSessionDocument); - await db.scores.insert([score, score2]); + await MONGODB_KILL.scores.insert([score, score2]); // This function doesn't return anything, instead, // we need to check external state. await DeleteScore(score); - const dbScore = await db.scores.findOne({ + const dbScore = await MONGODB_KILL.scores.findOne({ scoreID: "scoreid_1", }); t.equal(dbScore, null, "Should remove the score from the database."); - const dbImport = await db.imports.findOne({ + const dbImport = await MONGODB_KILL.imports.findOne({ importID: "mockImportID", }); @@ -74,7 +74,7 @@ t.test("#DeleteScore", (t) => { "Should remove scoreid_1 from the importDocuments scoreIDs.", ); - const dbSession = await db.sessions.findOne({ + const dbSession = await MONGODB_KILL.sessions.findOne({ sessionID: "mockSessionID", }); @@ -92,14 +92,14 @@ t.test("#DeleteScore", (t) => { async (t) => { const score = deepmerge(TestingIIDXSPScore, { scoreID: "scoreid_1" }); - await db.scores.insert(score); + await MONGODB_KILL.scores.insert(score); // This function doesn't return anything, instead, // we need to check external state. await DeleteScore(score); - const dbScore = await db.scores.findOne({ scoreID: score.scoreID }); + const dbScore = await MONGODB_KILL.scores.findOne({ scoreID: score.scoreID }); t.equal(dbScore, null, "Should be removed from the database."); @@ -110,7 +110,7 @@ t.test("#DeleteScore", (t) => { t.test("Should destroy its parents if this killed the only child", async (t) => { const score = deepmerge(TestingIIDXSPScore, { scoreID: "scoreid_1" }); - await db.imports.insert( + await MONGODB_KILL.imports.insert( deepmerge( mockImportDocument, { scoreIDs: ["scoreid_1"] }, @@ -119,7 +119,7 @@ t.test("#DeleteScore", (t) => { }, ), ); - await db.sessions.insert( + await MONGODB_KILL.sessions.insert( deepmerge( mockSessionDocument, { @@ -131,23 +131,23 @@ t.test("#DeleteScore", (t) => { ), ); - await db.scores.insert(score); + await MONGODB_KILL.scores.insert(score); await DeleteScore(score); - const dbScore = await db.scores.findOne({ + const dbScore = await MONGODB_KILL.scores.findOne({ scoreID: "scoreid_1", }); t.equal(dbScore, null, "Should remove the score from the database."); - const dbImport = await db.imports.findOne({ + const dbImport = await MONGODB_KILL.imports.findOne({ importID: "mockImportID", }); t.equal(dbImport, null, "Should remove the import from the database."); - const dbSession = await db.sessions.findOne({ + const dbSession = await MONGODB_KILL.sessions.findOne({ sessionID: "mockSessionID", }); @@ -157,12 +157,12 @@ t.test("#DeleteScore", (t) => { }); t.test("Should update classes if the user's classes should need to change.", async (t) => { - await db.charts.sdvx.insert( + await MONGODB_KILL.charts.sdvx.insert( mkFakeSDVXChart("chart_1", { difficulty: "EXH", }), ); - await db.charts.sdvx.insert( + await MONGODB_KILL.charts.sdvx.insert( mkFakeSDVXChart("chart_2", { difficulty: "NOV", }), @@ -181,8 +181,8 @@ t.test("#DeleteScore", (t) => { calculatedData: { VF6: 4 }, }); - await db.scores.insert([score, score2]); - await db["personal-bests"].insert([ + await MONGODB_KILL.scores.insert([score, score2]); + await MONGODB_KILL["personal-bests"].insert([ mkFakeSDVXPB({ chartID: "chart_1", calculatedData: { @@ -197,7 +197,7 @@ t.test("#DeleteScore", (t) => { }), ]); - await db["game-stats"].insert({ + await MONGODB_KILL["game-stats"].insert({ userID: 1, game: "sdvx", playtype: "Single", @@ -211,7 +211,7 @@ t.test("#DeleteScore", (t) => { await DeleteScore(score); - const res = await db["game-stats"].findOne({ + const res = await MONGODB_KILL["game-stats"].findOne({ userID: 1, game: "sdvx", playtype: "Single", diff --git a/typescript/server/src/lib/score-mutation/delete-scores.ts b/typescript/server/src/lib/score-mutation/delete-scores.ts index 86fb39f3c..71a7d4ab9 100644 --- a/typescript/server/src/lib/score-mutation/delete-scores.ts +++ b/typescript/server/src/lib/score-mutation/delete-scores.ts @@ -7,7 +7,7 @@ import { ProcessPBs } from "#lib/score-import/framework/pb/process-pbs"; import { UpdateUsersQuests } from "#lib/score-import/framework/quests/quests"; import { UpdateUsersGamePlaytypeStats } from "#lib/score-import/framework/ugpt-stats/update-ugpt-stats"; /* eslint-disable no-await-in-loop */ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { RecalcSessions } from "#utils/calculations/recalc-sessions"; /** @@ -19,11 +19,11 @@ export async function DeleteScore( blacklist = false, attemptPBReprocess = true, ) { - await db.scores.remove({ + await MONGODB_KILL.scores.remove({ scoreID: score.scoreID, }); - const sessions = await db.sessions.find({ + const sessions = await MONGODB_KILL.sessions.find({ scoreIDs: score.scoreID, }); @@ -31,13 +31,13 @@ export async function DeleteScore( // If a session only has one score, then pulling it should kill // the session. if (session.scoreIDs.length === 1) { - await db.sessions.remove({ + await MONGODB_KILL.sessions.remove({ sessionID: session.sessionID, }); } } - await db.sessions.update( + await MONGODB_KILL.sessions.update( { sessionID: { $in: sessions.map((e) => e.sessionID) }, }, @@ -53,17 +53,17 @@ export async function DeleteScore( await RecalcSessions({ sessionID: { $in: sessions.map((e) => e.sessionID) } }); - const importDoc = await db.imports.findOne({ + const importDoc = await MONGODB_KILL.imports.findOne({ scoreIDs: score.scoreID, }); if (importDoc) { if (importDoc.scoreIDs.length === 1) { - await db.imports.remove({ + await MONGODB_KILL.imports.remove({ importID: importDoc.importID, }); } else { - await db.imports.update( + await MONGODB_KILL.imports.update( { importID: importDoc.importID, }, @@ -79,7 +79,7 @@ export async function DeleteScore( } } - const userHasOtherScores = await db.scores.findOne({ + const userHasOtherScores = await MONGODB_KILL.scores.findOne({ userID: score.userID, chartID: score.chartID, }); @@ -87,7 +87,7 @@ export async function DeleteScore( if (userHasOtherScores && attemptPBReprocess) { await ProcessPBs(score.game, score.playtype, score.userID, new Set([score.chartID]), log); } else { - await db["personal-bests"].remove({ + await MONGODB_KILL["personal-bests"].remove({ userID: score.userID, chartID: score.chartID, }); @@ -98,14 +98,14 @@ export async function DeleteScore( await UpdateUsersGamePlaytypeStats(score.game, score.playtype, score.userID, null, log); if (blacklist) { - const alreadyBlacklisted = await db["score-blacklist"].findOne({ + const alreadyBlacklisted = await MONGODB_KILL["score-blacklist"].findOne({ userID: score.userID, scoreID: score.scoreID, }); if (!alreadyBlacklisted) { log.info(`Blacklisted ${score.scoreID}.`); - await db["score-blacklist"].insert({ + await MONGODB_KILL["score-blacklist"].insert({ userID: score.userID, scoreID: score.scoreID, score, @@ -120,11 +120,11 @@ export async function DeleteMultipleScores(scores: Array, blackli const scoreIDs = scores.map((e) => e.scoreID); const chartIDs = scores.map((e) => e.chartID); - await db.scores.remove({ + await MONGODB_KILL.scores.remove({ scoreID: { $in: scoreIDs }, }); - const sessions = await db.sessions.find({ + const sessions = await MONGODB_KILL.sessions.find({ scoreIDs: { $in: scoreIDs }, }); @@ -132,13 +132,13 @@ export async function DeleteMultipleScores(scores: Array, blackli // If a session only has one score, then pulling it should kill // the session. if (session.scoreIDs.length === 1) { - await db.sessions.remove({ + await MONGODB_KILL.sessions.remove({ sessionID: session.sessionID, }); } } - await db.sessions.update( + await MONGODB_KILL.sessions.update( { sessionID: { $in: sessions.map((e) => e.sessionID) }, }, @@ -153,20 +153,20 @@ export async function DeleteMultipleScores(scores: Array, blackli ); // remove all sessions that no longer have scores in them. - await db.sessions.remove({ + await MONGODB_KILL.sessions.remove({ sessionID: { $in: sessions.map((e) => e.sessionID) }, scoreIDs: { $size: 0 }, }); await RecalcSessions({ sessionID: { $in: sessions.map((e) => e.sessionID) } }); - const importDoc = await db.imports.findOne({ + const importDoc = await MONGODB_KILL.imports.findOne({ scoreIDs: { $in: scoreIDs }, }); if (importDoc) { // pull all scoreIDs from this import. - await db.imports.update( + await MONGODB_KILL.imports.update( { importID: importDoc.importID, }, @@ -181,14 +181,14 @@ export async function DeleteMultipleScores(scores: Array, blackli ); // remove this import if no scores belong to it anymore. - await db.imports.remove({ + await MONGODB_KILL.imports.remove({ importID: importDoc.importID, scoreIDs: { $size: 0 }, }); } for (const score of scores) { - const userHasOtherScores = await db.scores.findOne({ + const userHasOtherScores = await MONGODB_KILL.scores.findOne({ userID: score.userID, chartID: score.chartID, }); @@ -202,7 +202,7 @@ export async function DeleteMultipleScores(scores: Array, blackli log, ); } else { - await db["personal-bests"].remove({ + await MONGODB_KILL["personal-bests"].remove({ userID: score.userID, chartID: score.chartID, }); @@ -211,14 +211,14 @@ export async function DeleteMultipleScores(scores: Array, blackli } if (blacklist) { - const alreadyBlacklisted = await db["score-blacklist"].findOne({ + const alreadyBlacklisted = await MONGODB_KILL["score-blacklist"].findOne({ userID: score.userID, scoreID: score.scoreID, }); if (!alreadyBlacklisted) { log.info(`Blacklisted ${score.scoreID}.`); - await db["score-blacklist"].insert({ + await MONGODB_KILL["score-blacklist"].insert({ userID: score.userID, scoreID: score.scoreID, score, diff --git a/typescript/server/src/lib/score-mutation/update-isprimary.ts b/typescript/server/src/lib/score-mutation/update-isprimary.ts index 5471bf9aa..eddecbeb6 100644 --- a/typescript/server/src/lib/score-mutation/update-isprimary.ts +++ b/typescript/server/src/lib/score-mutation/update-isprimary.ts @@ -1,23 +1,23 @@ import { TachiConfig } from "#lib/setup/config"; /* eslint-disable no-await-in-loop */ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; export default async function UpdateIsPrimaryStatus() { for (const game of TachiConfig.GAMES) { const chartIDs = ( - await db.anyCharts[game].find({ + await MONGODB_KILL.anyCharts[game].find({ isPrimary: false, }) ).map((e) => e.chartID); - await db.scores.update( + await MONGODB_KILL.scores.update( { chartID: { $in: chartIDs }, }, { $set: { isPrimary: false } }, ); - await db["personal-bests"].update( + await MONGODB_KILL["personal-bests"].update( { chartID: { $in: chartIDs }, }, diff --git a/typescript/server/src/lib/score-mutation/update-score.test.ts b/typescript/server/src/lib/score-mutation/update-score.oldtest.ts similarity index 84% rename from typescript/server/src/lib/score-mutation/update-score.test.ts rename to typescript/server/src/lib/score-mutation/update-score.oldtest.ts index fb25667b1..cce845fd2 100644 --- a/typescript/server/src/lib/score-mutation/update-score.test.ts +++ b/typescript/server/src/lib/score-mutation/update-score.oldtest.ts @@ -1,7 +1,7 @@ import type { ImportDocument, ScoreDocument, SessionDocument } from "tachi-common"; import { CreateScoreID } from "#lib/score-import/framework/score-importing/score-id"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import ResetDBState from "#test-utils/resets"; import { TestingIIDXSPScore } from "#test-utils/test-data"; import deepmerge from "deepmerge"; @@ -52,20 +52,20 @@ t.test("#UpdateScore", (t) => { const newScoreID = CreateScoreID("iidx:SP", score.userID, score, score.chartID); - await db.imports.insert(mockImportDocument); - await db.sessions.insert(mockSessionDocument); + await MONGODB_KILL.imports.insert(mockImportDocument); + await MONGODB_KILL.sessions.insert(mockSessionDocument); // This function doesn't return anything, instead, // we need to check external state. await UpdateScore(TestingIIDXSPScore, score); - const dbScore = await db.scores.findOne({ + const dbScore = await MONGODB_KILL.scores.findOne({ scoreID: "scoreid_1", }); t.equal(dbScore, null, "Should have updated the scoreID from the database."); - const dbNewScore = await db.scores.findOne({ + const dbNewScore = await MONGODB_KILL.scores.findOne({ scoreID: newScoreID, }); @@ -75,7 +75,7 @@ t.test("#UpdateScore", (t) => { "The new score inserted into the database should have the new scoreData.", ); - const dbImport = await db.imports.findOne({ + const dbImport = await MONGODB_KILL.imports.findOne({ importID: "mockImportID", }); @@ -85,7 +85,7 @@ t.test("#UpdateScore", (t) => { "Should update TESTING_SCORE_ID to the new hash.", ); - const dbSession = await db.sessions.findOne({ + const dbSession = await MONGODB_KILL.sessions.findOne({ sessionID: "mockSessionID", }); diff --git a/typescript/server/src/lib/score-mutation/update-score.ts b/typescript/server/src/lib/score-mutation/update-score.ts index 4eb061f78..dadaa0569 100644 --- a/typescript/server/src/lib/score-mutation/update-score.ts +++ b/typescript/server/src/lib/score-mutation/update-score.ts @@ -8,7 +8,7 @@ import { CreateFullScoreData } from "#lib/score-import/framework/score-importing import { CreateScoreID } from "#lib/score-import/framework/score-importing/score-id"; import { GetGPTString, type GPTString, type ScoreDocument } from "tachi-common"; /* eslint-disable no-await-in-loop */ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { UpdateAllPBs } from "#utils/calculations/recalc-scores"; import { FormatUserDoc, GetUserWithID } from "#utils/user"; @@ -46,7 +46,7 @@ export default async function UpdateScore( const chartID = newScore.chartID; - const chart = await db.anyCharts[oldScore.game].findOne({ + const chart = await MONGODB_KILL.anyCharts[oldScore.game].findOne({ chartID, }); @@ -105,7 +105,7 @@ export default async function UpdateScore( delete newScore._id; } - await db.scores.update( + await MONGODB_KILL.scores.update( { scoreID: oldScoreID, }, @@ -116,7 +116,7 @@ export default async function UpdateScore( log.warn( `Score ID ${newScoreID} already existed -- this update caused a collision. Removing old score and updating old references anyway.`, ); - await db.scores.remove({ + await MONGODB_KILL.scores.remove({ scoreID: oldScoreID, }); } @@ -131,13 +131,13 @@ export default async function UpdateScore( return; } - const sessions = await db.sessions.find({ + const sessions = await MONGODB_KILL.sessions.find({ scoreIDs: oldScoreID, }); // another session already has the new score? (i.e. migrating to an already // existing score?) - const existsElsewhere = await db.sessions.findOne({ + const existsElsewhere = await MONGODB_KILL.sessions.findOne({ scoreIDs: newScoreID, }); @@ -166,7 +166,7 @@ export default async function UpdateScore( } } - const scores = await db.scores.find({ + const scores = await MONGODB_KILL.scores.find({ scoreID: { $in: newScoreIDs }, }); @@ -176,7 +176,7 @@ export default async function UpdateScore( scores, ); - await db.sessions.update( + await MONGODB_KILL.sessions.update( { sessionID: session.sessionID, }, @@ -207,14 +207,14 @@ export default async function UpdateScore( } } - const imports = await db.imports.find({ + const imports = await MONGODB_KILL.imports.find({ scoreIDs: oldScoreID, }); log.debug(`Updating ${imports.length} imports.`); for (const importDoc of imports) { - await db.imports.update( + await MONGODB_KILL.imports.update( { importID: importDoc.importID, }, diff --git a/typescript/server/src/lib/search/search.test.ts b/typescript/server/src/lib/search/search.oldtest.ts similarity index 91% rename from typescript/server/src/lib/search/search.test.ts rename to typescript/server/src/lib/search/search.oldtest.ts index 9cb8621f3..57f25a926 100644 --- a/typescript/server/src/lib/search/search.test.ts +++ b/typescript/server/src/lib/search/search.oldtest.ts @@ -1,4 +1,4 @@ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import ResetDBState from "#test-utils/resets"; import { LoadTachiIIDXData } from "#test-utils/test-data"; import t from "tap"; @@ -9,10 +9,10 @@ t.test("#SearchSpecificGameSongs", (t) => { t.beforeEach(ResetDBState); t.beforeEach(LoadTachiIIDXData); t.beforeEach(async () => { - await db.songs.iidx.dropIndexes(); + await MONGODB_KILL.songs.iidx.dropIndexes(); // Casting here is due to a known bug with monk. - await db.songs.iidx.createIndex({ + await MONGODB_KILL.songs.iidx.createIndex({ title: "text", artist: "text", altTitles: "text", diff --git a/typescript/server/src/lib/search/search.ts b/typescript/server/src/lib/search/search.ts index d83a0f902..91c9454f0 100644 --- a/typescript/server/src/lib/search/search.ts +++ b/typescript/server/src/lib/search/search.ts @@ -1,11 +1,14 @@ import type { FilterQuery } from "mongodb"; import type { ICollection } from "monk"; +import { SELECT_USER, ToUserDocument } from "#lib/db-formats/user.js"; import { log } from "#lib/log/log.js"; import { TachiConfig } from "#lib/setup/config"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; +import DB from "#services/pg/db.js"; import { GetSongForIDGuaranteed } from "#utils/db"; -import { EscapeStringRegexp } from "#utils/misc"; +import { EscapeForILIKE } from "#utils/misc"; +import { UnixMillisecondsToISO8601 } from "#utils/time.js"; import { GetOnlineCutoff } from "#utils/user"; import { type ChartDocument, @@ -39,7 +42,7 @@ const SEARCH_CONTROLS = { quests: { keys: ["name"], primary: "questID" }, users: { keys: ["username"], primary: "id" }, folders: { keys: ["title", "searchTerms"], primary: "folderID" }, -} satisfies Partial>; +} satisfies Partial>; interface SearchData { primaryKey: number | string; @@ -159,7 +162,7 @@ export function SearchSpecificGameSongs( search: string, limit = 100, ): Promise> { - return SearchCollection(db.anySongs[game], search, "songs", {}, limit); + return SearchCollection(MONGODB_KILL.anySongs[game], search, "songs", {}, limit); } export async function SearchSpecificGameSongsAndCharts( @@ -178,7 +181,7 @@ export async function SearchSpecificGameSongsAndCharts( chartQuery.playtype = playtype; } - const charts = (await db.anyCharts[game].find(chartQuery)) as Array; + const charts = (await MONGODB_KILL.anyCharts[game].find(chartQuery)) as Array; return { songs, charts }; } @@ -202,23 +205,24 @@ export async function SearchGlobalGameSongsAndCharts( chartQuery.playtype = playtype; } - const charts = (await db.anyCharts[game].find(chartQuery)) as unknown as Array< + const charts = (await MONGODB_KILL.anyCharts[game].find(chartQuery)) as unknown as Array< { __playcount: integer } & ChartDocument >; - const playcounts: Array<{ _id: string; playcount: integer }> = await db.scores.aggregate([ - { - $match: { - chartID: { $in: charts.map((e) => e.chartID) }, + const playcounts: Array<{ _id: string; playcount: integer }> = + await MONGODB_KILL.scores.aggregate([ + { + $match: { + chartID: { $in: charts.map((e) => e.chartID) }, + }, }, - }, - { - $group: { - _id: "$chartID", - playcount: { $sum: 1 }, + { + $group: { + _id: "$chartID", + playcount: { $sum: 1 }, + }, }, - }, - ]); + ]); const playcountLookup = Object.fromEntries(playcounts.map((e) => [e._id, e.playcount])); const songMap = CreateSongMap(songs); @@ -264,7 +268,7 @@ export function SearchSessions( baseMatch.userID = userID; } - return SearchCollection(db.sessions, search, "sessions", baseMatch, limit); + return SearchCollection(MONGODB_KILL.sessions, search, "sessions", baseMatch, limit); } /** @@ -275,20 +279,26 @@ export function SearchSessions( * aren't allowed spaces in their name. In short, $text is very * poor at actually matching usernames. */ -export function SearchUsersRegExp(search: string, matchOnline = false) { - const regexEsc = EscapeStringRegexp(search.toLowerCase()); +export function SearchUsersRegExp( + search: string, + matchOnline = false, +): Promise> { + const likeEsc = EscapeForILIKE(search.toLowerCase()); - const matchQuery: FilterQuery = { - usernameLowercase: { $regex: new RegExp(regexEsc, "u") }, - }; + const onlineCutoff = UnixMillisecondsToISO8601(GetOnlineCutoff()); + + let q = DB.selectFrom("account") + .select(SELECT_USER) + .where("normalized_username", "like", `%${likeEsc}%`); if (matchOnline) { - matchQuery.lastSeen = { $gt: GetOnlineCutoff() }; + q = q.where("last_seen", ">", onlineCutoff); } - return db.users.find(matchQuery, { - limit: 25, - }); + return q + .limit(25) + .execute() + .then((res) => res.map(ToUserDocument)); } /** @@ -352,10 +362,14 @@ export async function SearchGamesSongsCharts(search: string, gpts: Array, limit?: integer, ) { - return SearchCollection(db.folders, search, "folders", existingMatch, limit); -} - -function AddGamePropToSong( - songDocument: SongDocument, - game: GameGroup, -): { game: GameGroup } & SongDocument { - // @ts-expect-error Yep, that's intentional. - songDocument.game = game; - - return songDocument as { game: GameGroup } & SongDocument; + return SearchCollection(MONGODB_KILL.folders, search, "folders", existingMatch, limit); } diff --git a/typescript/server/src/lib/showcase/evaluator.test.ts b/typescript/server/src/lib/showcase/evaluator.oldtest.ts similarity index 91% rename from typescript/server/src/lib/showcase/evaluator.test.ts rename to typescript/server/src/lib/showcase/evaluator.oldtest.ts index 52536bc9a..ffcddf00e 100644 --- a/typescript/server/src/lib/showcase/evaluator.test.ts +++ b/typescript/server/src/lib/showcase/evaluator.oldtest.ts @@ -1,4 +1,4 @@ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import ResetDBState from "#test-utils/resets"; import { Testing511SPA, TestingIIDXFolderSP10, TestingIIDXSPScorePB } from "#test-utils/test-data"; import { CreateFolderChartLookup } from "#utils/folder"; @@ -17,7 +17,8 @@ t.test("#EvaluateShowcaseStat", (t) => { await CreateFolderChartLookup(TestingIIDXFolderSP10, true); }); t.beforeEach( - async () => await db["personal-bests"].insert(deepmerge(TestingIIDXSPScorePB, {})), + async () => + await MONGODB_KILL["personal-bests"].insert(deepmerge(TestingIIDXSPScorePB, {})), ); t.test("Should evaluate a folder stat.", async (t) => { diff --git a/typescript/server/src/lib/showcase/evaluator.ts b/typescript/server/src/lib/showcase/evaluator.ts index 1ef07e91d..11e90daa3 100644 --- a/typescript/server/src/lib/showcase/evaluator.ts +++ b/typescript/server/src/lib/showcase/evaluator.ts @@ -1,4 +1,4 @@ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetFolderChartIDs } from "#utils/folder"; import { GetGPTConfig, @@ -36,12 +36,12 @@ async function EvaluateShowcaseChartStat( ) { // requires special handling if (details.metric === "playcount") { - return { value: await db.scores.count({ chartID: details.chartID, userID }) }; + return { value: await MONGODB_KILL.scores.count({ chartID: details.chartID, userID }) }; } const mongoProp = PropToMongoProp(gpt, details.metric); - const pb = await db["personal-bests"].findOne( + const pb = await MONGODB_KILL["personal-bests"].findOne( { chartID: details.chartID, userID }, { projection: { [mongoProp]: 1 } }, ); @@ -84,7 +84,7 @@ async function EvaluateShowcaseFolderStat( const mongoProp = PropToMongoProp(gpt, details.metric); - const value = await db["personal-bests"].count({ + const value = await MONGODB_KILL["personal-bests"].count({ userID, // @optimisable - This is slightly inefficent, maybe we can use relational-style querying? diff --git a/typescript/server/src/lib/showcase/get-related.ts b/typescript/server/src/lib/showcase/get-related.ts index 900290cce..4594425e1 100644 --- a/typescript/server/src/lib/showcase/get-related.ts +++ b/typescript/server/src/lib/showcase/get-related.ts @@ -1,19 +1,19 @@ import type { GameGroup, ShowcaseStatDetails } from "tachi-common"; import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; export async function GetRelatedStatDocuments(stat: ShowcaseStatDetails, game: GameGroup) { switch (stat.mode) { case "chart": { - const chart = await db.anyCharts[game].findOne({ chartID: stat.chartID }); + const chart = await MONGODB_KILL.anyCharts[game].findOne({ chartID: stat.chartID }); if (!chart) { log.error({ stat }, `This stat refers to a chart that does not exist?`); throw new Error(`Stat refers to a chart that does not exist? ${stat.chartID}.`); } - const song = await db.anySongs[game].findOne({ id: chart.songID }); + const song = await MONGODB_KILL.anySongs[game].findOne({ id: chart.songID }); if (!song) { log.error({ chart }, `Song-Chart Mismatch - ${chart.songID}.`); @@ -32,7 +32,7 @@ export async function GetRelatedStatDocuments(stat: ShowcaseStatDetails, game: G throw new Error(`Legacy FolderIDs used in showcase stat.`); } - const folder = await db.folders.findOne({ + const folder = await MONGODB_KILL.folders.findOne({ folderID: stat.folderID, }); diff --git a/typescript/server/src/lib/showcase/get-stats.test.ts b/typescript/server/src/lib/showcase/get-stats.oldtest.ts similarity index 86% rename from typescript/server/src/lib/showcase/get-stats.test.ts rename to typescript/server/src/lib/showcase/get-stats.oldtest.ts index 1daf91eba..69d822c35 100644 --- a/typescript/server/src/lib/showcase/get-stats.test.ts +++ b/typescript/server/src/lib/showcase/get-stats.oldtest.ts @@ -1,4 +1,4 @@ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import ResetDBState from "#test-utils/resets"; import { Testing511SPA, TestingIIDXFolderSP10, TestingIIDXSPScorePB } from "#test-utils/test-data"; import { CreateFolderChartLookup } from "#utils/folder"; @@ -12,12 +12,12 @@ t.test("#EvalulateUsersStatsShowcase", (t) => { t.beforeEach(ResetDBState); t.beforeEach(async () => { - await db.folders.insert(TestingIIDXFolderSP10); + await MONGODB_KILL.folders.insert(TestingIIDXFolderSP10); await CreateFolderChartLookup(TestingIIDXFolderSP10, true); - await db["game-settings"].remove({}); + await MONGODB_KILL["game-settings"].remove({}); - await db["game-settings"].insert({ + await MONGODB_KILL["game-settings"].insert({ userID: 1, game: "iidx", playtype: "SP", @@ -49,7 +49,7 @@ t.test("#EvalulateUsersStatsShowcase", (t) => { rivals: [], }); - await db["personal-bests"].insert(deepmerge(TestingIIDXSPScorePB, {})); + await MONGODB_KILL["personal-bests"].insert(deepmerge(TestingIIDXSPScorePB, {})); }); t.test("Should evaluate a user's preferred stats.", async (t) => { @@ -89,7 +89,7 @@ t.test("#EvalulateUsersStatsShowcase", (t) => { }); t.test("Should throw an error if the user does not have game-settings.", async (t) => { - await db["game-settings"].remove({}); + await MONGODB_KILL["game-settings"].remove({}); t.rejects(() => EvaluateUsersStatsShowcase(1, "iidx", "SP")); @@ -97,7 +97,7 @@ t.test("#EvalulateUsersStatsShowcase", (t) => { }); t.test("Should project another users stats onto the given user if set.", async (t) => { - await db["personal-bests"].insert( + await MONGODB_KILL["personal-bests"].insert( deepmerge(TestingIIDXSPScorePB, { userID: 2, scoreData: { diff --git a/typescript/server/src/lib/showcase/get-stats.ts b/typescript/server/src/lib/showcase/get-stats.ts index 266753087..b7e9ccb20 100644 --- a/typescript/server/src/lib/showcase/get-stats.ts +++ b/typescript/server/src/lib/showcase/get-stats.ts @@ -1,5 +1,5 @@ import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { type GameGroup, GetGPTString, @@ -24,7 +24,7 @@ export async function EvaluateUsersStatsShowcase( projectUserStats?: integer, ) { const getSettingsID = projectUserStats ?? userID; - const settings = await db["game-settings"].findOne({ + const settings = await MONGODB_KILL["game-settings"].findOne({ userID: getSettingsID, game, playtype, diff --git a/typescript/server/src/lib/showcase/showcase.test.ts b/typescript/server/src/lib/showcase/showcase.oldtest.ts similarity index 88% rename from typescript/server/src/lib/showcase/showcase.test.ts rename to typescript/server/src/lib/showcase/showcase.oldtest.ts index a45978129..f6ea37667 100644 --- a/typescript/server/src/lib/showcase/showcase.test.ts +++ b/typescript/server/src/lib/showcase/showcase.oldtest.ts @@ -1,4 +1,4 @@ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { mkFakeGameSettings } from "#test-utils/misc"; import ResetDBState from "#test-utils/resets"; import t from "tap"; @@ -8,8 +8,8 @@ import { RemoveStaleFolderShowcaseStats } from "./showcase"; t.test("#RemoveStaleFolderShowcaseStats", (t) => { t.beforeEach(ResetDBState); t.beforeEach(async () => { - await db["game-settings"].remove({}); - await db["game-settings"].insert([ + await MONGODB_KILL["game-settings"].remove({}); + await MONGODB_KILL["game-settings"].insert([ mkFakeGameSettings(1, "iidx", "SP", { preferences: { stats: [ @@ -56,7 +56,7 @@ t.test("#RemoveStaleFolderShowcaseStats", (t) => { t.test("Should remove folder stats that refer to unused folders.", async (t) => { await RemoveStaleFolderShowcaseStats(["REMOVED_FOLDER"]); - const dbResult = await db["game-settings"].find(); + const dbResult = await MONGODB_KILL["game-settings"].find(); t.strictSame( dbResult.map((e) => e.preferences.stats), diff --git a/typescript/server/src/lib/showcase/showcase.ts b/typescript/server/src/lib/showcase/showcase.ts index 6031e0743..c148bcbc4 100644 --- a/typescript/server/src/lib/showcase/showcase.ts +++ b/typescript/server/src/lib/showcase/showcase.ts @@ -1,11 +1,11 @@ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; /** * When a folder is removed, any showcase stats referring to that folder * need to be changed aswell. */ export function RemoveStaleFolderShowcaseStats(removedFolderIDs: Array) { - return db["game-settings"].update( + return MONGODB_KILL["game-settings"].update( {}, { $pull: { diff --git a/typescript/server/src/lib/targets/goal-utils.ts b/typescript/server/src/lib/targets/goal-utils.ts index bafd87819..be8cb63c1 100644 --- a/typescript/server/src/lib/targets/goal-utils.ts +++ b/typescript/server/src/lib/targets/goal-utils.ts @@ -1,7 +1,7 @@ import type { GoalCriteriaFormatter } from "#game-implementations/types"; import { GPT_SERVER_IMPLEMENTATIONS } from "#game-implementations/game-implementations"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetFolderForIDGuaranteed, HumaniseChartID } from "#utils/db"; import { GetFolderChartIDs } from "#utils/folder"; import { HumanisedJoinArray, OnlyFloatToDP } from "#utils/misc"; @@ -186,7 +186,7 @@ export async function ValidateGoalChartsAndCriteria( switch (charts.type) { case "single": { - const chart = await db.anyCharts[game].findOne({ + const chart = await MONGODB_KILL.anyCharts[game].findOne({ playtype, chartID: charts.data, }); @@ -202,7 +202,7 @@ export async function ValidateGoalChartsAndCriteria( } case "folder": { - const folder = await db.folders.findOne({ + const folder = await MONGODB_KILL.folders.findOne({ game, playtype, folderID: charts.data, @@ -225,7 +225,7 @@ export async function ValidateGoalChartsAndCriteria( ); } - const multiCharts = await db.anyCharts[game].find({ + const multiCharts = await MONGODB_KILL.anyCharts[game].find({ playtype, chartID: { $in: charts.data }, }); @@ -297,7 +297,7 @@ export async function ValidateGoalChartsAndCriteria( let err; if (config.chartDependentMax) { - const chart = await db.anyCharts[game].findOne({ + const chart = await MONGODB_KILL.anyCharts[game].findOne({ playtype, // guaranteed by previous if statement chartID: charts.data as string, diff --git a/typescript/server/src/lib/targets/goals.test.ts b/typescript/server/src/lib/targets/goals.oldtest.ts similarity index 86% rename from typescript/server/src/lib/targets/goals.test.ts rename to typescript/server/src/lib/targets/goals.oldtest.ts index cbc580282..a0af7ba37 100644 --- a/typescript/server/src/lib/targets/goals.test.ts +++ b/typescript/server/src/lib/targets/goals.oldtest.ts @@ -1,5 +1,5 @@ import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { dmf, mkFakePBIIDXSP } from "#test-utils/misc"; import ResetDBState from "#test-utils/resets"; import { @@ -31,7 +31,7 @@ t.test("#EvaluateGoalForUser", (t) => { t.test("Should correctly evaluate against single goals.", (t) => { t.beforeEach(async () => { - await db["personal-bests"].insert(TestingIIDXSPScorePB); + await MONGODB_KILL["personal-bests"].insert(TestingIIDXSPScorePB); // @ts-expect-error why does mongodb feel like mutating our documents! delete TestingIIDXSPScorePB._id; @@ -56,7 +56,7 @@ t.test("#EvaluateGoalForUser", (t) => { }); t.test("Should correctly evaluate goals if user fails.", async (t) => { - await db["personal-bests"].update( + await MONGODB_KILL["personal-bests"].update( { userID: 1, chartID: Testing511SPA.chartID }, { $set: { @@ -84,7 +84,7 @@ t.test("#EvaluateGoalForUser", (t) => { }); t.test("Should correctly evaluate goals when user has no score.", async (t) => { - await db["personal-bests"].remove({}); + await MONGODB_KILL["personal-bests"].remove({}); const res = await EvaluateGoalForUser(HC511Goal, 1, log); @@ -116,9 +116,9 @@ t.test("#EvaluateGoalForUser", (t) => { }); t.beforeEach(async () => { - await db["personal-bests"].insert(TestingIIDXSPScorePB); + await MONGODB_KILL["personal-bests"].insert(TestingIIDXSPScorePB); - await db["personal-bests"].insert( + await MONGODB_KILL["personal-bests"].insert( dmf(TestingIIDXSPScorePB, { scoreData: { lamp: "CLEAR", enumIndexes: { lamp: 4 } }, chartID: "fake_other_chart_id", @@ -145,11 +145,11 @@ t.test("#EvaluateGoalForUser", (t) => { }); t.test("Should work if other chart is >= HARD CLEAR", async (t) => { - await db["personal-bests"].update( + await MONGODB_KILL["personal-bests"].update( { userID: 1, chartID: Testing511SPA.chartID }, { $set: { "scoreData.lamp": "CLEAR", "scoreData.enumIndexes.lamp": 4 } }, ); - await db["personal-bests"].update( + await MONGODB_KILL["personal-bests"].update( { userID: 1, chartID: "fake_other_chart_id" }, { $set: { "scoreData.lamp": "HARD CLEAR", "scoreData.enumIndexes.lamp": 5 } }, ); @@ -172,7 +172,7 @@ t.test("#EvaluateGoalForUser", (t) => { }); t.test("Should work if user has no scores", async (t) => { - await db["personal-bests"].remove({}); + await MONGODB_KILL["personal-bests"].remove({}); const res = await EvaluateGoalForUser(multiGoal, 1, log); @@ -204,16 +204,16 @@ t.test("#EvaluateGoalForUser", (t) => { }); t.beforeEach(async () => { - await db["personal-bests"].insert(TestingIIDXSPScorePB); + await MONGODB_KILL["personal-bests"].insert(TestingIIDXSPScorePB); - await db["personal-bests"].insert( + await MONGODB_KILL["personal-bests"].insert( dmf(TestingIIDXSPScorePB, { scoreData: { lamp: "CLEAR", enumIndexes: { lamp: 4 } }, chartID: "other_sp10", }), ); - await db.charts.iidx.insert([ + await MONGODB_KILL.charts.iidx.insert([ dmf(Testing511SPA, { songID: 123, level: "9", @@ -250,12 +250,12 @@ t.test("#EvaluateGoalForUser", (t) => { }); t.test("Should work if other chart is >= HARD CLEAR", async (t) => { - await db["personal-bests"].update( + await MONGODB_KILL["personal-bests"].update( { userID: 1, chartID: Testing511SPA.chartID }, { $set: { "scoreData.lamp": "CLEAR", "scoreData.enumIndexes.lamp": 4 } }, ); - await db["personal-bests"].update( + await MONGODB_KILL["personal-bests"].update( { userID: 1, chartID: "other_sp10" }, { $set: { "scoreData.lamp": "HARD CLEAR", "scoreData.enumIndexes.lamp": 5 } }, ); @@ -278,7 +278,7 @@ t.test("#EvaluateGoalForUser", (t) => { }); t.test("Should work if user has no scores", async (t) => { - await db["personal-bests"].remove({}); + await MONGODB_KILL["personal-bests"].remove({}); const res = await EvaluateGoalForUser(folderGoal, 1, log); @@ -369,10 +369,10 @@ t.test("#GetRelevantFolderGoals", (t) => { folderID: "foo", }); - await db.folders.insert(TestingIIDXFolderSP10); - await db.folders.insert(sp11folder); - await db.goals.insert(fakeFolderGoalDocument); - await db.goals.insert(notFolderGoalDocument); + await MONGODB_KILL.folders.insert(TestingIIDXFolderSP10); + await MONGODB_KILL.folders.insert(sp11folder); + await MONGODB_KILL.goals.insert(fakeFolderGoalDocument); + await MONGODB_KILL.goals.insert(notFolderGoalDocument); await CreateFolderChartLookup(TestingIIDXFolderSP10, true); await CreateFolderChartLookup(sp11folder, true); }); @@ -399,18 +399,18 @@ t.test("#GetRelevantGoals", (t) => { t.beforeEach(ResetDBState); t.beforeEach(async () => { // use the real data so we have enough charts loaded for this to test properly. - await db.songs.iidx.remove({}); - await db.charts.iidx.remove({}); - await db.charts.iidx.insert( + await MONGODB_KILL.songs.iidx.remove({}); + await MONGODB_KILL.charts.iidx.remove({}); + await MONGODB_KILL.charts.iidx.insert( GetKTDataJSON("./tachi/tachi-charts-iidx.json") as Array< ChartDocument<"iidx:DP" | "iidx:SP"> >, ); - await db.songs.iidx.insert( + await MONGODB_KILL.songs.iidx.insert( GetKTDataJSON("./tachi/tachi-songs-iidx.json") as Array>, ); - const lotsOfCharts = await db.charts.iidx.find({}, { limit: 20 }); + const lotsOfCharts = await MONGODB_KILL.charts.iidx.find({}, { limit: 20 }); const goals: Array = lotsOfCharts.map((e) => ({ charts: { type: "single", @@ -427,9 +427,9 @@ t.test("#GetRelevantGoals", (t) => { }, })); - await db.goals.insert(goals); + await MONGODB_KILL.goals.insert(goals); - await db["goal-subs"].insert( + await MONGODB_KILL["goal-subs"].insert( goals.map((e) => ({ achieved: false, wasInstantlyAchieved: false, @@ -454,8 +454,8 @@ t.test("#GetRelevantGoals", (t) => { // lets pretend our session had scores on charts 0->4 and 20->25. We also only have goals on // charts 1->20, so only 5 of these should resolve. const ourCharts = [ - ...(await db.charts.iidx.find({}, { limit: 5 })), - ...(await db.charts.iidx.find({}, { skip: 20, limit: 5 })), + ...(await MONGODB_KILL.charts.iidx.find({}, { limit: 5 })), + ...(await MONGODB_KILL.charts.iidx.find({}, { skip: 20, limit: 5 })), ]; const chartIDs = new Set(ourCharts.map((e) => e.chartID)); diff --git a/typescript/server/src/lib/targets/goals.ts b/typescript/server/src/lib/targets/goals.ts index d6f8d2996..432158c6a 100644 --- a/typescript/server/src/lib/targets/goals.ts +++ b/typescript/server/src/lib/targets/goals.ts @@ -4,7 +4,7 @@ import type { FilterQuery } from "mongodb"; import { GPT_SERVER_IMPLEMENTATIONS } from "#game-implementations/game-implementations"; import { SubscribeFailReasons } from "#lib/constants/err-codes"; import { type KtLogger, log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetFolderChartIDs } from "#utils/folder"; import fjsh from "fast-json-stable-hash"; import { @@ -93,7 +93,7 @@ export async function EvaluateGoalForUser( switch (goal.criteria.mode) { case "single": { - const res = await db["personal-bests"].findOne(scoreQuery); + const res = await MONGODB_KILL["personal-bests"].findOne(scoreQuery); const outOfHuman = HumaniseGoalOutOf(gptString, goal.criteria.key, goal.criteria.value); @@ -126,7 +126,7 @@ export async function EvaluateGoalForUser( chartID: { $in: chartIDs }, }; - const nextBestScore = await db["personal-bests"].findOne(nextBestQuery, { + const nextBestScore = await MONGODB_KILL["personal-bests"].findOne(nextBestQuery, { sort: { [scoreDataKey]: -1 }, }); @@ -176,7 +176,7 @@ export async function EvaluateGoalForUser( count = Math.floor(goal.criteria.countNum * totalChartCount); } - const userCount = await db["personal-bests"].count(scoreQuery); + const userCount = await MONGODB_KILL["personal-bests"].count(scoreQuery); return { achieved: userCount >= count, @@ -338,14 +338,14 @@ export async function SubscribeToGoal( goalDocument: GoalDocument, isStandaloneAssignment: boolean, ) { - const goalExists = await db.goals.findOne({ goalID: goalDocument.goalID }); + const goalExists = await MONGODB_KILL.goals.findOne({ goalID: goalDocument.goalID }); if (!goalExists) { - await db.goals.insert(goalDocument); + await MONGODB_KILL.goals.insert(goalDocument); log.info(`Inserting new goal '${goalDocument.name}'.`); } - const userAlreadySubscribed = await db["goal-subs"].findOne({ + const userAlreadySubscribed = await MONGODB_KILL["goal-subs"].findOne({ userID, goalID: goalDocument.goalID, }); @@ -365,7 +365,7 @@ export async function SubscribeToGoal( // otherwise, this is a standalone assignment to a goal that was already assigned // as a consequence of a quest. Mark it as standalone - await db["goal-subs"].update( + await MONGODB_KILL["goal-subs"].update( { userID, goalID: goalDocument.goalID, @@ -411,13 +411,13 @@ export async function SubscribeToGoal( wasAssignedStandalone: isStandaloneAssignment, }; - await db["goal-subs"].insert(goalSub); + await MONGODB_KILL["goal-subs"].insert(goalSub); return goalSub; } export function GetQuestsThatContainGoal(goalID: string) { - return db.quests.find({ + return MONGODB_KILL.quests.find({ "questData.goals.goalID": goalID, }); } @@ -432,41 +432,40 @@ export function GetQuestsThatContainGoal(goalID: string) { export async function GetQuestSubsWhichDependOnThisGoalSub( goalSub: GoalSubscriptionDocument, ): Promise> { - const dependencies: Array<{ quest: QuestDocument } & QuestSubscriptionDocument> = await db[ - "quest-subs" - ].aggregate([ - { - // find all quests that this user is subscribed to - $match: { - userID: goalSub.userID, - game: goalSub.game, - playtype: goalSub.playtype, + const dependencies: Array<{ quest: QuestDocument } & QuestSubscriptionDocument> = + await MONGODB_KILL["quest-subs"].aggregate([ + { + // find all quests that this user is subscribed to + $match: { + userID: goalSub.userID, + game: goalSub.game, + playtype: goalSub.playtype, + }, }, - }, - { - // look up the parent quests - $lookup: { - from: "quests", - localField: "questID", - foreignField: "questID", - as: "parentQuestSubs", + { + // look up the parent quests + $lookup: { + from: "quests", + localField: "questID", + foreignField: "questID", + as: "parentQuestSubs", + }, }, - }, - { - // then project it onto the $quest field. This will be null - // if the quest has no parent, which we hopefully won't have - // to consider (illegal) - $set: { - quest: { $arrayElemAt: ["$parentQuestSubs", 0] }, + { + // then project it onto the $quest field. This will be null + // if the quest has no parent, which we hopefully won't have + // to consider (illegal) + $set: { + quest: { $arrayElemAt: ["$parentQuestSubs", 0] }, + }, }, - }, - { - // then finally, filter to only quests that pertain to this goal. - $match: { - "quest.questData.goals.goalID": goalSub.goalID, + { + // then finally, filter to only quests that pertain to this goal. + $match: { + "quest.questData.goals.goalID": goalSub.goalID, + }, }, - }, - ]); + ]); return dependencies; } @@ -507,7 +506,7 @@ export async function UnsubscribeFromGoal( } // if we have no reason to prevent the removal, remove it. - await db["goal-subs"].remove({ + await MONGODB_KILL["goal-subs"].remove({ userID: goalSub.userID, goalID: goalSub.goalID, }); @@ -553,7 +552,7 @@ export async function UnsubscribeFromOrphanedGoalSubs( game: GameGroup, playtype: Playtype, ) { - const goalSubs = await db["goal-subs"].find({ game, playtype, userID }); + const goalSubs = await MONGODB_KILL["goal-subs"].find({ game, playtype, userID }); const maybeToRemove = await Promise.all( goalSubs.map(async (goalSub) => { @@ -578,7 +577,7 @@ export async function UnsubscribeFromOrphanedGoalSubs( )} as they were orphanned.`, ); - await db["goal-subs"].remove({ + await MONGODB_KILL["goal-subs"].remove({ userID, goalID: { $in: toRemove }, }); @@ -612,7 +611,7 @@ export async function GetRelevantGoals( gsQuery.achieved = false; } - const goalSubs = await db["goal-subs"].find(gsQuery); + const goalSubs = await MONGODB_KILL["goal-subs"].find(gsQuery); log.debug(`Found user has ${goalSubs.length} goals.`); @@ -630,7 +629,7 @@ export async function GetRelevantGoals( const promises = [ // this gets the relevantGoals for direct and multi - db.goals.find({ + MONGODB_KILL.goals.find({ "charts.type": { $in: ["single", "multi"] }, "charts.data": { $in: chartIDsArr }, goalID: { $in: goalIDs }, @@ -669,7 +668,7 @@ export async function GetRelevantFolderGoals(goalIDs: Array, chartIDsArr // it's weird to do this in mongodb, but this seems like the right // way to actually handle this. - const result: Array = await db.goals.aggregate([ + const result: Array = await MONGODB_KILL.goals.aggregate([ { $match: { "charts.type": "folder", @@ -715,7 +714,7 @@ export async function EditGoal(oldGoal: GoalDocument, newGoal: GoalDocument) { newGoal.goalID = newGoalID; - await db["goal-subs"].update( + await MONGODB_KILL["goal-subs"].update( { goalID: oldGoal.goalID, }, @@ -748,7 +747,7 @@ export async function EditGoal(oldGoal: GoalDocument, newGoal: GoalDocument) { }); } - await db.quests.update( + await MONGODB_KILL.quests.update( { questID: quest.questID }, { $set: { questData: newQuestData }, @@ -756,7 +755,7 @@ export async function EditGoal(oldGoal: GoalDocument, newGoal: GoalDocument) { ); } - await db.goals.remove({ goalID: oldGoal.goalID }); + await MONGODB_KILL.goals.remove({ goalID: oldGoal.goalID }); newGoal.name = await CreateGoalName( newGoal.charts, @@ -765,7 +764,7 @@ export async function EditGoal(oldGoal: GoalDocument, newGoal: GoalDocument) { newGoal.playtype, ); try { - await db.goals.insert(newGoal); + await MONGODB_KILL.goals.insert(newGoal); } catch (err) { log.info(`Goal ${newGoal.name} already existed.`); } diff --git a/typescript/server/src/lib/targets/quests.ts b/typescript/server/src/lib/targets/quests.ts index e13b4ace6..04869cc27 100644 --- a/typescript/server/src/lib/targets/quests.ts +++ b/typescript/server/src/lib/targets/quests.ts @@ -11,7 +11,7 @@ import type { import { SubscribeFailReasons } from "#lib/constants/err-codes"; import { log } from "#lib/log/log.js"; import { BulkSendNotification } from "#lib/notifications/notifications"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { type EvaluatedGoalReturn, @@ -37,7 +37,7 @@ export function GetGoalIDsFromQuest(quest: QuestDocument) { export async function GetGoalsInQuest(quest: QuestDocument) { const goalIDs = GetGoalIDsFromQuest(quest); - const goals = await db.goals.find({ + const goals = await MONGODB_KILL.goals.find({ goalID: { $in: goalIDs }, }); @@ -68,7 +68,7 @@ export async function GetGoalsInQuest(quest: QuestDocument) { export async function GetGoalsInQuests(quests: Array) { const goalIDs = quests.flatMap((quest) => GetGoalIDsFromQuest(quest)); - const goals = await db.goals.find({ + const goals = await MONGODB_KILL.goals.find({ goalID: { $in: goalIDs }, }); @@ -98,7 +98,7 @@ type EvaluatedGoalResult = { goalID: string } & EvaluatedGoalReturn; export async function EvaluateQuestProgress(userID: integer, quest: QuestDocument) { const goals = await GetGoalsInQuest(quest); - const isSubscribedToQuest = await db["quest-subs"].findOne({ + const isSubscribedToQuest = await MONGODB_KILL["quest-subs"].findOne({ questID: quest.questID, userID, }); @@ -108,7 +108,7 @@ export async function EvaluateQuestProgress(userID: integer, quest: QuestDocumen const goalSubMap = new Map(); if (isSubscribedToQuest) { - const goalSubs = await db["goal-subs"].find({ + const goalSubs = await MONGODB_KILL["goal-subs"].find({ goalID: { $in: goals.map((e) => e.goalID) }, userID, }); @@ -228,7 +228,7 @@ export async function SubscribeToQuest( | SubscribeFailReasons.ALREADY_ACHIEVED | SubscribeFailReasons.ALREADY_SUBSCRIBED > { - const isSubscribedToQuest = await db["quest-subs"].findOne({ + const isSubscribedToQuest = await MONGODB_KILL["quest-subs"].findOne({ userID, questID: quest.questID, }); @@ -263,7 +263,7 @@ export async function SubscribeToQuest( // evaluating goals is fairly cheap though. await Promise.all(result.goals.map((goal) => SubscribeToGoal(userID, goal, false))); - await db["quest-subs"].insert(questSub); + await MONGODB_KILL["quest-subs"].insert(questSub); log.info(`User ${userID} subscribed to '${quest.name}'.`); @@ -282,14 +282,14 @@ export async function SubscribeToQuest( export async function UpdateQuestSubscriptions(questID: string) { log.info(`Received update-subscribe call to quest ${questID}.`); - const subscriptions = await db["quest-subs"].find({ questID }); + const subscriptions = await MONGODB_KILL["quest-subs"].find({ questID }); - const maybeQuest = await db.quests.findOne({ questID }); + const maybeQuest = await MONGODB_KILL.quests.findOne({ questID }); // if the quest was deleted, we have to take a more manual approach. if (!maybeQuest) { // first, remove all subs to this quest - await db["quest-subs"].remove({ + await MONGODB_KILL["quest-subs"].remove({ questID, }); @@ -342,12 +342,12 @@ export async function UnsubscribeFromQuest( // remove the quest sub // (preventing HAS_QUEST_DEPENDENCIES when this is the quest we're removing anyway) - await db["quest-subs"].remove({ + await MONGODB_KILL["quest-subs"].remove({ questID: questSub.questID, userID: questSub.userID, }); - const goalSubs = await db["goal-subs"].find({ + const goalSubs = await MONGODB_KILL["goal-subs"].find({ userID: questSub.userID, goalID: { $in: goalIDs }, }); @@ -367,7 +367,7 @@ export async function GetParentQuests( playtype: Playtype, goalSubs: Array, ) { - const questSubs: Array<{ questID: string }> = await db["quest-subs"].find( + const questSubs: Array<{ questID: string }> = await MONGODB_KILL["quest-subs"].find( { game, playtype, @@ -382,7 +382,7 @@ export async function GetParentQuests( const questSubIDs = questSubs.map((e) => e.questID); - const quests = await db.quests.find({ + const quests = await MONGODB_KILL.quests.find({ questID: { $in: questSubIDs }, "questData.goals.goalID": { $in: goalSubs.map((e) => e.goalID) }, }); @@ -394,7 +394,7 @@ export async function GetParentQuests( * Find all quests not in any questlines. */ export async function FindStandaloneQuests(game: GameGroup, playtype: Playtype) { - const res: Array = await db.quests.aggregate([ + const res: Array = await MONGODB_KILL.quests.aggregate([ { $match: { game, diff --git a/typescript/server/src/lib/webhooks/webhooks.ts b/typescript/server/src/lib/webhooks/webhooks.ts index 5ca881c03..4f21c5ad4 100644 --- a/typescript/server/src/lib/webhooks/webhooks.ts +++ b/typescript/server/src/lib/webhooks/webhooks.ts @@ -1,12 +1,12 @@ import type { WebhookEvents } from "tachi-common"; import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import fetch from "#utils/fetch"; // @todo make use of aggressive caching here? export async function GetWebhookUrlInfo() { - const urls = await db["api-clients"].find( + const urls = await MONGODB_KILL["api-clients"].find( { webhookUri: { $ne: null } }, { projection: { webhookUri: 1, clientSecret: 1 } }, ); diff --git a/typescript/server/src/main.ts b/typescript/server/src/main.ts index 96e01a12e..79fd73758 100644 --- a/typescript/server/src/main.ts +++ b/typescript/server/src/main.ts @@ -7,16 +7,17 @@ import { log } from "#lib/log/log.js"; import { Env, ServerConfig, TachiConfig } from "#lib/setup/config"; import { AddNewUser } from "#server/router/api/v1/auth/auth.js"; import server from "#server/server"; -import db, { monkDB } from "#services/mongo/db"; +import MONGODB_KILL, { monkDB } from "#services/mongo/db"; import { UpdateIndexes } from "#services/mongo/indexes"; import { InitSequenceDocs } from "#services/mongo/sequence-docs"; +import DB from "#services/pg/db.js"; import fetch from "#utils/fetch"; import { InitaliseFolderChartLookup } from "#utils/folder"; +import { GetUserWithID } from "#utils/user.js"; import { spawn } from "child_process"; import fs from "fs"; import https from "https"; import path from "path"; -import { UserAuthLevels } from "tachi-common"; import { applyMigrations } from "tachi-db-migration-engine"; log.info( @@ -35,7 +36,7 @@ async function RunOnInit() { await applyMigrations(Env.POSTGRES_URL, Env.MIGRATIONS_DIR); - await db["folder-chart-lookup"].findOne().then((r) => { + await MONGODB_KILL["folder-chart-lookup"].findOne().then((r) => { // If there are no folder chart lookups, initialise them. if (!r) { InitaliseFolderChartLookup().catch((err: unknown) => { @@ -45,12 +46,21 @@ async function RunOnInit() { }); if (Env.NODE_ENV === "dev") { - const exists = await db.users.findOne({ id: 1 }); + const exists = await GetUserWithID(1); if (!exists) { log.info("First time setup in LOCAL DEV: Creating an admin user for you."); - await AddNewUser("admin", "password", "admin@example.com", 1); - await db.users.update({ id: 1 }, { $set: { authLevel: UserAuthLevels.ADMIN } }); + + await DB.transaction().execute(async (txn) => { + await AddNewUser(txn, "admin", "password", "admin@example.com"); + + await txn + .updateTable("account") + .set({ auth_level: "admin" }) + .where("id", "=", 1) + .execute(); + }); + log.info("Done! You have an admin user with password 'password'"); } } diff --git a/typescript/server/src/proto/generated/cards/cards_connect.ts b/typescript/server/src/proto/generated/cards/cards_connect.ts new file mode 100644 index 000000000..59148a119 --- /dev/null +++ b/typescript/server/src/proto/generated/cards/cards_connect.ts @@ -0,0 +1,25 @@ +// @generated by protoc-gen-connect-es v1.7.0 with parameter "target=ts" +// @generated from file cards/cards.proto (package mythos.cards.v0, syntax proto3) +/* eslint-disable */ +// @ts-nocheck + +import { LookupRequest, LookupResponse } from "./cards_pb.js"; +import { MethodKind } from "@bufbuild/protobuf"; + +/** + * @generated from service mythos.cards.v0.Cards + */ +export const Cards = { + typeName: "mythos.cards.v0.Cards", + methods: { + /** + * @generated from rpc mythos.cards.v0.Cards.Lookup + */ + lookup: { + name: "Lookup", + I: LookupRequest, + O: LookupResponse, + kind: MethodKind.Unary, + }, + }, +} as const; diff --git a/typescript/server/src/proto/generated/cards/cards_grpc_pb.d.ts b/typescript/server/src/proto/generated/cards/cards_grpc_pb.d.ts deleted file mode 100644 index 6053d37a1..000000000 --- a/typescript/server/src/proto/generated/cards/cards_grpc_pb.d.ts +++ /dev/null @@ -1,36 +0,0 @@ -// GENERATED CODE -- DO NOT EDIT! - -// package: mythos.cards.v0 -// file: cards/cards.proto - -import * as cards_cards_pb from "../cards/cards_pb"; -import * as grpc from "@grpc/grpc-js"; - -interface ICardsService extends grpc.ServiceDefinition { - lookup: grpc.MethodDefinition; -} - -export const CardsService: ICardsService; - -export interface ICardsServer extends grpc.UntypedServiceImplementation { - lookup: grpc.handleUnaryCall; -} - -export class CardsClient extends grpc.Client { - constructor(address: string, credentials: grpc.ChannelCredentials, options?: object); - lookup( - argument: cards_cards_pb.LookupRequest, - callback: grpc.requestCallback, - ): grpc.ClientUnaryCall; - lookup( - argument: cards_cards_pb.LookupRequest, - metadataOrOptions: grpc.Metadata | grpc.CallOptions | null, - callback: grpc.requestCallback, - ): grpc.ClientUnaryCall; - lookup( - argument: cards_cards_pb.LookupRequest, - metadata: grpc.Metadata | null, - options: grpc.CallOptions | null, - callback: grpc.requestCallback, - ): grpc.ClientUnaryCall; -} diff --git a/typescript/server/src/proto/generated/cards/cards_grpc_pb.js b/typescript/server/src/proto/generated/cards/cards_grpc_pb.js deleted file mode 100644 index f9dacaa34..000000000 --- a/typescript/server/src/proto/generated/cards/cards_grpc_pb.js +++ /dev/null @@ -1,43 +0,0 @@ -// GENERATED CODE -- DO NOT EDIT! - -"use strict"; -var grpc = require("@grpc/grpc-js"); -var cards_cards_pb = require("../cards/cards_pb.js"); - -function serialize_mythos_cards_v0_LookupRequest(arg) { - if (!(arg instanceof cards_cards_pb.LookupRequest)) { - throw new Error("Expected argument of type mythos.cards.v0.LookupRequest"); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_mythos_cards_v0_LookupRequest(buffer_arg) { - return cards_cards_pb.LookupRequest.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_mythos_cards_v0_LookupResponse(arg) { - if (!(arg instanceof cards_cards_pb.LookupResponse)) { - throw new Error("Expected argument of type mythos.cards.v0.LookupResponse"); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_mythos_cards_v0_LookupResponse(buffer_arg) { - return cards_cards_pb.LookupResponse.deserializeBinary(new Uint8Array(buffer_arg)); -} - -var CardsService = (exports.CardsService = { - lookup: { - path: "/mythos.cards.v0.Cards/Lookup", - requestStream: false, - responseStream: false, - requestType: cards_cards_pb.LookupRequest, - responseType: cards_cards_pb.LookupResponse, - requestSerialize: serialize_mythos_cards_v0_LookupRequest, - requestDeserialize: deserialize_mythos_cards_v0_LookupRequest, - responseSerialize: serialize_mythos_cards_v0_LookupResponse, - responseDeserialize: deserialize_mythos_cards_v0_LookupResponse, - }, -}); - -exports.CardsClient = grpc.makeGenericClientConstructor(CardsService, "Cards"); diff --git a/typescript/server/src/proto/generated/cards/cards_pb.d.ts b/typescript/server/src/proto/generated/cards/cards_pb.d.ts deleted file mode 100644 index f3a225567..000000000 --- a/typescript/server/src/proto/generated/cards/cards_pb.d.ts +++ /dev/null @@ -1,89 +0,0 @@ -// package: mythos.cards.v0 -// file: cards/cards.proto - -import * as jspb from "google-protobuf"; - -export class LookupRequest extends jspb.Message { - getAccessCode(): string; - setAccessCode(value: string): void; - - clearTitlesList(): void; - getTitlesList(): Array; - setTitlesList(value: Array): void; - addTitles(value: string, index?: number): string; - - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): LookupRequest.AsObject; - static toObject(includeInstance: boolean, msg: LookupRequest): LookupRequest.AsObject; - static extensions: { [key: number]: jspb.ExtensionFieldInfo }; - static extensionsBinary: { [key: number]: jspb.ExtensionFieldBinaryInfo }; - static serializeBinaryToWriter(message: LookupRequest, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): LookupRequest; - static deserializeBinaryFromReader( - message: LookupRequest, - reader: jspb.BinaryReader, - ): LookupRequest; -} - -export namespace LookupRequest { - export type AsObject = { - accessCode: string; - titlesList: Array; - }; -} - -export class LookupResponse extends jspb.Message { - getPlayerApiId(): string; - setPlayerApiId(value: string): void; - - clearTitlesList(): void; - getTitlesList(): Array; - setTitlesList(value: Array): void; - addTitles(value?: LookupResponse.TitleEntry, index?: number): LookupResponse.TitleEntry; - - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): LookupResponse.AsObject; - static toObject(includeInstance: boolean, msg: LookupResponse): LookupResponse.AsObject; - static extensions: { [key: number]: jspb.ExtensionFieldInfo }; - static extensionsBinary: { [key: number]: jspb.ExtensionFieldBinaryInfo }; - static serializeBinaryToWriter(message: LookupResponse, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): LookupResponse; - static deserializeBinaryFromReader( - message: LookupResponse, - reader: jspb.BinaryReader, - ): LookupResponse; -} - -export namespace LookupResponse { - export type AsObject = { - playerApiId: string; - titlesList: Array; - }; - - export class TitleEntry extends jspb.Message { - getTitleKind(): string; - setTitleKind(value: string): void; - - getTitleApiId(): string; - setTitleApiId(value: string): void; - - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): TitleEntry.AsObject; - static toObject(includeInstance: boolean, msg: TitleEntry): TitleEntry.AsObject; - static extensions: { [key: number]: jspb.ExtensionFieldInfo }; - static extensionsBinary: { [key: number]: jspb.ExtensionFieldBinaryInfo }; - static serializeBinaryToWriter(message: TitleEntry, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): TitleEntry; - static deserializeBinaryFromReader( - message: TitleEntry, - reader: jspb.BinaryReader, - ): TitleEntry; - } - - export namespace TitleEntry { - export type AsObject = { - titleKind: string; - titleApiId: string; - }; - } -} diff --git a/typescript/server/src/proto/generated/cards/cards_pb.js b/typescript/server/src/proto/generated/cards/cards_pb.js deleted file mode 100644 index 1e08f6fcc..000000000 --- a/typescript/server/src/proto/generated/cards/cards_pb.js +++ /dev/null @@ -1,620 +0,0 @@ -// source: cards/cards.proto -/** - * @fileoverview - * @enhanceable - * @suppress {missingRequire} reports error on implicit type usages. - * @suppress {messageConventions} JS Compiler reports an error if a variable or - * field starts with 'MSG_' and isn't a translatable message. - * @public - */ -// GENERATED CODE -- DO NOT EDIT! -/* eslint-disable */ -// @ts-nocheck - -var jspb = require("google-protobuf"); -var goog = jspb; -var global = function () { - if (this) { - return this; - } - if (typeof window !== "undefined") { - return window; - } - if (typeof global !== "undefined") { - return global; - } - if (typeof self !== "undefined") { - return self; - } - return Function("return this")(); -}.call(null); - -goog.exportSymbol("proto.mythos.cards.v0.LookupRequest", null, global); -goog.exportSymbol("proto.mythos.cards.v0.LookupResponse", null, global); -goog.exportSymbol("proto.mythos.cards.v0.LookupResponse.TitleEntry", null, global); -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.mythos.cards.v0.LookupRequest = function (opt_data) { - jspb.Message.initialize( - this, - opt_data, - 0, - -1, - proto.mythos.cards.v0.LookupRequest.repeatedFields_, - null, - ); -}; -goog.inherits(proto.mythos.cards.v0.LookupRequest, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.mythos.cards.v0.LookupRequest.displayName = "proto.mythos.cards.v0.LookupRequest"; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.mythos.cards.v0.LookupResponse = function (opt_data) { - jspb.Message.initialize( - this, - opt_data, - 0, - -1, - proto.mythos.cards.v0.LookupResponse.repeatedFields_, - null, - ); -}; -goog.inherits(proto.mythos.cards.v0.LookupResponse, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.mythos.cards.v0.LookupResponse.displayName = "proto.mythos.cards.v0.LookupResponse"; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.mythos.cards.v0.LookupResponse.TitleEntry = function (opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.mythos.cards.v0.LookupResponse.TitleEntry, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.mythos.cards.v0.LookupResponse.TitleEntry.displayName = - "proto.mythos.cards.v0.LookupResponse.TitleEntry"; -} - -/** - * List of repeated fields within this message type. - * @private {!Array} - * @const - */ -proto.mythos.cards.v0.LookupRequest.repeatedFields_ = [2]; - -if (jspb.Message.GENERATE_TO_OBJECT) { - /** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ - proto.mythos.cards.v0.LookupRequest.prototype.toObject = function (opt_includeInstance) { - return proto.mythos.cards.v0.LookupRequest.toObject(opt_includeInstance, this); - }; - - /** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.mythos.cards.v0.LookupRequest} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ - proto.mythos.cards.v0.LookupRequest.toObject = function (includeInstance, msg) { - var f, - obj = { - accessCode: jspb.Message.getFieldWithDefault(msg, 1, ""), - titlesList: (f = jspb.Message.getRepeatedField(msg, 2)) == null ? undefined : f, - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; - }; -} - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.mythos.cards.v0.LookupRequest} - */ -proto.mythos.cards.v0.LookupRequest.deserializeBinary = function (bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.mythos.cards.v0.LookupRequest(); - return proto.mythos.cards.v0.LookupRequest.deserializeBinaryFromReader(msg, reader); -}; - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.mythos.cards.v0.LookupRequest} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.mythos.cards.v0.LookupRequest} - */ -proto.mythos.cards.v0.LookupRequest.deserializeBinaryFromReader = function (msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {string} */ (reader.readString()); - msg.setAccessCode(value); - break; - case 2: - var value = /** @type {string} */ (reader.readString()); - msg.addTitles(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.mythos.cards.v0.LookupRequest.prototype.serializeBinary = function () { - var writer = new jspb.BinaryWriter(); - proto.mythos.cards.v0.LookupRequest.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.mythos.cards.v0.LookupRequest} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.mythos.cards.v0.LookupRequest.serializeBinaryToWriter = function (message, writer) { - var f = undefined; - f = message.getAccessCode(); - if (f.length > 0) { - writer.writeString(1, f); - } - f = message.getTitlesList(); - if (f.length > 0) { - writer.writeRepeatedString(2, f); - } -}; - -/** - * optional string access_code = 1; - * @return {string} - */ -proto.mythos.cards.v0.LookupRequest.prototype.getAccessCode = function () { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); -}; - -/** - * @param {string} value - * @return {!proto.mythos.cards.v0.LookupRequest} returns this - */ -proto.mythos.cards.v0.LookupRequest.prototype.setAccessCode = function (value) { - return jspb.Message.setProto3StringField(this, 1, value); -}; - -/** - * repeated string titles = 2; - * @return {!Array} - */ -proto.mythos.cards.v0.LookupRequest.prototype.getTitlesList = function () { - return /** @type {!Array} */ (jspb.Message.getRepeatedField(this, 2)); -}; - -/** - * @param {!Array} value - * @return {!proto.mythos.cards.v0.LookupRequest} returns this - */ -proto.mythos.cards.v0.LookupRequest.prototype.setTitlesList = function (value) { - return jspb.Message.setField(this, 2, value || []); -}; - -/** - * @param {string} value - * @param {number=} opt_index - * @return {!proto.mythos.cards.v0.LookupRequest} returns this - */ -proto.mythos.cards.v0.LookupRequest.prototype.addTitles = function (value, opt_index) { - return jspb.Message.addToRepeatedField(this, 2, value, opt_index); -}; - -/** - * Clears the list making it empty but non-null. - * @return {!proto.mythos.cards.v0.LookupRequest} returns this - */ -proto.mythos.cards.v0.LookupRequest.prototype.clearTitlesList = function () { - return this.setTitlesList([]); -}; - -/** - * List of repeated fields within this message type. - * @private {!Array} - * @const - */ -proto.mythos.cards.v0.LookupResponse.repeatedFields_ = [2]; - -if (jspb.Message.GENERATE_TO_OBJECT) { - /** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ - proto.mythos.cards.v0.LookupResponse.prototype.toObject = function (opt_includeInstance) { - return proto.mythos.cards.v0.LookupResponse.toObject(opt_includeInstance, this); - }; - - /** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.mythos.cards.v0.LookupResponse} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ - proto.mythos.cards.v0.LookupResponse.toObject = function (includeInstance, msg) { - var f, - obj = { - playerApiId: jspb.Message.getFieldWithDefault(msg, 1, ""), - titlesList: jspb.Message.toObjectList( - msg.getTitlesList(), - proto.mythos.cards.v0.LookupResponse.TitleEntry.toObject, - includeInstance, - ), - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; - }; -} - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.mythos.cards.v0.LookupResponse} - */ -proto.mythos.cards.v0.LookupResponse.deserializeBinary = function (bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.mythos.cards.v0.LookupResponse(); - return proto.mythos.cards.v0.LookupResponse.deserializeBinaryFromReader(msg, reader); -}; - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.mythos.cards.v0.LookupResponse} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.mythos.cards.v0.LookupResponse} - */ -proto.mythos.cards.v0.LookupResponse.deserializeBinaryFromReader = function (msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {string} */ (reader.readString()); - msg.setPlayerApiId(value); - break; - case 2: - var value = new proto.mythos.cards.v0.LookupResponse.TitleEntry(); - reader.readMessage( - value, - proto.mythos.cards.v0.LookupResponse.TitleEntry.deserializeBinaryFromReader, - ); - msg.addTitles(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.mythos.cards.v0.LookupResponse.prototype.serializeBinary = function () { - var writer = new jspb.BinaryWriter(); - proto.mythos.cards.v0.LookupResponse.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.mythos.cards.v0.LookupResponse} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.mythos.cards.v0.LookupResponse.serializeBinaryToWriter = function (message, writer) { - var f = undefined; - f = message.getPlayerApiId(); - if (f.length > 0) { - writer.writeString(1, f); - } - f = message.getTitlesList(); - if (f.length > 0) { - writer.writeRepeatedMessage( - 2, - f, - proto.mythos.cards.v0.LookupResponse.TitleEntry.serializeBinaryToWriter, - ); - } -}; - -if (jspb.Message.GENERATE_TO_OBJECT) { - /** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ - proto.mythos.cards.v0.LookupResponse.TitleEntry.prototype.toObject = function ( - opt_includeInstance, - ) { - return proto.mythos.cards.v0.LookupResponse.TitleEntry.toObject(opt_includeInstance, this); - }; - - /** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.mythos.cards.v0.LookupResponse.TitleEntry} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ - proto.mythos.cards.v0.LookupResponse.TitleEntry.toObject = function (includeInstance, msg) { - var f, - obj = { - titleKind: jspb.Message.getFieldWithDefault(msg, 1, ""), - titleApiId: jspb.Message.getFieldWithDefault(msg, 2, ""), - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; - }; -} - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.mythos.cards.v0.LookupResponse.TitleEntry} - */ -proto.mythos.cards.v0.LookupResponse.TitleEntry.deserializeBinary = function (bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.mythos.cards.v0.LookupResponse.TitleEntry(); - return proto.mythos.cards.v0.LookupResponse.TitleEntry.deserializeBinaryFromReader(msg, reader); -}; - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.mythos.cards.v0.LookupResponse.TitleEntry} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.mythos.cards.v0.LookupResponse.TitleEntry} - */ -proto.mythos.cards.v0.LookupResponse.TitleEntry.deserializeBinaryFromReader = function ( - msg, - reader, -) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {string} */ (reader.readString()); - msg.setTitleKind(value); - break; - case 2: - var value = /** @type {string} */ (reader.readString()); - msg.setTitleApiId(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.mythos.cards.v0.LookupResponse.TitleEntry.prototype.serializeBinary = function () { - var writer = new jspb.BinaryWriter(); - proto.mythos.cards.v0.LookupResponse.TitleEntry.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.mythos.cards.v0.LookupResponse.TitleEntry} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.mythos.cards.v0.LookupResponse.TitleEntry.serializeBinaryToWriter = function ( - message, - writer, -) { - var f = undefined; - f = message.getTitleKind(); - if (f.length > 0) { - writer.writeString(1, f); - } - f = message.getTitleApiId(); - if (f.length > 0) { - writer.writeString(2, f); - } -}; - -/** - * optional string title_kind = 1; - * @return {string} - */ -proto.mythos.cards.v0.LookupResponse.TitleEntry.prototype.getTitleKind = function () { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); -}; - -/** - * @param {string} value - * @return {!proto.mythos.cards.v0.LookupResponse.TitleEntry} returns this - */ -proto.mythos.cards.v0.LookupResponse.TitleEntry.prototype.setTitleKind = function (value) { - return jspb.Message.setProto3StringField(this, 1, value); -}; - -/** - * optional string title_api_id = 2; - * @return {string} - */ -proto.mythos.cards.v0.LookupResponse.TitleEntry.prototype.getTitleApiId = function () { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); -}; - -/** - * @param {string} value - * @return {!proto.mythos.cards.v0.LookupResponse.TitleEntry} returns this - */ -proto.mythos.cards.v0.LookupResponse.TitleEntry.prototype.setTitleApiId = function (value) { - return jspb.Message.setProto3StringField(this, 2, value); -}; - -/** - * optional string player_api_id = 1; - * @return {string} - */ -proto.mythos.cards.v0.LookupResponse.prototype.getPlayerApiId = function () { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); -}; - -/** - * @param {string} value - * @return {!proto.mythos.cards.v0.LookupResponse} returns this - */ -proto.mythos.cards.v0.LookupResponse.prototype.setPlayerApiId = function (value) { - return jspb.Message.setProto3StringField(this, 1, value); -}; - -/** - * repeated TitleEntry titles = 2; - * @return {!Array} - */ -proto.mythos.cards.v0.LookupResponse.prototype.getTitlesList = function () { - return /** @type{!Array} */ ( - jspb.Message.getRepeatedWrapperField( - this, - proto.mythos.cards.v0.LookupResponse.TitleEntry, - 2, - ) - ); -}; - -/** - * @param {!Array} value - * @return {!proto.mythos.cards.v0.LookupResponse} returns this - */ -proto.mythos.cards.v0.LookupResponse.prototype.setTitlesList = function (value) { - return jspb.Message.setRepeatedWrapperField(this, 2, value); -}; - -/** - * @param {!proto.mythos.cards.v0.LookupResponse.TitleEntry=} opt_value - * @param {number=} opt_index - * @return {!proto.mythos.cards.v0.LookupResponse.TitleEntry} - */ -proto.mythos.cards.v0.LookupResponse.prototype.addTitles = function (opt_value, opt_index) { - return jspb.Message.addToRepeatedWrapperField( - this, - 2, - opt_value, - proto.mythos.cards.v0.LookupResponse.TitleEntry, - opt_index, - ); -}; - -/** - * Clears the list making it empty but non-null. - * @return {!proto.mythos.cards.v0.LookupResponse} returns this - */ -proto.mythos.cards.v0.LookupResponse.prototype.clearTitlesList = function () { - return this.setTitlesList([]); -}; - -goog.object.extend(exports, proto.mythos.cards.v0); diff --git a/typescript/server/src/proto/generated/cards/cards_pb.ts b/typescript/server/src/proto/generated/cards/cards_pb.ts new file mode 100644 index 000000000..3e86fb908 --- /dev/null +++ b/typescript/server/src/proto/generated/cards/cards_pb.ts @@ -0,0 +1,101 @@ +// @generated by protoc-gen-es v2.11.0 with parameter "target=ts" +// @generated from file cards/cards.proto (package mythos.cards.v0, syntax proto3) +/* eslint-disable */ + +import type { GenFile, GenMessage, GenService } from "@bufbuild/protobuf/codegenv2"; +import { fileDesc, messageDesc, serviceDesc } from "@bufbuild/protobuf/codegenv2"; +import type { Message } from "@bufbuild/protobuf"; + +/** + * Describes the file cards/cards.proto. + */ +export const file_cards_cards: GenFile = + /*@__PURE__*/ + fileDesc( + "ChFjYXJkcy9jYXJkcy5wcm90bxIPbXl0aG9zLmNhcmRzLnYwIjQKDUxvb2t1cFJlcXVlc3QSEwoLYWNjZXNzX2NvZGUYASABKAkSDgoGdGl0bGVzGAIgAygJIpsBCg5Mb29rdXBSZXNwb25zZRIVCg1wbGF5ZXJfYXBpX2lkGAEgASgJEjoKBnRpdGxlcxgCIAMoCzIqLm15dGhvcy5jYXJkcy52MC5Mb29rdXBSZXNwb25zZS5UaXRsZUVudHJ5GjYKClRpdGxlRW50cnkSEgoKdGl0bGVfa2luZBgBIAEoCRIUCgx0aXRsZV9hcGlfaWQYAiABKAkyUgoFQ2FyZHMSSQoGTG9va3VwEh4ubXl0aG9zLmNhcmRzLnYwLkxvb2t1cFJlcXVlc3QaHy5teXRob3MuY2FyZHMudjAuTG9va3VwUmVzcG9uc2ViBnByb3RvMw", + ); + +/** + * @generated from message mythos.cards.v0.LookupRequest + */ +export type LookupRequest = Message<"mythos.cards.v0.LookupRequest"> & { + /** + * @generated from field: string access_code = 1; + */ + accessCode: string; + + /** + * @generated from field: repeated string titles = 2; + */ + titles: string[]; +}; + +/** + * Describes the message mythos.cards.v0.LookupRequest. + * Use `create(LookupRequestSchema)` to create a new message. + */ +export const LookupRequestSchema: GenMessage = + /*@__PURE__*/ + messageDesc(file_cards_cards, 0); + +/** + * @generated from message mythos.cards.v0.LookupResponse + */ +export type LookupResponse = Message<"mythos.cards.v0.LookupResponse"> & { + /** + * Tachi note: player_api_id currently unused + * + * @generated from field: string player_api_id = 1; + */ + playerApiId: string; + + /** + * @generated from field: repeated mythos.cards.v0.LookupResponse.TitleEntry titles = 2; + */ + titles: LookupResponse_TitleEntry[]; +}; + +/** + * Describes the message mythos.cards.v0.LookupResponse. + * Use `create(LookupResponseSchema)` to create a new message. + */ +export const LookupResponseSchema: GenMessage = + /*@__PURE__*/ + messageDesc(file_cards_cards, 1); + +/** + * @generated from message mythos.cards.v0.LookupResponse.TitleEntry + */ +export type LookupResponse_TitleEntry = Message<"mythos.cards.v0.LookupResponse.TitleEntry"> & { + /** + * @generated from field: string title_kind = 1; + */ + titleKind: string; + + /** + * @generated from field: string title_api_id = 2; + */ + titleApiId: string; +}; + +/** + * Describes the message mythos.cards.v0.LookupResponse.TitleEntry. + * Use `create(LookupResponse_TitleEntrySchema)` to create a new message. + */ +export const LookupResponse_TitleEntrySchema: GenMessage = + /*@__PURE__*/ + messageDesc(file_cards_cards, 1, 0); + +/** + * @generated from service mythos.cards.v0.Cards + */ +export const Cards: GenService<{ + /** + * @generated from rpc mythos.cards.v0.Cards.Lookup + */ + lookup: { + methodKind: "unary"; + input: typeof LookupRequestSchema; + output: typeof LookupResponseSchema; + }; +}> = /*@__PURE__*/ serviceDesc(file_cards_cards, 0); diff --git a/typescript/server/src/proto/generated/chunithm/common_grpc_pb.d.ts b/typescript/server/src/proto/generated/chunithm/common_grpc_pb.d.ts deleted file mode 100644 index 51b4d6959..000000000 --- a/typescript/server/src/proto/generated/chunithm/common_grpc_pb.d.ts +++ /dev/null @@ -1 +0,0 @@ -// GENERATED CODE -- NO SERVICES IN PROTO diff --git a/typescript/server/src/proto/generated/chunithm/common_grpc_pb.js b/typescript/server/src/proto/generated/chunithm/common_grpc_pb.js deleted file mode 100644 index 51b4d6959..000000000 --- a/typescript/server/src/proto/generated/chunithm/common_grpc_pb.js +++ /dev/null @@ -1 +0,0 @@ -// GENERATED CODE -- NO SERVICES IN PROTO diff --git a/typescript/server/src/proto/generated/chunithm/common_pb.d.ts b/typescript/server/src/proto/generated/chunithm/common_pb.d.ts deleted file mode 100644 index 9612ef2de..000000000 --- a/typescript/server/src/proto/generated/chunithm/common_pb.d.ts +++ /dev/null @@ -1,67 +0,0 @@ -// package: mythos.chunithm.v0 -// file: chunithm/common.proto - -import * as jspb from "google-protobuf"; - -export interface ChunithmClearStatusMap { - CHUNITHM_CLEAR_STATUS_UNSPECIFIED: 0; - CHUNITHM_CLEAR_STATUS_FAILED: 1; - CHUNITHM_CLEAR_STATUS_CLEAR: 2; - CHUNITHM_CLEAR_STATUS_HARD: 3; - CHUNITHM_CLEAR_STATUS_ABSOLUTE: 4; - CHUNITHM_CLEAR_STATUS_ABSOLUTE_PLUS: 5; - CHUNITHM_CLEAR_STATUS_CATASTROPHY: 6; -} - -export const ChunithmClearStatus: ChunithmClearStatusMap; - -export interface ChunithmFullChainStatusMap { - CHUNITHM_FULL_CHAIN_STATUS_UNSPECIFIED: 0; - CHUNITHM_FULL_CHAIN_STATUS_NONE: 1; - CHUNITHM_FULL_CHAIN_STATUS_FULL_CHAIN_FC: 2; - CHUNITHM_FULL_CHAIN_STATUS_FULL_CHAIN_AJ: 3; -} - -export const ChunithmFullChainStatus: ChunithmFullChainStatusMap; - -export interface ChunithmLevelMap { - CHUNITHM_LEVEL_UNSPECIFIED: 0; - CHUNITHM_LEVEL_BASIC: 1; - CHUNITHM_LEVEL_ADVANCED: 2; - CHUNITHM_LEVEL_EXPERT: 3; - CHUNITHM_LEVEL_MASTER: 4; - CHUNITHM_LEVEL_ULTIMA: 5; - CHUNITHM_LEVEL_WORLDS_END: 6; -} - -export const ChunithmLevel: ChunithmLevelMap; - -export interface ChunithmScoreRankMap { - CHUNITHM_SCORE_RANK_UNSPECIFIED: 0; - CHUNITHM_SCORE_RANK_D: 1; - CHUNITHM_SCORE_RANK_C: 2; - CHUNITHM_SCORE_RANK_B: 3; - CHUNITHM_SCORE_RANK_BB: 4; - CHUNITHM_SCORE_RANK_BBB: 5; - CHUNITHM_SCORE_RANK_A: 6; - CHUNITHM_SCORE_RANK_AA: 7; - CHUNITHM_SCORE_RANK_AAA: 8; - CHUNITHM_SCORE_RANK_S: 9; - CHUNITHM_SCORE_RANK_S_PLUS: 10; - CHUNITHM_SCORE_RANK_SS: 11; - CHUNITHM_SCORE_RANK_SS_PLUS: 12; - CHUNITHM_SCORE_RANK_SSS: 13; - CHUNITHM_SCORE_RANK_SSS_PLUS: 14; -} - -export const ChunithmScoreRank: ChunithmScoreRankMap; - -export interface ChunithmComboStatusMap { - CHUNITHM_COMBO_STATUS_UNSPECIFIED: 0; - CHUNITHM_COMBO_STATUS_NONE: 1; - CHUNITHM_COMBO_STATUS_FULL_COMBO: 2; - CHUNITHM_COMBO_STATUS_ALL_JUSTICE: 3; - CHUNITHM_COMBO_STATUS_ALL_JUSTICE_CRITICAL: 4; -} - -export const ChunithmComboStatus: ChunithmComboStatusMap; diff --git a/typescript/server/src/proto/generated/chunithm/common_pb.js b/typescript/server/src/proto/generated/chunithm/common_pb.js deleted file mode 100644 index 39492cdfa..000000000 --- a/typescript/server/src/proto/generated/chunithm/common_pb.js +++ /dev/null @@ -1,105 +0,0 @@ -// source: chunithm/common.proto -/** - * @fileoverview - * @enhanceable - * @suppress {missingRequire} reports error on implicit type usages. - * @suppress {messageConventions} JS Compiler reports an error if a variable or - * field starts with 'MSG_' and isn't a translatable message. - * @public - */ -// GENERATED CODE -- DO NOT EDIT! -/* eslint-disable */ -// @ts-nocheck - -var jspb = require("google-protobuf"); -var goog = jspb; -var global = function () { - if (this) { - return this; - } - if (typeof window !== "undefined") { - return window; - } - if (typeof global !== "undefined") { - return global; - } - if (typeof self !== "undefined") { - return self; - } - return Function("return this")(); -}.call(null); - -goog.exportSymbol("proto.mythos.chunithm.v0.ChunithmClearStatus", null, global); -goog.exportSymbol("proto.mythos.chunithm.v0.ChunithmComboStatus", null, global); -goog.exportSymbol("proto.mythos.chunithm.v0.ChunithmFullChainStatus", null, global); -goog.exportSymbol("proto.mythos.chunithm.v0.ChunithmLevel", null, global); -goog.exportSymbol("proto.mythos.chunithm.v0.ChunithmScoreRank", null, global); -/** - * @enum {number} - */ -proto.mythos.chunithm.v0.ChunithmClearStatus = { - CHUNITHM_CLEAR_STATUS_UNSPECIFIED: 0, - CHUNITHM_CLEAR_STATUS_FAILED: 1, - CHUNITHM_CLEAR_STATUS_CLEAR: 2, - CHUNITHM_CLEAR_STATUS_HARD: 3, - CHUNITHM_CLEAR_STATUS_ABSOLUTE: 4, - CHUNITHM_CLEAR_STATUS_ABSOLUTE_PLUS: 5, - CHUNITHM_CLEAR_STATUS_CATASTROPHY: 6, -}; - -/** - * @enum {number} - */ -proto.mythos.chunithm.v0.ChunithmFullChainStatus = { - CHUNITHM_FULL_CHAIN_STATUS_UNSPECIFIED: 0, - CHUNITHM_FULL_CHAIN_STATUS_NONE: 1, - CHUNITHM_FULL_CHAIN_STATUS_FULL_CHAIN_FC: 2, - CHUNITHM_FULL_CHAIN_STATUS_FULL_CHAIN_AJ: 3, -}; - -/** - * @enum {number} - */ -proto.mythos.chunithm.v0.ChunithmLevel = { - CHUNITHM_LEVEL_UNSPECIFIED: 0, - CHUNITHM_LEVEL_BASIC: 1, - CHUNITHM_LEVEL_ADVANCED: 2, - CHUNITHM_LEVEL_EXPERT: 3, - CHUNITHM_LEVEL_MASTER: 4, - CHUNITHM_LEVEL_ULTIMA: 5, - CHUNITHM_LEVEL_WORLDS_END: 6, -}; - -/** - * @enum {number} - */ -proto.mythos.chunithm.v0.ChunithmScoreRank = { - CHUNITHM_SCORE_RANK_UNSPECIFIED: 0, - CHUNITHM_SCORE_RANK_D: 1, - CHUNITHM_SCORE_RANK_C: 2, - CHUNITHM_SCORE_RANK_B: 3, - CHUNITHM_SCORE_RANK_BB: 4, - CHUNITHM_SCORE_RANK_BBB: 5, - CHUNITHM_SCORE_RANK_A: 6, - CHUNITHM_SCORE_RANK_AA: 7, - CHUNITHM_SCORE_RANK_AAA: 8, - CHUNITHM_SCORE_RANK_S: 9, - CHUNITHM_SCORE_RANK_S_PLUS: 10, - CHUNITHM_SCORE_RANK_SS: 11, - CHUNITHM_SCORE_RANK_SS_PLUS: 12, - CHUNITHM_SCORE_RANK_SSS: 13, - CHUNITHM_SCORE_RANK_SSS_PLUS: 14, -}; - -/** - * @enum {number} - */ -proto.mythos.chunithm.v0.ChunithmComboStatus = { - CHUNITHM_COMBO_STATUS_UNSPECIFIED: 0, - CHUNITHM_COMBO_STATUS_NONE: 1, - CHUNITHM_COMBO_STATUS_FULL_COMBO: 2, - CHUNITHM_COMBO_STATUS_ALL_JUSTICE: 3, - CHUNITHM_COMBO_STATUS_ALL_JUSTICE_CRITICAL: 4, -}; - -goog.object.extend(exports, proto.mythos.chunithm.v0); diff --git a/typescript/server/src/proto/generated/chunithm/common_pb.ts b/typescript/server/src/proto/generated/chunithm/common_pb.ts new file mode 100644 index 000000000..14898acfd --- /dev/null +++ b/typescript/server/src/proto/generated/chunithm/common_pb.ts @@ -0,0 +1,269 @@ +// @generated by protoc-gen-es v2.11.0 with parameter "target=ts" +// @generated from file chunithm/common.proto (package mythos.chunithm.v0, syntax proto3) +/* eslint-disable */ + +import type { GenEnum, GenFile } from "@bufbuild/protobuf/codegenv2"; +import { enumDesc, fileDesc } from "@bufbuild/protobuf/codegenv2"; + +/** + * Describes the file chunithm/common.proto. + */ +export const file_chunithm_common: GenFile = + /*@__PURE__*/ + fileDesc( + "ChVjaHVuaXRobS9jb21tb24ucHJvdG8SEm15dGhvcy5jaHVuaXRobS52MCqTAgoTQ2h1bml0aG1DbGVhclN0YXR1cxIlCiFDSFVOSVRITV9DTEVBUl9TVEFUVVNfVU5TUEVDSUZJRUQQABIgChxDSFVOSVRITV9DTEVBUl9TVEFUVVNfRkFJTEVEEAESHwobQ0hVTklUSE1fQ0xFQVJfU1RBVFVTX0NMRUFSEAISHgoaQ0hVTklUSE1fQ0xFQVJfU1RBVFVTX0hBUkQQAxIiCh5DSFVOSVRITV9DTEVBUl9TVEFUVVNfQUJTT0xVVEUQBBInCiNDSFVOSVRITV9DTEVBUl9TVEFUVVNfQUJTT0xVVEVfUExVUxAFEiUKIUNIVU5JVEhNX0NMRUFSX1NUQVRVU19DQVRBU1RST1BIWRAGKsYBChdDaHVuaXRobUZ1bGxDaGFpblN0YXR1cxIqCiZDSFVOSVRITV9GVUxMX0NIQUlOX1NUQVRVU19VTlNQRUNJRklFRBAAEiMKH0NIVU5JVEhNX0ZVTExfQ0hBSU5fU1RBVFVTX05PTkUQARIsCihDSFVOSVRITV9GVUxMX0NIQUlOX1NUQVRVU19GVUxMX0NIQUlOX0ZDEAISLAooQ0hVTklUSE1fRlVMTF9DSEFJTl9TVEFUVVNfRlVMTF9DSEFJTl9BShADKtYBCg1DaHVuaXRobUxldmVsEh4KGkNIVU5JVEhNX0xFVkVMX1VOU1BFQ0lGSUVEEAASGAoUQ0hVTklUSE1fTEVWRUxfQkFTSUMQARIbChdDSFVOSVRITV9MRVZFTF9BRFZBTkNFRBACEhkKFUNIVU5JVEhNX0xFVkVMX0VYUEVSVBADEhkKFUNIVU5JVEhNX0xFVkVMX01BU1RFUhAEEhkKFUNIVU5JVEhNX0xFVkVMX1VMVElNQRAFEh0KGUNIVU5JVEhNX0xFVkVMX1dPUkxEU19FTkQQBirNAwoRQ2h1bml0aG1TY29yZVJhbmsSIwofQ0hVTklUSE1fU0NPUkVfUkFOS19VTlNQRUNJRklFRBAAEhkKFUNIVU5JVEhNX1NDT1JFX1JBTktfRBABEhkKFUNIVU5JVEhNX1NDT1JFX1JBTktfQxACEhkKFUNIVU5JVEhNX1NDT1JFX1JBTktfQhADEhoKFkNIVU5JVEhNX1NDT1JFX1JBTktfQkIQBBIbChdDSFVOSVRITV9TQ09SRV9SQU5LX0JCQhAFEhkKFUNIVU5JVEhNX1NDT1JFX1JBTktfQRAGEhoKFkNIVU5JVEhNX1NDT1JFX1JBTktfQUEQBxIbChdDSFVOSVRITV9TQ09SRV9SQU5LX0FBQRAIEhkKFUNIVU5JVEhNX1NDT1JFX1JBTktfUxAJEh4KGkNIVU5JVEhNX1NDT1JFX1JBTktfU19QTFVTEAoSGgoWQ0hVTklUSE1fU0NPUkVfUkFOS19TUxALEh8KG0NIVU5JVEhNX1NDT1JFX1JBTktfU1NfUExVUxAMEhsKF0NIVU5JVEhNX1NDT1JFX1JBTktfU1NTEA0SIAocQ0hVTklUSE1fU0NPUkVfUkFOS19TU1NfUExVUxAOKtkBChNDaHVuaXRobUNvbWJvU3RhdHVzEiUKIUNIVU5JVEhNX0NPTUJPX1NUQVRVU19VTlNQRUNJRklFRBAAEh4KGkNIVU5JVEhNX0NPTUJPX1NUQVRVU19OT05FEAESJAogQ0hVTklUSE1fQ09NQk9fU1RBVFVTX0ZVTExfQ09NQk8QAhIlCiFDSFVOSVRITV9DT01CT19TVEFUVVNfQUxMX0pVU1RJQ0UQAxIuCipDSFVOSVRITV9DT01CT19TVEFUVVNfQUxMX0pVU1RJQ0VfQ1JJVElDQUwQBGIGcHJvdG8z", + ); + +/** + * @generated from enum mythos.chunithm.v0.ChunithmClearStatus + */ +export enum ChunithmClearStatus { + /** + * @generated from enum value: CHUNITHM_CLEAR_STATUS_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + + /** + * @generated from enum value: CHUNITHM_CLEAR_STATUS_FAILED = 1; + */ + FAILED = 1, + + /** + * @generated from enum value: CHUNITHM_CLEAR_STATUS_CLEAR = 2; + */ + CLEAR = 2, + + /** + * @generated from enum value: CHUNITHM_CLEAR_STATUS_HARD = 3; + */ + HARD = 3, + + /** + * @generated from enum value: CHUNITHM_CLEAR_STATUS_ABSOLUTE = 4; + */ + ABSOLUTE = 4, + + /** + * @generated from enum value: CHUNITHM_CLEAR_STATUS_ABSOLUTE_PLUS = 5; + */ + ABSOLUTE_PLUS = 5, + + /** + * @generated from enum value: CHUNITHM_CLEAR_STATUS_CATASTROPHY = 6; + */ + CATASTROPHY = 6, +} + +/** + * Describes the enum mythos.chunithm.v0.ChunithmClearStatus. + */ +export const ChunithmClearStatusSchema: GenEnum = + /*@__PURE__*/ + enumDesc(file_chunithm_common, 0); + +/** + * @generated from enum mythos.chunithm.v0.ChunithmFullChainStatus + */ +export enum ChunithmFullChainStatus { + /** + * @generated from enum value: CHUNITHM_FULL_CHAIN_STATUS_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + + /** + * @generated from enum value: CHUNITHM_FULL_CHAIN_STATUS_NONE = 1; + */ + NONE = 1, + + /** + * Full Chain w/ FC + * + * @generated from enum value: CHUNITHM_FULL_CHAIN_STATUS_FULL_CHAIN_FC = 2; + */ + FULL_CHAIN_FC = 2, + + /** + * Full Chain w/ AJ + * + * @generated from enum value: CHUNITHM_FULL_CHAIN_STATUS_FULL_CHAIN_AJ = 3; + */ + FULL_CHAIN_AJ = 3, +} + +/** + * Describes the enum mythos.chunithm.v0.ChunithmFullChainStatus. + */ +export const ChunithmFullChainStatusSchema: GenEnum = + /*@__PURE__*/ + enumDesc(file_chunithm_common, 1); + +/** + * @generated from enum mythos.chunithm.v0.ChunithmLevel + */ +export enum ChunithmLevel { + /** + * @generated from enum value: CHUNITHM_LEVEL_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + + /** + * @generated from enum value: CHUNITHM_LEVEL_BASIC = 1; + */ + BASIC = 1, + + /** + * @generated from enum value: CHUNITHM_LEVEL_ADVANCED = 2; + */ + ADVANCED = 2, + + /** + * @generated from enum value: CHUNITHM_LEVEL_EXPERT = 3; + */ + EXPERT = 3, + + /** + * @generated from enum value: CHUNITHM_LEVEL_MASTER = 4; + */ + MASTER = 4, + + /** + * @generated from enum value: CHUNITHM_LEVEL_ULTIMA = 5; + */ + ULTIMA = 5, + + /** + * @generated from enum value: CHUNITHM_LEVEL_WORLDS_END = 6; + */ + WORLDS_END = 6, +} + +/** + * Describes the enum mythos.chunithm.v0.ChunithmLevel. + */ +export const ChunithmLevelSchema: GenEnum = + /*@__PURE__*/ + enumDesc(file_chunithm_common, 2); + +/** + * @generated from enum mythos.chunithm.v0.ChunithmScoreRank + */ +export enum ChunithmScoreRank { + /** + * @generated from enum value: CHUNITHM_SCORE_RANK_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + + /** + * @generated from enum value: CHUNITHM_SCORE_RANK_D = 1; + */ + D = 1, + + /** + * @generated from enum value: CHUNITHM_SCORE_RANK_C = 2; + */ + C = 2, + + /** + * @generated from enum value: CHUNITHM_SCORE_RANK_B = 3; + */ + B = 3, + + /** + * @generated from enum value: CHUNITHM_SCORE_RANK_BB = 4; + */ + BB = 4, + + /** + * @generated from enum value: CHUNITHM_SCORE_RANK_BBB = 5; + */ + BBB = 5, + + /** + * @generated from enum value: CHUNITHM_SCORE_RANK_A = 6; + */ + A = 6, + + /** + * @generated from enum value: CHUNITHM_SCORE_RANK_AA = 7; + */ + AA = 7, + + /** + * @generated from enum value: CHUNITHM_SCORE_RANK_AAA = 8; + */ + AAA = 8, + + /** + * @generated from enum value: CHUNITHM_SCORE_RANK_S = 9; + */ + S = 9, + + /** + * @generated from enum value: CHUNITHM_SCORE_RANK_S_PLUS = 10; + */ + S_PLUS = 10, + + /** + * @generated from enum value: CHUNITHM_SCORE_RANK_SS = 11; + */ + SS = 11, + + /** + * @generated from enum value: CHUNITHM_SCORE_RANK_SS_PLUS = 12; + */ + SS_PLUS = 12, + + /** + * @generated from enum value: CHUNITHM_SCORE_RANK_SSS = 13; + */ + SSS = 13, + + /** + * @generated from enum value: CHUNITHM_SCORE_RANK_SSS_PLUS = 14; + */ + SSS_PLUS = 14, +} + +/** + * Describes the enum mythos.chunithm.v0.ChunithmScoreRank. + */ +export const ChunithmScoreRankSchema: GenEnum = + /*@__PURE__*/ + enumDesc(file_chunithm_common, 3); + +/** + * @generated from enum mythos.chunithm.v0.ChunithmComboStatus + */ +export enum ChunithmComboStatus { + /** + * @generated from enum value: CHUNITHM_COMBO_STATUS_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + + /** + * @generated from enum value: CHUNITHM_COMBO_STATUS_NONE = 1; + */ + NONE = 1, + + /** + * @generated from enum value: CHUNITHM_COMBO_STATUS_FULL_COMBO = 2; + */ + FULL_COMBO = 2, + + /** + * @generated from enum value: CHUNITHM_COMBO_STATUS_ALL_JUSTICE = 3; + */ + ALL_JUSTICE = 3, + + /** + * @generated from enum value: CHUNITHM_COMBO_STATUS_ALL_JUSTICE_CRITICAL = 4; + */ + ALL_JUSTICE_CRITICAL = 4, +} + +/** + * Describes the enum mythos.chunithm.v0.ChunithmComboStatus. + */ +export const ChunithmComboStatusSchema: GenEnum = + /*@__PURE__*/ + enumDesc(file_chunithm_common, 4); diff --git a/typescript/server/src/proto/generated/chunithm/playlog_grpc_pb.d.ts b/typescript/server/src/proto/generated/chunithm/playlog_grpc_pb.d.ts deleted file mode 100644 index 51b4d6959..000000000 --- a/typescript/server/src/proto/generated/chunithm/playlog_grpc_pb.d.ts +++ /dev/null @@ -1 +0,0 @@ -// GENERATED CODE -- NO SERVICES IN PROTO diff --git a/typescript/server/src/proto/generated/chunithm/playlog_grpc_pb.js b/typescript/server/src/proto/generated/chunithm/playlog_grpc_pb.js deleted file mode 100644 index 51b4d6959..000000000 --- a/typescript/server/src/proto/generated/chunithm/playlog_grpc_pb.js +++ /dev/null @@ -1 +0,0 @@ -// GENERATED CODE -- NO SERVICES IN PROTO diff --git a/typescript/server/src/proto/generated/chunithm/playlog_pb.d.ts b/typescript/server/src/proto/generated/chunithm/playlog_pb.d.ts deleted file mode 100644 index 7e6277bf5..000000000 --- a/typescript/server/src/proto/generated/chunithm/playlog_pb.d.ts +++ /dev/null @@ -1,117 +0,0 @@ -// package: mythos.chunithm.v0 -// file: chunithm/playlog.proto - -import * as jspb from "google-protobuf"; -import * as chunithm_common_pb from "../chunithm/common_pb"; - -export class PlaylogInfo extends jspb.Message { - getMusicId(): number; - setMusicId(value: number): void; - - getLevel(): chunithm_common_pb.ChunithmLevelMap[keyof chunithm_common_pb.ChunithmLevelMap]; - setLevel( - value: chunithm_common_pb.ChunithmLevelMap[keyof chunithm_common_pb.ChunithmLevelMap], - ): void; - - getScore(): number; - setScore(value: number): void; - - getScoreRank(): chunithm_common_pb.ChunithmScoreRankMap[keyof chunithm_common_pb.ChunithmScoreRankMap]; - setScoreRank( - value: chunithm_common_pb.ChunithmScoreRankMap[keyof chunithm_common_pb.ChunithmScoreRankMap], - ): void; - - getComboStatus(): chunithm_common_pb.ChunithmComboStatusMap[keyof chunithm_common_pb.ChunithmComboStatusMap]; - setComboStatus( - value: chunithm_common_pb.ChunithmComboStatusMap[keyof chunithm_common_pb.ChunithmComboStatusMap], - ): void; - - getFullChainStatus(): chunithm_common_pb.ChunithmFullChainStatusMap[keyof chunithm_common_pb.ChunithmFullChainStatusMap]; - setFullChainStatus( - value: chunithm_common_pb.ChunithmFullChainStatusMap[keyof chunithm_common_pb.ChunithmFullChainStatusMap], - ): void; - - getClearStatus(): chunithm_common_pb.ChunithmClearStatusMap[keyof chunithm_common_pb.ChunithmClearStatusMap]; - setClearStatus( - value: chunithm_common_pb.ChunithmClearStatusMap[keyof chunithm_common_pb.ChunithmClearStatusMap], - ): void; - - getIsNewRecord(): boolean; - setIsNewRecord(value: boolean): void; - - getTrack(): number; - setTrack(value: number): void; - - getUserPlayDate(): string; - setUserPlayDate(value: string): void; - - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): PlaylogInfo.AsObject; - static toObject(includeInstance: boolean, msg: PlaylogInfo): PlaylogInfo.AsObject; - static extensions: { [key: number]: jspb.ExtensionFieldInfo }; - static extensionsBinary: { [key: number]: jspb.ExtensionFieldBinaryInfo }; - static serializeBinaryToWriter(message: PlaylogInfo, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): PlaylogInfo; - static deserializeBinaryFromReader( - message: PlaylogInfo, - reader: jspb.BinaryReader, - ): PlaylogInfo; -} - -export namespace PlaylogInfo { - export type AsObject = { - musicId: number; - level: chunithm_common_pb.ChunithmLevelMap[keyof chunithm_common_pb.ChunithmLevelMap]; - score: number; - scoreRank: chunithm_common_pb.ChunithmScoreRankMap[keyof chunithm_common_pb.ChunithmScoreRankMap]; - comboStatus: chunithm_common_pb.ChunithmComboStatusMap[keyof chunithm_common_pb.ChunithmComboStatusMap]; - fullChainStatus: chunithm_common_pb.ChunithmFullChainStatusMap[keyof chunithm_common_pb.ChunithmFullChainStatusMap]; - clearStatus: chunithm_common_pb.ChunithmClearStatusMap[keyof chunithm_common_pb.ChunithmClearStatusMap]; - isNewRecord: boolean; - track: number; - userPlayDate: string; - }; -} - -export class PlaylogJudge extends jspb.Message { - getJudgeHeaven(): number; - setJudgeHeaven(value: number): void; - - getJudgeCritical(): number; - setJudgeCritical(value: number): void; - - getJudgeJustice(): number; - setJudgeJustice(value: number): void; - - getJudgeAttack(): number; - setJudgeAttack(value: number): void; - - getJudgeMiss(): number; - setJudgeMiss(value: number): void; - - getMaxCombo(): number; - setMaxCombo(value: number): void; - - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): PlaylogJudge.AsObject; - static toObject(includeInstance: boolean, msg: PlaylogJudge): PlaylogJudge.AsObject; - static extensions: { [key: number]: jspb.ExtensionFieldInfo }; - static extensionsBinary: { [key: number]: jspb.ExtensionFieldBinaryInfo }; - static serializeBinaryToWriter(message: PlaylogJudge, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): PlaylogJudge; - static deserializeBinaryFromReader( - message: PlaylogJudge, - reader: jspb.BinaryReader, - ): PlaylogJudge; -} - -export namespace PlaylogJudge { - export type AsObject = { - judgeHeaven: number; - judgeCritical: number; - judgeJustice: number; - judgeAttack: number; - judgeMiss: number; - maxCombo: number; - }; -} diff --git a/typescript/server/src/proto/generated/chunithm/playlog_pb.js b/typescript/server/src/proto/generated/chunithm/playlog_pb.js deleted file mode 100644 index 65ba74429..000000000 --- a/typescript/server/src/proto/generated/chunithm/playlog_pb.js +++ /dev/null @@ -1,683 +0,0 @@ -// source: chunithm/playlog.proto -/** - * @fileoverview - * @enhanceable - * @suppress {missingRequire} reports error on implicit type usages. - * @suppress {messageConventions} JS Compiler reports an error if a variable or - * field starts with 'MSG_' and isn't a translatable message. - * @public - */ -// GENERATED CODE -- DO NOT EDIT! -/* eslint-disable */ -// @ts-nocheck - -var jspb = require("google-protobuf"); -var goog = jspb; -var global = function () { - if (this) { - return this; - } - if (typeof window !== "undefined") { - return window; - } - if (typeof global !== "undefined") { - return global; - } - if (typeof self !== "undefined") { - return self; - } - return Function("return this")(); -}.call(null); - -var chunithm_common_pb = require("../chunithm/common_pb.js"); -goog.object.extend(proto, chunithm_common_pb); -goog.exportSymbol("proto.mythos.chunithm.v0.PlaylogInfo", null, global); -goog.exportSymbol("proto.mythos.chunithm.v0.PlaylogJudge", null, global); -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.mythos.chunithm.v0.PlaylogInfo = function (opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.mythos.chunithm.v0.PlaylogInfo, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.mythos.chunithm.v0.PlaylogInfo.displayName = "proto.mythos.chunithm.v0.PlaylogInfo"; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.mythos.chunithm.v0.PlaylogJudge = function (opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.mythos.chunithm.v0.PlaylogJudge, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.mythos.chunithm.v0.PlaylogJudge.displayName = "proto.mythos.chunithm.v0.PlaylogJudge"; -} - -if (jspb.Message.GENERATE_TO_OBJECT) { - /** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ - proto.mythos.chunithm.v0.PlaylogInfo.prototype.toObject = function (opt_includeInstance) { - return proto.mythos.chunithm.v0.PlaylogInfo.toObject(opt_includeInstance, this); - }; - - /** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.mythos.chunithm.v0.PlaylogInfo} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ - proto.mythos.chunithm.v0.PlaylogInfo.toObject = function (includeInstance, msg) { - var f, - obj = { - musicId: jspb.Message.getFieldWithDefault(msg, 1, 0), - level: jspb.Message.getFieldWithDefault(msg, 2, 0), - score: jspb.Message.getFieldWithDefault(msg, 3, 0), - scoreRank: jspb.Message.getFieldWithDefault(msg, 4, 0), - comboStatus: jspb.Message.getFieldWithDefault(msg, 5, 0), - fullChainStatus: jspb.Message.getFieldWithDefault(msg, 6, 0), - clearStatus: jspb.Message.getFieldWithDefault(msg, 7, 0), - isNewRecord: jspb.Message.getBooleanFieldWithDefault(msg, 8, false), - track: jspb.Message.getFieldWithDefault(msg, 9, 0), - userPlayDate: jspb.Message.getFieldWithDefault(msg, 10, ""), - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; - }; -} - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.mythos.chunithm.v0.PlaylogInfo} - */ -proto.mythos.chunithm.v0.PlaylogInfo.deserializeBinary = function (bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.mythos.chunithm.v0.PlaylogInfo(); - return proto.mythos.chunithm.v0.PlaylogInfo.deserializeBinaryFromReader(msg, reader); -}; - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.mythos.chunithm.v0.PlaylogInfo} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.mythos.chunithm.v0.PlaylogInfo} - */ -proto.mythos.chunithm.v0.PlaylogInfo.deserializeBinaryFromReader = function (msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {number} */ (reader.readInt32()); - msg.setMusicId(value); - break; - case 2: - var value = /** @type {!proto.mythos.chunithm.v0.ChunithmLevel} */ ( - reader.readEnum() - ); - msg.setLevel(value); - break; - case 3: - var value = /** @type {number} */ (reader.readInt32()); - msg.setScore(value); - break; - case 4: - var value = /** @type {!proto.mythos.chunithm.v0.ChunithmScoreRank} */ ( - reader.readEnum() - ); - msg.setScoreRank(value); - break; - case 5: - var value = /** @type {!proto.mythos.chunithm.v0.ChunithmComboStatus} */ ( - reader.readEnum() - ); - msg.setComboStatus(value); - break; - case 6: - var value = /** @type {!proto.mythos.chunithm.v0.ChunithmFullChainStatus} */ ( - reader.readEnum() - ); - msg.setFullChainStatus(value); - break; - case 7: - var value = /** @type {!proto.mythos.chunithm.v0.ChunithmClearStatus} */ ( - reader.readEnum() - ); - msg.setClearStatus(value); - break; - case 8: - var value = /** @type {boolean} */ (reader.readBool()); - msg.setIsNewRecord(value); - break; - case 9: - var value = /** @type {number} */ (reader.readInt32()); - msg.setTrack(value); - break; - case 10: - var value = /** @type {string} */ (reader.readString()); - msg.setUserPlayDate(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.mythos.chunithm.v0.PlaylogInfo.prototype.serializeBinary = function () { - var writer = new jspb.BinaryWriter(); - proto.mythos.chunithm.v0.PlaylogInfo.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.mythos.chunithm.v0.PlaylogInfo} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.mythos.chunithm.v0.PlaylogInfo.serializeBinaryToWriter = function (message, writer) { - var f = undefined; - f = message.getMusicId(); - if (f !== 0) { - writer.writeInt32(1, f); - } - f = message.getLevel(); - if (f !== 0.0) { - writer.writeEnum(2, f); - } - f = message.getScore(); - if (f !== 0) { - writer.writeInt32(3, f); - } - f = message.getScoreRank(); - if (f !== 0.0) { - writer.writeEnum(4, f); - } - f = message.getComboStatus(); - if (f !== 0.0) { - writer.writeEnum(5, f); - } - f = message.getFullChainStatus(); - if (f !== 0.0) { - writer.writeEnum(6, f); - } - f = message.getClearStatus(); - if (f !== 0.0) { - writer.writeEnum(7, f); - } - f = message.getIsNewRecord(); - if (f) { - writer.writeBool(8, f); - } - f = message.getTrack(); - if (f !== 0) { - writer.writeInt32(9, f); - } - f = message.getUserPlayDate(); - if (f.length > 0) { - writer.writeString(10, f); - } -}; - -/** - * optional int32 music_id = 1; - * @return {number} - */ -proto.mythos.chunithm.v0.PlaylogInfo.prototype.getMusicId = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.chunithm.v0.PlaylogInfo} returns this - */ -proto.mythos.chunithm.v0.PlaylogInfo.prototype.setMusicId = function (value) { - return jspb.Message.setProto3IntField(this, 1, value); -}; - -/** - * optional ChunithmLevel level = 2; - * @return {!proto.mythos.chunithm.v0.ChunithmLevel} - */ -proto.mythos.chunithm.v0.PlaylogInfo.prototype.getLevel = function () { - return /** @type {!proto.mythos.chunithm.v0.ChunithmLevel} */ ( - jspb.Message.getFieldWithDefault(this, 2, 0) - ); -}; - -/** - * @param {!proto.mythos.chunithm.v0.ChunithmLevel} value - * @return {!proto.mythos.chunithm.v0.PlaylogInfo} returns this - */ -proto.mythos.chunithm.v0.PlaylogInfo.prototype.setLevel = function (value) { - return jspb.Message.setProto3EnumField(this, 2, value); -}; - -/** - * optional int32 score = 3; - * @return {number} - */ -proto.mythos.chunithm.v0.PlaylogInfo.prototype.getScore = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.chunithm.v0.PlaylogInfo} returns this - */ -proto.mythos.chunithm.v0.PlaylogInfo.prototype.setScore = function (value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - -/** - * optional ChunithmScoreRank score_rank = 4; - * @return {!proto.mythos.chunithm.v0.ChunithmScoreRank} - */ -proto.mythos.chunithm.v0.PlaylogInfo.prototype.getScoreRank = function () { - return /** @type {!proto.mythos.chunithm.v0.ChunithmScoreRank} */ ( - jspb.Message.getFieldWithDefault(this, 4, 0) - ); -}; - -/** - * @param {!proto.mythos.chunithm.v0.ChunithmScoreRank} value - * @return {!proto.mythos.chunithm.v0.PlaylogInfo} returns this - */ -proto.mythos.chunithm.v0.PlaylogInfo.prototype.setScoreRank = function (value) { - return jspb.Message.setProto3EnumField(this, 4, value); -}; - -/** - * optional ChunithmComboStatus combo_status = 5; - * @return {!proto.mythos.chunithm.v0.ChunithmComboStatus} - */ -proto.mythos.chunithm.v0.PlaylogInfo.prototype.getComboStatus = function () { - return /** @type {!proto.mythos.chunithm.v0.ChunithmComboStatus} */ ( - jspb.Message.getFieldWithDefault(this, 5, 0) - ); -}; - -/** - * @param {!proto.mythos.chunithm.v0.ChunithmComboStatus} value - * @return {!proto.mythos.chunithm.v0.PlaylogInfo} returns this - */ -proto.mythos.chunithm.v0.PlaylogInfo.prototype.setComboStatus = function (value) { - return jspb.Message.setProto3EnumField(this, 5, value); -}; - -/** - * optional ChunithmFullChainStatus full_chain_status = 6; - * @return {!proto.mythos.chunithm.v0.ChunithmFullChainStatus} - */ -proto.mythos.chunithm.v0.PlaylogInfo.prototype.getFullChainStatus = function () { - return /** @type {!proto.mythos.chunithm.v0.ChunithmFullChainStatus} */ ( - jspb.Message.getFieldWithDefault(this, 6, 0) - ); -}; - -/** - * @param {!proto.mythos.chunithm.v0.ChunithmFullChainStatus} value - * @return {!proto.mythos.chunithm.v0.PlaylogInfo} returns this - */ -proto.mythos.chunithm.v0.PlaylogInfo.prototype.setFullChainStatus = function (value) { - return jspb.Message.setProto3EnumField(this, 6, value); -}; - -/** - * optional ChunithmClearStatus clear_status = 7; - * @return {!proto.mythos.chunithm.v0.ChunithmClearStatus} - */ -proto.mythos.chunithm.v0.PlaylogInfo.prototype.getClearStatus = function () { - return /** @type {!proto.mythos.chunithm.v0.ChunithmClearStatus} */ ( - jspb.Message.getFieldWithDefault(this, 7, 0) - ); -}; - -/** - * @param {!proto.mythos.chunithm.v0.ChunithmClearStatus} value - * @return {!proto.mythos.chunithm.v0.PlaylogInfo} returns this - */ -proto.mythos.chunithm.v0.PlaylogInfo.prototype.setClearStatus = function (value) { - return jspb.Message.setProto3EnumField(this, 7, value); -}; - -/** - * optional bool is_new_record = 8; - * @return {boolean} - */ -proto.mythos.chunithm.v0.PlaylogInfo.prototype.getIsNewRecord = function () { - return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 8, false)); -}; - -/** - * @param {boolean} value - * @return {!proto.mythos.chunithm.v0.PlaylogInfo} returns this - */ -proto.mythos.chunithm.v0.PlaylogInfo.prototype.setIsNewRecord = function (value) { - return jspb.Message.setProto3BooleanField(this, 8, value); -}; - -/** - * optional int32 track = 9; - * @return {number} - */ -proto.mythos.chunithm.v0.PlaylogInfo.prototype.getTrack = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 9, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.chunithm.v0.PlaylogInfo} returns this - */ -proto.mythos.chunithm.v0.PlaylogInfo.prototype.setTrack = function (value) { - return jspb.Message.setProto3IntField(this, 9, value); -}; - -/** - * optional string user_play_date = 10; - * @return {string} - */ -proto.mythos.chunithm.v0.PlaylogInfo.prototype.getUserPlayDate = function () { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 10, "")); -}; - -/** - * @param {string} value - * @return {!proto.mythos.chunithm.v0.PlaylogInfo} returns this - */ -proto.mythos.chunithm.v0.PlaylogInfo.prototype.setUserPlayDate = function (value) { - return jspb.Message.setProto3StringField(this, 10, value); -}; - -if (jspb.Message.GENERATE_TO_OBJECT) { - /** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ - proto.mythos.chunithm.v0.PlaylogJudge.prototype.toObject = function (opt_includeInstance) { - return proto.mythos.chunithm.v0.PlaylogJudge.toObject(opt_includeInstance, this); - }; - - /** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.mythos.chunithm.v0.PlaylogJudge} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ - proto.mythos.chunithm.v0.PlaylogJudge.toObject = function (includeInstance, msg) { - var f, - obj = { - judgeHeaven: jspb.Message.getFieldWithDefault(msg, 1, 0), - judgeCritical: jspb.Message.getFieldWithDefault(msg, 2, 0), - judgeJustice: jspb.Message.getFieldWithDefault(msg, 3, 0), - judgeAttack: jspb.Message.getFieldWithDefault(msg, 4, 0), - judgeMiss: jspb.Message.getFieldWithDefault(msg, 5, 0), - maxCombo: jspb.Message.getFieldWithDefault(msg, 6, 0), - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; - }; -} - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.mythos.chunithm.v0.PlaylogJudge} - */ -proto.mythos.chunithm.v0.PlaylogJudge.deserializeBinary = function (bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.mythos.chunithm.v0.PlaylogJudge(); - return proto.mythos.chunithm.v0.PlaylogJudge.deserializeBinaryFromReader(msg, reader); -}; - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.mythos.chunithm.v0.PlaylogJudge} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.mythos.chunithm.v0.PlaylogJudge} - */ -proto.mythos.chunithm.v0.PlaylogJudge.deserializeBinaryFromReader = function (msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {number} */ (reader.readInt32()); - msg.setJudgeHeaven(value); - break; - case 2: - var value = /** @type {number} */ (reader.readInt32()); - msg.setJudgeCritical(value); - break; - case 3: - var value = /** @type {number} */ (reader.readInt32()); - msg.setJudgeJustice(value); - break; - case 4: - var value = /** @type {number} */ (reader.readInt32()); - msg.setJudgeAttack(value); - break; - case 5: - var value = /** @type {number} */ (reader.readInt32()); - msg.setJudgeMiss(value); - break; - case 6: - var value = /** @type {number} */ (reader.readInt32()); - msg.setMaxCombo(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.mythos.chunithm.v0.PlaylogJudge.prototype.serializeBinary = function () { - var writer = new jspb.BinaryWriter(); - proto.mythos.chunithm.v0.PlaylogJudge.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.mythos.chunithm.v0.PlaylogJudge} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.mythos.chunithm.v0.PlaylogJudge.serializeBinaryToWriter = function (message, writer) { - var f = undefined; - f = message.getJudgeHeaven(); - if (f !== 0) { - writer.writeInt32(1, f); - } - f = message.getJudgeCritical(); - if (f !== 0) { - writer.writeInt32(2, f); - } - f = message.getJudgeJustice(); - if (f !== 0) { - writer.writeInt32(3, f); - } - f = message.getJudgeAttack(); - if (f !== 0) { - writer.writeInt32(4, f); - } - f = message.getJudgeMiss(); - if (f !== 0) { - writer.writeInt32(5, f); - } - f = message.getMaxCombo(); - if (f !== 0) { - writer.writeInt32(6, f); - } -}; - -/** - * optional int32 judge_heaven = 1; - * @return {number} - */ -proto.mythos.chunithm.v0.PlaylogJudge.prototype.getJudgeHeaven = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.chunithm.v0.PlaylogJudge} returns this - */ -proto.mythos.chunithm.v0.PlaylogJudge.prototype.setJudgeHeaven = function (value) { - return jspb.Message.setProto3IntField(this, 1, value); -}; - -/** - * optional int32 judge_critical = 2; - * @return {number} - */ -proto.mythos.chunithm.v0.PlaylogJudge.prototype.getJudgeCritical = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.chunithm.v0.PlaylogJudge} returns this - */ -proto.mythos.chunithm.v0.PlaylogJudge.prototype.setJudgeCritical = function (value) { - return jspb.Message.setProto3IntField(this, 2, value); -}; - -/** - * optional int32 judge_justice = 3; - * @return {number} - */ -proto.mythos.chunithm.v0.PlaylogJudge.prototype.getJudgeJustice = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.chunithm.v0.PlaylogJudge} returns this - */ -proto.mythos.chunithm.v0.PlaylogJudge.prototype.setJudgeJustice = function (value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - -/** - * optional int32 judge_attack = 4; - * @return {number} - */ -proto.mythos.chunithm.v0.PlaylogJudge.prototype.getJudgeAttack = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.chunithm.v0.PlaylogJudge} returns this - */ -proto.mythos.chunithm.v0.PlaylogJudge.prototype.setJudgeAttack = function (value) { - return jspb.Message.setProto3IntField(this, 4, value); -}; - -/** - * optional int32 judge_miss = 5; - * @return {number} - */ -proto.mythos.chunithm.v0.PlaylogJudge.prototype.getJudgeMiss = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 5, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.chunithm.v0.PlaylogJudge} returns this - */ -proto.mythos.chunithm.v0.PlaylogJudge.prototype.setJudgeMiss = function (value) { - return jspb.Message.setProto3IntField(this, 5, value); -}; - -/** - * optional int32 max_combo = 6; - * @return {number} - */ -proto.mythos.chunithm.v0.PlaylogJudge.prototype.getMaxCombo = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 6, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.chunithm.v0.PlaylogJudge} returns this - */ -proto.mythos.chunithm.v0.PlaylogJudge.prototype.setMaxCombo = function (value) { - return jspb.Message.setProto3IntField(this, 6, value); -}; - -goog.object.extend(exports, proto.mythos.chunithm.v0); diff --git a/typescript/server/src/proto/generated/chunithm/playlog_pb.ts b/typescript/server/src/proto/generated/chunithm/playlog_pb.ts new file mode 100644 index 000000000..91b2944f1 --- /dev/null +++ b/typescript/server/src/proto/generated/chunithm/playlog_pb.ts @@ -0,0 +1,137 @@ +// @generated by protoc-gen-es v2.11.0 with parameter "target=ts" +// @generated from file chunithm/playlog.proto (package mythos.chunithm.v0, syntax proto3) +/* eslint-disable */ + +import type { GenFile, GenMessage } from "@bufbuild/protobuf/codegenv2"; +import { fileDesc, messageDesc } from "@bufbuild/protobuf/codegenv2"; +import type { + ChunithmClearStatus, + ChunithmComboStatus, + ChunithmFullChainStatus, + ChunithmLevel, + ChunithmScoreRank, +} from "./common_pb"; +import { file_chunithm_common } from "./common_pb"; +import type { Message } from "@bufbuild/protobuf"; + +/** + * Describes the file chunithm/playlog.proto. + */ +export const file_chunithm_playlog: GenFile = + /*@__PURE__*/ + fileDesc( + "ChZjaHVuaXRobS9wbGF5bG9nLnByb3RvEhJteXRob3MuY2h1bml0aG0udjAinwMKC1BsYXlsb2dJbmZvEhAKCG11c2ljX2lkGAEgASgFEjAKBWxldmVsGAIgASgOMiEubXl0aG9zLmNodW5pdGhtLnYwLkNodW5pdGhtTGV2ZWwSDQoFc2NvcmUYAyABKAUSOQoKc2NvcmVfcmFuaxgEIAEoDjIlLm15dGhvcy5jaHVuaXRobS52MC5DaHVuaXRobVNjb3JlUmFuaxI9Cgxjb21ib19zdGF0dXMYBSABKA4yJy5teXRob3MuY2h1bml0aG0udjAuQ2h1bml0aG1Db21ib1N0YXR1cxJGChFmdWxsX2NoYWluX3N0YXR1cxgGIAEoDjIrLm15dGhvcy5jaHVuaXRobS52MC5DaHVuaXRobUZ1bGxDaGFpblN0YXR1cxI9CgxjbGVhcl9zdGF0dXMYByABKA4yJy5teXRob3MuY2h1bml0aG0udjAuQ2h1bml0aG1DbGVhclN0YXR1cxIVCg1pc19uZXdfcmVjb3JkGAggASgIEg0KBXRyYWNrGAkgASgFEhYKDnVzZXJfcGxheV9kYXRlGAogASgJIpABCgxQbGF5bG9nSnVkZ2USFAoManVkZ2VfaGVhdmVuGAEgASgFEhYKDmp1ZGdlX2NyaXRpY2FsGAIgASgFEhUKDWp1ZGdlX2p1c3RpY2UYAyABKAUSFAoManVkZ2VfYXR0YWNrGAQgASgFEhIKCmp1ZGdlX21pc3MYBSABKAUSEQoJbWF4X2NvbWJvGAYgASgFYgZwcm90bzM", + [file_chunithm_common], + ); + +/** + * @generated from message mythos.chunithm.v0.PlaylogInfo + */ +export type PlaylogInfo = Message<"mythos.chunithm.v0.PlaylogInfo"> & { + /** + * @generated from field: int32 music_id = 1; + */ + musicId: number; + + /** + * @generated from field: mythos.chunithm.v0.ChunithmLevel level = 2; + */ + level: ChunithmLevel; + + /** + * @generated from field: int32 score = 3; + */ + score: number; + + /** + * rank + * + * @generated from field: mythos.chunithm.v0.ChunithmScoreRank score_rank = 4; + */ + scoreRank: ChunithmScoreRank; + + /** + * @generated from field: mythos.chunithm.v0.ChunithmComboStatus combo_status = 5; + */ + comboStatus: ChunithmComboStatus; + + /** + * full_chain_kind + * + * @generated from field: mythos.chunithm.v0.ChunithmFullChainStatus full_chain_status = 6; + */ + fullChainStatus: ChunithmFullChainStatus; + + /** + * @generated from field: mythos.chunithm.v0.ChunithmClearStatus clear_status = 7; + */ + clearStatus: ChunithmClearStatus; + + /** + * @generated from field: bool is_new_record = 8; + */ + isNewRecord: boolean; + + /** + * @generated from field: int32 track = 9; + */ + track: number; + + /** + * @generated from field: string user_play_date = 10; + */ + userPlayDate: string; +}; + +/** + * Describes the message mythos.chunithm.v0.PlaylogInfo. + * Use `create(PlaylogInfoSchema)` to create a new message. + */ +export const PlaylogInfoSchema: GenMessage = + /*@__PURE__*/ + messageDesc(file_chunithm_playlog, 0); + +/** + * @generated from message mythos.chunithm.v0.PlaylogJudge + */ +export type PlaylogJudge = Message<"mythos.chunithm.v0.PlaylogJudge"> & { + /** + * @generated from field: int32 judge_heaven = 1; + */ + judgeHeaven: number; + + /** + * @generated from field: int32 judge_critical = 2; + */ + judgeCritical: number; + + /** + * @generated from field: int32 judge_justice = 3; + */ + judgeJustice: number; + + /** + * @generated from field: int32 judge_attack = 4; + */ + judgeAttack: number; + + /** + * judge_guilty + * + * @generated from field: int32 judge_miss = 5; + */ + judgeMiss: number; + + /** + * @generated from field: int32 max_combo = 6; + */ + maxCombo: number; +}; + +/** + * Describes the message mythos.chunithm.v0.PlaylogJudge. + * Use `create(PlaylogJudgeSchema)` to create a new message. + */ +export const PlaylogJudgeSchema: GenMessage = + /*@__PURE__*/ + messageDesc(file_chunithm_playlog, 1); diff --git a/typescript/server/src/proto/generated/chunithm/user_connect.ts b/typescript/server/src/proto/generated/chunithm/user_connect.ts new file mode 100644 index 000000000..39f8154e9 --- /dev/null +++ b/typescript/server/src/proto/generated/chunithm/user_connect.ts @@ -0,0 +1,25 @@ +// @generated by protoc-gen-connect-es v1.7.0 with parameter "target=ts" +// @generated from file chunithm/user.proto (package mythos.chunithm.v0, syntax proto3) +/* eslint-disable */ +// @ts-nocheck + +import { GetPlaylogRequest, GetPlaylogStreamItem } from "./user_pb.js"; +import { MethodKind } from "@bufbuild/protobuf"; + +/** + * @generated from service mythos.chunithm.v0.ChunithmUser + */ +export const ChunithmUser = { + typeName: "mythos.chunithm.v0.ChunithmUser", + methods: { + /** + * @generated from rpc mythos.chunithm.v0.ChunithmUser.GetPlaylog + */ + getPlaylog: { + name: "GetPlaylog", + I: GetPlaylogRequest, + O: GetPlaylogStreamItem, + kind: MethodKind.ServerStreaming, + }, + }, +} as const; diff --git a/typescript/server/src/proto/generated/chunithm/user_grpc_pb.d.ts b/typescript/server/src/proto/generated/chunithm/user_grpc_pb.d.ts deleted file mode 100644 index b6af87cb6..000000000 --- a/typescript/server/src/proto/generated/chunithm/user_grpc_pb.d.ts +++ /dev/null @@ -1,36 +0,0 @@ -// GENERATED CODE -- DO NOT EDIT! - -// package: mythos.chunithm.v0 -// file: chunithm/user.proto - -import * as chunithm_user_pb from "../chunithm/user_pb"; -import * as grpc from "@grpc/grpc-js"; - -interface IChunithmUserService extends grpc.ServiceDefinition { - getPlaylog: grpc.MethodDefinition< - chunithm_user_pb.GetPlaylogRequest, - chunithm_user_pb.GetPlaylogStreamItem - >; -} - -export const ChunithmUserService: IChunithmUserService; - -export interface IChunithmUserServer extends grpc.UntypedServiceImplementation { - getPlaylog: grpc.handleServerStreamingCall< - chunithm_user_pb.GetPlaylogRequest, - chunithm_user_pb.GetPlaylogStreamItem - >; -} - -export class ChunithmUserClient extends grpc.Client { - constructor(address: string, credentials: grpc.ChannelCredentials, options?: object); - getPlaylog( - argument: chunithm_user_pb.GetPlaylogRequest, - metadataOrOptions?: grpc.Metadata | grpc.CallOptions | null, - ): grpc.ClientReadableStream; - getPlaylog( - argument: chunithm_user_pb.GetPlaylogRequest, - metadata?: grpc.Metadata | null, - options?: grpc.CallOptions | null, - ): grpc.ClientReadableStream; -} diff --git a/typescript/server/src/proto/generated/chunithm/user_grpc_pb.js b/typescript/server/src/proto/generated/chunithm/user_grpc_pb.js deleted file mode 100644 index b5c9fd158..000000000 --- a/typescript/server/src/proto/generated/chunithm/user_grpc_pb.js +++ /dev/null @@ -1,44 +0,0 @@ -// GENERATED CODE -- DO NOT EDIT! - -"use strict"; -var grpc = require("@grpc/grpc-js"); -var chunithm_user_pb = require("../chunithm/user_pb.js"); -var chunithm_playlog_pb = require("../chunithm/playlog_pb.js"); - -function serialize_mythos_chunithm_v0_GetPlaylogRequest(arg) { - if (!(arg instanceof chunithm_user_pb.GetPlaylogRequest)) { - throw new Error("Expected argument of type mythos.chunithm.v0.GetPlaylogRequest"); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_mythos_chunithm_v0_GetPlaylogRequest(buffer_arg) { - return chunithm_user_pb.GetPlaylogRequest.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_mythos_chunithm_v0_GetPlaylogStreamItem(arg) { - if (!(arg instanceof chunithm_user_pb.GetPlaylogStreamItem)) { - throw new Error("Expected argument of type mythos.chunithm.v0.GetPlaylogStreamItem"); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_mythos_chunithm_v0_GetPlaylogStreamItem(buffer_arg) { - return chunithm_user_pb.GetPlaylogStreamItem.deserializeBinary(new Uint8Array(buffer_arg)); -} - -var ChunithmUserService = (exports.ChunithmUserService = { - getPlaylog: { - path: "/mythos.chunithm.v0.ChunithmUser/GetPlaylog", - requestStream: false, - responseStream: true, - requestType: chunithm_user_pb.GetPlaylogRequest, - responseType: chunithm_user_pb.GetPlaylogStreamItem, - requestSerialize: serialize_mythos_chunithm_v0_GetPlaylogRequest, - requestDeserialize: deserialize_mythos_chunithm_v0_GetPlaylogRequest, - responseSerialize: serialize_mythos_chunithm_v0_GetPlaylogStreamItem, - responseDeserialize: deserialize_mythos_chunithm_v0_GetPlaylogStreamItem, - }, -}); - -exports.ChunithmUserClient = grpc.makeGenericClientConstructor(ChunithmUserService, "ChunithmUser"); diff --git a/typescript/server/src/proto/generated/chunithm/user_pb.d.ts b/typescript/server/src/proto/generated/chunithm/user_pb.d.ts deleted file mode 100644 index 76f4adc85..000000000 --- a/typescript/server/src/proto/generated/chunithm/user_pb.d.ts +++ /dev/null @@ -1,78 +0,0 @@ -// package: mythos.chunithm.v0 -// file: chunithm/user.proto - -import * as jspb from "google-protobuf"; -import * as chunithm_playlog_pb from "../chunithm/playlog_pb"; - -export class GetPlaylogRequest extends jspb.Message { - getProfileApiId(): string; - setProfileApiId(value: string): void; - - hasLastUserPlayDate(): boolean; - clearLastUserPlayDate(): void; - getLastUserPlayDate(): string; - setLastUserPlayDate(value: string): void; - - hasLimit(): boolean; - clearLimit(): void; - getLimit(): number; - setLimit(value: number): void; - - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): GetPlaylogRequest.AsObject; - static toObject(includeInstance: boolean, msg: GetPlaylogRequest): GetPlaylogRequest.AsObject; - static extensions: { [key: number]: jspb.ExtensionFieldInfo }; - static extensionsBinary: { [key: number]: jspb.ExtensionFieldBinaryInfo }; - static serializeBinaryToWriter(message: GetPlaylogRequest, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): GetPlaylogRequest; - static deserializeBinaryFromReader( - message: GetPlaylogRequest, - reader: jspb.BinaryReader, - ): GetPlaylogRequest; -} - -export namespace GetPlaylogRequest { - export type AsObject = { - profileApiId: string; - lastUserPlayDate: string; - limit: number; - }; -} - -export class GetPlaylogStreamItem extends jspb.Message { - getPlaylogApiId(): string; - setPlaylogApiId(value: string): void; - - hasInfo(): boolean; - clearInfo(): void; - getInfo(): chunithm_playlog_pb.PlaylogInfo | undefined; - setInfo(value?: chunithm_playlog_pb.PlaylogInfo): void; - - hasJudge(): boolean; - clearJudge(): void; - getJudge(): chunithm_playlog_pb.PlaylogJudge | undefined; - setJudge(value?: chunithm_playlog_pb.PlaylogJudge): void; - - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): GetPlaylogStreamItem.AsObject; - static toObject( - includeInstance: boolean, - msg: GetPlaylogStreamItem, - ): GetPlaylogStreamItem.AsObject; - static extensions: { [key: number]: jspb.ExtensionFieldInfo }; - static extensionsBinary: { [key: number]: jspb.ExtensionFieldBinaryInfo }; - static serializeBinaryToWriter(message: GetPlaylogStreamItem, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): GetPlaylogStreamItem; - static deserializeBinaryFromReader( - message: GetPlaylogStreamItem, - reader: jspb.BinaryReader, - ): GetPlaylogStreamItem; -} - -export namespace GetPlaylogStreamItem { - export type AsObject = { - playlogApiId: string; - info?: chunithm_playlog_pb.PlaylogInfo.AsObject; - judge?: chunithm_playlog_pb.PlaylogJudge.AsObject; - }; -} diff --git a/typescript/server/src/proto/generated/chunithm/user_pb.js b/typescript/server/src/proto/generated/chunithm/user_pb.js deleted file mode 100644 index c561ec2c6..000000000 --- a/typescript/server/src/proto/generated/chunithm/user_pb.js +++ /dev/null @@ -1,497 +0,0 @@ -// source: chunithm/user.proto -/** - * @fileoverview - * @enhanceable - * @suppress {missingRequire} reports error on implicit type usages. - * @suppress {messageConventions} JS Compiler reports an error if a variable or - * field starts with 'MSG_' and isn't a translatable message. - * @public - */ -// GENERATED CODE -- DO NOT EDIT! -/* eslint-disable */ -// @ts-nocheck - -var jspb = require("google-protobuf"); -var goog = jspb; -var global = function () { - if (this) { - return this; - } - if (typeof window !== "undefined") { - return window; - } - if (typeof global !== "undefined") { - return global; - } - if (typeof self !== "undefined") { - return self; - } - return Function("return this")(); -}.call(null); - -var chunithm_playlog_pb = require("../chunithm/playlog_pb.js"); -goog.object.extend(proto, chunithm_playlog_pb); -goog.exportSymbol("proto.mythos.chunithm.v0.GetPlaylogRequest", null, global); -goog.exportSymbol("proto.mythos.chunithm.v0.GetPlaylogStreamItem", null, global); -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.mythos.chunithm.v0.GetPlaylogRequest = function (opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.mythos.chunithm.v0.GetPlaylogRequest, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.mythos.chunithm.v0.GetPlaylogRequest.displayName = - "proto.mythos.chunithm.v0.GetPlaylogRequest"; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.mythos.chunithm.v0.GetPlaylogStreamItem = function (opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.mythos.chunithm.v0.GetPlaylogStreamItem, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.mythos.chunithm.v0.GetPlaylogStreamItem.displayName = - "proto.mythos.chunithm.v0.GetPlaylogStreamItem"; -} - -if (jspb.Message.GENERATE_TO_OBJECT) { - /** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ - proto.mythos.chunithm.v0.GetPlaylogRequest.prototype.toObject = function (opt_includeInstance) { - return proto.mythos.chunithm.v0.GetPlaylogRequest.toObject(opt_includeInstance, this); - }; - - /** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.mythos.chunithm.v0.GetPlaylogRequest} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ - proto.mythos.chunithm.v0.GetPlaylogRequest.toObject = function (includeInstance, msg) { - var f, - obj = { - profileApiId: jspb.Message.getFieldWithDefault(msg, 1, ""), - lastUserPlayDate: jspb.Message.getFieldWithDefault(msg, 2, ""), - limit: jspb.Message.getFieldWithDefault(msg, 3, 0), - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; - }; -} - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.mythos.chunithm.v0.GetPlaylogRequest} - */ -proto.mythos.chunithm.v0.GetPlaylogRequest.deserializeBinary = function (bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.mythos.chunithm.v0.GetPlaylogRequest(); - return proto.mythos.chunithm.v0.GetPlaylogRequest.deserializeBinaryFromReader(msg, reader); -}; - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.mythos.chunithm.v0.GetPlaylogRequest} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.mythos.chunithm.v0.GetPlaylogRequest} - */ -proto.mythos.chunithm.v0.GetPlaylogRequest.deserializeBinaryFromReader = function (msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {string} */ (reader.readString()); - msg.setProfileApiId(value); - break; - case 2: - var value = /** @type {string} */ (reader.readString()); - msg.setLastUserPlayDate(value); - break; - case 3: - var value = /** @type {number} */ (reader.readUint64()); - msg.setLimit(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.mythos.chunithm.v0.GetPlaylogRequest.prototype.serializeBinary = function () { - var writer = new jspb.BinaryWriter(); - proto.mythos.chunithm.v0.GetPlaylogRequest.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.mythos.chunithm.v0.GetPlaylogRequest} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.mythos.chunithm.v0.GetPlaylogRequest.serializeBinaryToWriter = function (message, writer) { - var f = undefined; - f = message.getProfileApiId(); - if (f.length > 0) { - writer.writeString(1, f); - } - f = /** @type {string} */ (jspb.Message.getField(message, 2)); - if (f != null) { - writer.writeString(2, f); - } - f = /** @type {number} */ (jspb.Message.getField(message, 3)); - if (f != null) { - writer.writeUint64(3, f); - } -}; - -/** - * optional string profile_api_id = 1; - * @return {string} - */ -proto.mythos.chunithm.v0.GetPlaylogRequest.prototype.getProfileApiId = function () { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); -}; - -/** - * @param {string} value - * @return {!proto.mythos.chunithm.v0.GetPlaylogRequest} returns this - */ -proto.mythos.chunithm.v0.GetPlaylogRequest.prototype.setProfileApiId = function (value) { - return jspb.Message.setProto3StringField(this, 1, value); -}; - -/** - * optional string last_user_play_date = 2; - * @return {string} - */ -proto.mythos.chunithm.v0.GetPlaylogRequest.prototype.getLastUserPlayDate = function () { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); -}; - -/** - * @param {string} value - * @return {!proto.mythos.chunithm.v0.GetPlaylogRequest} returns this - */ -proto.mythos.chunithm.v0.GetPlaylogRequest.prototype.setLastUserPlayDate = function (value) { - return jspb.Message.setField(this, 2, value); -}; - -/** - * Clears the field making it undefined. - * @return {!proto.mythos.chunithm.v0.GetPlaylogRequest} returns this - */ -proto.mythos.chunithm.v0.GetPlaylogRequest.prototype.clearLastUserPlayDate = function () { - return jspb.Message.setField(this, 2, undefined); -}; - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.mythos.chunithm.v0.GetPlaylogRequest.prototype.hasLastUserPlayDate = function () { - return jspb.Message.getField(this, 2) != null; -}; - -/** - * optional uint64 limit = 3; - * @return {number} - */ -proto.mythos.chunithm.v0.GetPlaylogRequest.prototype.getLimit = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.chunithm.v0.GetPlaylogRequest} returns this - */ -proto.mythos.chunithm.v0.GetPlaylogRequest.prototype.setLimit = function (value) { - return jspb.Message.setField(this, 3, value); -}; - -/** - * Clears the field making it undefined. - * @return {!proto.mythos.chunithm.v0.GetPlaylogRequest} returns this - */ -proto.mythos.chunithm.v0.GetPlaylogRequest.prototype.clearLimit = function () { - return jspb.Message.setField(this, 3, undefined); -}; - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.mythos.chunithm.v0.GetPlaylogRequest.prototype.hasLimit = function () { - return jspb.Message.getField(this, 3) != null; -}; - -if (jspb.Message.GENERATE_TO_OBJECT) { - /** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ - proto.mythos.chunithm.v0.GetPlaylogStreamItem.prototype.toObject = function ( - opt_includeInstance, - ) { - return proto.mythos.chunithm.v0.GetPlaylogStreamItem.toObject(opt_includeInstance, this); - }; - - /** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.mythos.chunithm.v0.GetPlaylogStreamItem} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ - proto.mythos.chunithm.v0.GetPlaylogStreamItem.toObject = function (includeInstance, msg) { - var f, - obj = { - playlogApiId: jspb.Message.getFieldWithDefault(msg, 1, ""), - info: - (f = msg.getInfo()) && - chunithm_playlog_pb.PlaylogInfo.toObject(includeInstance, f), - judge: - (f = msg.getJudge()) && - chunithm_playlog_pb.PlaylogJudge.toObject(includeInstance, f), - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; - }; -} - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.mythos.chunithm.v0.GetPlaylogStreamItem} - */ -proto.mythos.chunithm.v0.GetPlaylogStreamItem.deserializeBinary = function (bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.mythos.chunithm.v0.GetPlaylogStreamItem(); - return proto.mythos.chunithm.v0.GetPlaylogStreamItem.deserializeBinaryFromReader(msg, reader); -}; - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.mythos.chunithm.v0.GetPlaylogStreamItem} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.mythos.chunithm.v0.GetPlaylogStreamItem} - */ -proto.mythos.chunithm.v0.GetPlaylogStreamItem.deserializeBinaryFromReader = function (msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {string} */ (reader.readString()); - msg.setPlaylogApiId(value); - break; - case 2: - var value = new chunithm_playlog_pb.PlaylogInfo(); - reader.readMessage( - value, - chunithm_playlog_pb.PlaylogInfo.deserializeBinaryFromReader, - ); - msg.setInfo(value); - break; - case 3: - var value = new chunithm_playlog_pb.PlaylogJudge(); - reader.readMessage( - value, - chunithm_playlog_pb.PlaylogJudge.deserializeBinaryFromReader, - ); - msg.setJudge(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.mythos.chunithm.v0.GetPlaylogStreamItem.prototype.serializeBinary = function () { - var writer = new jspb.BinaryWriter(); - proto.mythos.chunithm.v0.GetPlaylogStreamItem.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.mythos.chunithm.v0.GetPlaylogStreamItem} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.mythos.chunithm.v0.GetPlaylogStreamItem.serializeBinaryToWriter = function (message, writer) { - var f = undefined; - f = message.getPlaylogApiId(); - if (f.length > 0) { - writer.writeString(1, f); - } - f = message.getInfo(); - if (f != null) { - writer.writeMessage(2, f, chunithm_playlog_pb.PlaylogInfo.serializeBinaryToWriter); - } - f = message.getJudge(); - if (f != null) { - writer.writeMessage(3, f, chunithm_playlog_pb.PlaylogJudge.serializeBinaryToWriter); - } -}; - -/** - * optional string playlog_api_id = 1; - * @return {string} - */ -proto.mythos.chunithm.v0.GetPlaylogStreamItem.prototype.getPlaylogApiId = function () { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); -}; - -/** - * @param {string} value - * @return {!proto.mythos.chunithm.v0.GetPlaylogStreamItem} returns this - */ -proto.mythos.chunithm.v0.GetPlaylogStreamItem.prototype.setPlaylogApiId = function (value) { - return jspb.Message.setProto3StringField(this, 1, value); -}; - -/** - * optional PlaylogInfo info = 2; - * @return {?proto.mythos.chunithm.v0.PlaylogInfo} - */ -proto.mythos.chunithm.v0.GetPlaylogStreamItem.prototype.getInfo = function () { - return /** @type{?proto.mythos.chunithm.v0.PlaylogInfo} */ ( - jspb.Message.getWrapperField(this, chunithm_playlog_pb.PlaylogInfo, 2) - ); -}; - -/** - * @param {?proto.mythos.chunithm.v0.PlaylogInfo|undefined} value - * @return {!proto.mythos.chunithm.v0.GetPlaylogStreamItem} returns this - */ -proto.mythos.chunithm.v0.GetPlaylogStreamItem.prototype.setInfo = function (value) { - return jspb.Message.setWrapperField(this, 2, value); -}; - -/** - * Clears the message field making it undefined. - * @return {!proto.mythos.chunithm.v0.GetPlaylogStreamItem} returns this - */ -proto.mythos.chunithm.v0.GetPlaylogStreamItem.prototype.clearInfo = function () { - return this.setInfo(undefined); -}; - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.mythos.chunithm.v0.GetPlaylogStreamItem.prototype.hasInfo = function () { - return jspb.Message.getField(this, 2) != null; -}; - -/** - * optional PlaylogJudge judge = 3; - * @return {?proto.mythos.chunithm.v0.PlaylogJudge} - */ -proto.mythos.chunithm.v0.GetPlaylogStreamItem.prototype.getJudge = function () { - return /** @type{?proto.mythos.chunithm.v0.PlaylogJudge} */ ( - jspb.Message.getWrapperField(this, chunithm_playlog_pb.PlaylogJudge, 3) - ); -}; - -/** - * @param {?proto.mythos.chunithm.v0.PlaylogJudge|undefined} value - * @return {!proto.mythos.chunithm.v0.GetPlaylogStreamItem} returns this - */ -proto.mythos.chunithm.v0.GetPlaylogStreamItem.prototype.setJudge = function (value) { - return jspb.Message.setWrapperField(this, 3, value); -}; - -/** - * Clears the message field making it undefined. - * @return {!proto.mythos.chunithm.v0.GetPlaylogStreamItem} returns this - */ -proto.mythos.chunithm.v0.GetPlaylogStreamItem.prototype.clearJudge = function () { - return this.setJudge(undefined); -}; - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.mythos.chunithm.v0.GetPlaylogStreamItem.prototype.hasJudge = function () { - return jspb.Message.getField(this, 3) != null; -}; - -goog.object.extend(exports, proto.mythos.chunithm.v0); diff --git a/typescript/server/src/proto/generated/chunithm/user_pb.ts b/typescript/server/src/proto/generated/chunithm/user_pb.ts new file mode 100644 index 000000000..3e2a5f3b3 --- /dev/null +++ b/typescript/server/src/proto/generated/chunithm/user_pb.ts @@ -0,0 +1,89 @@ +// @generated by protoc-gen-es v2.11.0 with parameter "target=ts" +// @generated from file chunithm/user.proto (package mythos.chunithm.v0, syntax proto3) +/* eslint-disable */ + +import type { GenFile, GenMessage, GenService } from "@bufbuild/protobuf/codegenv2"; +import { fileDesc, messageDesc, serviceDesc } from "@bufbuild/protobuf/codegenv2"; +import type { PlaylogInfo, PlaylogJudge } from "./playlog_pb"; +import { file_chunithm_playlog } from "./playlog_pb"; +import type { Message } from "@bufbuild/protobuf"; + +/** + * Describes the file chunithm/user.proto. + */ +export const file_chunithm_user: GenFile = + /*@__PURE__*/ + fileDesc( + "ChNjaHVuaXRobS91c2VyLnByb3RvEhJteXRob3MuY2h1bml0aG0udjAigwEKEUdldFBsYXlsb2dSZXF1ZXN0EhYKDnByb2ZpbGVfYXBpX2lkGAEgASgJEiAKE2xhc3RfdXNlcl9wbGF5X2RhdGUYAiABKAlIAIgBARISCgVsaW1pdBgDIAEoBEgBiAEBQhYKFF9sYXN0X3VzZXJfcGxheV9kYXRlQggKBl9saW1pdCKOAQoUR2V0UGxheWxvZ1N0cmVhbUl0ZW0SFgoOcGxheWxvZ19hcGlfaWQYASABKAkSLQoEaW5mbxgCIAEoCzIfLm15dGhvcy5jaHVuaXRobS52MC5QbGF5bG9nSW5mbxIvCgVqdWRnZRgDIAEoCzIgLm15dGhvcy5jaHVuaXRobS52MC5QbGF5bG9nSnVkZ2UybwoMQ2h1bml0aG1Vc2VyEl8KCkdldFBsYXlsb2cSJS5teXRob3MuY2h1bml0aG0udjAuR2V0UGxheWxvZ1JlcXVlc3QaKC5teXRob3MuY2h1bml0aG0udjAuR2V0UGxheWxvZ1N0cmVhbUl0ZW0wAWIGcHJvdG8z", + [file_chunithm_playlog], + ); + +/** + * @generated from message mythos.chunithm.v0.GetPlaylogRequest + */ +export type GetPlaylogRequest = Message<"mythos.chunithm.v0.GetPlaylogRequest"> & { + /** + * @generated from field: string profile_api_id = 1; + */ + profileApiId: string; + + /** + * @generated from field: optional string last_user_play_date = 2; + */ + lastUserPlayDate?: string; + + /** + * @generated from field: optional uint64 limit = 3; + */ + limit?: bigint; +}; + +/** + * Describes the message mythos.chunithm.v0.GetPlaylogRequest. + * Use `create(GetPlaylogRequestSchema)` to create a new message. + */ +export const GetPlaylogRequestSchema: GenMessage = + /*@__PURE__*/ + messageDesc(file_chunithm_user, 0); + +/** + * @generated from message mythos.chunithm.v0.GetPlaylogStreamItem + */ +export type GetPlaylogStreamItem = Message<"mythos.chunithm.v0.GetPlaylogStreamItem"> & { + /** + * @generated from field: string playlog_api_id = 1; + */ + playlogApiId: string; + + /** + * @generated from field: mythos.chunithm.v0.PlaylogInfo info = 2; + */ + info?: PlaylogInfo; + + /** + * @generated from field: mythos.chunithm.v0.PlaylogJudge judge = 3; + */ + judge?: PlaylogJudge; +}; + +/** + * Describes the message mythos.chunithm.v0.GetPlaylogStreamItem. + * Use `create(GetPlaylogStreamItemSchema)` to create a new message. + */ +export const GetPlaylogStreamItemSchema: GenMessage = + /*@__PURE__*/ + messageDesc(file_chunithm_user, 1); + +/** + * @generated from service mythos.chunithm.v0.ChunithmUser + */ +export const ChunithmUser: GenService<{ + /** + * @generated from rpc mythos.chunithm.v0.ChunithmUser.GetPlaylog + */ + getPlaylog: { + methodKind: "server_streaming"; + input: typeof GetPlaylogRequestSchema; + output: typeof GetPlaylogStreamItemSchema; + }; +}> = /*@__PURE__*/ serviceDesc(file_chunithm_user, 0); diff --git a/typescript/server/src/proto/generated/maimai/common_grpc_pb.d.ts b/typescript/server/src/proto/generated/maimai/common_grpc_pb.d.ts deleted file mode 100644 index 51b4d6959..000000000 --- a/typescript/server/src/proto/generated/maimai/common_grpc_pb.d.ts +++ /dev/null @@ -1 +0,0 @@ -// GENERATED CODE -- NO SERVICES IN PROTO diff --git a/typescript/server/src/proto/generated/maimai/common_grpc_pb.js b/typescript/server/src/proto/generated/maimai/common_grpc_pb.js deleted file mode 100644 index 51b4d6959..000000000 --- a/typescript/server/src/proto/generated/maimai/common_grpc_pb.js +++ /dev/null @@ -1 +0,0 @@ -// GENERATED CODE -- NO SERVICES IN PROTO diff --git a/typescript/server/src/proto/generated/maimai/common_pb.d.ts b/typescript/server/src/proto/generated/maimai/common_pb.d.ts deleted file mode 100644 index efefab8d2..000000000 --- a/typescript/server/src/proto/generated/maimai/common_pb.d.ts +++ /dev/null @@ -1,66 +0,0 @@ -// package: mythos.maimai.v0 -// file: maimai/common.proto - -import * as jspb from "google-protobuf"; - -export interface MaimaiRankingTypeMap { - MAIMAI_RANKING_TYPE_UNSPECIFIED: 0; - MAIMAI_RANKING_TYPE_ACHIEVEMENT: 1; - MAIMAI_RANKING_TYPE_DX_SCORE: 2; -} - -export const MaimaiRankingType: MaimaiRankingTypeMap; - -export interface MaimaiLevelMap { - MAIMAI_LEVEL_UNSPECIFIED: 0; - MAIMAI_LEVEL_BASIC: 1; - MAIMAI_LEVEL_ADVANCED: 2; - MAIMAI_LEVEL_EXPERT: 3; - MAIMAI_LEVEL_MASTER: 4; - MAIMAI_LEVEL_REMASTER: 5; - MAIMAI_LEVEL_UTAGE: 6; -} - -export const MaimaiLevel: MaimaiLevelMap; - -export interface MaimaiComboStatusMap { - MAIMAI_COMBO_STATUS_UNSPECIFIED: 0; - MAIMAI_COMBO_STATUS_NONE: 1; - MAIMAI_COMBO_STATUS_FULL_COMBO: 2; - MAIMAI_COMBO_STATUS_FULL_COMBO_PLUS: 3; - MAIMAI_COMBO_STATUS_ALL_PERFECT: 4; - MAIMAI_COMBO_STATUS_ALL_PERFECT_PLUS: 5; -} - -export const MaimaiComboStatus: MaimaiComboStatusMap; - -export interface MaimaiSyncStatusMap { - MAIMAI_SYNC_STATUS_UNSPECIFIED: 0; - MAIMAI_SYNC_STATUS_NONE: 1; - MAIMAI_SYNC_STATUS_FULL_SYNC: 2; - MAIMAI_SYNC_STATUS_FULL_SYNC_PLUS: 3; - MAIMAI_SYNC_STATUS_FULL_SYNC_DX: 4; - MAIMAI_SYNC_STATUS_FULL_SYNC_DX_PLUS: 5; -} - -export const MaimaiSyncStatus: MaimaiSyncStatusMap; - -export interface MaimaiScoreRankMap { - MAIMAI_SCORE_RANK_UNSPECIFIED: 0; - MAIMAI_SCORE_RANK_D: 1; - MAIMAI_SCORE_RANK_C: 2; - MAIMAI_SCORE_RANK_B: 3; - MAIMAI_SCORE_RANK_BB: 4; - MAIMAI_SCORE_RANK_BBB: 5; - MAIMAI_SCORE_RANK_A: 6; - MAIMAI_SCORE_RANK_AA: 7; - MAIMAI_SCORE_RANK_AAA: 8; - MAIMAI_SCORE_RANK_S: 9; - MAIMAI_SCORE_RANK_S_PLUS: 10; - MAIMAI_SCORE_RANK_SS: 11; - MAIMAI_SCORE_RANK_SS_PLUS: 12; - MAIMAI_SCORE_RANK_SSS: 13; - MAIMAI_SCORE_RANK_SSS_PLUS: 14; -} - -export const MaimaiScoreRank: MaimaiScoreRankMap; diff --git a/typescript/server/src/proto/generated/maimai/common_pb.js b/typescript/server/src/proto/generated/maimai/common_pb.js deleted file mode 100644 index ecf98a1f5..000000000 --- a/typescript/server/src/proto/generated/maimai/common_pb.js +++ /dev/null @@ -1,104 +0,0 @@ -// source: maimai/common.proto -/** - * @fileoverview - * @enhanceable - * @suppress {missingRequire} reports error on implicit type usages. - * @suppress {messageConventions} JS Compiler reports an error if a variable or - * field starts with 'MSG_' and isn't a translatable message. - * @public - */ -// GENERATED CODE -- DO NOT EDIT! -/* eslint-disable */ -// @ts-nocheck - -var jspb = require("google-protobuf"); -var goog = jspb; -var global = function () { - if (this) { - return this; - } - if (typeof window !== "undefined") { - return window; - } - if (typeof global !== "undefined") { - return global; - } - if (typeof self !== "undefined") { - return self; - } - return Function("return this")(); -}.call(null); - -goog.exportSymbol("proto.mythos.maimai.v0.MaimaiComboStatus", null, global); -goog.exportSymbol("proto.mythos.maimai.v0.MaimaiLevel", null, global); -goog.exportSymbol("proto.mythos.maimai.v0.MaimaiRankingType", null, global); -goog.exportSymbol("proto.mythos.maimai.v0.MaimaiScoreRank", null, global); -goog.exportSymbol("proto.mythos.maimai.v0.MaimaiSyncStatus", null, global); -/** - * @enum {number} - */ -proto.mythos.maimai.v0.MaimaiRankingType = { - MAIMAI_RANKING_TYPE_UNSPECIFIED: 0, - MAIMAI_RANKING_TYPE_ACHIEVEMENT: 1, - MAIMAI_RANKING_TYPE_DX_SCORE: 2, -}; - -/** - * @enum {number} - */ -proto.mythos.maimai.v0.MaimaiLevel = { - MAIMAI_LEVEL_UNSPECIFIED: 0, - MAIMAI_LEVEL_BASIC: 1, - MAIMAI_LEVEL_ADVANCED: 2, - MAIMAI_LEVEL_EXPERT: 3, - MAIMAI_LEVEL_MASTER: 4, - MAIMAI_LEVEL_REMASTER: 5, - MAIMAI_LEVEL_UTAGE: 6, -}; - -/** - * @enum {number} - */ -proto.mythos.maimai.v0.MaimaiComboStatus = { - MAIMAI_COMBO_STATUS_UNSPECIFIED: 0, - MAIMAI_COMBO_STATUS_NONE: 1, - MAIMAI_COMBO_STATUS_FULL_COMBO: 2, - MAIMAI_COMBO_STATUS_FULL_COMBO_PLUS: 3, - MAIMAI_COMBO_STATUS_ALL_PERFECT: 4, - MAIMAI_COMBO_STATUS_ALL_PERFECT_PLUS: 5, -}; - -/** - * @enum {number} - */ -proto.mythos.maimai.v0.MaimaiSyncStatus = { - MAIMAI_SYNC_STATUS_UNSPECIFIED: 0, - MAIMAI_SYNC_STATUS_NONE: 1, - MAIMAI_SYNC_STATUS_FULL_SYNC: 2, - MAIMAI_SYNC_STATUS_FULL_SYNC_PLUS: 3, - MAIMAI_SYNC_STATUS_FULL_SYNC_DX: 4, - MAIMAI_SYNC_STATUS_FULL_SYNC_DX_PLUS: 5, -}; - -/** - * @enum {number} - */ -proto.mythos.maimai.v0.MaimaiScoreRank = { - MAIMAI_SCORE_RANK_UNSPECIFIED: 0, - MAIMAI_SCORE_RANK_D: 1, - MAIMAI_SCORE_RANK_C: 2, - MAIMAI_SCORE_RANK_B: 3, - MAIMAI_SCORE_RANK_BB: 4, - MAIMAI_SCORE_RANK_BBB: 5, - MAIMAI_SCORE_RANK_A: 6, - MAIMAI_SCORE_RANK_AA: 7, - MAIMAI_SCORE_RANK_AAA: 8, - MAIMAI_SCORE_RANK_S: 9, - MAIMAI_SCORE_RANK_S_PLUS: 10, - MAIMAI_SCORE_RANK_SS: 11, - MAIMAI_SCORE_RANK_SS_PLUS: 12, - MAIMAI_SCORE_RANK_SSS: 13, - MAIMAI_SCORE_RANK_SSS_PLUS: 14, -}; - -goog.object.extend(exports, proto.mythos.maimai.v0); diff --git a/typescript/server/src/proto/generated/maimai/common_pb.ts b/typescript/server/src/proto/generated/maimai/common_pb.ts new file mode 100644 index 000000000..f4d013a42 --- /dev/null +++ b/typescript/server/src/proto/generated/maimai/common_pb.ts @@ -0,0 +1,260 @@ +// @generated by protoc-gen-es v2.11.0 with parameter "target=ts" +// @generated from file maimai/common.proto (package mythos.maimai.v0, syntax proto3) +/* eslint-disable */ + +import type { GenEnum, GenFile } from "@bufbuild/protobuf/codegenv2"; +import { enumDesc, fileDesc } from "@bufbuild/protobuf/codegenv2"; + +/** + * Describes the file maimai/common.proto. + */ +export const file_maimai_common: GenFile = + /*@__PURE__*/ + fileDesc( + "ChNtYWltYWkvY29tbW9uLnByb3RvEhBteXRob3MubWFpbWFpLnYwKn8KEU1haW1haVJhbmtpbmdUeXBlEiMKH01BSU1BSV9SQU5LSU5HX1RZUEVfVU5TUEVDSUZJRUQQABIjCh9NQUlNQUlfUkFOS0lOR19UWVBFX0FDSElFVkVNRU5UEAESIAocTUFJTUFJX1JBTktJTkdfVFlQRV9EWF9TQ09SRRACKsMBCgtNYWltYWlMZXZlbBIcChhNQUlNQUlfTEVWRUxfVU5TUEVDSUZJRUQQABIWChJNQUlNQUlfTEVWRUxfQkFTSUMQARIZChVNQUlNQUlfTEVWRUxfQURWQU5DRUQQAhIXChNNQUlNQUlfTEVWRUxfRVhQRVJUEAMSFwoTTUFJTUFJX0xFVkVMX01BU1RFUhAEEhkKFU1BSU1BSV9MRVZFTF9SRU1BU1RFUhAFEhYKEk1BSU1BSV9MRVZFTF9VVEFHRRAGKvIBChFNYWltYWlDb21ib1N0YXR1cxIjCh9NQUlNQUlfQ09NQk9fU1RBVFVTX1VOU1BFQ0lGSUVEEAASHAoYTUFJTUFJX0NPTUJPX1NUQVRVU19OT05FEAESIgoeTUFJTUFJX0NPTUJPX1NUQVRVU19GVUxMX0NPTUJPEAISJwojTUFJTUFJX0NPTUJPX1NUQVRVU19GVUxMX0NPTUJPX1BMVVMQAxIjCh9NQUlNQUlfQ09NQk9fU1RBVFVTX0FMTF9QRVJGRUNUEAQSKAokTUFJTUFJX0NPTUJPX1NUQVRVU19BTExfUEVSRkVDVF9QTFVTEAUq6wEKEE1haW1haVN5bmNTdGF0dXMSIgoeTUFJTUFJX1NZTkNfU1RBVFVTX1VOU1BFQ0lGSUVEEAASGwoXTUFJTUFJX1NZTkNfU1RBVFVTX05PTkUQARIgChxNQUlNQUlfU1lOQ19TVEFUVVNfRlVMTF9TWU5DEAISJQohTUFJTUFJX1NZTkNfU1RBVFVTX0ZVTExfU1lOQ19QTFVTEAMSIwofTUFJTUFJX1NZTkNfU1RBVFVTX0ZVTExfU1lOQ19EWBAEEigKJE1BSU1BSV9TWU5DX1NUQVRVU19GVUxMX1NZTkNfRFhfUExVUxAFKq0DCg9NYWltYWlTY29yZVJhbmsSIQodTUFJTUFJX1NDT1JFX1JBTktfVU5TUEVDSUZJRUQQABIXChNNQUlNQUlfU0NPUkVfUkFOS19EEAESFwoTTUFJTUFJX1NDT1JFX1JBTktfQxACEhcKE01BSU1BSV9TQ09SRV9SQU5LX0IQAxIYChRNQUlNQUlfU0NPUkVfUkFOS19CQhAEEhkKFU1BSU1BSV9TQ09SRV9SQU5LX0JCQhAFEhcKE01BSU1BSV9TQ09SRV9SQU5LX0EQBhIYChRNQUlNQUlfU0NPUkVfUkFOS19BQRAHEhkKFU1BSU1BSV9TQ09SRV9SQU5LX0FBQRAIEhcKE01BSU1BSV9TQ09SRV9SQU5LX1MQCRIcChhNQUlNQUlfU0NPUkVfUkFOS19TX1BMVVMQChIYChRNQUlNQUlfU0NPUkVfUkFOS19TUxALEh0KGU1BSU1BSV9TQ09SRV9SQU5LX1NTX1BMVVMQDBIZChVNQUlNQUlfU0NPUkVfUkFOS19TU1MQDRIeChpNQUlNQUlfU0NPUkVfUkFOS19TU1NfUExVUxAOYgZwcm90bzM", + ); + +/** + * @generated from enum mythos.maimai.v0.MaimaiRankingType + */ +export enum MaimaiRankingType { + /** + * @generated from enum value: MAIMAI_RANKING_TYPE_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + + /** + * @generated from enum value: MAIMAI_RANKING_TYPE_ACHIEVEMENT = 1; + */ + ACHIEVEMENT = 1, + + /** + * @generated from enum value: MAIMAI_RANKING_TYPE_DX_SCORE = 2; + */ + DX_SCORE = 2, +} + +/** + * Describes the enum mythos.maimai.v0.MaimaiRankingType. + */ +export const MaimaiRankingTypeSchema: GenEnum = + /*@__PURE__*/ + enumDesc(file_maimai_common, 0); + +/** + * @generated from enum mythos.maimai.v0.MaimaiLevel + */ +export enum MaimaiLevel { + /** + * @generated from enum value: MAIMAI_LEVEL_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + + /** + * @generated from enum value: MAIMAI_LEVEL_BASIC = 1; + */ + BASIC = 1, + + /** + * @generated from enum value: MAIMAI_LEVEL_ADVANCED = 2; + */ + ADVANCED = 2, + + /** + * @generated from enum value: MAIMAI_LEVEL_EXPERT = 3; + */ + EXPERT = 3, + + /** + * @generated from enum value: MAIMAI_LEVEL_MASTER = 4; + */ + MASTER = 4, + + /** + * @generated from enum value: MAIMAI_LEVEL_REMASTER = 5; + */ + REMASTER = 5, + + /** + * @generated from enum value: MAIMAI_LEVEL_UTAGE = 6; + */ + UTAGE = 6, +} + +/** + * Describes the enum mythos.maimai.v0.MaimaiLevel. + */ +export const MaimaiLevelSchema: GenEnum = + /*@__PURE__*/ + enumDesc(file_maimai_common, 1); + +/** + * @generated from enum mythos.maimai.v0.MaimaiComboStatus + */ +export enum MaimaiComboStatus { + /** + * @generated from enum value: MAIMAI_COMBO_STATUS_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + + /** + * @generated from enum value: MAIMAI_COMBO_STATUS_NONE = 1; + */ + NONE = 1, + + /** + * @generated from enum value: MAIMAI_COMBO_STATUS_FULL_COMBO = 2; + */ + FULL_COMBO = 2, + + /** + * @generated from enum value: MAIMAI_COMBO_STATUS_FULL_COMBO_PLUS = 3; + */ + FULL_COMBO_PLUS = 3, + + /** + * @generated from enum value: MAIMAI_COMBO_STATUS_ALL_PERFECT = 4; + */ + ALL_PERFECT = 4, + + /** + * @generated from enum value: MAIMAI_COMBO_STATUS_ALL_PERFECT_PLUS = 5; + */ + ALL_PERFECT_PLUS = 5, +} + +/** + * Describes the enum mythos.maimai.v0.MaimaiComboStatus. + */ +export const MaimaiComboStatusSchema: GenEnum = + /*@__PURE__*/ + enumDesc(file_maimai_common, 2); + +/** + * @generated from enum mythos.maimai.v0.MaimaiSyncStatus + */ +export enum MaimaiSyncStatus { + /** + * @generated from enum value: MAIMAI_SYNC_STATUS_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + + /** + * @generated from enum value: MAIMAI_SYNC_STATUS_NONE = 1; + */ + NONE = 1, + + /** + * @generated from enum value: MAIMAI_SYNC_STATUS_FULL_SYNC = 2; + */ + FULL_SYNC = 2, + + /** + * @generated from enum value: MAIMAI_SYNC_STATUS_FULL_SYNC_PLUS = 3; + */ + FULL_SYNC_PLUS = 3, + + /** + * @generated from enum value: MAIMAI_SYNC_STATUS_FULL_SYNC_DX = 4; + */ + FULL_SYNC_DX = 4, + + /** + * @generated from enum value: MAIMAI_SYNC_STATUS_FULL_SYNC_DX_PLUS = 5; + */ + FULL_SYNC_DX_PLUS = 5, +} + +/** + * Describes the enum mythos.maimai.v0.MaimaiSyncStatus. + */ +export const MaimaiSyncStatusSchema: GenEnum = + /*@__PURE__*/ + enumDesc(file_maimai_common, 3); + +/** + * @generated from enum mythos.maimai.v0.MaimaiScoreRank + */ +export enum MaimaiScoreRank { + /** + * @generated from enum value: MAIMAI_SCORE_RANK_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + + /** + * @generated from enum value: MAIMAI_SCORE_RANK_D = 1; + */ + D = 1, + + /** + * @generated from enum value: MAIMAI_SCORE_RANK_C = 2; + */ + C = 2, + + /** + * @generated from enum value: MAIMAI_SCORE_RANK_B = 3; + */ + B = 3, + + /** + * @generated from enum value: MAIMAI_SCORE_RANK_BB = 4; + */ + BB = 4, + + /** + * @generated from enum value: MAIMAI_SCORE_RANK_BBB = 5; + */ + BBB = 5, + + /** + * @generated from enum value: MAIMAI_SCORE_RANK_A = 6; + */ + A = 6, + + /** + * @generated from enum value: MAIMAI_SCORE_RANK_AA = 7; + */ + AA = 7, + + /** + * @generated from enum value: MAIMAI_SCORE_RANK_AAA = 8; + */ + AAA = 8, + + /** + * @generated from enum value: MAIMAI_SCORE_RANK_S = 9; + */ + S = 9, + + /** + * @generated from enum value: MAIMAI_SCORE_RANK_S_PLUS = 10; + */ + S_PLUS = 10, + + /** + * @generated from enum value: MAIMAI_SCORE_RANK_SS = 11; + */ + SS = 11, + + /** + * @generated from enum value: MAIMAI_SCORE_RANK_SS_PLUS = 12; + */ + SS_PLUS = 12, + + /** + * @generated from enum value: MAIMAI_SCORE_RANK_SSS = 13; + */ + SSS = 13, + + /** + * @generated from enum value: MAIMAI_SCORE_RANK_SSS_PLUS = 14; + */ + SSS_PLUS = 14, +} + +/** + * Describes the enum mythos.maimai.v0.MaimaiScoreRank. + */ +export const MaimaiScoreRankSchema: GenEnum = + /*@__PURE__*/ + enumDesc(file_maimai_common, 4); diff --git a/typescript/server/src/proto/generated/maimai/playlog_grpc_pb.d.ts b/typescript/server/src/proto/generated/maimai/playlog_grpc_pb.d.ts deleted file mode 100644 index 51b4d6959..000000000 --- a/typescript/server/src/proto/generated/maimai/playlog_grpc_pb.d.ts +++ /dev/null @@ -1 +0,0 @@ -// GENERATED CODE -- NO SERVICES IN PROTO diff --git a/typescript/server/src/proto/generated/maimai/playlog_grpc_pb.js b/typescript/server/src/proto/generated/maimai/playlog_grpc_pb.js deleted file mode 100644 index 51b4d6959..000000000 --- a/typescript/server/src/proto/generated/maimai/playlog_grpc_pb.js +++ /dev/null @@ -1 +0,0 @@ -// GENERATED CODE -- NO SERVICES IN PROTO diff --git a/typescript/server/src/proto/generated/maimai/playlog_pb.d.ts b/typescript/server/src/proto/generated/maimai/playlog_pb.d.ts deleted file mode 100644 index a92f6f05e..000000000 --- a/typescript/server/src/proto/generated/maimai/playlog_pb.d.ts +++ /dev/null @@ -1,129 +0,0 @@ -// package: mythos.maimai.v0 -// file: maimai/playlog.proto - -import * as jspb from "google-protobuf"; -import * as maimai_common_pb from "../maimai/common_pb"; - -export class PlaylogInfo extends jspb.Message { - getMusicId(): number; - setMusicId(value: number): void; - - getLevel(): maimai_common_pb.MaimaiLevelMap[keyof maimai_common_pb.MaimaiLevelMap]; - setLevel(value: maimai_common_pb.MaimaiLevelMap[keyof maimai_common_pb.MaimaiLevelMap]): void; - - getAchievement(): number; - setAchievement(value: number): void; - - getDeluxscore(): number; - setDeluxscore(value: number): void; - - getScoreRank(): maimai_common_pb.MaimaiScoreRankMap[keyof maimai_common_pb.MaimaiScoreRankMap]; - setScoreRank( - value: maimai_common_pb.MaimaiScoreRankMap[keyof maimai_common_pb.MaimaiScoreRankMap], - ): void; - - getComboStatus(): maimai_common_pb.MaimaiComboStatusMap[keyof maimai_common_pb.MaimaiComboStatusMap]; - setComboStatus( - value: maimai_common_pb.MaimaiComboStatusMap[keyof maimai_common_pb.MaimaiComboStatusMap], - ): void; - - getSyncStatus(): maimai_common_pb.MaimaiSyncStatusMap[keyof maimai_common_pb.MaimaiSyncStatusMap]; - setSyncStatus( - value: maimai_common_pb.MaimaiSyncStatusMap[keyof maimai_common_pb.MaimaiSyncStatusMap], - ): void; - - getIsClear(): boolean; - setIsClear(value: boolean): void; - - getIsAchieveNewRecord(): boolean; - setIsAchieveNewRecord(value: boolean): void; - - getIsDeluxscoreNewRecord(): boolean; - setIsDeluxscoreNewRecord(value: boolean): void; - - getTrack(): number; - setTrack(value: number): void; - - getUserPlayDate(): string; - setUserPlayDate(value: string): void; - - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): PlaylogInfo.AsObject; - static toObject(includeInstance: boolean, msg: PlaylogInfo): PlaylogInfo.AsObject; - static extensions: { [key: number]: jspb.ExtensionFieldInfo }; - static extensionsBinary: { [key: number]: jspb.ExtensionFieldBinaryInfo }; - static serializeBinaryToWriter(message: PlaylogInfo, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): PlaylogInfo; - static deserializeBinaryFromReader( - message: PlaylogInfo, - reader: jspb.BinaryReader, - ): PlaylogInfo; -} - -export namespace PlaylogInfo { - export type AsObject = { - musicId: number; - level: maimai_common_pb.MaimaiLevelMap[keyof maimai_common_pb.MaimaiLevelMap]; - achievement: number; - deluxscore: number; - scoreRank: maimai_common_pb.MaimaiScoreRankMap[keyof maimai_common_pb.MaimaiScoreRankMap]; - comboStatus: maimai_common_pb.MaimaiComboStatusMap[keyof maimai_common_pb.MaimaiComboStatusMap]; - syncStatus: maimai_common_pb.MaimaiSyncStatusMap[keyof maimai_common_pb.MaimaiSyncStatusMap]; - isClear: boolean; - isAchieveNewRecord: boolean; - isDeluxscoreNewRecord: boolean; - track: number; - userPlayDate: string; - }; -} - -export class PlaylogJudge extends jspb.Message { - getJudgeCriticalPerfect(): number; - setJudgeCriticalPerfect(value: number): void; - - getJudgePerfect(): number; - setJudgePerfect(value: number): void; - - getJudgeGreat(): number; - setJudgeGreat(value: number): void; - - getJudgeGood(): number; - setJudgeGood(value: number): void; - - getJudgeMiss(): number; - setJudgeMiss(value: number): void; - - getMaxCombo(): number; - setMaxCombo(value: number): void; - - getFastCount(): number; - setFastCount(value: number): void; - - getLateCount(): number; - setLateCount(value: number): void; - - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): PlaylogJudge.AsObject; - static toObject(includeInstance: boolean, msg: PlaylogJudge): PlaylogJudge.AsObject; - static extensions: { [key: number]: jspb.ExtensionFieldInfo }; - static extensionsBinary: { [key: number]: jspb.ExtensionFieldBinaryInfo }; - static serializeBinaryToWriter(message: PlaylogJudge, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): PlaylogJudge; - static deserializeBinaryFromReader( - message: PlaylogJudge, - reader: jspb.BinaryReader, - ): PlaylogJudge; -} - -export namespace PlaylogJudge { - export type AsObject = { - judgeCriticalPerfect: number; - judgePerfect: number; - judgeGreat: number; - judgeGood: number; - judgeMiss: number; - maxCombo: number; - fastCount: number; - lateCount: number; - }; -} diff --git a/typescript/server/src/proto/generated/maimai/playlog_pb.js b/typescript/server/src/proto/generated/maimai/playlog_pb.js deleted file mode 100644 index db695e766..000000000 --- a/typescript/server/src/proto/generated/maimai/playlog_pb.js +++ /dev/null @@ -1,777 +0,0 @@ -// source: maimai/playlog.proto -/** - * @fileoverview - * @enhanceable - * @suppress {missingRequire} reports error on implicit type usages. - * @suppress {messageConventions} JS Compiler reports an error if a variable or - * field starts with 'MSG_' and isn't a translatable message. - * @public - */ -// GENERATED CODE -- DO NOT EDIT! -/* eslint-disable */ -// @ts-nocheck - -var jspb = require("google-protobuf"); -var goog = jspb; -var global = function () { - if (this) { - return this; - } - if (typeof window !== "undefined") { - return window; - } - if (typeof global !== "undefined") { - return global; - } - if (typeof self !== "undefined") { - return self; - } - return Function("return this")(); -}.call(null); - -var maimai_common_pb = require("../maimai/common_pb.js"); -goog.object.extend(proto, maimai_common_pb); -goog.exportSymbol("proto.mythos.maimai.v0.PlaylogInfo", null, global); -goog.exportSymbol("proto.mythos.maimai.v0.PlaylogJudge", null, global); -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.mythos.maimai.v0.PlaylogInfo = function (opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.mythos.maimai.v0.PlaylogInfo, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.mythos.maimai.v0.PlaylogInfo.displayName = "proto.mythos.maimai.v0.PlaylogInfo"; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.mythos.maimai.v0.PlaylogJudge = function (opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.mythos.maimai.v0.PlaylogJudge, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.mythos.maimai.v0.PlaylogJudge.displayName = "proto.mythos.maimai.v0.PlaylogJudge"; -} - -if (jspb.Message.GENERATE_TO_OBJECT) { - /** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ - proto.mythos.maimai.v0.PlaylogInfo.prototype.toObject = function (opt_includeInstance) { - return proto.mythos.maimai.v0.PlaylogInfo.toObject(opt_includeInstance, this); - }; - - /** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.mythos.maimai.v0.PlaylogInfo} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ - proto.mythos.maimai.v0.PlaylogInfo.toObject = function (includeInstance, msg) { - var f, - obj = { - musicId: jspb.Message.getFieldWithDefault(msg, 1, 0), - level: jspb.Message.getFieldWithDefault(msg, 2, 0), - achievement: jspb.Message.getFieldWithDefault(msg, 3, 0), - deluxscore: jspb.Message.getFieldWithDefault(msg, 4, 0), - scoreRank: jspb.Message.getFieldWithDefault(msg, 5, 0), - comboStatus: jspb.Message.getFieldWithDefault(msg, 6, 0), - syncStatus: jspb.Message.getFieldWithDefault(msg, 7, 0), - isClear: jspb.Message.getBooleanFieldWithDefault(msg, 8, false), - isAchieveNewRecord: jspb.Message.getBooleanFieldWithDefault(msg, 9, false), - isDeluxscoreNewRecord: jspb.Message.getBooleanFieldWithDefault(msg, 10, false), - track: jspb.Message.getFieldWithDefault(msg, 11, 0), - userPlayDate: jspb.Message.getFieldWithDefault(msg, 12, ""), - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; - }; -} - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.mythos.maimai.v0.PlaylogInfo} - */ -proto.mythos.maimai.v0.PlaylogInfo.deserializeBinary = function (bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.mythos.maimai.v0.PlaylogInfo(); - return proto.mythos.maimai.v0.PlaylogInfo.deserializeBinaryFromReader(msg, reader); -}; - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.mythos.maimai.v0.PlaylogInfo} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.mythos.maimai.v0.PlaylogInfo} - */ -proto.mythos.maimai.v0.PlaylogInfo.deserializeBinaryFromReader = function (msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {number} */ (reader.readInt32()); - msg.setMusicId(value); - break; - case 2: - var value = /** @type {!proto.mythos.maimai.v0.MaimaiLevel} */ (reader.readEnum()); - msg.setLevel(value); - break; - case 3: - var value = /** @type {number} */ (reader.readInt32()); - msg.setAchievement(value); - break; - case 4: - var value = /** @type {number} */ (reader.readInt32()); - msg.setDeluxscore(value); - break; - case 5: - var value = /** @type {!proto.mythos.maimai.v0.MaimaiScoreRank} */ ( - reader.readEnum() - ); - msg.setScoreRank(value); - break; - case 6: - var value = /** @type {!proto.mythos.maimai.v0.MaimaiComboStatus} */ ( - reader.readEnum() - ); - msg.setComboStatus(value); - break; - case 7: - var value = /** @type {!proto.mythos.maimai.v0.MaimaiSyncStatus} */ ( - reader.readEnum() - ); - msg.setSyncStatus(value); - break; - case 8: - var value = /** @type {boolean} */ (reader.readBool()); - msg.setIsClear(value); - break; - case 9: - var value = /** @type {boolean} */ (reader.readBool()); - msg.setIsAchieveNewRecord(value); - break; - case 10: - var value = /** @type {boolean} */ (reader.readBool()); - msg.setIsDeluxscoreNewRecord(value); - break; - case 11: - var value = /** @type {number} */ (reader.readInt32()); - msg.setTrack(value); - break; - case 12: - var value = /** @type {string} */ (reader.readString()); - msg.setUserPlayDate(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.mythos.maimai.v0.PlaylogInfo.prototype.serializeBinary = function () { - var writer = new jspb.BinaryWriter(); - proto.mythos.maimai.v0.PlaylogInfo.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.mythos.maimai.v0.PlaylogInfo} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.mythos.maimai.v0.PlaylogInfo.serializeBinaryToWriter = function (message, writer) { - var f = undefined; - f = message.getMusicId(); - if (f !== 0) { - writer.writeInt32(1, f); - } - f = message.getLevel(); - if (f !== 0.0) { - writer.writeEnum(2, f); - } - f = message.getAchievement(); - if (f !== 0) { - writer.writeInt32(3, f); - } - f = message.getDeluxscore(); - if (f !== 0) { - writer.writeInt32(4, f); - } - f = message.getScoreRank(); - if (f !== 0.0) { - writer.writeEnum(5, f); - } - f = message.getComboStatus(); - if (f !== 0.0) { - writer.writeEnum(6, f); - } - f = message.getSyncStatus(); - if (f !== 0.0) { - writer.writeEnum(7, f); - } - f = message.getIsClear(); - if (f) { - writer.writeBool(8, f); - } - f = message.getIsAchieveNewRecord(); - if (f) { - writer.writeBool(9, f); - } - f = message.getIsDeluxscoreNewRecord(); - if (f) { - writer.writeBool(10, f); - } - f = message.getTrack(); - if (f !== 0) { - writer.writeInt32(11, f); - } - f = message.getUserPlayDate(); - if (f.length > 0) { - writer.writeString(12, f); - } -}; - -/** - * optional int32 music_id = 1; - * @return {number} - */ -proto.mythos.maimai.v0.PlaylogInfo.prototype.getMusicId = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.maimai.v0.PlaylogInfo} returns this - */ -proto.mythos.maimai.v0.PlaylogInfo.prototype.setMusicId = function (value) { - return jspb.Message.setProto3IntField(this, 1, value); -}; - -/** - * optional MaimaiLevel level = 2; - * @return {!proto.mythos.maimai.v0.MaimaiLevel} - */ -proto.mythos.maimai.v0.PlaylogInfo.prototype.getLevel = function () { - return /** @type {!proto.mythos.maimai.v0.MaimaiLevel} */ ( - jspb.Message.getFieldWithDefault(this, 2, 0) - ); -}; - -/** - * @param {!proto.mythos.maimai.v0.MaimaiLevel} value - * @return {!proto.mythos.maimai.v0.PlaylogInfo} returns this - */ -proto.mythos.maimai.v0.PlaylogInfo.prototype.setLevel = function (value) { - return jspb.Message.setProto3EnumField(this, 2, value); -}; - -/** - * optional int32 achievement = 3; - * @return {number} - */ -proto.mythos.maimai.v0.PlaylogInfo.prototype.getAchievement = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.maimai.v0.PlaylogInfo} returns this - */ -proto.mythos.maimai.v0.PlaylogInfo.prototype.setAchievement = function (value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - -/** - * optional int32 deluxscore = 4; - * @return {number} - */ -proto.mythos.maimai.v0.PlaylogInfo.prototype.getDeluxscore = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.maimai.v0.PlaylogInfo} returns this - */ -proto.mythos.maimai.v0.PlaylogInfo.prototype.setDeluxscore = function (value) { - return jspb.Message.setProto3IntField(this, 4, value); -}; - -/** - * optional MaimaiScoreRank score_rank = 5; - * @return {!proto.mythos.maimai.v0.MaimaiScoreRank} - */ -proto.mythos.maimai.v0.PlaylogInfo.prototype.getScoreRank = function () { - return /** @type {!proto.mythos.maimai.v0.MaimaiScoreRank} */ ( - jspb.Message.getFieldWithDefault(this, 5, 0) - ); -}; - -/** - * @param {!proto.mythos.maimai.v0.MaimaiScoreRank} value - * @return {!proto.mythos.maimai.v0.PlaylogInfo} returns this - */ -proto.mythos.maimai.v0.PlaylogInfo.prototype.setScoreRank = function (value) { - return jspb.Message.setProto3EnumField(this, 5, value); -}; - -/** - * optional MaimaiComboStatus combo_status = 6; - * @return {!proto.mythos.maimai.v0.MaimaiComboStatus} - */ -proto.mythos.maimai.v0.PlaylogInfo.prototype.getComboStatus = function () { - return /** @type {!proto.mythos.maimai.v0.MaimaiComboStatus} */ ( - jspb.Message.getFieldWithDefault(this, 6, 0) - ); -}; - -/** - * @param {!proto.mythos.maimai.v0.MaimaiComboStatus} value - * @return {!proto.mythos.maimai.v0.PlaylogInfo} returns this - */ -proto.mythos.maimai.v0.PlaylogInfo.prototype.setComboStatus = function (value) { - return jspb.Message.setProto3EnumField(this, 6, value); -}; - -/** - * optional MaimaiSyncStatus sync_status = 7; - * @return {!proto.mythos.maimai.v0.MaimaiSyncStatus} - */ -proto.mythos.maimai.v0.PlaylogInfo.prototype.getSyncStatus = function () { - return /** @type {!proto.mythos.maimai.v0.MaimaiSyncStatus} */ ( - jspb.Message.getFieldWithDefault(this, 7, 0) - ); -}; - -/** - * @param {!proto.mythos.maimai.v0.MaimaiSyncStatus} value - * @return {!proto.mythos.maimai.v0.PlaylogInfo} returns this - */ -proto.mythos.maimai.v0.PlaylogInfo.prototype.setSyncStatus = function (value) { - return jspb.Message.setProto3EnumField(this, 7, value); -}; - -/** - * optional bool is_clear = 8; - * @return {boolean} - */ -proto.mythos.maimai.v0.PlaylogInfo.prototype.getIsClear = function () { - return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 8, false)); -}; - -/** - * @param {boolean} value - * @return {!proto.mythos.maimai.v0.PlaylogInfo} returns this - */ -proto.mythos.maimai.v0.PlaylogInfo.prototype.setIsClear = function (value) { - return jspb.Message.setProto3BooleanField(this, 8, value); -}; - -/** - * optional bool is_achieve_new_record = 9; - * @return {boolean} - */ -proto.mythos.maimai.v0.PlaylogInfo.prototype.getIsAchieveNewRecord = function () { - return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 9, false)); -}; - -/** - * @param {boolean} value - * @return {!proto.mythos.maimai.v0.PlaylogInfo} returns this - */ -proto.mythos.maimai.v0.PlaylogInfo.prototype.setIsAchieveNewRecord = function (value) { - return jspb.Message.setProto3BooleanField(this, 9, value); -}; - -/** - * optional bool is_deluxscore_new_record = 10; - * @return {boolean} - */ -proto.mythos.maimai.v0.PlaylogInfo.prototype.getIsDeluxscoreNewRecord = function () { - return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 10, false)); -}; - -/** - * @param {boolean} value - * @return {!proto.mythos.maimai.v0.PlaylogInfo} returns this - */ -proto.mythos.maimai.v0.PlaylogInfo.prototype.setIsDeluxscoreNewRecord = function (value) { - return jspb.Message.setProto3BooleanField(this, 10, value); -}; - -/** - * optional int32 track = 11; - * @return {number} - */ -proto.mythos.maimai.v0.PlaylogInfo.prototype.getTrack = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 11, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.maimai.v0.PlaylogInfo} returns this - */ -proto.mythos.maimai.v0.PlaylogInfo.prototype.setTrack = function (value) { - return jspb.Message.setProto3IntField(this, 11, value); -}; - -/** - * optional string user_play_date = 12; - * @return {string} - */ -proto.mythos.maimai.v0.PlaylogInfo.prototype.getUserPlayDate = function () { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 12, "")); -}; - -/** - * @param {string} value - * @return {!proto.mythos.maimai.v0.PlaylogInfo} returns this - */ -proto.mythos.maimai.v0.PlaylogInfo.prototype.setUserPlayDate = function (value) { - return jspb.Message.setProto3StringField(this, 12, value); -}; - -if (jspb.Message.GENERATE_TO_OBJECT) { - /** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ - proto.mythos.maimai.v0.PlaylogJudge.prototype.toObject = function (opt_includeInstance) { - return proto.mythos.maimai.v0.PlaylogJudge.toObject(opt_includeInstance, this); - }; - - /** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.mythos.maimai.v0.PlaylogJudge} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ - proto.mythos.maimai.v0.PlaylogJudge.toObject = function (includeInstance, msg) { - var f, - obj = { - judgeCriticalPerfect: jspb.Message.getFieldWithDefault(msg, 1, 0), - judgePerfect: jspb.Message.getFieldWithDefault(msg, 2, 0), - judgeGreat: jspb.Message.getFieldWithDefault(msg, 3, 0), - judgeGood: jspb.Message.getFieldWithDefault(msg, 4, 0), - judgeMiss: jspb.Message.getFieldWithDefault(msg, 5, 0), - maxCombo: jspb.Message.getFieldWithDefault(msg, 6, 0), - fastCount: jspb.Message.getFieldWithDefault(msg, 7, 0), - lateCount: jspb.Message.getFieldWithDefault(msg, 8, 0), - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; - }; -} - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.mythos.maimai.v0.PlaylogJudge} - */ -proto.mythos.maimai.v0.PlaylogJudge.deserializeBinary = function (bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.mythos.maimai.v0.PlaylogJudge(); - return proto.mythos.maimai.v0.PlaylogJudge.deserializeBinaryFromReader(msg, reader); -}; - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.mythos.maimai.v0.PlaylogJudge} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.mythos.maimai.v0.PlaylogJudge} - */ -proto.mythos.maimai.v0.PlaylogJudge.deserializeBinaryFromReader = function (msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {number} */ (reader.readInt32()); - msg.setJudgeCriticalPerfect(value); - break; - case 2: - var value = /** @type {number} */ (reader.readInt32()); - msg.setJudgePerfect(value); - break; - case 3: - var value = /** @type {number} */ (reader.readInt32()); - msg.setJudgeGreat(value); - break; - case 4: - var value = /** @type {number} */ (reader.readInt32()); - msg.setJudgeGood(value); - break; - case 5: - var value = /** @type {number} */ (reader.readInt32()); - msg.setJudgeMiss(value); - break; - case 6: - var value = /** @type {number} */ (reader.readInt32()); - msg.setMaxCombo(value); - break; - case 7: - var value = /** @type {number} */ (reader.readInt32()); - msg.setFastCount(value); - break; - case 8: - var value = /** @type {number} */ (reader.readInt32()); - msg.setLateCount(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.mythos.maimai.v0.PlaylogJudge.prototype.serializeBinary = function () { - var writer = new jspb.BinaryWriter(); - proto.mythos.maimai.v0.PlaylogJudge.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.mythos.maimai.v0.PlaylogJudge} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.mythos.maimai.v0.PlaylogJudge.serializeBinaryToWriter = function (message, writer) { - var f = undefined; - f = message.getJudgeCriticalPerfect(); - if (f !== 0) { - writer.writeInt32(1, f); - } - f = message.getJudgePerfect(); - if (f !== 0) { - writer.writeInt32(2, f); - } - f = message.getJudgeGreat(); - if (f !== 0) { - writer.writeInt32(3, f); - } - f = message.getJudgeGood(); - if (f !== 0) { - writer.writeInt32(4, f); - } - f = message.getJudgeMiss(); - if (f !== 0) { - writer.writeInt32(5, f); - } - f = message.getMaxCombo(); - if (f !== 0) { - writer.writeInt32(6, f); - } - f = message.getFastCount(); - if (f !== 0) { - writer.writeInt32(7, f); - } - f = message.getLateCount(); - if (f !== 0) { - writer.writeInt32(8, f); - } -}; - -/** - * optional int32 judge_critical_perfect = 1; - * @return {number} - */ -proto.mythos.maimai.v0.PlaylogJudge.prototype.getJudgeCriticalPerfect = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.maimai.v0.PlaylogJudge} returns this - */ -proto.mythos.maimai.v0.PlaylogJudge.prototype.setJudgeCriticalPerfect = function (value) { - return jspb.Message.setProto3IntField(this, 1, value); -}; - -/** - * optional int32 judge_perfect = 2; - * @return {number} - */ -proto.mythos.maimai.v0.PlaylogJudge.prototype.getJudgePerfect = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.maimai.v0.PlaylogJudge} returns this - */ -proto.mythos.maimai.v0.PlaylogJudge.prototype.setJudgePerfect = function (value) { - return jspb.Message.setProto3IntField(this, 2, value); -}; - -/** - * optional int32 judge_great = 3; - * @return {number} - */ -proto.mythos.maimai.v0.PlaylogJudge.prototype.getJudgeGreat = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.maimai.v0.PlaylogJudge} returns this - */ -proto.mythos.maimai.v0.PlaylogJudge.prototype.setJudgeGreat = function (value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - -/** - * optional int32 judge_good = 4; - * @return {number} - */ -proto.mythos.maimai.v0.PlaylogJudge.prototype.getJudgeGood = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.maimai.v0.PlaylogJudge} returns this - */ -proto.mythos.maimai.v0.PlaylogJudge.prototype.setJudgeGood = function (value) { - return jspb.Message.setProto3IntField(this, 4, value); -}; - -/** - * optional int32 judge_miss = 5; - * @return {number} - */ -proto.mythos.maimai.v0.PlaylogJudge.prototype.getJudgeMiss = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 5, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.maimai.v0.PlaylogJudge} returns this - */ -proto.mythos.maimai.v0.PlaylogJudge.prototype.setJudgeMiss = function (value) { - return jspb.Message.setProto3IntField(this, 5, value); -}; - -/** - * optional int32 max_combo = 6; - * @return {number} - */ -proto.mythos.maimai.v0.PlaylogJudge.prototype.getMaxCombo = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 6, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.maimai.v0.PlaylogJudge} returns this - */ -proto.mythos.maimai.v0.PlaylogJudge.prototype.setMaxCombo = function (value) { - return jspb.Message.setProto3IntField(this, 6, value); -}; - -/** - * optional int32 fast_count = 7; - * @return {number} - */ -proto.mythos.maimai.v0.PlaylogJudge.prototype.getFastCount = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 7, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.maimai.v0.PlaylogJudge} returns this - */ -proto.mythos.maimai.v0.PlaylogJudge.prototype.setFastCount = function (value) { - return jspb.Message.setProto3IntField(this, 7, value); -}; - -/** - * optional int32 late_count = 8; - * @return {number} - */ -proto.mythos.maimai.v0.PlaylogJudge.prototype.getLateCount = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 8, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.maimai.v0.PlaylogJudge} returns this - */ -proto.mythos.maimai.v0.PlaylogJudge.prototype.setLateCount = function (value) { - return jspb.Message.setProto3IntField(this, 8, value); -}; - -goog.object.extend(exports, proto.mythos.maimai.v0); diff --git a/typescript/server/src/proto/generated/maimai/playlog_pb.ts b/typescript/server/src/proto/generated/maimai/playlog_pb.ts new file mode 100644 index 000000000..2e058f47d --- /dev/null +++ b/typescript/server/src/proto/generated/maimai/playlog_pb.ts @@ -0,0 +1,150 @@ +// @generated by protoc-gen-es v2.11.0 with parameter "target=ts" +// @generated from file maimai/playlog.proto (package mythos.maimai.v0, syntax proto3) +/* eslint-disable */ + +import type { GenFile, GenMessage } from "@bufbuild/protobuf/codegenv2"; +import { fileDesc, messageDesc } from "@bufbuild/protobuf/codegenv2"; +import type { + MaimaiComboStatus, + MaimaiLevel, + MaimaiScoreRank, + MaimaiSyncStatus, +} from "./common_pb"; +import { file_maimai_common } from "./common_pb"; +import type { Message } from "@bufbuild/protobuf"; + +/** + * Describes the file maimai/playlog.proto. + */ +export const file_maimai_playlog: GenFile = + /*@__PURE__*/ + fileDesc( + "ChRtYWltYWkvcGxheWxvZy5wcm90bxIQbXl0aG9zLm1haW1haS52MCKbAwoLUGxheWxvZ0luZm8SEAoIbXVzaWNfaWQYASABKAUSLAoFbGV2ZWwYAiABKA4yHS5teXRob3MubWFpbWFpLnYwLk1haW1haUxldmVsEhMKC2FjaGlldmVtZW50GAMgASgFEhIKCmRlbHV4c2NvcmUYBCABKAUSNQoKc2NvcmVfcmFuaxgFIAEoDjIhLm15dGhvcy5tYWltYWkudjAuTWFpbWFpU2NvcmVSYW5rEjkKDGNvbWJvX3N0YXR1cxgGIAEoDjIjLm15dGhvcy5tYWltYWkudjAuTWFpbWFpQ29tYm9TdGF0dXMSNwoLc3luY19zdGF0dXMYByABKA4yIi5teXRob3MubWFpbWFpLnYwLk1haW1haVN5bmNTdGF0dXMSEAoIaXNfY2xlYXIYCCABKAgSHQoVaXNfYWNoaWV2ZV9uZXdfcmVjb3JkGAkgASgIEiAKGGlzX2RlbHV4c2NvcmVfbmV3X3JlY29yZBgKIAEoCBINCgV0cmFjaxgLIAEoBRIWCg51c2VyX3BsYXlfZGF0ZRgMIAEoCSK9AQoMUGxheWxvZ0p1ZGdlEh4KFmp1ZGdlX2NyaXRpY2FsX3BlcmZlY3QYASABKAUSFQoNanVkZ2VfcGVyZmVjdBgCIAEoBRITCgtqdWRnZV9ncmVhdBgDIAEoBRISCgpqdWRnZV9nb29kGAQgASgFEhIKCmp1ZGdlX21pc3MYBSABKAUSEQoJbWF4X2NvbWJvGAYgASgFEhIKCmZhc3RfY291bnQYByABKAUSEgoKbGF0ZV9jb3VudBgIIAEoBWIGcHJvdG8z", + [file_maimai_common], + ); + +/** + * @generated from message mythos.maimai.v0.PlaylogInfo + */ +export type PlaylogInfo = Message<"mythos.maimai.v0.PlaylogInfo"> & { + /** + * @generated from field: int32 music_id = 1; + */ + musicId: number; + + /** + * @generated from field: mythos.maimai.v0.MaimaiLevel level = 2; + */ + level: MaimaiLevel; + + /** + * @generated from field: int32 achievement = 3; + */ + achievement: number; + + /** + * @generated from field: int32 deluxscore = 4; + */ + deluxscore: number; + + /** + * @generated from field: mythos.maimai.v0.MaimaiScoreRank score_rank = 5; + */ + scoreRank: MaimaiScoreRank; + + /** + * @generated from field: mythos.maimai.v0.MaimaiComboStatus combo_status = 6; + */ + comboStatus: MaimaiComboStatus; + + /** + * @generated from field: mythos.maimai.v0.MaimaiSyncStatus sync_status = 7; + */ + syncStatus: MaimaiSyncStatus; + + /** + * @generated from field: bool is_clear = 8; + */ + isClear: boolean; + + /** + * @generated from field: bool is_achieve_new_record = 9; + */ + isAchieveNewRecord: boolean; + + /** + * @generated from field: bool is_deluxscore_new_record = 10; + */ + isDeluxscoreNewRecord: boolean; + + /** + * @generated from field: int32 track = 11; + */ + track: number; + + /** + * @generated from field: string user_play_date = 12; + */ + userPlayDate: string; +}; + +/** + * Describes the message mythos.maimai.v0.PlaylogInfo. + * Use `create(PlaylogInfoSchema)` to create a new message. + */ +export const PlaylogInfoSchema: GenMessage = + /*@__PURE__*/ + messageDesc(file_maimai_playlog, 0); + +/** + * @generated from message mythos.maimai.v0.PlaylogJudge + */ +export type PlaylogJudge = Message<"mythos.maimai.v0.PlaylogJudge"> & { + /** + * @generated from field: int32 judge_critical_perfect = 1; + */ + judgeCriticalPerfect: number; + + /** + * @generated from field: int32 judge_perfect = 2; + */ + judgePerfect: number; + + /** + * @generated from field: int32 judge_great = 3; + */ + judgeGreat: number; + + /** + * @generated from field: int32 judge_good = 4; + */ + judgeGood: number; + + /** + * @generated from field: int32 judge_miss = 5; + */ + judgeMiss: number; + + /** + * @generated from field: int32 max_combo = 6; + */ + maxCombo: number; + + /** + * @generated from field: int32 fast_count = 7; + */ + fastCount: number; + + /** + * @generated from field: int32 late_count = 8; + */ + lateCount: number; +}; + +/** + * Describes the message mythos.maimai.v0.PlaylogJudge. + * Use `create(PlaylogJudgeSchema)` to create a new message. + */ +export const PlaylogJudgeSchema: GenMessage = + /*@__PURE__*/ + messageDesc(file_maimai_playlog, 1); diff --git a/typescript/server/src/proto/generated/maimai/user_connect.ts b/typescript/server/src/proto/generated/maimai/user_connect.ts new file mode 100644 index 000000000..f00425376 --- /dev/null +++ b/typescript/server/src/proto/generated/maimai/user_connect.ts @@ -0,0 +1,25 @@ +// @generated by protoc-gen-connect-es v1.7.0 with parameter "target=ts" +// @generated from file maimai/user.proto (package mythos.maimai.v0, syntax proto3) +/* eslint-disable */ +// @ts-nocheck + +import { GetPlaylogRequest, GetPlaylogStreamItem } from "./user_pb.js"; +import { MethodKind } from "@bufbuild/protobuf"; + +/** + * @generated from service mythos.maimai.v0.MaimaiUser + */ +export const MaimaiUser = { + typeName: "mythos.maimai.v0.MaimaiUser", + methods: { + /** + * @generated from rpc mythos.maimai.v0.MaimaiUser.GetPlaylog + */ + getPlaylog: { + name: "GetPlaylog", + I: GetPlaylogRequest, + O: GetPlaylogStreamItem, + kind: MethodKind.ServerStreaming, + }, + }, +} as const; diff --git a/typescript/server/src/proto/generated/maimai/user_grpc_pb.d.ts b/typescript/server/src/proto/generated/maimai/user_grpc_pb.d.ts deleted file mode 100644 index 5deba6f25..000000000 --- a/typescript/server/src/proto/generated/maimai/user_grpc_pb.d.ts +++ /dev/null @@ -1,36 +0,0 @@ -// GENERATED CODE -- DO NOT EDIT! - -// package: mythos.maimai.v0 -// file: maimai/user.proto - -import * as maimai_user_pb from "../maimai/user_pb"; -import * as grpc from "@grpc/grpc-js"; - -interface IMaimaiUserService extends grpc.ServiceDefinition { - getPlaylog: grpc.MethodDefinition< - maimai_user_pb.GetPlaylogRequest, - maimai_user_pb.GetPlaylogStreamItem - >; -} - -export const MaimaiUserService: IMaimaiUserService; - -export interface IMaimaiUserServer extends grpc.UntypedServiceImplementation { - getPlaylog: grpc.handleServerStreamingCall< - maimai_user_pb.GetPlaylogRequest, - maimai_user_pb.GetPlaylogStreamItem - >; -} - -export class MaimaiUserClient extends grpc.Client { - constructor(address: string, credentials: grpc.ChannelCredentials, options?: object); - getPlaylog( - argument: maimai_user_pb.GetPlaylogRequest, - metadataOrOptions?: grpc.Metadata | grpc.CallOptions | null, - ): grpc.ClientReadableStream; - getPlaylog( - argument: maimai_user_pb.GetPlaylogRequest, - metadata?: grpc.Metadata | null, - options?: grpc.CallOptions | null, - ): grpc.ClientReadableStream; -} diff --git a/typescript/server/src/proto/generated/maimai/user_grpc_pb.js b/typescript/server/src/proto/generated/maimai/user_grpc_pb.js deleted file mode 100644 index 3223c9683..000000000 --- a/typescript/server/src/proto/generated/maimai/user_grpc_pb.js +++ /dev/null @@ -1,44 +0,0 @@ -// GENERATED CODE -- DO NOT EDIT! - -"use strict"; -var grpc = require("@grpc/grpc-js"); -var maimai_user_pb = require("../maimai/user_pb.js"); -var maimai_playlog_pb = require("../maimai/playlog_pb.js"); - -function serialize_mythos_maimai_v0_GetPlaylogRequest(arg) { - if (!(arg instanceof maimai_user_pb.GetPlaylogRequest)) { - throw new Error("Expected argument of type mythos.maimai.v0.GetPlaylogRequest"); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_mythos_maimai_v0_GetPlaylogRequest(buffer_arg) { - return maimai_user_pb.GetPlaylogRequest.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_mythos_maimai_v0_GetPlaylogStreamItem(arg) { - if (!(arg instanceof maimai_user_pb.GetPlaylogStreamItem)) { - throw new Error("Expected argument of type mythos.maimai.v0.GetPlaylogStreamItem"); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_mythos_maimai_v0_GetPlaylogStreamItem(buffer_arg) { - return maimai_user_pb.GetPlaylogStreamItem.deserializeBinary(new Uint8Array(buffer_arg)); -} - -var MaimaiUserService = (exports.MaimaiUserService = { - getPlaylog: { - path: "/mythos.maimai.v0.MaimaiUser/GetPlaylog", - requestStream: false, - responseStream: true, - requestType: maimai_user_pb.GetPlaylogRequest, - responseType: maimai_user_pb.GetPlaylogStreamItem, - requestSerialize: serialize_mythos_maimai_v0_GetPlaylogRequest, - requestDeserialize: deserialize_mythos_maimai_v0_GetPlaylogRequest, - responseSerialize: serialize_mythos_maimai_v0_GetPlaylogStreamItem, - responseDeserialize: deserialize_mythos_maimai_v0_GetPlaylogStreamItem, - }, -}); - -exports.MaimaiUserClient = grpc.makeGenericClientConstructor(MaimaiUserService, "MaimaiUser"); diff --git a/typescript/server/src/proto/generated/maimai/user_pb.d.ts b/typescript/server/src/proto/generated/maimai/user_pb.d.ts deleted file mode 100644 index bf348e712..000000000 --- a/typescript/server/src/proto/generated/maimai/user_pb.d.ts +++ /dev/null @@ -1,78 +0,0 @@ -// package: mythos.maimai.v0 -// file: maimai/user.proto - -import * as jspb from "google-protobuf"; -import * as maimai_playlog_pb from "../maimai/playlog_pb"; - -export class GetPlaylogRequest extends jspb.Message { - getProfileApiId(): string; - setProfileApiId(value: string): void; - - hasLastUserPlayDate(): boolean; - clearLastUserPlayDate(): void; - getLastUserPlayDate(): string; - setLastUserPlayDate(value: string): void; - - hasLimit(): boolean; - clearLimit(): void; - getLimit(): number; - setLimit(value: number): void; - - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): GetPlaylogRequest.AsObject; - static toObject(includeInstance: boolean, msg: GetPlaylogRequest): GetPlaylogRequest.AsObject; - static extensions: { [key: number]: jspb.ExtensionFieldInfo }; - static extensionsBinary: { [key: number]: jspb.ExtensionFieldBinaryInfo }; - static serializeBinaryToWriter(message: GetPlaylogRequest, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): GetPlaylogRequest; - static deserializeBinaryFromReader( - message: GetPlaylogRequest, - reader: jspb.BinaryReader, - ): GetPlaylogRequest; -} - -export namespace GetPlaylogRequest { - export type AsObject = { - profileApiId: string; - lastUserPlayDate: string; - limit: number; - }; -} - -export class GetPlaylogStreamItem extends jspb.Message { - getPlaylogApiId(): string; - setPlaylogApiId(value: string): void; - - hasInfo(): boolean; - clearInfo(): void; - getInfo(): maimai_playlog_pb.PlaylogInfo | undefined; - setInfo(value?: maimai_playlog_pb.PlaylogInfo): void; - - hasJudge(): boolean; - clearJudge(): void; - getJudge(): maimai_playlog_pb.PlaylogJudge | undefined; - setJudge(value?: maimai_playlog_pb.PlaylogJudge): void; - - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): GetPlaylogStreamItem.AsObject; - static toObject( - includeInstance: boolean, - msg: GetPlaylogStreamItem, - ): GetPlaylogStreamItem.AsObject; - static extensions: { [key: number]: jspb.ExtensionFieldInfo }; - static extensionsBinary: { [key: number]: jspb.ExtensionFieldBinaryInfo }; - static serializeBinaryToWriter(message: GetPlaylogStreamItem, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): GetPlaylogStreamItem; - static deserializeBinaryFromReader( - message: GetPlaylogStreamItem, - reader: jspb.BinaryReader, - ): GetPlaylogStreamItem; -} - -export namespace GetPlaylogStreamItem { - export type AsObject = { - playlogApiId: string; - info?: maimai_playlog_pb.PlaylogInfo.AsObject; - judge?: maimai_playlog_pb.PlaylogJudge.AsObject; - }; -} diff --git a/typescript/server/src/proto/generated/maimai/user_pb.js b/typescript/server/src/proto/generated/maimai/user_pb.js deleted file mode 100644 index f51c3771c..000000000 --- a/typescript/server/src/proto/generated/maimai/user_pb.js +++ /dev/null @@ -1,497 +0,0 @@ -// source: maimai/user.proto -/** - * @fileoverview - * @enhanceable - * @suppress {missingRequire} reports error on implicit type usages. - * @suppress {messageConventions} JS Compiler reports an error if a variable or - * field starts with 'MSG_' and isn't a translatable message. - * @public - */ -// GENERATED CODE -- DO NOT EDIT! -/* eslint-disable */ -// @ts-nocheck - -var jspb = require("google-protobuf"); -var goog = jspb; -var global = function () { - if (this) { - return this; - } - if (typeof window !== "undefined") { - return window; - } - if (typeof global !== "undefined") { - return global; - } - if (typeof self !== "undefined") { - return self; - } - return Function("return this")(); -}.call(null); - -var maimai_playlog_pb = require("../maimai/playlog_pb.js"); -goog.object.extend(proto, maimai_playlog_pb); -goog.exportSymbol("proto.mythos.maimai.v0.GetPlaylogRequest", null, global); -goog.exportSymbol("proto.mythos.maimai.v0.GetPlaylogStreamItem", null, global); -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.mythos.maimai.v0.GetPlaylogRequest = function (opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.mythos.maimai.v0.GetPlaylogRequest, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.mythos.maimai.v0.GetPlaylogRequest.displayName = - "proto.mythos.maimai.v0.GetPlaylogRequest"; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.mythos.maimai.v0.GetPlaylogStreamItem = function (opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.mythos.maimai.v0.GetPlaylogStreamItem, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.mythos.maimai.v0.GetPlaylogStreamItem.displayName = - "proto.mythos.maimai.v0.GetPlaylogStreamItem"; -} - -if (jspb.Message.GENERATE_TO_OBJECT) { - /** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ - proto.mythos.maimai.v0.GetPlaylogRequest.prototype.toObject = function (opt_includeInstance) { - return proto.mythos.maimai.v0.GetPlaylogRequest.toObject(opt_includeInstance, this); - }; - - /** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.mythos.maimai.v0.GetPlaylogRequest} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ - proto.mythos.maimai.v0.GetPlaylogRequest.toObject = function (includeInstance, msg) { - var f, - obj = { - profileApiId: jspb.Message.getFieldWithDefault(msg, 1, ""), - lastUserPlayDate: jspb.Message.getFieldWithDefault(msg, 2, ""), - limit: jspb.Message.getFieldWithDefault(msg, 3, 0), - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; - }; -} - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.mythos.maimai.v0.GetPlaylogRequest} - */ -proto.mythos.maimai.v0.GetPlaylogRequest.deserializeBinary = function (bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.mythos.maimai.v0.GetPlaylogRequest(); - return proto.mythos.maimai.v0.GetPlaylogRequest.deserializeBinaryFromReader(msg, reader); -}; - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.mythos.maimai.v0.GetPlaylogRequest} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.mythos.maimai.v0.GetPlaylogRequest} - */ -proto.mythos.maimai.v0.GetPlaylogRequest.deserializeBinaryFromReader = function (msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {string} */ (reader.readString()); - msg.setProfileApiId(value); - break; - case 2: - var value = /** @type {string} */ (reader.readString()); - msg.setLastUserPlayDate(value); - break; - case 3: - var value = /** @type {number} */ (reader.readUint64()); - msg.setLimit(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.mythos.maimai.v0.GetPlaylogRequest.prototype.serializeBinary = function () { - var writer = new jspb.BinaryWriter(); - proto.mythos.maimai.v0.GetPlaylogRequest.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.mythos.maimai.v0.GetPlaylogRequest} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.mythos.maimai.v0.GetPlaylogRequest.serializeBinaryToWriter = function (message, writer) { - var f = undefined; - f = message.getProfileApiId(); - if (f.length > 0) { - writer.writeString(1, f); - } - f = /** @type {string} */ (jspb.Message.getField(message, 2)); - if (f != null) { - writer.writeString(2, f); - } - f = /** @type {number} */ (jspb.Message.getField(message, 3)); - if (f != null) { - writer.writeUint64(3, f); - } -}; - -/** - * optional string profile_api_id = 1; - * @return {string} - */ -proto.mythos.maimai.v0.GetPlaylogRequest.prototype.getProfileApiId = function () { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); -}; - -/** - * @param {string} value - * @return {!proto.mythos.maimai.v0.GetPlaylogRequest} returns this - */ -proto.mythos.maimai.v0.GetPlaylogRequest.prototype.setProfileApiId = function (value) { - return jspb.Message.setProto3StringField(this, 1, value); -}; - -/** - * optional string last_user_play_date = 2; - * @return {string} - */ -proto.mythos.maimai.v0.GetPlaylogRequest.prototype.getLastUserPlayDate = function () { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); -}; - -/** - * @param {string} value - * @return {!proto.mythos.maimai.v0.GetPlaylogRequest} returns this - */ -proto.mythos.maimai.v0.GetPlaylogRequest.prototype.setLastUserPlayDate = function (value) { - return jspb.Message.setField(this, 2, value); -}; - -/** - * Clears the field making it undefined. - * @return {!proto.mythos.maimai.v0.GetPlaylogRequest} returns this - */ -proto.mythos.maimai.v0.GetPlaylogRequest.prototype.clearLastUserPlayDate = function () { - return jspb.Message.setField(this, 2, undefined); -}; - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.mythos.maimai.v0.GetPlaylogRequest.prototype.hasLastUserPlayDate = function () { - return jspb.Message.getField(this, 2) != null; -}; - -/** - * optional uint64 limit = 3; - * @return {number} - */ -proto.mythos.maimai.v0.GetPlaylogRequest.prototype.getLimit = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.maimai.v0.GetPlaylogRequest} returns this - */ -proto.mythos.maimai.v0.GetPlaylogRequest.prototype.setLimit = function (value) { - return jspb.Message.setField(this, 3, value); -}; - -/** - * Clears the field making it undefined. - * @return {!proto.mythos.maimai.v0.GetPlaylogRequest} returns this - */ -proto.mythos.maimai.v0.GetPlaylogRequest.prototype.clearLimit = function () { - return jspb.Message.setField(this, 3, undefined); -}; - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.mythos.maimai.v0.GetPlaylogRequest.prototype.hasLimit = function () { - return jspb.Message.getField(this, 3) != null; -}; - -if (jspb.Message.GENERATE_TO_OBJECT) { - /** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ - proto.mythos.maimai.v0.GetPlaylogStreamItem.prototype.toObject = function ( - opt_includeInstance, - ) { - return proto.mythos.maimai.v0.GetPlaylogStreamItem.toObject(opt_includeInstance, this); - }; - - /** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.mythos.maimai.v0.GetPlaylogStreamItem} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ - proto.mythos.maimai.v0.GetPlaylogStreamItem.toObject = function (includeInstance, msg) { - var f, - obj = { - playlogApiId: jspb.Message.getFieldWithDefault(msg, 1, ""), - info: - (f = msg.getInfo()) && - maimai_playlog_pb.PlaylogInfo.toObject(includeInstance, f), - judge: - (f = msg.getJudge()) && - maimai_playlog_pb.PlaylogJudge.toObject(includeInstance, f), - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; - }; -} - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.mythos.maimai.v0.GetPlaylogStreamItem} - */ -proto.mythos.maimai.v0.GetPlaylogStreamItem.deserializeBinary = function (bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.mythos.maimai.v0.GetPlaylogStreamItem(); - return proto.mythos.maimai.v0.GetPlaylogStreamItem.deserializeBinaryFromReader(msg, reader); -}; - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.mythos.maimai.v0.GetPlaylogStreamItem} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.mythos.maimai.v0.GetPlaylogStreamItem} - */ -proto.mythos.maimai.v0.GetPlaylogStreamItem.deserializeBinaryFromReader = function (msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {string} */ (reader.readString()); - msg.setPlaylogApiId(value); - break; - case 2: - var value = new maimai_playlog_pb.PlaylogInfo(); - reader.readMessage( - value, - maimai_playlog_pb.PlaylogInfo.deserializeBinaryFromReader, - ); - msg.setInfo(value); - break; - case 3: - var value = new maimai_playlog_pb.PlaylogJudge(); - reader.readMessage( - value, - maimai_playlog_pb.PlaylogJudge.deserializeBinaryFromReader, - ); - msg.setJudge(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.mythos.maimai.v0.GetPlaylogStreamItem.prototype.serializeBinary = function () { - var writer = new jspb.BinaryWriter(); - proto.mythos.maimai.v0.GetPlaylogStreamItem.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.mythos.maimai.v0.GetPlaylogStreamItem} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.mythos.maimai.v0.GetPlaylogStreamItem.serializeBinaryToWriter = function (message, writer) { - var f = undefined; - f = message.getPlaylogApiId(); - if (f.length > 0) { - writer.writeString(1, f); - } - f = message.getInfo(); - if (f != null) { - writer.writeMessage(2, f, maimai_playlog_pb.PlaylogInfo.serializeBinaryToWriter); - } - f = message.getJudge(); - if (f != null) { - writer.writeMessage(3, f, maimai_playlog_pb.PlaylogJudge.serializeBinaryToWriter); - } -}; - -/** - * optional string playlog_api_id = 1; - * @return {string} - */ -proto.mythos.maimai.v0.GetPlaylogStreamItem.prototype.getPlaylogApiId = function () { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); -}; - -/** - * @param {string} value - * @return {!proto.mythos.maimai.v0.GetPlaylogStreamItem} returns this - */ -proto.mythos.maimai.v0.GetPlaylogStreamItem.prototype.setPlaylogApiId = function (value) { - return jspb.Message.setProto3StringField(this, 1, value); -}; - -/** - * optional PlaylogInfo info = 2; - * @return {?proto.mythos.maimai.v0.PlaylogInfo} - */ -proto.mythos.maimai.v0.GetPlaylogStreamItem.prototype.getInfo = function () { - return /** @type{?proto.mythos.maimai.v0.PlaylogInfo} */ ( - jspb.Message.getWrapperField(this, maimai_playlog_pb.PlaylogInfo, 2) - ); -}; - -/** - * @param {?proto.mythos.maimai.v0.PlaylogInfo|undefined} value - * @return {!proto.mythos.maimai.v0.GetPlaylogStreamItem} returns this - */ -proto.mythos.maimai.v0.GetPlaylogStreamItem.prototype.setInfo = function (value) { - return jspb.Message.setWrapperField(this, 2, value); -}; - -/** - * Clears the message field making it undefined. - * @return {!proto.mythos.maimai.v0.GetPlaylogStreamItem} returns this - */ -proto.mythos.maimai.v0.GetPlaylogStreamItem.prototype.clearInfo = function () { - return this.setInfo(undefined); -}; - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.mythos.maimai.v0.GetPlaylogStreamItem.prototype.hasInfo = function () { - return jspb.Message.getField(this, 2) != null; -}; - -/** - * optional PlaylogJudge judge = 3; - * @return {?proto.mythos.maimai.v0.PlaylogJudge} - */ -proto.mythos.maimai.v0.GetPlaylogStreamItem.prototype.getJudge = function () { - return /** @type{?proto.mythos.maimai.v0.PlaylogJudge} */ ( - jspb.Message.getWrapperField(this, maimai_playlog_pb.PlaylogJudge, 3) - ); -}; - -/** - * @param {?proto.mythos.maimai.v0.PlaylogJudge|undefined} value - * @return {!proto.mythos.maimai.v0.GetPlaylogStreamItem} returns this - */ -proto.mythos.maimai.v0.GetPlaylogStreamItem.prototype.setJudge = function (value) { - return jspb.Message.setWrapperField(this, 3, value); -}; - -/** - * Clears the message field making it undefined. - * @return {!proto.mythos.maimai.v0.GetPlaylogStreamItem} returns this - */ -proto.mythos.maimai.v0.GetPlaylogStreamItem.prototype.clearJudge = function () { - return this.setJudge(undefined); -}; - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.mythos.maimai.v0.GetPlaylogStreamItem.prototype.hasJudge = function () { - return jspb.Message.getField(this, 3) != null; -}; - -goog.object.extend(exports, proto.mythos.maimai.v0); diff --git a/typescript/server/src/proto/generated/maimai/user_pb.ts b/typescript/server/src/proto/generated/maimai/user_pb.ts new file mode 100644 index 000000000..9bff9fa3b --- /dev/null +++ b/typescript/server/src/proto/generated/maimai/user_pb.ts @@ -0,0 +1,89 @@ +// @generated by protoc-gen-es v2.11.0 with parameter "target=ts" +// @generated from file maimai/user.proto (package mythos.maimai.v0, syntax proto3) +/* eslint-disable */ + +import type { GenFile, GenMessage, GenService } from "@bufbuild/protobuf/codegenv2"; +import { fileDesc, messageDesc, serviceDesc } from "@bufbuild/protobuf/codegenv2"; +import type { PlaylogInfo, PlaylogJudge } from "./playlog_pb"; +import { file_maimai_playlog } from "./playlog_pb"; +import type { Message } from "@bufbuild/protobuf"; + +/** + * Describes the file maimai/user.proto. + */ +export const file_maimai_user: GenFile = + /*@__PURE__*/ + fileDesc( + "ChFtYWltYWkvdXNlci5wcm90bxIQbXl0aG9zLm1haW1haS52MCKDAQoRR2V0UGxheWxvZ1JlcXVlc3QSFgoOcHJvZmlsZV9hcGlfaWQYASABKAkSIAoTbGFzdF91c2VyX3BsYXlfZGF0ZRgCIAEoCUgAiAEBEhIKBWxpbWl0GAMgASgESAGIAQFCFgoUX2xhc3RfdXNlcl9wbGF5X2RhdGVCCAoGX2xpbWl0IooBChRHZXRQbGF5bG9nU3RyZWFtSXRlbRIWCg5wbGF5bG9nX2FwaV9pZBgBIAEoCRIrCgRpbmZvGAIgASgLMh0ubXl0aG9zLm1haW1haS52MC5QbGF5bG9nSW5mbxItCgVqdWRnZRgDIAEoCzIeLm15dGhvcy5tYWltYWkudjAuUGxheWxvZ0p1ZGdlMmkKCk1haW1haVVzZXISWwoKR2V0UGxheWxvZxIjLm15dGhvcy5tYWltYWkudjAuR2V0UGxheWxvZ1JlcXVlc3QaJi5teXRob3MubWFpbWFpLnYwLkdldFBsYXlsb2dTdHJlYW1JdGVtMAFiBnByb3RvMw", + [file_maimai_playlog], + ); + +/** + * @generated from message mythos.maimai.v0.GetPlaylogRequest + */ +export type GetPlaylogRequest = Message<"mythos.maimai.v0.GetPlaylogRequest"> & { + /** + * @generated from field: string profile_api_id = 1; + */ + profileApiId: string; + + /** + * @generated from field: optional string last_user_play_date = 2; + */ + lastUserPlayDate?: string; + + /** + * @generated from field: optional uint64 limit = 3; + */ + limit?: bigint; +}; + +/** + * Describes the message mythos.maimai.v0.GetPlaylogRequest. + * Use `create(GetPlaylogRequestSchema)` to create a new message. + */ +export const GetPlaylogRequestSchema: GenMessage = + /*@__PURE__*/ + messageDesc(file_maimai_user, 0); + +/** + * @generated from message mythos.maimai.v0.GetPlaylogStreamItem + */ +export type GetPlaylogStreamItem = Message<"mythos.maimai.v0.GetPlaylogStreamItem"> & { + /** + * @generated from field: string playlog_api_id = 1; + */ + playlogApiId: string; + + /** + * @generated from field: mythos.maimai.v0.PlaylogInfo info = 2; + */ + info?: PlaylogInfo; + + /** + * @generated from field: mythos.maimai.v0.PlaylogJudge judge = 3; + */ + judge?: PlaylogJudge; +}; + +/** + * Describes the message mythos.maimai.v0.GetPlaylogStreamItem. + * Use `create(GetPlaylogStreamItemSchema)` to create a new message. + */ +export const GetPlaylogStreamItemSchema: GenMessage = + /*@__PURE__*/ + messageDesc(file_maimai_user, 1); + +/** + * @generated from service mythos.maimai.v0.MaimaiUser + */ +export const MaimaiUser: GenService<{ + /** + * @generated from rpc mythos.maimai.v0.MaimaiUser.GetPlaylog + */ + getPlaylog: { + methodKind: "server_streaming"; + input: typeof GetPlaylogRequestSchema; + output: typeof GetPlaylogStreamItemSchema; + }; +}> = /*@__PURE__*/ serviceDesc(file_maimai_user, 0); diff --git a/typescript/server/src/proto/generated/ongeki/common_grpc_pb.d.ts b/typescript/server/src/proto/generated/ongeki/common_grpc_pb.d.ts deleted file mode 100644 index 51b4d6959..000000000 --- a/typescript/server/src/proto/generated/ongeki/common_grpc_pb.d.ts +++ /dev/null @@ -1 +0,0 @@ -// GENERATED CODE -- NO SERVICES IN PROTO diff --git a/typescript/server/src/proto/generated/ongeki/common_grpc_pb.js b/typescript/server/src/proto/generated/ongeki/common_grpc_pb.js deleted file mode 100644 index 51b4d6959..000000000 --- a/typescript/server/src/proto/generated/ongeki/common_grpc_pb.js +++ /dev/null @@ -1 +0,0 @@ -// GENERATED CODE -- NO SERVICES IN PROTO diff --git a/typescript/server/src/proto/generated/ongeki/common_pb.d.ts b/typescript/server/src/proto/generated/ongeki/common_pb.d.ts deleted file mode 100644 index c2a79ecb6..000000000 --- a/typescript/server/src/proto/generated/ongeki/common_pb.d.ts +++ /dev/null @@ -1,84 +0,0 @@ -// package: mythos.ongeki.v0 -// file: ongeki/common.proto - -import * as jspb from "google-protobuf"; - -export interface OngekiLevelMap { - ONGEKI_LEVEL_UNSPECIFIED: 0; - ONGEKI_LEVEL_BASIC: 1; - ONGEKI_LEVEL_ADVANCED: 2; - ONGEKI_LEVEL_EXPERT: 3; - ONGEKI_LEVEL_MASTER: 4; - ONGEKI_LEVEL_LUNATIC: 5; -} - -export const OngekiLevel: OngekiLevelMap; - -export interface OngekiRankingTypeMap { - ONGEKI_RANKING_TYPE_UNSPECIFIED: 0; - ONGEKI_RANKING_TYPE_TECH_SCORE: 1; - ONGEKI_RANKING_TYPE_BATTLE_SCORE: 2; - ONGEKI_RANKING_TYPE_OVER_DAMAGE: 3; - ONGEKI_RANKING_TYPE_PLATINUM_SCORE: 4; -} - -export const OngekiRankingType: OngekiRankingTypeMap; - -export interface OngekiTechScoreRankMap { - ONGEKI_TECH_SCORE_RANK_UNSPECIFIED: 0; - ONGEKI_TECH_SCORE_RANK_D: 1; - ONGEKI_TECH_SCORE_RANK_C: 2; - ONGEKI_TECH_SCORE_RANK_B: 3; - ONGEKI_TECH_SCORE_RANK_BB: 4; - ONGEKI_TECH_SCORE_RANK_BBB: 5; - ONGEKI_TECH_SCORE_RANK_A: 6; - ONGEKI_TECH_SCORE_RANK_AA: 7; - ONGEKI_TECH_SCORE_RANK_AAA: 8; - ONGEKI_TECH_SCORE_RANK_S: 9; - ONGEKI_TECH_SCORE_RANK_S_PLUS: 10; - ONGEKI_TECH_SCORE_RANK_SS: 11; - ONGEKI_TECH_SCORE_RANK_SS_PLUS: 12; - ONGEKI_TECH_SCORE_RANK_SSS: 13; - ONGEKI_TECH_SCORE_RANK_SSS_PLUS: 14; -} - -export const OngekiTechScoreRank: OngekiTechScoreRankMap; - -export interface OngekiBattleScoreRankMap { - ONGEKI_BATTLE_SCORE_RANK_UNSPECIFIED: 0; - ONGEKI_BATTLE_SCORE_RANK_NO_GOOD: 1; - ONGEKI_BATTLE_SCORE_RANK_USUALLY: 2; - ONGEKI_BATTLE_SCORE_RANK_GOOD: 3; - ONGEKI_BATTLE_SCORE_RANK_GREAT: 4; - ONGEKI_BATTLE_SCORE_RANK_EXCELLENT: 5; - ONGEKI_BATTLE_SCORE_RANK_UNBELIEVABLE: 6; -} - -export const OngekiBattleScoreRank: OngekiBattleScoreRankMap; - -export interface OngekiClearStatusMap { - ONGEKI_CLEAR_STATUS_UNSPECIFIED: 0; - ONGEKI_CLEAR_STATUS_FAILED: 1; - ONGEKI_CLEAR_STATUS_CLEARED: 2; - ONGEKI_CLEAR_STATUS_OVER_DAMAGE: 3; -} - -export const OngekiClearStatus: OngekiClearStatusMap; - -export interface OngekiComboStatusMap { - ONGEKI_COMBO_STATUS_UNSPECIFIED: 0; - ONGEKI_COMBO_STATUS_NONE: 1; - ONGEKI_COMBO_STATUS_FULL_COMBO: 2; - ONGEKI_COMBO_STATUS_ALL_BREAK: 3; -} - -export const OngekiComboStatus: OngekiComboStatusMap; - -export interface OngekiBossAttributeMap { - ONGEKI_BOSS_ATTRIBUTE_UNSPECIFIED: 0; - ONGEKI_BOSS_ATTRIBUTE_FIRE: 1; - ONGEKI_BOSS_ATTRIBUTE_AQUA: 2; - ONGEKI_BOSS_ATTRIBUTE_LEAF: 3; -} - -export const OngekiBossAttribute: OngekiBossAttributeMap; diff --git a/typescript/server/src/proto/generated/ongeki/common_pb.js b/typescript/server/src/proto/generated/ongeki/common_pb.js deleted file mode 100644 index 408ec7682..000000000 --- a/typescript/server/src/proto/generated/ongeki/common_pb.js +++ /dev/null @@ -1,126 +0,0 @@ -// source: ongeki/common.proto -/** - * @fileoverview - * @enhanceable - * @suppress {missingRequire} reports error on implicit type usages. - * @suppress {messageConventions} JS Compiler reports an error if a variable or - * field starts with 'MSG_' and isn't a translatable message. - * @public - */ -// GENERATED CODE -- DO NOT EDIT! -/* eslint-disable */ -// @ts-nocheck - -var jspb = require("google-protobuf"); -var goog = jspb; -var global = function () { - if (this) { - return this; - } - if (typeof window !== "undefined") { - return window; - } - if (typeof global !== "undefined") { - return global; - } - if (typeof self !== "undefined") { - return self; - } - return Function("return this")(); -}.call(null); - -goog.exportSymbol("proto.mythos.ongeki.v0.OngekiBattleScoreRank", null, global); -goog.exportSymbol("proto.mythos.ongeki.v0.OngekiBossAttribute", null, global); -goog.exportSymbol("proto.mythos.ongeki.v0.OngekiClearStatus", null, global); -goog.exportSymbol("proto.mythos.ongeki.v0.OngekiComboStatus", null, global); -goog.exportSymbol("proto.mythos.ongeki.v0.OngekiLevel", null, global); -goog.exportSymbol("proto.mythos.ongeki.v0.OngekiRankingType", null, global); -goog.exportSymbol("proto.mythos.ongeki.v0.OngekiTechScoreRank", null, global); -/** - * @enum {number} - */ -proto.mythos.ongeki.v0.OngekiLevel = { - ONGEKI_LEVEL_UNSPECIFIED: 0, - ONGEKI_LEVEL_BASIC: 1, - ONGEKI_LEVEL_ADVANCED: 2, - ONGEKI_LEVEL_EXPERT: 3, - ONGEKI_LEVEL_MASTER: 4, - ONGEKI_LEVEL_LUNATIC: 5, -}; - -/** - * @enum {number} - */ -proto.mythos.ongeki.v0.OngekiRankingType = { - ONGEKI_RANKING_TYPE_UNSPECIFIED: 0, - ONGEKI_RANKING_TYPE_TECH_SCORE: 1, - ONGEKI_RANKING_TYPE_BATTLE_SCORE: 2, - ONGEKI_RANKING_TYPE_OVER_DAMAGE: 3, - ONGEKI_RANKING_TYPE_PLATINUM_SCORE: 4, -}; - -/** - * @enum {number} - */ -proto.mythos.ongeki.v0.OngekiTechScoreRank = { - ONGEKI_TECH_SCORE_RANK_UNSPECIFIED: 0, - ONGEKI_TECH_SCORE_RANK_D: 1, - ONGEKI_TECH_SCORE_RANK_C: 2, - ONGEKI_TECH_SCORE_RANK_B: 3, - ONGEKI_TECH_SCORE_RANK_BB: 4, - ONGEKI_TECH_SCORE_RANK_BBB: 5, - ONGEKI_TECH_SCORE_RANK_A: 6, - ONGEKI_TECH_SCORE_RANK_AA: 7, - ONGEKI_TECH_SCORE_RANK_AAA: 8, - ONGEKI_TECH_SCORE_RANK_S: 9, - ONGEKI_TECH_SCORE_RANK_S_PLUS: 10, - ONGEKI_TECH_SCORE_RANK_SS: 11, - ONGEKI_TECH_SCORE_RANK_SS_PLUS: 12, - ONGEKI_TECH_SCORE_RANK_SSS: 13, - ONGEKI_TECH_SCORE_RANK_SSS_PLUS: 14, -}; - -/** - * @enum {number} - */ -proto.mythos.ongeki.v0.OngekiBattleScoreRank = { - ONGEKI_BATTLE_SCORE_RANK_UNSPECIFIED: 0, - ONGEKI_BATTLE_SCORE_RANK_NO_GOOD: 1, - ONGEKI_BATTLE_SCORE_RANK_USUALLY: 2, - ONGEKI_BATTLE_SCORE_RANK_GOOD: 3, - ONGEKI_BATTLE_SCORE_RANK_GREAT: 4, - ONGEKI_BATTLE_SCORE_RANK_EXCELLENT: 5, - ONGEKI_BATTLE_SCORE_RANK_UNBELIEVABLE: 6, -}; - -/** - * @enum {number} - */ -proto.mythos.ongeki.v0.OngekiClearStatus = { - ONGEKI_CLEAR_STATUS_UNSPECIFIED: 0, - ONGEKI_CLEAR_STATUS_FAILED: 1, - ONGEKI_CLEAR_STATUS_CLEARED: 2, - ONGEKI_CLEAR_STATUS_OVER_DAMAGE: 3, -}; - -/** - * @enum {number} - */ -proto.mythos.ongeki.v0.OngekiComboStatus = { - ONGEKI_COMBO_STATUS_UNSPECIFIED: 0, - ONGEKI_COMBO_STATUS_NONE: 1, - ONGEKI_COMBO_STATUS_FULL_COMBO: 2, - ONGEKI_COMBO_STATUS_ALL_BREAK: 3, -}; - -/** - * @enum {number} - */ -proto.mythos.ongeki.v0.OngekiBossAttribute = { - ONGEKI_BOSS_ATTRIBUTE_UNSPECIFIED: 0, - ONGEKI_BOSS_ATTRIBUTE_FIRE: 1, - ONGEKI_BOSS_ATTRIBUTE_AQUA: 2, - ONGEKI_BOSS_ATTRIBUTE_LEAF: 3, -}; - -goog.object.extend(exports, proto.mythos.ongeki.v0); diff --git a/typescript/server/src/proto/generated/ongeki/common_pb.ts b/typescript/server/src/proto/generated/ongeki/common_pb.ts new file mode 100644 index 000000000..781b936ee --- /dev/null +++ b/typescript/server/src/proto/generated/ongeki/common_pb.ts @@ -0,0 +1,326 @@ +// @generated by protoc-gen-es v2.11.0 with parameter "target=ts" +// @generated from file ongeki/common.proto (package mythos.ongeki.v0, syntax proto3) +/* eslint-disable */ + +import type { GenEnum, GenFile } from "@bufbuild/protobuf/codegenv2"; +import { enumDesc, fileDesc } from "@bufbuild/protobuf/codegenv2"; + +/** + * Describes the file ongeki/common.proto. + */ +export const file_ongeki_common: GenFile = + /*@__PURE__*/ + fileDesc( + "ChNvbmdla2kvY29tbW9uLnByb3RvEhBteXRob3Mub25nZWtpLnYwKqoBCgtPbmdla2lMZXZlbBIcChhPTkdFS0lfTEVWRUxfVU5TUEVDSUZJRUQQABIWChJPTkdFS0lfTEVWRUxfQkFTSUMQARIZChVPTkdFS0lfTEVWRUxfQURWQU5DRUQQAhIXChNPTkdFS0lfTEVWRUxfRVhQRVJUEAMSFwoTT05HRUtJX0xFVkVMX01BU1RFUhAEEhgKFE9OR0VLSV9MRVZFTF9MVU5BVElDEAUqzwEKEU9uZ2VraVJhbmtpbmdUeXBlEiMKH09OR0VLSV9SQU5LSU5HX1RZUEVfVU5TUEVDSUZJRUQQABIiCh5PTkdFS0lfUkFOS0lOR19UWVBFX1RFQ0hfU0NPUkUQARIkCiBPTkdFS0lfUkFOS0lOR19UWVBFX0JBVFRMRV9TQ09SRRACEiMKH09OR0VLSV9SQU5LSU5HX1RZUEVfT1ZFUl9EQU1BR0UQAxImCiJPTkdFS0lfUkFOS0lOR19UWVBFX1BMQVRJTlVNX1NDT1JFEAQq/AMKE09uZ2VraVRlY2hTY29yZVJhbmsSJgoiT05HRUtJX1RFQ0hfU0NPUkVfUkFOS19VTlNQRUNJRklFRBAAEhwKGE9OR0VLSV9URUNIX1NDT1JFX1JBTktfRBABEhwKGE9OR0VLSV9URUNIX1NDT1JFX1JBTktfQxACEhwKGE9OR0VLSV9URUNIX1NDT1JFX1JBTktfQhADEh0KGU9OR0VLSV9URUNIX1NDT1JFX1JBTktfQkIQBBIeChpPTkdFS0lfVEVDSF9TQ09SRV9SQU5LX0JCQhAFEhwKGE9OR0VLSV9URUNIX1NDT1JFX1JBTktfQRAGEh0KGU9OR0VLSV9URUNIX1NDT1JFX1JBTktfQUEQBxIeChpPTkdFS0lfVEVDSF9TQ09SRV9SQU5LX0FBQRAIEhwKGE9OR0VLSV9URUNIX1NDT1JFX1JBTktfUxAJEiEKHU9OR0VLSV9URUNIX1NDT1JFX1JBTktfU19QTFVTEAoSHQoZT05HRUtJX1RFQ0hfU0NPUkVfUkFOS19TUxALEiIKHk9OR0VLSV9URUNIX1NDT1JFX1JBTktfU1NfUExVUxAMEh4KGk9OR0VLSV9URUNIX1NDT1JFX1JBTktfU1NTEA0SIwofT05HRUtJX1RFQ0hfU0NPUkVfUkFOS19TU1NfUExVUxAOKqcCChVPbmdla2lCYXR0bGVTY29yZVJhbmsSKAokT05HRUtJX0JBVFRMRV9TQ09SRV9SQU5LX1VOU1BFQ0lGSUVEEAASJAogT05HRUtJX0JBVFRMRV9TQ09SRV9SQU5LX05PX0dPT0QQARIkCiBPTkdFS0lfQkFUVExFX1NDT1JFX1JBTktfVVNVQUxMWRACEiEKHU9OR0VLSV9CQVRUTEVfU0NPUkVfUkFOS19HT09EEAMSIgoeT05HRUtJX0JBVFRMRV9TQ09SRV9SQU5LX0dSRUFUEAQSJgoiT05HRUtJX0JBVFRMRV9TQ09SRV9SQU5LX0VYQ0VMTEVOVBAFEikKJU9OR0VLSV9CQVRUTEVfU0NPUkVfUkFOS19VTkJFTElFVkFCTEUQBiqeAQoRT25nZWtpQ2xlYXJTdGF0dXMSIwofT05HRUtJX0NMRUFSX1NUQVRVU19VTlNQRUNJRklFRBAAEh4KGk9OR0VLSV9DTEVBUl9TVEFUVVNfRkFJTEVEEAESHwobT05HRUtJX0NMRUFSX1NUQVRVU19DTEVBUkVEEAISIwofT05HRUtJX0NMRUFSX1NUQVRVU19PVkVSX0RBTUFHRRADKp0BChFPbmdla2lDb21ib1N0YXR1cxIjCh9PTkdFS0lfQ09NQk9fU1RBVFVTX1VOU1BFQ0lGSUVEEAASHAoYT05HRUtJX0NPTUJPX1NUQVRVU19OT05FEAESIgoeT05HRUtJX0NPTUJPX1NUQVRVU19GVUxMX0NPTUJPEAISIQodT05HRUtJX0NPTUJPX1NUQVRVU19BTExfQlJFQUsQAyqcAQoTT25nZWtpQm9zc0F0dHJpYnV0ZRIlCiFPTkdFS0lfQk9TU19BVFRSSUJVVEVfVU5TUEVDSUZJRUQQABIeChpPTkdFS0lfQk9TU19BVFRSSUJVVEVfRklSRRABEh4KGk9OR0VLSV9CT1NTX0FUVFJJQlVURV9BUVVBEAISHgoaT05HRUtJX0JPU1NfQVRUUklCVVRFX0xFQUYQA2IGcHJvdG8z", + ); + +/** + * @generated from enum mythos.ongeki.v0.OngekiLevel + */ +export enum OngekiLevel { + /** + * @generated from enum value: ONGEKI_LEVEL_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + + /** + * @generated from enum value: ONGEKI_LEVEL_BASIC = 1; + */ + BASIC = 1, + + /** + * @generated from enum value: ONGEKI_LEVEL_ADVANCED = 2; + */ + ADVANCED = 2, + + /** + * @generated from enum value: ONGEKI_LEVEL_EXPERT = 3; + */ + EXPERT = 3, + + /** + * @generated from enum value: ONGEKI_LEVEL_MASTER = 4; + */ + MASTER = 4, + + /** + * @generated from enum value: ONGEKI_LEVEL_LUNATIC = 5; + */ + LUNATIC = 5, +} + +/** + * Describes the enum mythos.ongeki.v0.OngekiLevel. + */ +export const OngekiLevelSchema: GenEnum = + /*@__PURE__*/ + enumDesc(file_ongeki_common, 0); + +/** + * @generated from enum mythos.ongeki.v0.OngekiRankingType + */ +export enum OngekiRankingType { + /** + * @generated from enum value: ONGEKI_RANKING_TYPE_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + + /** + * @generated from enum value: ONGEKI_RANKING_TYPE_TECH_SCORE = 1; + */ + TECH_SCORE = 1, + + /** + * @generated from enum value: ONGEKI_RANKING_TYPE_BATTLE_SCORE = 2; + */ + BATTLE_SCORE = 2, + + /** + * @generated from enum value: ONGEKI_RANKING_TYPE_OVER_DAMAGE = 3; + */ + OVER_DAMAGE = 3, + + /** + * @generated from enum value: ONGEKI_RANKING_TYPE_PLATINUM_SCORE = 4; + */ + PLATINUM_SCORE = 4, +} + +/** + * Describes the enum mythos.ongeki.v0.OngekiRankingType. + */ +export const OngekiRankingTypeSchema: GenEnum = + /*@__PURE__*/ + enumDesc(file_ongeki_common, 1); + +/** + * @generated from enum mythos.ongeki.v0.OngekiTechScoreRank + */ +export enum OngekiTechScoreRank { + /** + * @generated from enum value: ONGEKI_TECH_SCORE_RANK_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + + /** + * @generated from enum value: ONGEKI_TECH_SCORE_RANK_D = 1; + */ + D = 1, + + /** + * @generated from enum value: ONGEKI_TECH_SCORE_RANK_C = 2; + */ + C = 2, + + /** + * @generated from enum value: ONGEKI_TECH_SCORE_RANK_B = 3; + */ + B = 3, + + /** + * @generated from enum value: ONGEKI_TECH_SCORE_RANK_BB = 4; + */ + BB = 4, + + /** + * @generated from enum value: ONGEKI_TECH_SCORE_RANK_BBB = 5; + */ + BBB = 5, + + /** + * @generated from enum value: ONGEKI_TECH_SCORE_RANK_A = 6; + */ + A = 6, + + /** + * @generated from enum value: ONGEKI_TECH_SCORE_RANK_AA = 7; + */ + AA = 7, + + /** + * @generated from enum value: ONGEKI_TECH_SCORE_RANK_AAA = 8; + */ + AAA = 8, + + /** + * @generated from enum value: ONGEKI_TECH_SCORE_RANK_S = 9; + */ + S = 9, + + /** + * @generated from enum value: ONGEKI_TECH_SCORE_RANK_S_PLUS = 10; + */ + S_PLUS = 10, + + /** + * @generated from enum value: ONGEKI_TECH_SCORE_RANK_SS = 11; + */ + SS = 11, + + /** + * @generated from enum value: ONGEKI_TECH_SCORE_RANK_SS_PLUS = 12; + */ + SS_PLUS = 12, + + /** + * @generated from enum value: ONGEKI_TECH_SCORE_RANK_SSS = 13; + */ + SSS = 13, + + /** + * @generated from enum value: ONGEKI_TECH_SCORE_RANK_SSS_PLUS = 14; + */ + SSS_PLUS = 14, +} + +/** + * Describes the enum mythos.ongeki.v0.OngekiTechScoreRank. + */ +export const OngekiTechScoreRankSchema: GenEnum = + /*@__PURE__*/ + enumDesc(file_ongeki_common, 2); + +/** + * @generated from enum mythos.ongeki.v0.OngekiBattleScoreRank + */ +export enum OngekiBattleScoreRank { + /** + * @generated from enum value: ONGEKI_BATTLE_SCORE_RANK_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + + /** + * @generated from enum value: ONGEKI_BATTLE_SCORE_RANK_NO_GOOD = 1; + */ + NO_GOOD = 1, + + /** + * @generated from enum value: ONGEKI_BATTLE_SCORE_RANK_USUALLY = 2; + */ + USUALLY = 2, + + /** + * @generated from enum value: ONGEKI_BATTLE_SCORE_RANK_GOOD = 3; + */ + GOOD = 3, + + /** + * @generated from enum value: ONGEKI_BATTLE_SCORE_RANK_GREAT = 4; + */ + GREAT = 4, + + /** + * @generated from enum value: ONGEKI_BATTLE_SCORE_RANK_EXCELLENT = 5; + */ + EXCELLENT = 5, + + /** + * @generated from enum value: ONGEKI_BATTLE_SCORE_RANK_UNBELIEVABLE = 6; + */ + UNBELIEVABLE = 6, +} + +/** + * Describes the enum mythos.ongeki.v0.OngekiBattleScoreRank. + */ +export const OngekiBattleScoreRankSchema: GenEnum = + /*@__PURE__*/ + enumDesc(file_ongeki_common, 3); + +/** + * @generated from enum mythos.ongeki.v0.OngekiClearStatus + */ +export enum OngekiClearStatus { + /** + * @generated from enum value: ONGEKI_CLEAR_STATUS_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + + /** + * @generated from enum value: ONGEKI_CLEAR_STATUS_FAILED = 1; + */ + FAILED = 1, + + /** + * @generated from enum value: ONGEKI_CLEAR_STATUS_CLEARED = 2; + */ + CLEARED = 2, + + /** + * @generated from enum value: ONGEKI_CLEAR_STATUS_OVER_DAMAGE = 3; + */ + OVER_DAMAGE = 3, +} + +/** + * Describes the enum mythos.ongeki.v0.OngekiClearStatus. + */ +export const OngekiClearStatusSchema: GenEnum = + /*@__PURE__*/ + enumDesc(file_ongeki_common, 4); + +/** + * @generated from enum mythos.ongeki.v0.OngekiComboStatus + */ +export enum OngekiComboStatus { + /** + * @generated from enum value: ONGEKI_COMBO_STATUS_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + + /** + * @generated from enum value: ONGEKI_COMBO_STATUS_NONE = 1; + */ + NONE = 1, + + /** + * @generated from enum value: ONGEKI_COMBO_STATUS_FULL_COMBO = 2; + */ + FULL_COMBO = 2, + + /** + * @generated from enum value: ONGEKI_COMBO_STATUS_ALL_BREAK = 3; + */ + ALL_BREAK = 3, +} + +/** + * Describes the enum mythos.ongeki.v0.OngekiComboStatus. + */ +export const OngekiComboStatusSchema: GenEnum = + /*@__PURE__*/ + enumDesc(file_ongeki_common, 5); + +/** + * This starts at 1 for some reason + * + * @generated from enum mythos.ongeki.v0.OngekiBossAttribute + */ +export enum OngekiBossAttribute { + /** + * @generated from enum value: ONGEKI_BOSS_ATTRIBUTE_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + + /** + * @generated from enum value: ONGEKI_BOSS_ATTRIBUTE_FIRE = 1; + */ + FIRE = 1, + + /** + * @generated from enum value: ONGEKI_BOSS_ATTRIBUTE_AQUA = 2; + */ + AQUA = 2, + + /** + * @generated from enum value: ONGEKI_BOSS_ATTRIBUTE_LEAF = 3; + */ + LEAF = 3, +} + +/** + * Describes the enum mythos.ongeki.v0.OngekiBossAttribute. + */ +export const OngekiBossAttributeSchema: GenEnum = + /*@__PURE__*/ + enumDesc(file_ongeki_common, 6); diff --git a/typescript/server/src/proto/generated/ongeki/playlog_grpc_pb.d.ts b/typescript/server/src/proto/generated/ongeki/playlog_grpc_pb.d.ts deleted file mode 100644 index 51b4d6959..000000000 --- a/typescript/server/src/proto/generated/ongeki/playlog_grpc_pb.d.ts +++ /dev/null @@ -1 +0,0 @@ -// GENERATED CODE -- NO SERVICES IN PROTO diff --git a/typescript/server/src/proto/generated/ongeki/playlog_grpc_pb.js b/typescript/server/src/proto/generated/ongeki/playlog_grpc_pb.js deleted file mode 100644 index 51b4d6959..000000000 --- a/typescript/server/src/proto/generated/ongeki/playlog_grpc_pb.js +++ /dev/null @@ -1 +0,0 @@ -// GENERATED CODE -- NO SERVICES IN PROTO diff --git a/typescript/server/src/proto/generated/ongeki/playlog_pb.d.ts b/typescript/server/src/proto/generated/ongeki/playlog_pb.d.ts deleted file mode 100644 index 419a3cf03..000000000 --- a/typescript/server/src/proto/generated/ongeki/playlog_pb.d.ts +++ /dev/null @@ -1,309 +0,0 @@ -// package: mythos.ongeki.v0 -// file: ongeki/playlog.proto - -import * as jspb from "google-protobuf"; -import * as ongeki_common_pb from "../ongeki/common_pb"; - -export class PlaylogInfo extends jspb.Message { - getMusicId(): number; - setMusicId(value: number): void; - - getLevel(): ongeki_common_pb.OngekiLevelMap[keyof ongeki_common_pb.OngekiLevelMap]; - setLevel(value: ongeki_common_pb.OngekiLevelMap[keyof ongeki_common_pb.OngekiLevelMap]): void; - - getTechScore(): number; - setTechScore(value: number): void; - - getBattleScore(): number; - setBattleScore(value: number): void; - - getOverDamage(): number; - setOverDamage(value: number): void; - - getPlatinumScore(): number; - setPlatinumScore(value: number): void; - - getTechScoreRank(): ongeki_common_pb.OngekiTechScoreRankMap[keyof ongeki_common_pb.OngekiTechScoreRankMap]; - setTechScoreRank( - value: ongeki_common_pb.OngekiTechScoreRankMap[keyof ongeki_common_pb.OngekiTechScoreRankMap], - ): void; - - getBattleScoreRank(): ongeki_common_pb.OngekiBattleScoreRankMap[keyof ongeki_common_pb.OngekiBattleScoreRankMap]; - setBattleScoreRank( - value: ongeki_common_pb.OngekiBattleScoreRankMap[keyof ongeki_common_pb.OngekiBattleScoreRankMap], - ): void; - - getComboStatus(): ongeki_common_pb.OngekiComboStatusMap[keyof ongeki_common_pb.OngekiComboStatusMap]; - setComboStatus( - value: ongeki_common_pb.OngekiComboStatusMap[keyof ongeki_common_pb.OngekiComboStatusMap], - ): void; - - getClearStatus(): ongeki_common_pb.OngekiClearStatusMap[keyof ongeki_common_pb.OngekiClearStatusMap]; - setClearStatus( - value: ongeki_common_pb.OngekiClearStatusMap[keyof ongeki_common_pb.OngekiClearStatusMap], - ): void; - - getIsFullBell(): boolean; - setIsFullBell(value: boolean): void; - - getIsTechNewRecord(): boolean; - setIsTechNewRecord(value: boolean): void; - - getIsBattleNewRecord(): boolean; - setIsBattleNewRecord(value: boolean): void; - - getIsOverDamageNewRecord(): boolean; - setIsOverDamageNewRecord(value: boolean): void; - - getUserPlayDate(): string; - setUserPlayDate(value: string): void; - - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): PlaylogInfo.AsObject; - static toObject(includeInstance: boolean, msg: PlaylogInfo): PlaylogInfo.AsObject; - static extensions: { [key: number]: jspb.ExtensionFieldInfo }; - static extensionsBinary: { [key: number]: jspb.ExtensionFieldBinaryInfo }; - static serializeBinaryToWriter(message: PlaylogInfo, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): PlaylogInfo; - static deserializeBinaryFromReader( - message: PlaylogInfo, - reader: jspb.BinaryReader, - ): PlaylogInfo; -} - -export namespace PlaylogInfo { - export type AsObject = { - musicId: number; - level: ongeki_common_pb.OngekiLevelMap[keyof ongeki_common_pb.OngekiLevelMap]; - techScore: number; - battleScore: number; - overDamage: number; - platinumScore: number; - techScoreRank: ongeki_common_pb.OngekiTechScoreRankMap[keyof ongeki_common_pb.OngekiTechScoreRankMap]; - battleScoreRank: ongeki_common_pb.OngekiBattleScoreRankMap[keyof ongeki_common_pb.OngekiBattleScoreRankMap]; - comboStatus: ongeki_common_pb.OngekiComboStatusMap[keyof ongeki_common_pb.OngekiComboStatusMap]; - clearStatus: ongeki_common_pb.OngekiClearStatusMap[keyof ongeki_common_pb.OngekiClearStatusMap]; - isFullBell: boolean; - isTechNewRecord: boolean; - isBattleNewRecord: boolean; - isOverDamageNewRecord: boolean; - userPlayDate: string; - }; -} - -export class PlaylogJudge extends jspb.Message { - getJudgeCriticalBreak(): number; - setJudgeCriticalBreak(value: number): void; - - getJudgeBreak(): number; - setJudgeBreak(value: number): void; - - getJudgeHit(): number; - setJudgeHit(value: number): void; - - getJudgeMiss(): number; - setJudgeMiss(value: number): void; - - getMaxCombo(): number; - setMaxCombo(value: number): void; - - getBellCount(): number; - setBellCount(value: number): void; - - getTotalBellCount(): number; - setTotalBellCount(value: number): void; - - getDamageCount(): number; - setDamageCount(value: number): void; - - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): PlaylogJudge.AsObject; - static toObject(includeInstance: boolean, msg: PlaylogJudge): PlaylogJudge.AsObject; - static extensions: { [key: number]: jspb.ExtensionFieldInfo }; - static extensionsBinary: { [key: number]: jspb.ExtensionFieldBinaryInfo }; - static serializeBinaryToWriter(message: PlaylogJudge, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): PlaylogJudge; - static deserializeBinaryFromReader( - message: PlaylogJudge, - reader: jspb.BinaryReader, - ): PlaylogJudge; -} - -export namespace PlaylogJudge { - export type AsObject = { - judgeCriticalBreak: number; - judgeBreak: number; - judgeHit: number; - judgeMiss: number; - maxCombo: number; - bellCount: number; - totalBellCount: number; - damageCount: number; - }; -} - -export class PlaylogJudgeRate extends jspb.Message { - getRateTap(): number; - setRateTap(value: number): void; - - getRateHold(): number; - setRateHold(value: number): void; - - getRateFlick(): number; - setRateFlick(value: number): void; - - getRateSideTap(): number; - setRateSideTap(value: number): void; - - getRateSideHold(): number; - setRateSideHold(value: number): void; - - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): PlaylogJudgeRate.AsObject; - static toObject(includeInstance: boolean, msg: PlaylogJudgeRate): PlaylogJudgeRate.AsObject; - static extensions: { [key: number]: jspb.ExtensionFieldInfo }; - static extensionsBinary: { [key: number]: jspb.ExtensionFieldBinaryInfo }; - static serializeBinaryToWriter(message: PlaylogJudgeRate, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): PlaylogJudgeRate; - static deserializeBinaryFromReader( - message: PlaylogJudgeRate, - reader: jspb.BinaryReader, - ): PlaylogJudgeRate; -} - -export namespace PlaylogJudgeRate { - export type AsObject = { - rateTap: number; - rateHold: number; - rateFlick: number; - rateSideTap: number; - rateSideHold: number; - }; -} - -export class PlaylogMatchingUser extends jspb.Message { - getUserName(): string; - setUserName(value: string): void; - - getMusicLevel(): ongeki_common_pb.OngekiLevelMap[keyof ongeki_common_pb.OngekiLevelMap]; - setMusicLevel( - value: ongeki_common_pb.OngekiLevelMap[keyof ongeki_common_pb.OngekiLevelMap], - ): void; - - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): PlaylogMatchingUser.AsObject; - static toObject( - includeInstance: boolean, - msg: PlaylogMatchingUser, - ): PlaylogMatchingUser.AsObject; - static extensions: { [key: number]: jspb.ExtensionFieldInfo }; - static extensionsBinary: { [key: number]: jspb.ExtensionFieldBinaryInfo }; - static serializeBinaryToWriter(message: PlaylogMatchingUser, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): PlaylogMatchingUser; - static deserializeBinaryFromReader( - message: PlaylogMatchingUser, - reader: jspb.BinaryReader, - ): PlaylogMatchingUser; -} - -export namespace PlaylogMatchingUser { - export type AsObject = { - userName: string; - musicLevel: ongeki_common_pb.OngekiLevelMap[keyof ongeki_common_pb.OngekiLevelMap]; - }; -} - -export class PlaylogCard extends jspb.Message { - getCardId(): number; - setCardId(value: number): void; - - getCardLevel(): number; - setCardLevel(value: number): void; - - getCardAttack(): number; - setCardAttack(value: number): void; - - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): PlaylogCard.AsObject; - static toObject(includeInstance: boolean, msg: PlaylogCard): PlaylogCard.AsObject; - static extensions: { [key: number]: jspb.ExtensionFieldInfo }; - static extensionsBinary: { [key: number]: jspb.ExtensionFieldBinaryInfo }; - static serializeBinaryToWriter(message: PlaylogCard, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): PlaylogCard; - static deserializeBinaryFromReader( - message: PlaylogCard, - reader: jspb.BinaryReader, - ): PlaylogCard; -} - -export namespace PlaylogCard { - export type AsObject = { - cardId: number; - cardLevel: number; - cardAttack: number; - }; -} - -export class PlaylogBoss extends jspb.Message { - getBossId(): number; - setBossId(value: number): void; - - getBossLevel(): number; - setBossLevel(value: number): void; - - getBossAttribute(): ongeki_common_pb.OngekiBossAttributeMap[keyof ongeki_common_pb.OngekiBossAttributeMap]; - setBossAttribute( - value: ongeki_common_pb.OngekiBossAttributeMap[keyof ongeki_common_pb.OngekiBossAttributeMap], - ): void; - - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): PlaylogBoss.AsObject; - static toObject(includeInstance: boolean, msg: PlaylogBoss): PlaylogBoss.AsObject; - static extensions: { [key: number]: jspb.ExtensionFieldInfo }; - static extensionsBinary: { [key: number]: jspb.ExtensionFieldBinaryInfo }; - static serializeBinaryToWriter(message: PlaylogBoss, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): PlaylogBoss; - static deserializeBinaryFromReader( - message: PlaylogBoss, - reader: jspb.BinaryReader, - ): PlaylogBoss; -} - -export namespace PlaylogBoss { - export type AsObject = { - bossId: number; - bossLevel: number; - bossAttribute: ongeki_common_pb.OngekiBossAttributeMap[keyof ongeki_common_pb.OngekiBossAttributeMap]; - }; -} - -export class PlaylogEvent extends jspb.Message { - getEventId(): number; - setEventId(value: number): void; - - getEventName(): string; - setEventName(value: string): void; - - getEventPoint(): number; - setEventPoint(value: number): void; - - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): PlaylogEvent.AsObject; - static toObject(includeInstance: boolean, msg: PlaylogEvent): PlaylogEvent.AsObject; - static extensions: { [key: number]: jspb.ExtensionFieldInfo }; - static extensionsBinary: { [key: number]: jspb.ExtensionFieldBinaryInfo }; - static serializeBinaryToWriter(message: PlaylogEvent, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): PlaylogEvent; - static deserializeBinaryFromReader( - message: PlaylogEvent, - reader: jspb.BinaryReader, - ): PlaylogEvent; -} - -export namespace PlaylogEvent { - export type AsObject = { - eventId: number; - eventName: string; - eventPoint: number; - }; -} diff --git a/typescript/server/src/proto/generated/ongeki/playlog_pb.js b/typescript/server/src/proto/generated/ongeki/playlog_pb.js deleted file mode 100644 index a3d14e78e..000000000 --- a/typescript/server/src/proto/generated/ongeki/playlog_pb.js +++ /dev/null @@ -1,1833 +0,0 @@ -// source: ongeki/playlog.proto -/** - * @fileoverview - * @enhanceable - * @suppress {missingRequire} reports error on implicit type usages. - * @suppress {messageConventions} JS Compiler reports an error if a variable or - * field starts with 'MSG_' and isn't a translatable message. - * @public - */ -// GENERATED CODE -- DO NOT EDIT! -/* eslint-disable */ -// @ts-nocheck - -var jspb = require("google-protobuf"); -var goog = jspb; -var global = function () { - if (this) { - return this; - } - if (typeof window !== "undefined") { - return window; - } - if (typeof global !== "undefined") { - return global; - } - if (typeof self !== "undefined") { - return self; - } - return Function("return this")(); -}.call(null); - -var ongeki_common_pb = require("../ongeki/common_pb.js"); -goog.object.extend(proto, ongeki_common_pb); -goog.exportSymbol("proto.mythos.ongeki.v0.PlaylogBoss", null, global); -goog.exportSymbol("proto.mythos.ongeki.v0.PlaylogCard", null, global); -goog.exportSymbol("proto.mythos.ongeki.v0.PlaylogEvent", null, global); -goog.exportSymbol("proto.mythos.ongeki.v0.PlaylogInfo", null, global); -goog.exportSymbol("proto.mythos.ongeki.v0.PlaylogJudge", null, global); -goog.exportSymbol("proto.mythos.ongeki.v0.PlaylogJudgeRate", null, global); -goog.exportSymbol("proto.mythos.ongeki.v0.PlaylogMatchingUser", null, global); -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.mythos.ongeki.v0.PlaylogInfo = function (opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.mythos.ongeki.v0.PlaylogInfo, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.mythos.ongeki.v0.PlaylogInfo.displayName = "proto.mythos.ongeki.v0.PlaylogInfo"; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.mythos.ongeki.v0.PlaylogJudge = function (opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.mythos.ongeki.v0.PlaylogJudge, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.mythos.ongeki.v0.PlaylogJudge.displayName = "proto.mythos.ongeki.v0.PlaylogJudge"; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.mythos.ongeki.v0.PlaylogJudgeRate = function (opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.mythos.ongeki.v0.PlaylogJudgeRate, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.mythos.ongeki.v0.PlaylogJudgeRate.displayName = "proto.mythos.ongeki.v0.PlaylogJudgeRate"; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.mythos.ongeki.v0.PlaylogMatchingUser = function (opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.mythos.ongeki.v0.PlaylogMatchingUser, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.mythos.ongeki.v0.PlaylogMatchingUser.displayName = - "proto.mythos.ongeki.v0.PlaylogMatchingUser"; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.mythos.ongeki.v0.PlaylogCard = function (opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.mythos.ongeki.v0.PlaylogCard, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.mythos.ongeki.v0.PlaylogCard.displayName = "proto.mythos.ongeki.v0.PlaylogCard"; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.mythos.ongeki.v0.PlaylogBoss = function (opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.mythos.ongeki.v0.PlaylogBoss, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.mythos.ongeki.v0.PlaylogBoss.displayName = "proto.mythos.ongeki.v0.PlaylogBoss"; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.mythos.ongeki.v0.PlaylogEvent = function (opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.mythos.ongeki.v0.PlaylogEvent, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.mythos.ongeki.v0.PlaylogEvent.displayName = "proto.mythos.ongeki.v0.PlaylogEvent"; -} - -if (jspb.Message.GENERATE_TO_OBJECT) { - /** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ - proto.mythos.ongeki.v0.PlaylogInfo.prototype.toObject = function (opt_includeInstance) { - return proto.mythos.ongeki.v0.PlaylogInfo.toObject(opt_includeInstance, this); - }; - - /** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.mythos.ongeki.v0.PlaylogInfo} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ - proto.mythos.ongeki.v0.PlaylogInfo.toObject = function (includeInstance, msg) { - var f, - obj = { - musicId: jspb.Message.getFieldWithDefault(msg, 1, 0), - level: jspb.Message.getFieldWithDefault(msg, 2, 0), - techScore: jspb.Message.getFieldWithDefault(msg, 3, 0), - battleScore: jspb.Message.getFieldWithDefault(msg, 4, 0), - overDamage: jspb.Message.getFieldWithDefault(msg, 5, 0), - platinumScore: jspb.Message.getFieldWithDefault(msg, 6, 0), - techScoreRank: jspb.Message.getFieldWithDefault(msg, 7, 0), - battleScoreRank: jspb.Message.getFieldWithDefault(msg, 8, 0), - comboStatus: jspb.Message.getFieldWithDefault(msg, 9, 0), - clearStatus: jspb.Message.getFieldWithDefault(msg, 10, 0), - isFullBell: jspb.Message.getBooleanFieldWithDefault(msg, 11, false), - isTechNewRecord: jspb.Message.getBooleanFieldWithDefault(msg, 12, false), - isBattleNewRecord: jspb.Message.getBooleanFieldWithDefault(msg, 13, false), - isOverDamageNewRecord: jspb.Message.getBooleanFieldWithDefault(msg, 14, false), - userPlayDate: jspb.Message.getFieldWithDefault(msg, 15, ""), - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; - }; -} - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.mythos.ongeki.v0.PlaylogInfo} - */ -proto.mythos.ongeki.v0.PlaylogInfo.deserializeBinary = function (bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.mythos.ongeki.v0.PlaylogInfo(); - return proto.mythos.ongeki.v0.PlaylogInfo.deserializeBinaryFromReader(msg, reader); -}; - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.mythos.ongeki.v0.PlaylogInfo} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.mythos.ongeki.v0.PlaylogInfo} - */ -proto.mythos.ongeki.v0.PlaylogInfo.deserializeBinaryFromReader = function (msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {number} */ (reader.readInt32()); - msg.setMusicId(value); - break; - case 2: - var value = /** @type {!proto.mythos.ongeki.v0.OngekiLevel} */ (reader.readEnum()); - msg.setLevel(value); - break; - case 3: - var value = /** @type {number} */ (reader.readInt32()); - msg.setTechScore(value); - break; - case 4: - var value = /** @type {number} */ (reader.readInt32()); - msg.setBattleScore(value); - break; - case 5: - var value = /** @type {number} */ (reader.readInt32()); - msg.setOverDamage(value); - break; - case 6: - var value = /** @type {number} */ (reader.readInt32()); - msg.setPlatinumScore(value); - break; - case 7: - var value = /** @type {!proto.mythos.ongeki.v0.OngekiTechScoreRank} */ ( - reader.readEnum() - ); - msg.setTechScoreRank(value); - break; - case 8: - var value = /** @type {!proto.mythos.ongeki.v0.OngekiBattleScoreRank} */ ( - reader.readEnum() - ); - msg.setBattleScoreRank(value); - break; - case 9: - var value = /** @type {!proto.mythos.ongeki.v0.OngekiComboStatus} */ ( - reader.readEnum() - ); - msg.setComboStatus(value); - break; - case 10: - var value = /** @type {!proto.mythos.ongeki.v0.OngekiClearStatus} */ ( - reader.readEnum() - ); - msg.setClearStatus(value); - break; - case 11: - var value = /** @type {boolean} */ (reader.readBool()); - msg.setIsFullBell(value); - break; - case 12: - var value = /** @type {boolean} */ (reader.readBool()); - msg.setIsTechNewRecord(value); - break; - case 13: - var value = /** @type {boolean} */ (reader.readBool()); - msg.setIsBattleNewRecord(value); - break; - case 14: - var value = /** @type {boolean} */ (reader.readBool()); - msg.setIsOverDamageNewRecord(value); - break; - case 15: - var value = /** @type {string} */ (reader.readString()); - msg.setUserPlayDate(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.mythos.ongeki.v0.PlaylogInfo.prototype.serializeBinary = function () { - var writer = new jspb.BinaryWriter(); - proto.mythos.ongeki.v0.PlaylogInfo.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.mythos.ongeki.v0.PlaylogInfo} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.mythos.ongeki.v0.PlaylogInfo.serializeBinaryToWriter = function (message, writer) { - var f = undefined; - f = message.getMusicId(); - if (f !== 0) { - writer.writeInt32(1, f); - } - f = message.getLevel(); - if (f !== 0.0) { - writer.writeEnum(2, f); - } - f = message.getTechScore(); - if (f !== 0) { - writer.writeInt32(3, f); - } - f = message.getBattleScore(); - if (f !== 0) { - writer.writeInt32(4, f); - } - f = message.getOverDamage(); - if (f !== 0) { - writer.writeInt32(5, f); - } - f = message.getPlatinumScore(); - if (f !== 0) { - writer.writeInt32(6, f); - } - f = message.getTechScoreRank(); - if (f !== 0.0) { - writer.writeEnum(7, f); - } - f = message.getBattleScoreRank(); - if (f !== 0.0) { - writer.writeEnum(8, f); - } - f = message.getComboStatus(); - if (f !== 0.0) { - writer.writeEnum(9, f); - } - f = message.getClearStatus(); - if (f !== 0.0) { - writer.writeEnum(10, f); - } - f = message.getIsFullBell(); - if (f) { - writer.writeBool(11, f); - } - f = message.getIsTechNewRecord(); - if (f) { - writer.writeBool(12, f); - } - f = message.getIsBattleNewRecord(); - if (f) { - writer.writeBool(13, f); - } - f = message.getIsOverDamageNewRecord(); - if (f) { - writer.writeBool(14, f); - } - f = message.getUserPlayDate(); - if (f.length > 0) { - writer.writeString(15, f); - } -}; - -/** - * optional int32 music_id = 1; - * @return {number} - */ -proto.mythos.ongeki.v0.PlaylogInfo.prototype.getMusicId = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.ongeki.v0.PlaylogInfo} returns this - */ -proto.mythos.ongeki.v0.PlaylogInfo.prototype.setMusicId = function (value) { - return jspb.Message.setProto3IntField(this, 1, value); -}; - -/** - * optional OngekiLevel level = 2; - * @return {!proto.mythos.ongeki.v0.OngekiLevel} - */ -proto.mythos.ongeki.v0.PlaylogInfo.prototype.getLevel = function () { - return /** @type {!proto.mythos.ongeki.v0.OngekiLevel} */ ( - jspb.Message.getFieldWithDefault(this, 2, 0) - ); -}; - -/** - * @param {!proto.mythos.ongeki.v0.OngekiLevel} value - * @return {!proto.mythos.ongeki.v0.PlaylogInfo} returns this - */ -proto.mythos.ongeki.v0.PlaylogInfo.prototype.setLevel = function (value) { - return jspb.Message.setProto3EnumField(this, 2, value); -}; - -/** - * optional int32 tech_score = 3; - * @return {number} - */ -proto.mythos.ongeki.v0.PlaylogInfo.prototype.getTechScore = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.ongeki.v0.PlaylogInfo} returns this - */ -proto.mythos.ongeki.v0.PlaylogInfo.prototype.setTechScore = function (value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - -/** - * optional int32 battle_score = 4; - * @return {number} - */ -proto.mythos.ongeki.v0.PlaylogInfo.prototype.getBattleScore = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.ongeki.v0.PlaylogInfo} returns this - */ -proto.mythos.ongeki.v0.PlaylogInfo.prototype.setBattleScore = function (value) { - return jspb.Message.setProto3IntField(this, 4, value); -}; - -/** - * optional int32 over_damage = 5; - * @return {number} - */ -proto.mythos.ongeki.v0.PlaylogInfo.prototype.getOverDamage = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 5, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.ongeki.v0.PlaylogInfo} returns this - */ -proto.mythos.ongeki.v0.PlaylogInfo.prototype.setOverDamage = function (value) { - return jspb.Message.setProto3IntField(this, 5, value); -}; - -/** - * optional int32 platinum_score = 6; - * @return {number} - */ -proto.mythos.ongeki.v0.PlaylogInfo.prototype.getPlatinumScore = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 6, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.ongeki.v0.PlaylogInfo} returns this - */ -proto.mythos.ongeki.v0.PlaylogInfo.prototype.setPlatinumScore = function (value) { - return jspb.Message.setProto3IntField(this, 6, value); -}; - -/** - * optional OngekiTechScoreRank tech_score_rank = 7; - * @return {!proto.mythos.ongeki.v0.OngekiTechScoreRank} - */ -proto.mythos.ongeki.v0.PlaylogInfo.prototype.getTechScoreRank = function () { - return /** @type {!proto.mythos.ongeki.v0.OngekiTechScoreRank} */ ( - jspb.Message.getFieldWithDefault(this, 7, 0) - ); -}; - -/** - * @param {!proto.mythos.ongeki.v0.OngekiTechScoreRank} value - * @return {!proto.mythos.ongeki.v0.PlaylogInfo} returns this - */ -proto.mythos.ongeki.v0.PlaylogInfo.prototype.setTechScoreRank = function (value) { - return jspb.Message.setProto3EnumField(this, 7, value); -}; - -/** - * optional OngekiBattleScoreRank battle_score_rank = 8; - * @return {!proto.mythos.ongeki.v0.OngekiBattleScoreRank} - */ -proto.mythos.ongeki.v0.PlaylogInfo.prototype.getBattleScoreRank = function () { - return /** @type {!proto.mythos.ongeki.v0.OngekiBattleScoreRank} */ ( - jspb.Message.getFieldWithDefault(this, 8, 0) - ); -}; - -/** - * @param {!proto.mythos.ongeki.v0.OngekiBattleScoreRank} value - * @return {!proto.mythos.ongeki.v0.PlaylogInfo} returns this - */ -proto.mythos.ongeki.v0.PlaylogInfo.prototype.setBattleScoreRank = function (value) { - return jspb.Message.setProto3EnumField(this, 8, value); -}; - -/** - * optional OngekiComboStatus combo_status = 9; - * @return {!proto.mythos.ongeki.v0.OngekiComboStatus} - */ -proto.mythos.ongeki.v0.PlaylogInfo.prototype.getComboStatus = function () { - return /** @type {!proto.mythos.ongeki.v0.OngekiComboStatus} */ ( - jspb.Message.getFieldWithDefault(this, 9, 0) - ); -}; - -/** - * @param {!proto.mythos.ongeki.v0.OngekiComboStatus} value - * @return {!proto.mythos.ongeki.v0.PlaylogInfo} returns this - */ -proto.mythos.ongeki.v0.PlaylogInfo.prototype.setComboStatus = function (value) { - return jspb.Message.setProto3EnumField(this, 9, value); -}; - -/** - * optional OngekiClearStatus clear_status = 10; - * @return {!proto.mythos.ongeki.v0.OngekiClearStatus} - */ -proto.mythos.ongeki.v0.PlaylogInfo.prototype.getClearStatus = function () { - return /** @type {!proto.mythos.ongeki.v0.OngekiClearStatus} */ ( - jspb.Message.getFieldWithDefault(this, 10, 0) - ); -}; - -/** - * @param {!proto.mythos.ongeki.v0.OngekiClearStatus} value - * @return {!proto.mythos.ongeki.v0.PlaylogInfo} returns this - */ -proto.mythos.ongeki.v0.PlaylogInfo.prototype.setClearStatus = function (value) { - return jspb.Message.setProto3EnumField(this, 10, value); -}; - -/** - * optional bool is_full_bell = 11; - * @return {boolean} - */ -proto.mythos.ongeki.v0.PlaylogInfo.prototype.getIsFullBell = function () { - return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 11, false)); -}; - -/** - * @param {boolean} value - * @return {!proto.mythos.ongeki.v0.PlaylogInfo} returns this - */ -proto.mythos.ongeki.v0.PlaylogInfo.prototype.setIsFullBell = function (value) { - return jspb.Message.setProto3BooleanField(this, 11, value); -}; - -/** - * optional bool is_tech_new_record = 12; - * @return {boolean} - */ -proto.mythos.ongeki.v0.PlaylogInfo.prototype.getIsTechNewRecord = function () { - return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 12, false)); -}; - -/** - * @param {boolean} value - * @return {!proto.mythos.ongeki.v0.PlaylogInfo} returns this - */ -proto.mythos.ongeki.v0.PlaylogInfo.prototype.setIsTechNewRecord = function (value) { - return jspb.Message.setProto3BooleanField(this, 12, value); -}; - -/** - * optional bool is_battle_new_record = 13; - * @return {boolean} - */ -proto.mythos.ongeki.v0.PlaylogInfo.prototype.getIsBattleNewRecord = function () { - return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 13, false)); -}; - -/** - * @param {boolean} value - * @return {!proto.mythos.ongeki.v0.PlaylogInfo} returns this - */ -proto.mythos.ongeki.v0.PlaylogInfo.prototype.setIsBattleNewRecord = function (value) { - return jspb.Message.setProto3BooleanField(this, 13, value); -}; - -/** - * optional bool is_over_damage_new_record = 14; - * @return {boolean} - */ -proto.mythos.ongeki.v0.PlaylogInfo.prototype.getIsOverDamageNewRecord = function () { - return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 14, false)); -}; - -/** - * @param {boolean} value - * @return {!proto.mythos.ongeki.v0.PlaylogInfo} returns this - */ -proto.mythos.ongeki.v0.PlaylogInfo.prototype.setIsOverDamageNewRecord = function (value) { - return jspb.Message.setProto3BooleanField(this, 14, value); -}; - -/** - * optional string user_play_date = 15; - * @return {string} - */ -proto.mythos.ongeki.v0.PlaylogInfo.prototype.getUserPlayDate = function () { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 15, "")); -}; - -/** - * @param {string} value - * @return {!proto.mythos.ongeki.v0.PlaylogInfo} returns this - */ -proto.mythos.ongeki.v0.PlaylogInfo.prototype.setUserPlayDate = function (value) { - return jspb.Message.setProto3StringField(this, 15, value); -}; - -if (jspb.Message.GENERATE_TO_OBJECT) { - /** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ - proto.mythos.ongeki.v0.PlaylogJudge.prototype.toObject = function (opt_includeInstance) { - return proto.mythos.ongeki.v0.PlaylogJudge.toObject(opt_includeInstance, this); - }; - - /** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.mythos.ongeki.v0.PlaylogJudge} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ - proto.mythos.ongeki.v0.PlaylogJudge.toObject = function (includeInstance, msg) { - var f, - obj = { - judgeCriticalBreak: jspb.Message.getFieldWithDefault(msg, 17, 0), - judgeBreak: jspb.Message.getFieldWithDefault(msg, 18, 0), - judgeHit: jspb.Message.getFieldWithDefault(msg, 19, 0), - judgeMiss: jspb.Message.getFieldWithDefault(msg, 20, 0), - maxCombo: jspb.Message.getFieldWithDefault(msg, 21, 0), - bellCount: jspb.Message.getFieldWithDefault(msg, 22, 0), - totalBellCount: jspb.Message.getFieldWithDefault(msg, 23, 0), - damageCount: jspb.Message.getFieldWithDefault(msg, 24, 0), - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; - }; -} - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.mythos.ongeki.v0.PlaylogJudge} - */ -proto.mythos.ongeki.v0.PlaylogJudge.deserializeBinary = function (bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.mythos.ongeki.v0.PlaylogJudge(); - return proto.mythos.ongeki.v0.PlaylogJudge.deserializeBinaryFromReader(msg, reader); -}; - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.mythos.ongeki.v0.PlaylogJudge} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.mythos.ongeki.v0.PlaylogJudge} - */ -proto.mythos.ongeki.v0.PlaylogJudge.deserializeBinaryFromReader = function (msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 17: - var value = /** @type {number} */ (reader.readInt32()); - msg.setJudgeCriticalBreak(value); - break; - case 18: - var value = /** @type {number} */ (reader.readInt32()); - msg.setJudgeBreak(value); - break; - case 19: - var value = /** @type {number} */ (reader.readInt32()); - msg.setJudgeHit(value); - break; - case 20: - var value = /** @type {number} */ (reader.readInt32()); - msg.setJudgeMiss(value); - break; - case 21: - var value = /** @type {number} */ (reader.readInt32()); - msg.setMaxCombo(value); - break; - case 22: - var value = /** @type {number} */ (reader.readInt32()); - msg.setBellCount(value); - break; - case 23: - var value = /** @type {number} */ (reader.readInt32()); - msg.setTotalBellCount(value); - break; - case 24: - var value = /** @type {number} */ (reader.readInt32()); - msg.setDamageCount(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.mythos.ongeki.v0.PlaylogJudge.prototype.serializeBinary = function () { - var writer = new jspb.BinaryWriter(); - proto.mythos.ongeki.v0.PlaylogJudge.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.mythos.ongeki.v0.PlaylogJudge} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.mythos.ongeki.v0.PlaylogJudge.serializeBinaryToWriter = function (message, writer) { - var f = undefined; - f = message.getJudgeCriticalBreak(); - if (f !== 0) { - writer.writeInt32(17, f); - } - f = message.getJudgeBreak(); - if (f !== 0) { - writer.writeInt32(18, f); - } - f = message.getJudgeHit(); - if (f !== 0) { - writer.writeInt32(19, f); - } - f = message.getJudgeMiss(); - if (f !== 0) { - writer.writeInt32(20, f); - } - f = message.getMaxCombo(); - if (f !== 0) { - writer.writeInt32(21, f); - } - f = message.getBellCount(); - if (f !== 0) { - writer.writeInt32(22, f); - } - f = message.getTotalBellCount(); - if (f !== 0) { - writer.writeInt32(23, f); - } - f = message.getDamageCount(); - if (f !== 0) { - writer.writeInt32(24, f); - } -}; - -/** - * optional int32 judge_critical_break = 17; - * @return {number} - */ -proto.mythos.ongeki.v0.PlaylogJudge.prototype.getJudgeCriticalBreak = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 17, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.ongeki.v0.PlaylogJudge} returns this - */ -proto.mythos.ongeki.v0.PlaylogJudge.prototype.setJudgeCriticalBreak = function (value) { - return jspb.Message.setProto3IntField(this, 17, value); -}; - -/** - * optional int32 judge_break = 18; - * @return {number} - */ -proto.mythos.ongeki.v0.PlaylogJudge.prototype.getJudgeBreak = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 18, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.ongeki.v0.PlaylogJudge} returns this - */ -proto.mythos.ongeki.v0.PlaylogJudge.prototype.setJudgeBreak = function (value) { - return jspb.Message.setProto3IntField(this, 18, value); -}; - -/** - * optional int32 judge_hit = 19; - * @return {number} - */ -proto.mythos.ongeki.v0.PlaylogJudge.prototype.getJudgeHit = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 19, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.ongeki.v0.PlaylogJudge} returns this - */ -proto.mythos.ongeki.v0.PlaylogJudge.prototype.setJudgeHit = function (value) { - return jspb.Message.setProto3IntField(this, 19, value); -}; - -/** - * optional int32 judge_miss = 20; - * @return {number} - */ -proto.mythos.ongeki.v0.PlaylogJudge.prototype.getJudgeMiss = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 20, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.ongeki.v0.PlaylogJudge} returns this - */ -proto.mythos.ongeki.v0.PlaylogJudge.prototype.setJudgeMiss = function (value) { - return jspb.Message.setProto3IntField(this, 20, value); -}; - -/** - * optional int32 max_combo = 21; - * @return {number} - */ -proto.mythos.ongeki.v0.PlaylogJudge.prototype.getMaxCombo = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 21, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.ongeki.v0.PlaylogJudge} returns this - */ -proto.mythos.ongeki.v0.PlaylogJudge.prototype.setMaxCombo = function (value) { - return jspb.Message.setProto3IntField(this, 21, value); -}; - -/** - * optional int32 bell_count = 22; - * @return {number} - */ -proto.mythos.ongeki.v0.PlaylogJudge.prototype.getBellCount = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 22, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.ongeki.v0.PlaylogJudge} returns this - */ -proto.mythos.ongeki.v0.PlaylogJudge.prototype.setBellCount = function (value) { - return jspb.Message.setProto3IntField(this, 22, value); -}; - -/** - * optional int32 total_bell_count = 23; - * @return {number} - */ -proto.mythos.ongeki.v0.PlaylogJudge.prototype.getTotalBellCount = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 23, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.ongeki.v0.PlaylogJudge} returns this - */ -proto.mythos.ongeki.v0.PlaylogJudge.prototype.setTotalBellCount = function (value) { - return jspb.Message.setProto3IntField(this, 23, value); -}; - -/** - * optional int32 damage_count = 24; - * @return {number} - */ -proto.mythos.ongeki.v0.PlaylogJudge.prototype.getDamageCount = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 24, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.ongeki.v0.PlaylogJudge} returns this - */ -proto.mythos.ongeki.v0.PlaylogJudge.prototype.setDamageCount = function (value) { - return jspb.Message.setProto3IntField(this, 24, value); -}; - -if (jspb.Message.GENERATE_TO_OBJECT) { - /** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ - proto.mythos.ongeki.v0.PlaylogJudgeRate.prototype.toObject = function (opt_includeInstance) { - return proto.mythos.ongeki.v0.PlaylogJudgeRate.toObject(opt_includeInstance, this); - }; - - /** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.mythos.ongeki.v0.PlaylogJudgeRate} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ - proto.mythos.ongeki.v0.PlaylogJudgeRate.toObject = function (includeInstance, msg) { - var f, - obj = { - rateTap: jspb.Message.getFieldWithDefault(msg, 1, 0), - rateHold: jspb.Message.getFieldWithDefault(msg, 2, 0), - rateFlick: jspb.Message.getFieldWithDefault(msg, 3, 0), - rateSideTap: jspb.Message.getFieldWithDefault(msg, 4, 0), - rateSideHold: jspb.Message.getFieldWithDefault(msg, 5, 0), - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; - }; -} - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.mythos.ongeki.v0.PlaylogJudgeRate} - */ -proto.mythos.ongeki.v0.PlaylogJudgeRate.deserializeBinary = function (bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.mythos.ongeki.v0.PlaylogJudgeRate(); - return proto.mythos.ongeki.v0.PlaylogJudgeRate.deserializeBinaryFromReader(msg, reader); -}; - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.mythos.ongeki.v0.PlaylogJudgeRate} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.mythos.ongeki.v0.PlaylogJudgeRate} - */ -proto.mythos.ongeki.v0.PlaylogJudgeRate.deserializeBinaryFromReader = function (msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {number} */ (reader.readInt32()); - msg.setRateTap(value); - break; - case 2: - var value = /** @type {number} */ (reader.readInt32()); - msg.setRateHold(value); - break; - case 3: - var value = /** @type {number} */ (reader.readInt32()); - msg.setRateFlick(value); - break; - case 4: - var value = /** @type {number} */ (reader.readInt32()); - msg.setRateSideTap(value); - break; - case 5: - var value = /** @type {number} */ (reader.readInt32()); - msg.setRateSideHold(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.mythos.ongeki.v0.PlaylogJudgeRate.prototype.serializeBinary = function () { - var writer = new jspb.BinaryWriter(); - proto.mythos.ongeki.v0.PlaylogJudgeRate.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.mythos.ongeki.v0.PlaylogJudgeRate} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.mythos.ongeki.v0.PlaylogJudgeRate.serializeBinaryToWriter = function (message, writer) { - var f = undefined; - f = message.getRateTap(); - if (f !== 0) { - writer.writeInt32(1, f); - } - f = message.getRateHold(); - if (f !== 0) { - writer.writeInt32(2, f); - } - f = message.getRateFlick(); - if (f !== 0) { - writer.writeInt32(3, f); - } - f = message.getRateSideTap(); - if (f !== 0) { - writer.writeInt32(4, f); - } - f = message.getRateSideHold(); - if (f !== 0) { - writer.writeInt32(5, f); - } -}; - -/** - * optional int32 rate_tap = 1; - * @return {number} - */ -proto.mythos.ongeki.v0.PlaylogJudgeRate.prototype.getRateTap = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.ongeki.v0.PlaylogJudgeRate} returns this - */ -proto.mythos.ongeki.v0.PlaylogJudgeRate.prototype.setRateTap = function (value) { - return jspb.Message.setProto3IntField(this, 1, value); -}; - -/** - * optional int32 rate_hold = 2; - * @return {number} - */ -proto.mythos.ongeki.v0.PlaylogJudgeRate.prototype.getRateHold = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.ongeki.v0.PlaylogJudgeRate} returns this - */ -proto.mythos.ongeki.v0.PlaylogJudgeRate.prototype.setRateHold = function (value) { - return jspb.Message.setProto3IntField(this, 2, value); -}; - -/** - * optional int32 rate_flick = 3; - * @return {number} - */ -proto.mythos.ongeki.v0.PlaylogJudgeRate.prototype.getRateFlick = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.ongeki.v0.PlaylogJudgeRate} returns this - */ -proto.mythos.ongeki.v0.PlaylogJudgeRate.prototype.setRateFlick = function (value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - -/** - * optional int32 rate_side_tap = 4; - * @return {number} - */ -proto.mythos.ongeki.v0.PlaylogJudgeRate.prototype.getRateSideTap = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.ongeki.v0.PlaylogJudgeRate} returns this - */ -proto.mythos.ongeki.v0.PlaylogJudgeRate.prototype.setRateSideTap = function (value) { - return jspb.Message.setProto3IntField(this, 4, value); -}; - -/** - * optional int32 rate_side_hold = 5; - * @return {number} - */ -proto.mythos.ongeki.v0.PlaylogJudgeRate.prototype.getRateSideHold = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 5, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.ongeki.v0.PlaylogJudgeRate} returns this - */ -proto.mythos.ongeki.v0.PlaylogJudgeRate.prototype.setRateSideHold = function (value) { - return jspb.Message.setProto3IntField(this, 5, value); -}; - -if (jspb.Message.GENERATE_TO_OBJECT) { - /** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ - proto.mythos.ongeki.v0.PlaylogMatchingUser.prototype.toObject = function (opt_includeInstance) { - return proto.mythos.ongeki.v0.PlaylogMatchingUser.toObject(opt_includeInstance, this); - }; - - /** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.mythos.ongeki.v0.PlaylogMatchingUser} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ - proto.mythos.ongeki.v0.PlaylogMatchingUser.toObject = function (includeInstance, msg) { - var f, - obj = { - userName: jspb.Message.getFieldWithDefault(msg, 1, ""), - musicLevel: jspb.Message.getFieldWithDefault(msg, 2, 0), - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; - }; -} - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.mythos.ongeki.v0.PlaylogMatchingUser} - */ -proto.mythos.ongeki.v0.PlaylogMatchingUser.deserializeBinary = function (bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.mythos.ongeki.v0.PlaylogMatchingUser(); - return proto.mythos.ongeki.v0.PlaylogMatchingUser.deserializeBinaryFromReader(msg, reader); -}; - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.mythos.ongeki.v0.PlaylogMatchingUser} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.mythos.ongeki.v0.PlaylogMatchingUser} - */ -proto.mythos.ongeki.v0.PlaylogMatchingUser.deserializeBinaryFromReader = function (msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {string} */ (reader.readString()); - msg.setUserName(value); - break; - case 2: - var value = /** @type {!proto.mythos.ongeki.v0.OngekiLevel} */ (reader.readEnum()); - msg.setMusicLevel(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.mythos.ongeki.v0.PlaylogMatchingUser.prototype.serializeBinary = function () { - var writer = new jspb.BinaryWriter(); - proto.mythos.ongeki.v0.PlaylogMatchingUser.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.mythos.ongeki.v0.PlaylogMatchingUser} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.mythos.ongeki.v0.PlaylogMatchingUser.serializeBinaryToWriter = function (message, writer) { - var f = undefined; - f = message.getUserName(); - if (f.length > 0) { - writer.writeString(1, f); - } - f = message.getMusicLevel(); - if (f !== 0.0) { - writer.writeEnum(2, f); - } -}; - -/** - * optional string user_name = 1; - * @return {string} - */ -proto.mythos.ongeki.v0.PlaylogMatchingUser.prototype.getUserName = function () { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); -}; - -/** - * @param {string} value - * @return {!proto.mythos.ongeki.v0.PlaylogMatchingUser} returns this - */ -proto.mythos.ongeki.v0.PlaylogMatchingUser.prototype.setUserName = function (value) { - return jspb.Message.setProto3StringField(this, 1, value); -}; - -/** - * optional OngekiLevel music_level = 2; - * @return {!proto.mythos.ongeki.v0.OngekiLevel} - */ -proto.mythos.ongeki.v0.PlaylogMatchingUser.prototype.getMusicLevel = function () { - return /** @type {!proto.mythos.ongeki.v0.OngekiLevel} */ ( - jspb.Message.getFieldWithDefault(this, 2, 0) - ); -}; - -/** - * @param {!proto.mythos.ongeki.v0.OngekiLevel} value - * @return {!proto.mythos.ongeki.v0.PlaylogMatchingUser} returns this - */ -proto.mythos.ongeki.v0.PlaylogMatchingUser.prototype.setMusicLevel = function (value) { - return jspb.Message.setProto3EnumField(this, 2, value); -}; - -if (jspb.Message.GENERATE_TO_OBJECT) { - /** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ - proto.mythos.ongeki.v0.PlaylogCard.prototype.toObject = function (opt_includeInstance) { - return proto.mythos.ongeki.v0.PlaylogCard.toObject(opt_includeInstance, this); - }; - - /** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.mythos.ongeki.v0.PlaylogCard} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ - proto.mythos.ongeki.v0.PlaylogCard.toObject = function (includeInstance, msg) { - var f, - obj = { - cardId: jspb.Message.getFieldWithDefault(msg, 1, 0), - cardLevel: jspb.Message.getFieldWithDefault(msg, 2, 0), - cardAttack: jspb.Message.getFieldWithDefault(msg, 3, 0), - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; - }; -} - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.mythos.ongeki.v0.PlaylogCard} - */ -proto.mythos.ongeki.v0.PlaylogCard.deserializeBinary = function (bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.mythos.ongeki.v0.PlaylogCard(); - return proto.mythos.ongeki.v0.PlaylogCard.deserializeBinaryFromReader(msg, reader); -}; - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.mythos.ongeki.v0.PlaylogCard} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.mythos.ongeki.v0.PlaylogCard} - */ -proto.mythos.ongeki.v0.PlaylogCard.deserializeBinaryFromReader = function (msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {number} */ (reader.readInt32()); - msg.setCardId(value); - break; - case 2: - var value = /** @type {number} */ (reader.readInt32()); - msg.setCardLevel(value); - break; - case 3: - var value = /** @type {number} */ (reader.readInt32()); - msg.setCardAttack(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.mythos.ongeki.v0.PlaylogCard.prototype.serializeBinary = function () { - var writer = new jspb.BinaryWriter(); - proto.mythos.ongeki.v0.PlaylogCard.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.mythos.ongeki.v0.PlaylogCard} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.mythos.ongeki.v0.PlaylogCard.serializeBinaryToWriter = function (message, writer) { - var f = undefined; - f = message.getCardId(); - if (f !== 0) { - writer.writeInt32(1, f); - } - f = message.getCardLevel(); - if (f !== 0) { - writer.writeInt32(2, f); - } - f = message.getCardAttack(); - if (f !== 0) { - writer.writeInt32(3, f); - } -}; - -/** - * optional int32 card_id = 1; - * @return {number} - */ -proto.mythos.ongeki.v0.PlaylogCard.prototype.getCardId = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.ongeki.v0.PlaylogCard} returns this - */ -proto.mythos.ongeki.v0.PlaylogCard.prototype.setCardId = function (value) { - return jspb.Message.setProto3IntField(this, 1, value); -}; - -/** - * optional int32 card_level = 2; - * @return {number} - */ -proto.mythos.ongeki.v0.PlaylogCard.prototype.getCardLevel = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.ongeki.v0.PlaylogCard} returns this - */ -proto.mythos.ongeki.v0.PlaylogCard.prototype.setCardLevel = function (value) { - return jspb.Message.setProto3IntField(this, 2, value); -}; - -/** - * optional int32 card_attack = 3; - * @return {number} - */ -proto.mythos.ongeki.v0.PlaylogCard.prototype.getCardAttack = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.ongeki.v0.PlaylogCard} returns this - */ -proto.mythos.ongeki.v0.PlaylogCard.prototype.setCardAttack = function (value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - -if (jspb.Message.GENERATE_TO_OBJECT) { - /** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ - proto.mythos.ongeki.v0.PlaylogBoss.prototype.toObject = function (opt_includeInstance) { - return proto.mythos.ongeki.v0.PlaylogBoss.toObject(opt_includeInstance, this); - }; - - /** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.mythos.ongeki.v0.PlaylogBoss} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ - proto.mythos.ongeki.v0.PlaylogBoss.toObject = function (includeInstance, msg) { - var f, - obj = { - bossId: jspb.Message.getFieldWithDefault(msg, 1, 0), - bossLevel: jspb.Message.getFieldWithDefault(msg, 2, 0), - bossAttribute: jspb.Message.getFieldWithDefault(msg, 3, 0), - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; - }; -} - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.mythos.ongeki.v0.PlaylogBoss} - */ -proto.mythos.ongeki.v0.PlaylogBoss.deserializeBinary = function (bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.mythos.ongeki.v0.PlaylogBoss(); - return proto.mythos.ongeki.v0.PlaylogBoss.deserializeBinaryFromReader(msg, reader); -}; - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.mythos.ongeki.v0.PlaylogBoss} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.mythos.ongeki.v0.PlaylogBoss} - */ -proto.mythos.ongeki.v0.PlaylogBoss.deserializeBinaryFromReader = function (msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {number} */ (reader.readInt32()); - msg.setBossId(value); - break; - case 2: - var value = /** @type {number} */ (reader.readInt32()); - msg.setBossLevel(value); - break; - case 3: - var value = /** @type {!proto.mythos.ongeki.v0.OngekiBossAttribute} */ ( - reader.readEnum() - ); - msg.setBossAttribute(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.mythos.ongeki.v0.PlaylogBoss.prototype.serializeBinary = function () { - var writer = new jspb.BinaryWriter(); - proto.mythos.ongeki.v0.PlaylogBoss.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.mythos.ongeki.v0.PlaylogBoss} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.mythos.ongeki.v0.PlaylogBoss.serializeBinaryToWriter = function (message, writer) { - var f = undefined; - f = message.getBossId(); - if (f !== 0) { - writer.writeInt32(1, f); - } - f = message.getBossLevel(); - if (f !== 0) { - writer.writeInt32(2, f); - } - f = message.getBossAttribute(); - if (f !== 0.0) { - writer.writeEnum(3, f); - } -}; - -/** - * optional int32 boss_id = 1; - * @return {number} - */ -proto.mythos.ongeki.v0.PlaylogBoss.prototype.getBossId = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.ongeki.v0.PlaylogBoss} returns this - */ -proto.mythos.ongeki.v0.PlaylogBoss.prototype.setBossId = function (value) { - return jspb.Message.setProto3IntField(this, 1, value); -}; - -/** - * optional int32 boss_level = 2; - * @return {number} - */ -proto.mythos.ongeki.v0.PlaylogBoss.prototype.getBossLevel = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.ongeki.v0.PlaylogBoss} returns this - */ -proto.mythos.ongeki.v0.PlaylogBoss.prototype.setBossLevel = function (value) { - return jspb.Message.setProto3IntField(this, 2, value); -}; - -/** - * optional OngekiBossAttribute boss_attribute = 3; - * @return {!proto.mythos.ongeki.v0.OngekiBossAttribute} - */ -proto.mythos.ongeki.v0.PlaylogBoss.prototype.getBossAttribute = function () { - return /** @type {!proto.mythos.ongeki.v0.OngekiBossAttribute} */ ( - jspb.Message.getFieldWithDefault(this, 3, 0) - ); -}; - -/** - * @param {!proto.mythos.ongeki.v0.OngekiBossAttribute} value - * @return {!proto.mythos.ongeki.v0.PlaylogBoss} returns this - */ -proto.mythos.ongeki.v0.PlaylogBoss.prototype.setBossAttribute = function (value) { - return jspb.Message.setProto3EnumField(this, 3, value); -}; - -if (jspb.Message.GENERATE_TO_OBJECT) { - /** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ - proto.mythos.ongeki.v0.PlaylogEvent.prototype.toObject = function (opt_includeInstance) { - return proto.mythos.ongeki.v0.PlaylogEvent.toObject(opt_includeInstance, this); - }; - - /** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.mythos.ongeki.v0.PlaylogEvent} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ - proto.mythos.ongeki.v0.PlaylogEvent.toObject = function (includeInstance, msg) { - var f, - obj = { - eventId: jspb.Message.getFieldWithDefault(msg, 1, 0), - eventName: jspb.Message.getFieldWithDefault(msg, 2, ""), - eventPoint: jspb.Message.getFieldWithDefault(msg, 3, 0), - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; - }; -} - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.mythos.ongeki.v0.PlaylogEvent} - */ -proto.mythos.ongeki.v0.PlaylogEvent.deserializeBinary = function (bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.mythos.ongeki.v0.PlaylogEvent(); - return proto.mythos.ongeki.v0.PlaylogEvent.deserializeBinaryFromReader(msg, reader); -}; - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.mythos.ongeki.v0.PlaylogEvent} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.mythos.ongeki.v0.PlaylogEvent} - */ -proto.mythos.ongeki.v0.PlaylogEvent.deserializeBinaryFromReader = function (msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {number} */ (reader.readInt32()); - msg.setEventId(value); - break; - case 2: - var value = /** @type {string} */ (reader.readString()); - msg.setEventName(value); - break; - case 3: - var value = /** @type {number} */ (reader.readInt32()); - msg.setEventPoint(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.mythos.ongeki.v0.PlaylogEvent.prototype.serializeBinary = function () { - var writer = new jspb.BinaryWriter(); - proto.mythos.ongeki.v0.PlaylogEvent.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.mythos.ongeki.v0.PlaylogEvent} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.mythos.ongeki.v0.PlaylogEvent.serializeBinaryToWriter = function (message, writer) { - var f = undefined; - f = message.getEventId(); - if (f !== 0) { - writer.writeInt32(1, f); - } - f = message.getEventName(); - if (f.length > 0) { - writer.writeString(2, f); - } - f = message.getEventPoint(); - if (f !== 0) { - writer.writeInt32(3, f); - } -}; - -/** - * optional int32 event_id = 1; - * @return {number} - */ -proto.mythos.ongeki.v0.PlaylogEvent.prototype.getEventId = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.ongeki.v0.PlaylogEvent} returns this - */ -proto.mythos.ongeki.v0.PlaylogEvent.prototype.setEventId = function (value) { - return jspb.Message.setProto3IntField(this, 1, value); -}; - -/** - * optional string event_name = 2; - * @return {string} - */ -proto.mythos.ongeki.v0.PlaylogEvent.prototype.getEventName = function () { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); -}; - -/** - * @param {string} value - * @return {!proto.mythos.ongeki.v0.PlaylogEvent} returns this - */ -proto.mythos.ongeki.v0.PlaylogEvent.prototype.setEventName = function (value) { - return jspb.Message.setProto3StringField(this, 2, value); -}; - -/** - * optional int32 event_point = 3; - * @return {number} - */ -proto.mythos.ongeki.v0.PlaylogEvent.prototype.getEventPoint = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.ongeki.v0.PlaylogEvent} returns this - */ -proto.mythos.ongeki.v0.PlaylogEvent.prototype.setEventPoint = function (value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - -goog.object.extend(exports, proto.mythos.ongeki.v0); diff --git a/typescript/server/src/proto/generated/ongeki/playlog_pb.ts b/typescript/server/src/proto/generated/ongeki/playlog_pb.ts new file mode 100644 index 000000000..daf4fb53d --- /dev/null +++ b/typescript/server/src/proto/generated/ongeki/playlog_pb.ts @@ -0,0 +1,312 @@ +// @generated by protoc-gen-es v2.11.0 with parameter "target=ts" +// @generated from file ongeki/playlog.proto (package mythos.ongeki.v0, syntax proto3) +/* eslint-disable */ + +import type { GenFile, GenMessage } from "@bufbuild/protobuf/codegenv2"; +import { fileDesc, messageDesc } from "@bufbuild/protobuf/codegenv2"; +import type { + OngekiBattleScoreRank, + OngekiBossAttribute, + OngekiClearStatus, + OngekiComboStatus, + OngekiLevel, + OngekiTechScoreRank, +} from "./common_pb"; +import { file_ongeki_common } from "./common_pb"; +import type { Message } from "@bufbuild/protobuf"; + +/** + * Describes the file ongeki/playlog.proto. + */ +export const file_ongeki_playlog: GenFile = + /*@__PURE__*/ + fileDesc( + "ChRvbmdla2kvcGxheWxvZy5wcm90bxIQbXl0aG9zLm9uZ2VraS52MCKpBAoLUGxheWxvZ0luZm8SEAoIbXVzaWNfaWQYASABKAUSLAoFbGV2ZWwYAiABKA4yHS5teXRob3Mub25nZWtpLnYwLk9uZ2VraUxldmVsEhIKCnRlY2hfc2NvcmUYAyABKAUSFAoMYmF0dGxlX3Njb3JlGAQgASgFEhMKC292ZXJfZGFtYWdlGAUgASgFEhYKDnBsYXRpbnVtX3Njb3JlGAYgASgFEj4KD3RlY2hfc2NvcmVfcmFuaxgHIAEoDjIlLm15dGhvcy5vbmdla2kudjAuT25nZWtpVGVjaFNjb3JlUmFuaxJCChFiYXR0bGVfc2NvcmVfcmFuaxgIIAEoDjInLm15dGhvcy5vbmdla2kudjAuT25nZWtpQmF0dGxlU2NvcmVSYW5rEjkKDGNvbWJvX3N0YXR1cxgJIAEoDjIjLm15dGhvcy5vbmdla2kudjAuT25nZWtpQ29tYm9TdGF0dXMSOQoMY2xlYXJfc3RhdHVzGAogASgOMiMubXl0aG9zLm9uZ2VraS52MC5Pbmdla2lDbGVhclN0YXR1cxIUCgxpc19mdWxsX2JlbGwYCyABKAgSGgoSaXNfdGVjaF9uZXdfcmVjb3JkGAwgASgIEhwKFGlzX2JhdHRsZV9uZXdfcmVjb3JkGA0gASgIEiEKGWlzX292ZXJfZGFtYWdlX25ld19yZWNvcmQYDiABKAgSFgoOdXNlcl9wbGF5X2RhdGUYDyABKAkivwEKDFBsYXlsb2dKdWRnZRIcChRqdWRnZV9jcml0aWNhbF9icmVhaxgRIAEoBRITCgtqdWRnZV9icmVhaxgSIAEoBRIRCglqdWRnZV9oaXQYEyABKAUSEgoKanVkZ2VfbWlzcxgUIAEoBRIRCgltYXhfY29tYm8YFSABKAUSEgoKYmVsbF9jb3VudBgWIAEoBRIYChB0b3RhbF9iZWxsX2NvdW50GBcgASgFEhQKDGRhbWFnZV9jb3VudBgYIAEoBSJ6ChBQbGF5bG9nSnVkZ2VSYXRlEhAKCHJhdGVfdGFwGAEgASgFEhEKCXJhdGVfaG9sZBgCIAEoBRISCgpyYXRlX2ZsaWNrGAMgASgFEhUKDXJhdGVfc2lkZV90YXAYBCABKAUSFgoOcmF0ZV9zaWRlX2hvbGQYBSABKAUiXAoTUGxheWxvZ01hdGNoaW5nVXNlchIRCgl1c2VyX25hbWUYASABKAkSMgoLbXVzaWNfbGV2ZWwYAiABKA4yHS5teXRob3Mub25nZWtpLnYwLk9uZ2VraUxldmVsIkcKC1BsYXlsb2dDYXJkEg8KB2NhcmRfaWQYASABKAUSEgoKY2FyZF9sZXZlbBgCIAEoBRITCgtjYXJkX2F0dGFjaxgDIAEoBSJxCgtQbGF5bG9nQm9zcxIPCgdib3NzX2lkGAEgASgFEhIKCmJvc3NfbGV2ZWwYAiABKAUSPQoOYm9zc19hdHRyaWJ1dGUYAyABKA4yJS5teXRob3Mub25nZWtpLnYwLk9uZ2VraUJvc3NBdHRyaWJ1dGUiSQoMUGxheWxvZ0V2ZW50EhAKCGV2ZW50X2lkGAEgASgFEhIKCmV2ZW50X25hbWUYAiABKAkSEwoLZXZlbnRfcG9pbnQYAyABKAViBnByb3RvMw", + [file_ongeki_common], + ); + +/** + * @generated from message mythos.ongeki.v0.PlaylogInfo + */ +export type PlaylogInfo = Message<"mythos.ongeki.v0.PlaylogInfo"> & { + /** + * @generated from field: int32 music_id = 1; + */ + musicId: number; + + /** + * @generated from field: mythos.ongeki.v0.OngekiLevel level = 2; + */ + level: OngekiLevel; + + /** + * @generated from field: int32 tech_score = 3; + */ + techScore: number; + + /** + * @generated from field: int32 battle_score = 4; + */ + battleScore: number; + + /** + * @generated from field: int32 over_damage = 5; + */ + overDamage: number; + + /** + * @generated from field: int32 platinum_score = 6; + */ + platinumScore: number; + + /** + * @generated from field: mythos.ongeki.v0.OngekiTechScoreRank tech_score_rank = 7; + */ + techScoreRank: OngekiTechScoreRank; + + /** + * @generated from field: mythos.ongeki.v0.OngekiBattleScoreRank battle_score_rank = 8; + */ + battleScoreRank: OngekiBattleScoreRank; + + /** + * @generated from field: mythos.ongeki.v0.OngekiComboStatus combo_status = 9; + */ + comboStatus: OngekiComboStatus; + + /** + * @generated from field: mythos.ongeki.v0.OngekiClearStatus clear_status = 10; + */ + clearStatus: OngekiClearStatus; + + /** + * @generated from field: bool is_full_bell = 11; + */ + isFullBell: boolean; + + /** + * @generated from field: bool is_tech_new_record = 12; + */ + isTechNewRecord: boolean; + + /** + * @generated from field: bool is_battle_new_record = 13; + */ + isBattleNewRecord: boolean; + + /** + * @generated from field: bool is_over_damage_new_record = 14; + */ + isOverDamageNewRecord: boolean; + + /** + * @generated from field: string user_play_date = 15; + */ + userPlayDate: string; +}; + +/** + * Describes the message mythos.ongeki.v0.PlaylogInfo. + * Use `create(PlaylogInfoSchema)` to create a new message. + */ +export const PlaylogInfoSchema: GenMessage = + /*@__PURE__*/ + messageDesc(file_ongeki_playlog, 0); + +/** + * @generated from message mythos.ongeki.v0.PlaylogJudge + */ +export type PlaylogJudge = Message<"mythos.ongeki.v0.PlaylogJudge"> & { + /** + * @generated from field: int32 judge_critical_break = 17; + */ + judgeCriticalBreak: number; + + /** + * @generated from field: int32 judge_break = 18; + */ + judgeBreak: number; + + /** + * @generated from field: int32 judge_hit = 19; + */ + judgeHit: number; + + /** + * @generated from field: int32 judge_miss = 20; + */ + judgeMiss: number; + + /** + * @generated from field: int32 max_combo = 21; + */ + maxCombo: number; + + /** + * @generated from field: int32 bell_count = 22; + */ + bellCount: number; + + /** + * @generated from field: int32 total_bell_count = 23; + */ + totalBellCount: number; + + /** + * @generated from field: int32 damage_count = 24; + */ + damageCount: number; +}; + +/** + * Describes the message mythos.ongeki.v0.PlaylogJudge. + * Use `create(PlaylogJudgeSchema)` to create a new message. + */ +export const PlaylogJudgeSchema: GenMessage = + /*@__PURE__*/ + messageDesc(file_ongeki_playlog, 1); + +/** + * @generated from message mythos.ongeki.v0.PlaylogJudgeRate + */ +export type PlaylogJudgeRate = Message<"mythos.ongeki.v0.PlaylogJudgeRate"> & { + /** + * @generated from field: int32 rate_tap = 1; + */ + rateTap: number; + + /** + * @generated from field: int32 rate_hold = 2; + */ + rateHold: number; + + /** + * @generated from field: int32 rate_flick = 3; + */ + rateFlick: number; + + /** + * @generated from field: int32 rate_side_tap = 4; + */ + rateSideTap: number; + + /** + * @generated from field: int32 rate_side_hold = 5; + */ + rateSideHold: number; +}; + +/** + * Describes the message mythos.ongeki.v0.PlaylogJudgeRate. + * Use `create(PlaylogJudgeRateSchema)` to create a new message. + */ +export const PlaylogJudgeRateSchema: GenMessage = + /*@__PURE__*/ + messageDesc(file_ongeki_playlog, 2); + +/** + * @generated from message mythos.ongeki.v0.PlaylogMatchingUser + */ +export type PlaylogMatchingUser = Message<"mythos.ongeki.v0.PlaylogMatchingUser"> & { + /** + * @generated from field: string user_name = 1; + */ + userName: string; + + /** + * @generated from field: mythos.ongeki.v0.OngekiLevel music_level = 2; + */ + musicLevel: OngekiLevel; +}; + +/** + * Describes the message mythos.ongeki.v0.PlaylogMatchingUser. + * Use `create(PlaylogMatchingUserSchema)` to create a new message. + */ +export const PlaylogMatchingUserSchema: GenMessage = + /*@__PURE__*/ + messageDesc(file_ongeki_playlog, 3); + +/** + * @generated from message mythos.ongeki.v0.PlaylogCard + */ +export type PlaylogCard = Message<"mythos.ongeki.v0.PlaylogCard"> & { + /** + * @generated from field: int32 card_id = 1; + */ + cardId: number; + + /** + * @generated from field: int32 card_level = 2; + */ + cardLevel: number; + + /** + * @generated from field: int32 card_attack = 3; + */ + cardAttack: number; +}; + +/** + * Describes the message mythos.ongeki.v0.PlaylogCard. + * Use `create(PlaylogCardSchema)` to create a new message. + */ +export const PlaylogCardSchema: GenMessage = + /*@__PURE__*/ + messageDesc(file_ongeki_playlog, 4); + +/** + * @generated from message mythos.ongeki.v0.PlaylogBoss + */ +export type PlaylogBoss = Message<"mythos.ongeki.v0.PlaylogBoss"> & { + /** + * @generated from field: int32 boss_id = 1; + */ + bossId: number; + + /** + * @generated from field: int32 boss_level = 2; + */ + bossLevel: number; + + /** + * @generated from field: mythos.ongeki.v0.OngekiBossAttribute boss_attribute = 3; + */ + bossAttribute: OngekiBossAttribute; +}; + +/** + * Describes the message mythos.ongeki.v0.PlaylogBoss. + * Use `create(PlaylogBossSchema)` to create a new message. + */ +export const PlaylogBossSchema: GenMessage = + /*@__PURE__*/ + messageDesc(file_ongeki_playlog, 5); + +/** + * @generated from message mythos.ongeki.v0.PlaylogEvent + */ +export type PlaylogEvent = Message<"mythos.ongeki.v0.PlaylogEvent"> & { + /** + * @generated from field: int32 event_id = 1; + */ + eventId: number; + + /** + * @generated from field: string event_name = 2; + */ + eventName: string; + + /** + * @generated from field: int32 event_point = 3; + */ + eventPoint: number; +}; + +/** + * Describes the message mythos.ongeki.v0.PlaylogEvent. + * Use `create(PlaylogEventSchema)` to create a new message. + */ +export const PlaylogEventSchema: GenMessage = + /*@__PURE__*/ + messageDesc(file_ongeki_playlog, 6); diff --git a/typescript/server/src/proto/generated/ongeki/user_connect.ts b/typescript/server/src/proto/generated/ongeki/user_connect.ts new file mode 100644 index 000000000..08015fa86 --- /dev/null +++ b/typescript/server/src/proto/generated/ongeki/user_connect.ts @@ -0,0 +1,25 @@ +// @generated by protoc-gen-connect-es v1.7.0 with parameter "target=ts" +// @generated from file ongeki/user.proto (package mythos.ongeki.v0, syntax proto3) +/* eslint-disable */ +// @ts-nocheck + +import { GetPlaylogRequest, GetPlaylogStreamItem } from "./user_pb.js"; +import { MethodKind } from "@bufbuild/protobuf"; + +/** + * @generated from service mythos.ongeki.v0.OngekiUser + */ +export const OngekiUser = { + typeName: "mythos.ongeki.v0.OngekiUser", + methods: { + /** + * @generated from rpc mythos.ongeki.v0.OngekiUser.GetPlaylog + */ + getPlaylog: { + name: "GetPlaylog", + I: GetPlaylogRequest, + O: GetPlaylogStreamItem, + kind: MethodKind.ServerStreaming, + }, + }, +} as const; diff --git a/typescript/server/src/proto/generated/ongeki/user_grpc_pb.d.ts b/typescript/server/src/proto/generated/ongeki/user_grpc_pb.d.ts deleted file mode 100644 index 96fae2fce..000000000 --- a/typescript/server/src/proto/generated/ongeki/user_grpc_pb.d.ts +++ /dev/null @@ -1,36 +0,0 @@ -// GENERATED CODE -- DO NOT EDIT! - -// package: mythos.ongeki.v0 -// file: ongeki/user.proto - -import * as ongeki_user_pb from "../ongeki/user_pb"; -import * as grpc from "@grpc/grpc-js"; - -interface IOngekiUserService extends grpc.ServiceDefinition { - getPlaylog: grpc.MethodDefinition< - ongeki_user_pb.GetPlaylogRequest, - ongeki_user_pb.GetPlaylogStreamItem - >; -} - -export const OngekiUserService: IOngekiUserService; - -export interface IOngekiUserServer extends grpc.UntypedServiceImplementation { - getPlaylog: grpc.handleServerStreamingCall< - ongeki_user_pb.GetPlaylogRequest, - ongeki_user_pb.GetPlaylogStreamItem - >; -} - -export class OngekiUserClient extends grpc.Client { - constructor(address: string, credentials: grpc.ChannelCredentials, options?: object); - getPlaylog( - argument: ongeki_user_pb.GetPlaylogRequest, - metadataOrOptions?: grpc.Metadata | grpc.CallOptions | null, - ): grpc.ClientReadableStream; - getPlaylog( - argument: ongeki_user_pb.GetPlaylogRequest, - metadata?: grpc.Metadata | null, - options?: grpc.CallOptions | null, - ): grpc.ClientReadableStream; -} diff --git a/typescript/server/src/proto/generated/ongeki/user_grpc_pb.js b/typescript/server/src/proto/generated/ongeki/user_grpc_pb.js deleted file mode 100644 index 5bef421af..000000000 --- a/typescript/server/src/proto/generated/ongeki/user_grpc_pb.js +++ /dev/null @@ -1,44 +0,0 @@ -// GENERATED CODE -- DO NOT EDIT! - -"use strict"; -var grpc = require("@grpc/grpc-js"); -var ongeki_user_pb = require("../ongeki/user_pb.js"); -var ongeki_playlog_pb = require("../ongeki/playlog_pb.js"); - -function serialize_mythos_ongeki_v0_GetPlaylogRequest(arg) { - if (!(arg instanceof ongeki_user_pb.GetPlaylogRequest)) { - throw new Error("Expected argument of type mythos.ongeki.v0.GetPlaylogRequest"); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_mythos_ongeki_v0_GetPlaylogRequest(buffer_arg) { - return ongeki_user_pb.GetPlaylogRequest.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_mythos_ongeki_v0_GetPlaylogStreamItem(arg) { - if (!(arg instanceof ongeki_user_pb.GetPlaylogStreamItem)) { - throw new Error("Expected argument of type mythos.ongeki.v0.GetPlaylogStreamItem"); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_mythos_ongeki_v0_GetPlaylogStreamItem(buffer_arg) { - return ongeki_user_pb.GetPlaylogStreamItem.deserializeBinary(new Uint8Array(buffer_arg)); -} - -var OngekiUserService = (exports.OngekiUserService = { - getPlaylog: { - path: "/mythos.ongeki.v0.OngekiUser/GetPlaylog", - requestStream: false, - responseStream: true, - requestType: ongeki_user_pb.GetPlaylogRequest, - responseType: ongeki_user_pb.GetPlaylogStreamItem, - requestSerialize: serialize_mythos_ongeki_v0_GetPlaylogRequest, - requestDeserialize: deserialize_mythos_ongeki_v0_GetPlaylogRequest, - responseSerialize: serialize_mythos_ongeki_v0_GetPlaylogStreamItem, - responseDeserialize: deserialize_mythos_ongeki_v0_GetPlaylogStreamItem, - }, -}); - -exports.OngekiUserClient = grpc.makeGenericClientConstructor(OngekiUserService, "OngekiUser"); diff --git a/typescript/server/src/proto/generated/ongeki/user_pb.d.ts b/typescript/server/src/proto/generated/ongeki/user_pb.d.ts deleted file mode 100644 index a3974f774..000000000 --- a/typescript/server/src/proto/generated/ongeki/user_pb.d.ts +++ /dev/null @@ -1,78 +0,0 @@ -// package: mythos.ongeki.v0 -// file: ongeki/user.proto - -import * as jspb from "google-protobuf"; -import * as ongeki_playlog_pb from "../ongeki/playlog_pb"; - -export class GetPlaylogRequest extends jspb.Message { - getProfileApiId(): string; - setProfileApiId(value: string): void; - - hasLastUserPlayDate(): boolean; - clearLastUserPlayDate(): void; - getLastUserPlayDate(): string; - setLastUserPlayDate(value: string): void; - - hasLimit(): boolean; - clearLimit(): void; - getLimit(): number; - setLimit(value: number): void; - - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): GetPlaylogRequest.AsObject; - static toObject(includeInstance: boolean, msg: GetPlaylogRequest): GetPlaylogRequest.AsObject; - static extensions: { [key: number]: jspb.ExtensionFieldInfo }; - static extensionsBinary: { [key: number]: jspb.ExtensionFieldBinaryInfo }; - static serializeBinaryToWriter(message: GetPlaylogRequest, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): GetPlaylogRequest; - static deserializeBinaryFromReader( - message: GetPlaylogRequest, - reader: jspb.BinaryReader, - ): GetPlaylogRequest; -} - -export namespace GetPlaylogRequest { - export type AsObject = { - profileApiId: string; - lastUserPlayDate: string; - limit: number; - }; -} - -export class GetPlaylogStreamItem extends jspb.Message { - getPlaylogApiId(): string; - setPlaylogApiId(value: string): void; - - hasInfo(): boolean; - clearInfo(): void; - getInfo(): ongeki_playlog_pb.PlaylogInfo | undefined; - setInfo(value?: ongeki_playlog_pb.PlaylogInfo): void; - - hasJudge(): boolean; - clearJudge(): void; - getJudge(): ongeki_playlog_pb.PlaylogJudge | undefined; - setJudge(value?: ongeki_playlog_pb.PlaylogJudge): void; - - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): GetPlaylogStreamItem.AsObject; - static toObject( - includeInstance: boolean, - msg: GetPlaylogStreamItem, - ): GetPlaylogStreamItem.AsObject; - static extensions: { [key: number]: jspb.ExtensionFieldInfo }; - static extensionsBinary: { [key: number]: jspb.ExtensionFieldBinaryInfo }; - static serializeBinaryToWriter(message: GetPlaylogStreamItem, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): GetPlaylogStreamItem; - static deserializeBinaryFromReader( - message: GetPlaylogStreamItem, - reader: jspb.BinaryReader, - ): GetPlaylogStreamItem; -} - -export namespace GetPlaylogStreamItem { - export type AsObject = { - playlogApiId: string; - info?: ongeki_playlog_pb.PlaylogInfo.AsObject; - judge?: ongeki_playlog_pb.PlaylogJudge.AsObject; - }; -} diff --git a/typescript/server/src/proto/generated/ongeki/user_pb.js b/typescript/server/src/proto/generated/ongeki/user_pb.js deleted file mode 100644 index 9d6ecee01..000000000 --- a/typescript/server/src/proto/generated/ongeki/user_pb.js +++ /dev/null @@ -1,497 +0,0 @@ -// source: ongeki/user.proto -/** - * @fileoverview - * @enhanceable - * @suppress {missingRequire} reports error on implicit type usages. - * @suppress {messageConventions} JS Compiler reports an error if a variable or - * field starts with 'MSG_' and isn't a translatable message. - * @public - */ -// GENERATED CODE -- DO NOT EDIT! -/* eslint-disable */ -// @ts-nocheck - -var jspb = require("google-protobuf"); -var goog = jspb; -var global = function () { - if (this) { - return this; - } - if (typeof window !== "undefined") { - return window; - } - if (typeof global !== "undefined") { - return global; - } - if (typeof self !== "undefined") { - return self; - } - return Function("return this")(); -}.call(null); - -var ongeki_playlog_pb = require("../ongeki/playlog_pb.js"); -goog.object.extend(proto, ongeki_playlog_pb); -goog.exportSymbol("proto.mythos.ongeki.v0.GetPlaylogRequest", null, global); -goog.exportSymbol("proto.mythos.ongeki.v0.GetPlaylogStreamItem", null, global); -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.mythos.ongeki.v0.GetPlaylogRequest = function (opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.mythos.ongeki.v0.GetPlaylogRequest, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.mythos.ongeki.v0.GetPlaylogRequest.displayName = - "proto.mythos.ongeki.v0.GetPlaylogRequest"; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.mythos.ongeki.v0.GetPlaylogStreamItem = function (opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.mythos.ongeki.v0.GetPlaylogStreamItem, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.mythos.ongeki.v0.GetPlaylogStreamItem.displayName = - "proto.mythos.ongeki.v0.GetPlaylogStreamItem"; -} - -if (jspb.Message.GENERATE_TO_OBJECT) { - /** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ - proto.mythos.ongeki.v0.GetPlaylogRequest.prototype.toObject = function (opt_includeInstance) { - return proto.mythos.ongeki.v0.GetPlaylogRequest.toObject(opt_includeInstance, this); - }; - - /** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.mythos.ongeki.v0.GetPlaylogRequest} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ - proto.mythos.ongeki.v0.GetPlaylogRequest.toObject = function (includeInstance, msg) { - var f, - obj = { - profileApiId: jspb.Message.getFieldWithDefault(msg, 1, ""), - lastUserPlayDate: jspb.Message.getFieldWithDefault(msg, 2, ""), - limit: jspb.Message.getFieldWithDefault(msg, 3, 0), - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; - }; -} - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.mythos.ongeki.v0.GetPlaylogRequest} - */ -proto.mythos.ongeki.v0.GetPlaylogRequest.deserializeBinary = function (bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.mythos.ongeki.v0.GetPlaylogRequest(); - return proto.mythos.ongeki.v0.GetPlaylogRequest.deserializeBinaryFromReader(msg, reader); -}; - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.mythos.ongeki.v0.GetPlaylogRequest} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.mythos.ongeki.v0.GetPlaylogRequest} - */ -proto.mythos.ongeki.v0.GetPlaylogRequest.deserializeBinaryFromReader = function (msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {string} */ (reader.readString()); - msg.setProfileApiId(value); - break; - case 2: - var value = /** @type {string} */ (reader.readString()); - msg.setLastUserPlayDate(value); - break; - case 3: - var value = /** @type {number} */ (reader.readUint64()); - msg.setLimit(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.mythos.ongeki.v0.GetPlaylogRequest.prototype.serializeBinary = function () { - var writer = new jspb.BinaryWriter(); - proto.mythos.ongeki.v0.GetPlaylogRequest.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.mythos.ongeki.v0.GetPlaylogRequest} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.mythos.ongeki.v0.GetPlaylogRequest.serializeBinaryToWriter = function (message, writer) { - var f = undefined; - f = message.getProfileApiId(); - if (f.length > 0) { - writer.writeString(1, f); - } - f = /** @type {string} */ (jspb.Message.getField(message, 2)); - if (f != null) { - writer.writeString(2, f); - } - f = /** @type {number} */ (jspb.Message.getField(message, 3)); - if (f != null) { - writer.writeUint64(3, f); - } -}; - -/** - * optional string profile_api_id = 1; - * @return {string} - */ -proto.mythos.ongeki.v0.GetPlaylogRequest.prototype.getProfileApiId = function () { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); -}; - -/** - * @param {string} value - * @return {!proto.mythos.ongeki.v0.GetPlaylogRequest} returns this - */ -proto.mythos.ongeki.v0.GetPlaylogRequest.prototype.setProfileApiId = function (value) { - return jspb.Message.setProto3StringField(this, 1, value); -}; - -/** - * optional string last_user_play_date = 2; - * @return {string} - */ -proto.mythos.ongeki.v0.GetPlaylogRequest.prototype.getLastUserPlayDate = function () { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); -}; - -/** - * @param {string} value - * @return {!proto.mythos.ongeki.v0.GetPlaylogRequest} returns this - */ -proto.mythos.ongeki.v0.GetPlaylogRequest.prototype.setLastUserPlayDate = function (value) { - return jspb.Message.setField(this, 2, value); -}; - -/** - * Clears the field making it undefined. - * @return {!proto.mythos.ongeki.v0.GetPlaylogRequest} returns this - */ -proto.mythos.ongeki.v0.GetPlaylogRequest.prototype.clearLastUserPlayDate = function () { - return jspb.Message.setField(this, 2, undefined); -}; - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.mythos.ongeki.v0.GetPlaylogRequest.prototype.hasLastUserPlayDate = function () { - return jspb.Message.getField(this, 2) != null; -}; - -/** - * optional uint64 limit = 3; - * @return {number} - */ -proto.mythos.ongeki.v0.GetPlaylogRequest.prototype.getLimit = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.ongeki.v0.GetPlaylogRequest} returns this - */ -proto.mythos.ongeki.v0.GetPlaylogRequest.prototype.setLimit = function (value) { - return jspb.Message.setField(this, 3, value); -}; - -/** - * Clears the field making it undefined. - * @return {!proto.mythos.ongeki.v0.GetPlaylogRequest} returns this - */ -proto.mythos.ongeki.v0.GetPlaylogRequest.prototype.clearLimit = function () { - return jspb.Message.setField(this, 3, undefined); -}; - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.mythos.ongeki.v0.GetPlaylogRequest.prototype.hasLimit = function () { - return jspb.Message.getField(this, 3) != null; -}; - -if (jspb.Message.GENERATE_TO_OBJECT) { - /** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ - proto.mythos.ongeki.v0.GetPlaylogStreamItem.prototype.toObject = function ( - opt_includeInstance, - ) { - return proto.mythos.ongeki.v0.GetPlaylogStreamItem.toObject(opt_includeInstance, this); - }; - - /** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.mythos.ongeki.v0.GetPlaylogStreamItem} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ - proto.mythos.ongeki.v0.GetPlaylogStreamItem.toObject = function (includeInstance, msg) { - var f, - obj = { - playlogApiId: jspb.Message.getFieldWithDefault(msg, 1, ""), - info: - (f = msg.getInfo()) && - ongeki_playlog_pb.PlaylogInfo.toObject(includeInstance, f), - judge: - (f = msg.getJudge()) && - ongeki_playlog_pb.PlaylogJudge.toObject(includeInstance, f), - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; - }; -} - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.mythos.ongeki.v0.GetPlaylogStreamItem} - */ -proto.mythos.ongeki.v0.GetPlaylogStreamItem.deserializeBinary = function (bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.mythos.ongeki.v0.GetPlaylogStreamItem(); - return proto.mythos.ongeki.v0.GetPlaylogStreamItem.deserializeBinaryFromReader(msg, reader); -}; - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.mythos.ongeki.v0.GetPlaylogStreamItem} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.mythos.ongeki.v0.GetPlaylogStreamItem} - */ -proto.mythos.ongeki.v0.GetPlaylogStreamItem.deserializeBinaryFromReader = function (msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {string} */ (reader.readString()); - msg.setPlaylogApiId(value); - break; - case 2: - var value = new ongeki_playlog_pb.PlaylogInfo(); - reader.readMessage( - value, - ongeki_playlog_pb.PlaylogInfo.deserializeBinaryFromReader, - ); - msg.setInfo(value); - break; - case 3: - var value = new ongeki_playlog_pb.PlaylogJudge(); - reader.readMessage( - value, - ongeki_playlog_pb.PlaylogJudge.deserializeBinaryFromReader, - ); - msg.setJudge(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.mythos.ongeki.v0.GetPlaylogStreamItem.prototype.serializeBinary = function () { - var writer = new jspb.BinaryWriter(); - proto.mythos.ongeki.v0.GetPlaylogStreamItem.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.mythos.ongeki.v0.GetPlaylogStreamItem} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.mythos.ongeki.v0.GetPlaylogStreamItem.serializeBinaryToWriter = function (message, writer) { - var f = undefined; - f = message.getPlaylogApiId(); - if (f.length > 0) { - writer.writeString(1, f); - } - f = message.getInfo(); - if (f != null) { - writer.writeMessage(2, f, ongeki_playlog_pb.PlaylogInfo.serializeBinaryToWriter); - } - f = message.getJudge(); - if (f != null) { - writer.writeMessage(3, f, ongeki_playlog_pb.PlaylogJudge.serializeBinaryToWriter); - } -}; - -/** - * optional string playlog_api_id = 1; - * @return {string} - */ -proto.mythos.ongeki.v0.GetPlaylogStreamItem.prototype.getPlaylogApiId = function () { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); -}; - -/** - * @param {string} value - * @return {!proto.mythos.ongeki.v0.GetPlaylogStreamItem} returns this - */ -proto.mythos.ongeki.v0.GetPlaylogStreamItem.prototype.setPlaylogApiId = function (value) { - return jspb.Message.setProto3StringField(this, 1, value); -}; - -/** - * optional PlaylogInfo info = 2; - * @return {?proto.mythos.ongeki.v0.PlaylogInfo} - */ -proto.mythos.ongeki.v0.GetPlaylogStreamItem.prototype.getInfo = function () { - return /** @type{?proto.mythos.ongeki.v0.PlaylogInfo} */ ( - jspb.Message.getWrapperField(this, ongeki_playlog_pb.PlaylogInfo, 2) - ); -}; - -/** - * @param {?proto.mythos.ongeki.v0.PlaylogInfo|undefined} value - * @return {!proto.mythos.ongeki.v0.GetPlaylogStreamItem} returns this - */ -proto.mythos.ongeki.v0.GetPlaylogStreamItem.prototype.setInfo = function (value) { - return jspb.Message.setWrapperField(this, 2, value); -}; - -/** - * Clears the message field making it undefined. - * @return {!proto.mythos.ongeki.v0.GetPlaylogStreamItem} returns this - */ -proto.mythos.ongeki.v0.GetPlaylogStreamItem.prototype.clearInfo = function () { - return this.setInfo(undefined); -}; - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.mythos.ongeki.v0.GetPlaylogStreamItem.prototype.hasInfo = function () { - return jspb.Message.getField(this, 2) != null; -}; - -/** - * optional PlaylogJudge judge = 3; - * @return {?proto.mythos.ongeki.v0.PlaylogJudge} - */ -proto.mythos.ongeki.v0.GetPlaylogStreamItem.prototype.getJudge = function () { - return /** @type{?proto.mythos.ongeki.v0.PlaylogJudge} */ ( - jspb.Message.getWrapperField(this, ongeki_playlog_pb.PlaylogJudge, 3) - ); -}; - -/** - * @param {?proto.mythos.ongeki.v0.PlaylogJudge|undefined} value - * @return {!proto.mythos.ongeki.v0.GetPlaylogStreamItem} returns this - */ -proto.mythos.ongeki.v0.GetPlaylogStreamItem.prototype.setJudge = function (value) { - return jspb.Message.setWrapperField(this, 3, value); -}; - -/** - * Clears the message field making it undefined. - * @return {!proto.mythos.ongeki.v0.GetPlaylogStreamItem} returns this - */ -proto.mythos.ongeki.v0.GetPlaylogStreamItem.prototype.clearJudge = function () { - return this.setJudge(undefined); -}; - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.mythos.ongeki.v0.GetPlaylogStreamItem.prototype.hasJudge = function () { - return jspb.Message.getField(this, 3) != null; -}; - -goog.object.extend(exports, proto.mythos.ongeki.v0); diff --git a/typescript/server/src/proto/generated/ongeki/user_pb.ts b/typescript/server/src/proto/generated/ongeki/user_pb.ts new file mode 100644 index 000000000..9bc95bc03 --- /dev/null +++ b/typescript/server/src/proto/generated/ongeki/user_pb.ts @@ -0,0 +1,89 @@ +// @generated by protoc-gen-es v2.11.0 with parameter "target=ts" +// @generated from file ongeki/user.proto (package mythos.ongeki.v0, syntax proto3) +/* eslint-disable */ + +import type { GenFile, GenMessage, GenService } from "@bufbuild/protobuf/codegenv2"; +import { fileDesc, messageDesc, serviceDesc } from "@bufbuild/protobuf/codegenv2"; +import type { PlaylogInfo, PlaylogJudge } from "./playlog_pb"; +import { file_ongeki_playlog } from "./playlog_pb"; +import type { Message } from "@bufbuild/protobuf"; + +/** + * Describes the file ongeki/user.proto. + */ +export const file_ongeki_user: GenFile = + /*@__PURE__*/ + fileDesc( + "ChFvbmdla2kvdXNlci5wcm90bxIQbXl0aG9zLm9uZ2VraS52MCKDAQoRR2V0UGxheWxvZ1JlcXVlc3QSFgoOcHJvZmlsZV9hcGlfaWQYASABKAkSIAoTbGFzdF91c2VyX3BsYXlfZGF0ZRgCIAEoCUgAiAEBEhIKBWxpbWl0GAMgASgESAGIAQFCFgoUX2xhc3RfdXNlcl9wbGF5X2RhdGVCCAoGX2xpbWl0IooBChRHZXRQbGF5bG9nU3RyZWFtSXRlbRIWCg5wbGF5bG9nX2FwaV9pZBgBIAEoCRIrCgRpbmZvGAIgASgLMh0ubXl0aG9zLm9uZ2VraS52MC5QbGF5bG9nSW5mbxItCgVqdWRnZRgDIAEoCzIeLm15dGhvcy5vbmdla2kudjAuUGxheWxvZ0p1ZGdlMmkKCk9uZ2VraVVzZXISWwoKR2V0UGxheWxvZxIjLm15dGhvcy5vbmdla2kudjAuR2V0UGxheWxvZ1JlcXVlc3QaJi5teXRob3Mub25nZWtpLnYwLkdldFBsYXlsb2dTdHJlYW1JdGVtMAFiBnByb3RvMw", + [file_ongeki_playlog], + ); + +/** + * @generated from message mythos.ongeki.v0.GetPlaylogRequest + */ +export type GetPlaylogRequest = Message<"mythos.ongeki.v0.GetPlaylogRequest"> & { + /** + * @generated from field: string profile_api_id = 1; + */ + profileApiId: string; + + /** + * @generated from field: optional string last_user_play_date = 2; + */ + lastUserPlayDate?: string; + + /** + * @generated from field: optional uint64 limit = 3; + */ + limit?: bigint; +}; + +/** + * Describes the message mythos.ongeki.v0.GetPlaylogRequest. + * Use `create(GetPlaylogRequestSchema)` to create a new message. + */ +export const GetPlaylogRequestSchema: GenMessage = + /*@__PURE__*/ + messageDesc(file_ongeki_user, 0); + +/** + * @generated from message mythos.ongeki.v0.GetPlaylogStreamItem + */ +export type GetPlaylogStreamItem = Message<"mythos.ongeki.v0.GetPlaylogStreamItem"> & { + /** + * @generated from field: string playlog_api_id = 1; + */ + playlogApiId: string; + + /** + * @generated from field: mythos.ongeki.v0.PlaylogInfo info = 2; + */ + info?: PlaylogInfo; + + /** + * @generated from field: mythos.ongeki.v0.PlaylogJudge judge = 3; + */ + judge?: PlaylogJudge; +}; + +/** + * Describes the message mythos.ongeki.v0.GetPlaylogStreamItem. + * Use `create(GetPlaylogStreamItemSchema)` to create a new message. + */ +export const GetPlaylogStreamItemSchema: GenMessage = + /*@__PURE__*/ + messageDesc(file_ongeki_user, 1); + +/** + * @generated from service mythos.ongeki.v0.OngekiUser + */ +export const OngekiUser: GenService<{ + /** + * @generated from rpc mythos.ongeki.v0.OngekiUser.GetPlaylog + */ + getPlaylog: { + methodKind: "server_streaming"; + input: typeof GetPlaylogRequestSchema; + output: typeof GetPlaylogStreamItemSchema; + }; +}> = /*@__PURE__*/ serviceDesc(file_ongeki_user, 0); diff --git a/typescript/server/src/proto/generated/wacca/common_grpc_pb.d.ts b/typescript/server/src/proto/generated/wacca/common_grpc_pb.d.ts deleted file mode 100644 index 51b4d6959..000000000 --- a/typescript/server/src/proto/generated/wacca/common_grpc_pb.d.ts +++ /dev/null @@ -1 +0,0 @@ -// GENERATED CODE -- NO SERVICES IN PROTO diff --git a/typescript/server/src/proto/generated/wacca/common_grpc_pb.js b/typescript/server/src/proto/generated/wacca/common_grpc_pb.js deleted file mode 100644 index 51b4d6959..000000000 --- a/typescript/server/src/proto/generated/wacca/common_grpc_pb.js +++ /dev/null @@ -1 +0,0 @@ -// GENERATED CODE -- NO SERVICES IN PROTO diff --git a/typescript/server/src/proto/generated/wacca/common_pb.d.ts b/typescript/server/src/proto/generated/wacca/common_pb.d.ts deleted file mode 100644 index 10381a1eb..000000000 --- a/typescript/server/src/proto/generated/wacca/common_pb.d.ts +++ /dev/null @@ -1,125 +0,0 @@ -// package: mythos.wacca.v0 -// file: wacca/common.proto - -import * as jspb from "google-protobuf"; - -export class WaccaClearStatus extends jspb.Message { - getIsClear(): boolean; - setIsClear(value: boolean): void; - - getIsMissless(): boolean; - setIsMissless(value: boolean): void; - - getIsFullCombo(): boolean; - setIsFullCombo(value: boolean): void; - - getIsAllMarvelous(): boolean; - setIsAllMarvelous(value: boolean): void; - - getIsGiveUp(): boolean; - setIsGiveUp(value: boolean): void; - - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): WaccaClearStatus.AsObject; - static toObject(includeInstance: boolean, msg: WaccaClearStatus): WaccaClearStatus.AsObject; - static extensions: { [key: number]: jspb.ExtensionFieldInfo }; - static extensionsBinary: { [key: number]: jspb.ExtensionFieldBinaryInfo }; - static serializeBinaryToWriter(message: WaccaClearStatus, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): WaccaClearStatus; - static deserializeBinaryFromReader( - message: WaccaClearStatus, - reader: jspb.BinaryReader, - ): WaccaClearStatus; -} - -export namespace WaccaClearStatus { - export type AsObject = { - isClear: boolean; - isMissless: boolean; - isFullCombo: boolean; - isAllMarvelous: boolean; - isGiveUp: boolean; - }; -} - -export class WaccaJudge extends jspb.Message { - getMarvelous(): number; - setMarvelous(value: number): void; - - getGreat(): number; - setGreat(value: number): void; - - getGood(): number; - setGood(value: number): void; - - getMiss(): number; - setMiss(value: number): void; - - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): WaccaJudge.AsObject; - static toObject(includeInstance: boolean, msg: WaccaJudge): WaccaJudge.AsObject; - static extensions: { [key: number]: jspb.ExtensionFieldInfo }; - static extensionsBinary: { [key: number]: jspb.ExtensionFieldBinaryInfo }; - static serializeBinaryToWriter(message: WaccaJudge, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): WaccaJudge; - static deserializeBinaryFromReader(message: WaccaJudge, reader: jspb.BinaryReader): WaccaJudge; -} - -export namespace WaccaJudge { - export type AsObject = { - marvelous: number; - great: number; - good: number; - miss: number; - }; -} - -export interface WaccaVersionMap { - WACCA_VERSION_UNSPECIFIED: 0; - WACCA_VERSION_WACCA: 100; - WACCA_VERSION_S: 150; - WACCA_VERSION_LILY: 200; - WACCA_VERSION_LILY_R: 250; - WACCA_VERSION_REVERSE: 300; - WACCA_VERSION_PLUS: 400; -} - -export const WaccaVersion: WaccaVersionMap; - -export interface WaccaMusicDifficultyMap { - WACCA_MUSIC_DIFFICULTY_UNSPECIFIED: 0; - WACCA_MUSIC_DIFFICULTY_NORMAL: 1; - WACCA_MUSIC_DIFFICULTY_HARD: 2; - WACCA_MUSIC_DIFFICULTY_EXPERT: 3; - WACCA_MUSIC_DIFFICULTY_INFERNO: 4; -} - -export const WaccaMusicDifficulty: WaccaMusicDifficultyMap; - -export interface WaccaMusicScoreGradeMap { - WACCA_MUSIC_SCORE_GRADE_UNSPECIFIED: 0; - WACCA_MUSIC_SCORE_GRADE_D: 1; - WACCA_MUSIC_SCORE_GRADE_C: 2; - WACCA_MUSIC_SCORE_GRADE_B: 3; - WACCA_MUSIC_SCORE_GRADE_A: 4; - WACCA_MUSIC_SCORE_GRADE_AA: 5; - WACCA_MUSIC_SCORE_GRADE_AAA: 6; - WACCA_MUSIC_SCORE_GRADE_S: 7; - WACCA_MUSIC_SCORE_GRADE_S_PLUS: 8; - WACCA_MUSIC_SCORE_GRADE_SS: 9; - WACCA_MUSIC_SCORE_GRADE_SS_PLUS: 10; - WACCA_MUSIC_SCORE_GRADE_SSS: 11; - WACCA_MUSIC_SCORE_GRADE_SSS_PLUS: 12; - WACCA_MUSIC_SCORE_GRADE_MASTER: 13; -} - -export const WaccaMusicScoreGrade: WaccaMusicScoreGradeMap; - -export interface WaccaPlayModeMap { - WACCA_PLAY_MODE_UNSPECIFIED: 0; - WACCA_PLAY_MODE_SINGLE: 1; - WACCA_PLAY_MODE_VERSUS: 2; - WACCA_PLAY_MODE_CO_OP: 3; -} - -export const WaccaPlayMode: WaccaPlayModeMap; diff --git a/typescript/server/src/proto/generated/wacca/common_pb.js b/typescript/server/src/proto/generated/wacca/common_pb.js deleted file mode 100644 index ee1b0e2d4..000000000 --- a/typescript/server/src/proto/generated/wacca/common_pb.js +++ /dev/null @@ -1,544 +0,0 @@ -// source: wacca/common.proto -/** - * @fileoverview - * @enhanceable - * @suppress {missingRequire} reports error on implicit type usages. - * @suppress {messageConventions} JS Compiler reports an error if a variable or - * field starts with 'MSG_' and isn't a translatable message. - * @public - */ -// GENERATED CODE -- DO NOT EDIT! -/* eslint-disable */ -// @ts-nocheck - -var jspb = require("google-protobuf"); -var goog = jspb; -var global = function () { - if (this) { - return this; - } - if (typeof window !== "undefined") { - return window; - } - if (typeof global !== "undefined") { - return global; - } - if (typeof self !== "undefined") { - return self; - } - return Function("return this")(); -}.call(null); - -goog.exportSymbol("proto.mythos.wacca.v0.WaccaClearStatus", null, global); -goog.exportSymbol("proto.mythos.wacca.v0.WaccaJudge", null, global); -goog.exportSymbol("proto.mythos.wacca.v0.WaccaMusicDifficulty", null, global); -goog.exportSymbol("proto.mythos.wacca.v0.WaccaMusicScoreGrade", null, global); -goog.exportSymbol("proto.mythos.wacca.v0.WaccaPlayMode", null, global); -goog.exportSymbol("proto.mythos.wacca.v0.WaccaVersion", null, global); -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.mythos.wacca.v0.WaccaClearStatus = function (opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.mythos.wacca.v0.WaccaClearStatus, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.mythos.wacca.v0.WaccaClearStatus.displayName = "proto.mythos.wacca.v0.WaccaClearStatus"; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.mythos.wacca.v0.WaccaJudge = function (opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.mythos.wacca.v0.WaccaJudge, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.mythos.wacca.v0.WaccaJudge.displayName = "proto.mythos.wacca.v0.WaccaJudge"; -} - -if (jspb.Message.GENERATE_TO_OBJECT) { - /** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ - proto.mythos.wacca.v0.WaccaClearStatus.prototype.toObject = function (opt_includeInstance) { - return proto.mythos.wacca.v0.WaccaClearStatus.toObject(opt_includeInstance, this); - }; - - /** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.mythos.wacca.v0.WaccaClearStatus} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ - proto.mythos.wacca.v0.WaccaClearStatus.toObject = function (includeInstance, msg) { - var f, - obj = { - isClear: jspb.Message.getBooleanFieldWithDefault(msg, 1, false), - isMissless: jspb.Message.getBooleanFieldWithDefault(msg, 2, false), - isFullCombo: jspb.Message.getBooleanFieldWithDefault(msg, 3, false), - isAllMarvelous: jspb.Message.getBooleanFieldWithDefault(msg, 4, false), - isGiveUp: jspb.Message.getBooleanFieldWithDefault(msg, 5, false), - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; - }; -} - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.mythos.wacca.v0.WaccaClearStatus} - */ -proto.mythos.wacca.v0.WaccaClearStatus.deserializeBinary = function (bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.mythos.wacca.v0.WaccaClearStatus(); - return proto.mythos.wacca.v0.WaccaClearStatus.deserializeBinaryFromReader(msg, reader); -}; - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.mythos.wacca.v0.WaccaClearStatus} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.mythos.wacca.v0.WaccaClearStatus} - */ -proto.mythos.wacca.v0.WaccaClearStatus.deserializeBinaryFromReader = function (msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {boolean} */ (reader.readBool()); - msg.setIsClear(value); - break; - case 2: - var value = /** @type {boolean} */ (reader.readBool()); - msg.setIsMissless(value); - break; - case 3: - var value = /** @type {boolean} */ (reader.readBool()); - msg.setIsFullCombo(value); - break; - case 4: - var value = /** @type {boolean} */ (reader.readBool()); - msg.setIsAllMarvelous(value); - break; - case 5: - var value = /** @type {boolean} */ (reader.readBool()); - msg.setIsGiveUp(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.mythos.wacca.v0.WaccaClearStatus.prototype.serializeBinary = function () { - var writer = new jspb.BinaryWriter(); - proto.mythos.wacca.v0.WaccaClearStatus.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.mythos.wacca.v0.WaccaClearStatus} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.mythos.wacca.v0.WaccaClearStatus.serializeBinaryToWriter = function (message, writer) { - var f = undefined; - f = message.getIsClear(); - if (f) { - writer.writeBool(1, f); - } - f = message.getIsMissless(); - if (f) { - writer.writeBool(2, f); - } - f = message.getIsFullCombo(); - if (f) { - writer.writeBool(3, f); - } - f = message.getIsAllMarvelous(); - if (f) { - writer.writeBool(4, f); - } - f = message.getIsGiveUp(); - if (f) { - writer.writeBool(5, f); - } -}; - -/** - * optional bool is_clear = 1; - * @return {boolean} - */ -proto.mythos.wacca.v0.WaccaClearStatus.prototype.getIsClear = function () { - return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 1, false)); -}; - -/** - * @param {boolean} value - * @return {!proto.mythos.wacca.v0.WaccaClearStatus} returns this - */ -proto.mythos.wacca.v0.WaccaClearStatus.prototype.setIsClear = function (value) { - return jspb.Message.setProto3BooleanField(this, 1, value); -}; - -/** - * optional bool is_missless = 2; - * @return {boolean} - */ -proto.mythos.wacca.v0.WaccaClearStatus.prototype.getIsMissless = function () { - return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 2, false)); -}; - -/** - * @param {boolean} value - * @return {!proto.mythos.wacca.v0.WaccaClearStatus} returns this - */ -proto.mythos.wacca.v0.WaccaClearStatus.prototype.setIsMissless = function (value) { - return jspb.Message.setProto3BooleanField(this, 2, value); -}; - -/** - * optional bool is_full_combo = 3; - * @return {boolean} - */ -proto.mythos.wacca.v0.WaccaClearStatus.prototype.getIsFullCombo = function () { - return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 3, false)); -}; - -/** - * @param {boolean} value - * @return {!proto.mythos.wacca.v0.WaccaClearStatus} returns this - */ -proto.mythos.wacca.v0.WaccaClearStatus.prototype.setIsFullCombo = function (value) { - return jspb.Message.setProto3BooleanField(this, 3, value); -}; - -/** - * optional bool is_all_marvelous = 4; - * @return {boolean} - */ -proto.mythos.wacca.v0.WaccaClearStatus.prototype.getIsAllMarvelous = function () { - return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 4, false)); -}; - -/** - * @param {boolean} value - * @return {!proto.mythos.wacca.v0.WaccaClearStatus} returns this - */ -proto.mythos.wacca.v0.WaccaClearStatus.prototype.setIsAllMarvelous = function (value) { - return jspb.Message.setProto3BooleanField(this, 4, value); -}; - -/** - * optional bool is_give_up = 5; - * @return {boolean} - */ -proto.mythos.wacca.v0.WaccaClearStatus.prototype.getIsGiveUp = function () { - return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 5, false)); -}; - -/** - * @param {boolean} value - * @return {!proto.mythos.wacca.v0.WaccaClearStatus} returns this - */ -proto.mythos.wacca.v0.WaccaClearStatus.prototype.setIsGiveUp = function (value) { - return jspb.Message.setProto3BooleanField(this, 5, value); -}; - -if (jspb.Message.GENERATE_TO_OBJECT) { - /** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ - proto.mythos.wacca.v0.WaccaJudge.prototype.toObject = function (opt_includeInstance) { - return proto.mythos.wacca.v0.WaccaJudge.toObject(opt_includeInstance, this); - }; - - /** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.mythos.wacca.v0.WaccaJudge} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ - proto.mythos.wacca.v0.WaccaJudge.toObject = function (includeInstance, msg) { - var f, - obj = { - marvelous: jspb.Message.getFieldWithDefault(msg, 1, 0), - great: jspb.Message.getFieldWithDefault(msg, 2, 0), - good: jspb.Message.getFieldWithDefault(msg, 3, 0), - miss: jspb.Message.getFieldWithDefault(msg, 4, 0), - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; - }; -} - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.mythos.wacca.v0.WaccaJudge} - */ -proto.mythos.wacca.v0.WaccaJudge.deserializeBinary = function (bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.mythos.wacca.v0.WaccaJudge(); - return proto.mythos.wacca.v0.WaccaJudge.deserializeBinaryFromReader(msg, reader); -}; - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.mythos.wacca.v0.WaccaJudge} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.mythos.wacca.v0.WaccaJudge} - */ -proto.mythos.wacca.v0.WaccaJudge.deserializeBinaryFromReader = function (msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {number} */ (reader.readInt32()); - msg.setMarvelous(value); - break; - case 2: - var value = /** @type {number} */ (reader.readInt32()); - msg.setGreat(value); - break; - case 3: - var value = /** @type {number} */ (reader.readInt32()); - msg.setGood(value); - break; - case 4: - var value = /** @type {number} */ (reader.readInt32()); - msg.setMiss(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.mythos.wacca.v0.WaccaJudge.prototype.serializeBinary = function () { - var writer = new jspb.BinaryWriter(); - proto.mythos.wacca.v0.WaccaJudge.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.mythos.wacca.v0.WaccaJudge} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.mythos.wacca.v0.WaccaJudge.serializeBinaryToWriter = function (message, writer) { - var f = undefined; - f = message.getMarvelous(); - if (f !== 0) { - writer.writeInt32(1, f); - } - f = message.getGreat(); - if (f !== 0) { - writer.writeInt32(2, f); - } - f = message.getGood(); - if (f !== 0) { - writer.writeInt32(3, f); - } - f = message.getMiss(); - if (f !== 0) { - writer.writeInt32(4, f); - } -}; - -/** - * optional int32 marvelous = 1; - * @return {number} - */ -proto.mythos.wacca.v0.WaccaJudge.prototype.getMarvelous = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.wacca.v0.WaccaJudge} returns this - */ -proto.mythos.wacca.v0.WaccaJudge.prototype.setMarvelous = function (value) { - return jspb.Message.setProto3IntField(this, 1, value); -}; - -/** - * optional int32 great = 2; - * @return {number} - */ -proto.mythos.wacca.v0.WaccaJudge.prototype.getGreat = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.wacca.v0.WaccaJudge} returns this - */ -proto.mythos.wacca.v0.WaccaJudge.prototype.setGreat = function (value) { - return jspb.Message.setProto3IntField(this, 2, value); -}; - -/** - * optional int32 good = 3; - * @return {number} - */ -proto.mythos.wacca.v0.WaccaJudge.prototype.getGood = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.wacca.v0.WaccaJudge} returns this - */ -proto.mythos.wacca.v0.WaccaJudge.prototype.setGood = function (value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - -/** - * optional int32 miss = 4; - * @return {number} - */ -proto.mythos.wacca.v0.WaccaJudge.prototype.getMiss = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.wacca.v0.WaccaJudge} returns this - */ -proto.mythos.wacca.v0.WaccaJudge.prototype.setMiss = function (value) { - return jspb.Message.setProto3IntField(this, 4, value); -}; - -/** - * @enum {number} - */ -proto.mythos.wacca.v0.WaccaVersion = { - WACCA_VERSION_UNSPECIFIED: 0, - WACCA_VERSION_WACCA: 100, - WACCA_VERSION_S: 150, - WACCA_VERSION_LILY: 200, - WACCA_VERSION_LILY_R: 250, - WACCA_VERSION_REVERSE: 300, - WACCA_VERSION_PLUS: 400, -}; - -/** - * @enum {number} - */ -proto.mythos.wacca.v0.WaccaMusicDifficulty = { - WACCA_MUSIC_DIFFICULTY_UNSPECIFIED: 0, - WACCA_MUSIC_DIFFICULTY_NORMAL: 1, - WACCA_MUSIC_DIFFICULTY_HARD: 2, - WACCA_MUSIC_DIFFICULTY_EXPERT: 3, - WACCA_MUSIC_DIFFICULTY_INFERNO: 4, -}; - -/** - * @enum {number} - */ -proto.mythos.wacca.v0.WaccaMusicScoreGrade = { - WACCA_MUSIC_SCORE_GRADE_UNSPECIFIED: 0, - WACCA_MUSIC_SCORE_GRADE_D: 1, - WACCA_MUSIC_SCORE_GRADE_C: 2, - WACCA_MUSIC_SCORE_GRADE_B: 3, - WACCA_MUSIC_SCORE_GRADE_A: 4, - WACCA_MUSIC_SCORE_GRADE_AA: 5, - WACCA_MUSIC_SCORE_GRADE_AAA: 6, - WACCA_MUSIC_SCORE_GRADE_S: 7, - WACCA_MUSIC_SCORE_GRADE_S_PLUS: 8, - WACCA_MUSIC_SCORE_GRADE_SS: 9, - WACCA_MUSIC_SCORE_GRADE_SS_PLUS: 10, - WACCA_MUSIC_SCORE_GRADE_SSS: 11, - WACCA_MUSIC_SCORE_GRADE_SSS_PLUS: 12, - WACCA_MUSIC_SCORE_GRADE_MASTER: 13, -}; - -/** - * @enum {number} - */ -proto.mythos.wacca.v0.WaccaPlayMode = { - WACCA_PLAY_MODE_UNSPECIFIED: 0, - WACCA_PLAY_MODE_SINGLE: 1, - WACCA_PLAY_MODE_VERSUS: 2, - WACCA_PLAY_MODE_CO_OP: 3, -}; - -goog.object.extend(exports, proto.mythos.wacca.v0); diff --git a/typescript/server/src/proto/generated/wacca/common_pb.ts b/typescript/server/src/proto/generated/wacca/common_pb.ts new file mode 100644 index 000000000..60e1f83ec --- /dev/null +++ b/typescript/server/src/proto/generated/wacca/common_pb.ts @@ -0,0 +1,285 @@ +// @generated by protoc-gen-es v2.11.0 with parameter "target=ts" +// @generated from file wacca/common.proto (package mythos.wacca.v0, syntax proto3) +/* eslint-disable */ + +import type { GenEnum, GenFile, GenMessage } from "@bufbuild/protobuf/codegenv2"; +import { enumDesc, fileDesc, messageDesc } from "@bufbuild/protobuf/codegenv2"; +import type { Message } from "@bufbuild/protobuf"; + +/** + * Describes the file wacca/common.proto. + */ +export const file_wacca_common: GenFile = + /*@__PURE__*/ + fileDesc( + "ChJ3YWNjYS9jb21tb24ucHJvdG8SD215dGhvcy53YWNjYS52MCJ+ChBXYWNjYUNsZWFyU3RhdHVzEhAKCGlzX2NsZWFyGAEgASgIEhMKC2lzX21pc3NsZXNzGAIgASgIEhUKDWlzX2Z1bGxfY29tYm8YAyABKAgSGAoQaXNfYWxsX21hcnZlbG91cxgEIAEoCBISCgppc19naXZlX3VwGAUgASgIIkoKCldhY2NhSnVkZ2USEQoJbWFydmVsb3VzGAEgASgFEg0KBWdyZWF0GAIgASgFEgwKBGdvb2QYAyABKAUSDAoEbWlzcxgEIAEoBSrFAQoMV2FjY2FWZXJzaW9uEh0KGVdBQ0NBX1ZFUlNJT05fVU5TUEVDSUZJRUQQABIXChNXQUNDQV9WRVJTSU9OX1dBQ0NBEGQSFAoPV0FDQ0FfVkVSU0lPTl9TEJYBEhcKEldBQ0NBX1ZFUlNJT05fTElMWRDIARIZChRXQUNDQV9WRVJTSU9OX0xJTFlfUhD6ARIaChVXQUNDQV9WRVJTSU9OX1JFVkVSU0UQrAISFwoSV0FDQ0FfVkVSU0lPTl9QTFVTEJADKskBChRXYWNjYU11c2ljRGlmZmljdWx0eRImCiJXQUNDQV9NVVNJQ19ESUZGSUNVTFRZX1VOU1BFQ0lGSUVEEAASIQodV0FDQ0FfTVVTSUNfRElGRklDVUxUWV9OT1JNQUwQARIfChtXQUNDQV9NVVNJQ19ESUZGSUNVTFRZX0hBUkQQAhIhCh1XQUNDQV9NVVNJQ19ESUZGSUNVTFRZX0VYUEVSVBADEiIKHldBQ0NBX01VU0lDX0RJRkZJQ1VMVFlfSU5GRVJOTxAEKu8DChRXYWNjYU11c2ljU2NvcmVHcmFkZRInCiNXQUNDQV9NVVNJQ19TQ09SRV9HUkFERV9VTlNQRUNJRklFRBAAEh0KGVdBQ0NBX01VU0lDX1NDT1JFX0dSQURFX0QQARIdChlXQUNDQV9NVVNJQ19TQ09SRV9HUkFERV9DEAISHQoZV0FDQ0FfTVVTSUNfU0NPUkVfR1JBREVfQhADEh0KGVdBQ0NBX01VU0lDX1NDT1JFX0dSQURFX0EQBBIeChpXQUNDQV9NVVNJQ19TQ09SRV9HUkFERV9BQRAFEh8KG1dBQ0NBX01VU0lDX1NDT1JFX0dSQURFX0FBQRAGEh0KGVdBQ0NBX01VU0lDX1NDT1JFX0dSQURFX1MQBxIiCh5XQUNDQV9NVVNJQ19TQ09SRV9HUkFERV9TX1BMVVMQCBIeChpXQUNDQV9NVVNJQ19TQ09SRV9HUkFERV9TUxAJEiMKH1dBQ0NBX01VU0lDX1NDT1JFX0dSQURFX1NTX1BMVVMQChIfChtXQUNDQV9NVVNJQ19TQ09SRV9HUkFERV9TU1MQCxIkCiBXQUNDQV9NVVNJQ19TQ09SRV9HUkFERV9TU1NfUExVUxAMEiIKHldBQ0NBX01VU0lDX1NDT1JFX0dSQURFX01BU1RFUhANKoMBCg1XYWNjYVBsYXlNb2RlEh8KG1dBQ0NBX1BMQVlfTU9ERV9VTlNQRUNJRklFRBAAEhoKFldBQ0NBX1BMQVlfTU9ERV9TSU5HTEUQARIaChZXQUNDQV9QTEFZX01PREVfVkVSU1VTEAISGQoVV0FDQ0FfUExBWV9NT0RFX0NPX09QEANiBnByb3RvMw", + ); + +/** + * @generated from message mythos.wacca.v0.WaccaClearStatus + */ +export type WaccaClearStatus = Message<"mythos.wacca.v0.WaccaClearStatus"> & { + /** + * @generated from field: bool is_clear = 1; + */ + isClear: boolean; + + /** + * @generated from field: bool is_missless = 2; + */ + isMissless: boolean; + + /** + * @generated from field: bool is_full_combo = 3; + */ + isFullCombo: boolean; + + /** + * @generated from field: bool is_all_marvelous = 4; + */ + isAllMarvelous: boolean; + + /** + * @generated from field: bool is_give_up = 5; + */ + isGiveUp: boolean; +}; + +/** + * Describes the message mythos.wacca.v0.WaccaClearStatus. + * Use `create(WaccaClearStatusSchema)` to create a new message. + */ +export const WaccaClearStatusSchema: GenMessage = + /*@__PURE__*/ + messageDesc(file_wacca_common, 0); + +/** + * @generated from message mythos.wacca.v0.WaccaJudge + */ +export type WaccaJudge = Message<"mythos.wacca.v0.WaccaJudge"> & { + /** + * @generated from field: int32 marvelous = 1; + */ + marvelous: number; + + /** + * @generated from field: int32 great = 2; + */ + great: number; + + /** + * @generated from field: int32 good = 3; + */ + good: number; + + /** + * @generated from field: int32 miss = 4; + */ + miss: number; +}; + +/** + * Describes the message mythos.wacca.v0.WaccaJudge. + * Use `create(WaccaJudgeSchema)` to create a new message. + */ +export const WaccaJudgeSchema: GenMessage = + /*@__PURE__*/ + messageDesc(file_wacca_common, 1); + +/** + * @generated from enum mythos.wacca.v0.WaccaVersion + */ +export enum WaccaVersion { + /** + * @generated from enum value: WACCA_VERSION_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + + /** + * @generated from enum value: WACCA_VERSION_WACCA = 100; + */ + WACCA = 100, + + /** + * @generated from enum value: WACCA_VERSION_S = 150; + */ + S = 150, + + /** + * @generated from enum value: WACCA_VERSION_LILY = 200; + */ + LILY = 200, + + /** + * @generated from enum value: WACCA_VERSION_LILY_R = 250; + */ + LILY_R = 250, + + /** + * @generated from enum value: WACCA_VERSION_REVERSE = 300; + */ + REVERSE = 300, + + /** + * @generated from enum value: WACCA_VERSION_PLUS = 400; + */ + PLUS = 400, +} + +/** + * Describes the enum mythos.wacca.v0.WaccaVersion. + */ +export const WaccaVersionSchema: GenEnum = + /*@__PURE__*/ + enumDesc(file_wacca_common, 0); + +/** + * @generated from enum mythos.wacca.v0.WaccaMusicDifficulty + */ +export enum WaccaMusicDifficulty { + /** + * @generated from enum value: WACCA_MUSIC_DIFFICULTY_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + + /** + * @generated from enum value: WACCA_MUSIC_DIFFICULTY_NORMAL = 1; + */ + NORMAL = 1, + + /** + * @generated from enum value: WACCA_MUSIC_DIFFICULTY_HARD = 2; + */ + HARD = 2, + + /** + * @generated from enum value: WACCA_MUSIC_DIFFICULTY_EXPERT = 3; + */ + EXPERT = 3, + + /** + * @generated from enum value: WACCA_MUSIC_DIFFICULTY_INFERNO = 4; + */ + INFERNO = 4, +} + +/** + * Describes the enum mythos.wacca.v0.WaccaMusicDifficulty. + */ +export const WaccaMusicDifficultySchema: GenEnum = + /*@__PURE__*/ + enumDesc(file_wacca_common, 1); + +/** + * @generated from enum mythos.wacca.v0.WaccaMusicScoreGrade + */ +export enum WaccaMusicScoreGrade { + /** + * @generated from enum value: WACCA_MUSIC_SCORE_GRADE_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + + /** + * @generated from enum value: WACCA_MUSIC_SCORE_GRADE_D = 1; + */ + D = 1, + + /** + * @generated from enum value: WACCA_MUSIC_SCORE_GRADE_C = 2; + */ + C = 2, + + /** + * @generated from enum value: WACCA_MUSIC_SCORE_GRADE_B = 3; + */ + B = 3, + + /** + * @generated from enum value: WACCA_MUSIC_SCORE_GRADE_A = 4; + */ + A = 4, + + /** + * @generated from enum value: WACCA_MUSIC_SCORE_GRADE_AA = 5; + */ + AA = 5, + + /** + * @generated from enum value: WACCA_MUSIC_SCORE_GRADE_AAA = 6; + */ + AAA = 6, + + /** + * @generated from enum value: WACCA_MUSIC_SCORE_GRADE_S = 7; + */ + S = 7, + + /** + * @generated from enum value: WACCA_MUSIC_SCORE_GRADE_S_PLUS = 8; + */ + S_PLUS = 8, + + /** + * @generated from enum value: WACCA_MUSIC_SCORE_GRADE_SS = 9; + */ + SS = 9, + + /** + * @generated from enum value: WACCA_MUSIC_SCORE_GRADE_SS_PLUS = 10; + */ + SS_PLUS = 10, + + /** + * @generated from enum value: WACCA_MUSIC_SCORE_GRADE_SSS = 11; + */ + SSS = 11, + + /** + * @generated from enum value: WACCA_MUSIC_SCORE_GRADE_SSS_PLUS = 12; + */ + SSS_PLUS = 12, + + /** + * @generated from enum value: WACCA_MUSIC_SCORE_GRADE_MASTER = 13; + */ + MASTER = 13, +} + +/** + * Describes the enum mythos.wacca.v0.WaccaMusicScoreGrade. + */ +export const WaccaMusicScoreGradeSchema: GenEnum = + /*@__PURE__*/ + enumDesc(file_wacca_common, 2); + +/** + * @generated from enum mythos.wacca.v0.WaccaPlayMode + */ +export enum WaccaPlayMode { + /** + * @generated from enum value: WACCA_PLAY_MODE_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + + /** + * @generated from enum value: WACCA_PLAY_MODE_SINGLE = 1; + */ + SINGLE = 1, + + /** + * @generated from enum value: WACCA_PLAY_MODE_VERSUS = 2; + */ + VERSUS = 2, + + /** + * @generated from enum value: WACCA_PLAY_MODE_CO_OP = 3; + */ + CO_OP = 3, +} + +/** + * Describes the enum mythos.wacca.v0.WaccaPlayMode. + */ +export const WaccaPlayModeSchema: GenEnum = + /*@__PURE__*/ + enumDesc(file_wacca_common, 3); diff --git a/typescript/server/src/proto/generated/wacca/user_connect.ts b/typescript/server/src/proto/generated/wacca/user_connect.ts new file mode 100644 index 000000000..e6f4f0e36 --- /dev/null +++ b/typescript/server/src/proto/generated/wacca/user_connect.ts @@ -0,0 +1,36 @@ +// @generated by protoc-gen-connect-es v1.7.0 with parameter "target=ts" +// @generated from file wacca/user.proto (package mythos.wacca.v0, syntax proto3) +/* eslint-disable */ +// @ts-nocheck + +import { DataRequest, DataResponse, PlaylogRequest, PlaylogStreamItem } from "./user_pb.js"; +import { MethodKind } from "@bufbuild/protobuf"; + +/** + * @generated from service mythos.wacca.v0.WaccaUser + */ +export const WaccaUser = { + typeName: "mythos.wacca.v0.WaccaUser", + methods: { + /** + * @generated from rpc mythos.wacca.v0.WaccaUser.GetData + */ + getData: { + name: "GetData", + I: DataRequest, + O: DataResponse, + kind: MethodKind.Unary, + }, + /** + * + * + * @generated from rpc mythos.wacca.v0.WaccaUser.GetPlaylog + */ + getPlaylog: { + name: "GetPlaylog", + I: PlaylogRequest, + O: PlaylogStreamItem, + kind: MethodKind.ServerStreaming, + }, + }, +} as const; diff --git a/typescript/server/src/proto/generated/wacca/user_grpc_pb.d.ts b/typescript/server/src/proto/generated/wacca/user_grpc_pb.d.ts deleted file mode 100644 index 3df6f94a6..000000000 --- a/typescript/server/src/proto/generated/wacca/user_grpc_pb.d.ts +++ /dev/null @@ -1,53 +0,0 @@ -// GENERATED CODE -- DO NOT EDIT! - -// package: mythos.wacca.v0 -// file: wacca/user.proto - -import * as wacca_user_pb from "../wacca/user_pb"; -import * as grpc from "@grpc/grpc-js"; - -interface IWaccaUserService extends grpc.ServiceDefinition { - getData: grpc.MethodDefinition; - getPlaylog: grpc.MethodDefinition< - wacca_user_pb.PlaylogRequest, - wacca_user_pb.PlaylogStreamItem - >; -} - -export const WaccaUserService: IWaccaUserService; - -export interface IWaccaUserServer extends grpc.UntypedServiceImplementation { - getData: grpc.handleUnaryCall; - getPlaylog: grpc.handleServerStreamingCall< - wacca_user_pb.PlaylogRequest, - wacca_user_pb.PlaylogStreamItem - >; -} - -export class WaccaUserClient extends grpc.Client { - constructor(address: string, credentials: grpc.ChannelCredentials, options?: object); - getData( - argument: wacca_user_pb.DataRequest, - callback: grpc.requestCallback, - ): grpc.ClientUnaryCall; - getData( - argument: wacca_user_pb.DataRequest, - metadataOrOptions: grpc.Metadata | grpc.CallOptions | null, - callback: grpc.requestCallback, - ): grpc.ClientUnaryCall; - getData( - argument: wacca_user_pb.DataRequest, - metadata: grpc.Metadata | null, - options: grpc.CallOptions | null, - callback: grpc.requestCallback, - ): grpc.ClientUnaryCall; - getPlaylog( - argument: wacca_user_pb.PlaylogRequest, - metadataOrOptions?: grpc.Metadata | grpc.CallOptions | null, - ): grpc.ClientReadableStream; - getPlaylog( - argument: wacca_user_pb.PlaylogRequest, - metadata?: grpc.Metadata | null, - options?: grpc.CallOptions | null, - ): grpc.ClientReadableStream; -} diff --git a/typescript/server/src/proto/generated/wacca/user_grpc_pb.js b/typescript/server/src/proto/generated/wacca/user_grpc_pb.js deleted file mode 100644 index 70cf09080..000000000 --- a/typescript/server/src/proto/generated/wacca/user_grpc_pb.js +++ /dev/null @@ -1,78 +0,0 @@ -// GENERATED CODE -- DO NOT EDIT! - -"use strict"; -var grpc = require("@grpc/grpc-js"); -var wacca_user_pb = require("../wacca/user_pb.js"); -var wacca_common_pb = require("../wacca/common_pb.js"); - -function serialize_mythos_wacca_v0_DataRequest(arg) { - if (!(arg instanceof wacca_user_pb.DataRequest)) { - throw new Error("Expected argument of type mythos.wacca.v0.DataRequest"); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_mythos_wacca_v0_DataRequest(buffer_arg) { - return wacca_user_pb.DataRequest.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_mythos_wacca_v0_DataResponse(arg) { - if (!(arg instanceof wacca_user_pb.DataResponse)) { - throw new Error("Expected argument of type mythos.wacca.v0.DataResponse"); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_mythos_wacca_v0_DataResponse(buffer_arg) { - return wacca_user_pb.DataResponse.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_mythos_wacca_v0_PlaylogRequest(arg) { - if (!(arg instanceof wacca_user_pb.PlaylogRequest)) { - throw new Error("Expected argument of type mythos.wacca.v0.PlaylogRequest"); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_mythos_wacca_v0_PlaylogRequest(buffer_arg) { - return wacca_user_pb.PlaylogRequest.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_mythos_wacca_v0_PlaylogStreamItem(arg) { - if (!(arg instanceof wacca_user_pb.PlaylogStreamItem)) { - throw new Error("Expected argument of type mythos.wacca.v0.PlaylogStreamItem"); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_mythos_wacca_v0_PlaylogStreamItem(buffer_arg) { - return wacca_user_pb.PlaylogStreamItem.deserializeBinary(new Uint8Array(buffer_arg)); -} - -var WaccaUserService = (exports.WaccaUserService = { - getData: { - path: "/mythos.wacca.v0.WaccaUser/GetData", - requestStream: false, - responseStream: false, - requestType: wacca_user_pb.DataRequest, - responseType: wacca_user_pb.DataResponse, - requestSerialize: serialize_mythos_wacca_v0_DataRequest, - requestDeserialize: deserialize_mythos_wacca_v0_DataRequest, - responseSerialize: serialize_mythos_wacca_v0_DataResponse, - responseDeserialize: deserialize_mythos_wacca_v0_DataResponse, - }, - getPlaylog: { - path: "/mythos.wacca.v0.WaccaUser/GetPlaylog", - requestStream: false, - responseStream: true, - requestType: wacca_user_pb.PlaylogRequest, - responseType: wacca_user_pb.PlaylogStreamItem, - requestSerialize: serialize_mythos_wacca_v0_PlaylogRequest, - requestDeserialize: deserialize_mythos_wacca_v0_PlaylogRequest, - responseSerialize: serialize_mythos_wacca_v0_PlaylogStreamItem, - responseDeserialize: deserialize_mythos_wacca_v0_PlaylogStreamItem, - }, - // -}); - -exports.WaccaUserClient = grpc.makeGenericClientConstructor(WaccaUserService, "WaccaUser"); diff --git a/typescript/server/src/proto/generated/wacca/user_pb.d.ts b/typescript/server/src/proto/generated/wacca/user_pb.d.ts deleted file mode 100644 index 1450c16b1..000000000 --- a/typescript/server/src/proto/generated/wacca/user_pb.d.ts +++ /dev/null @@ -1,306 +0,0 @@ -// package: mythos.wacca.v0 -// file: wacca/user.proto - -import * as jspb from "google-protobuf"; -import * as wacca_common_pb from "../wacca/common_pb"; - -export class DataRequest extends jspb.Message { - getApiId(): string; - setApiId(value: string): void; - - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): DataRequest.AsObject; - static toObject(includeInstance: boolean, msg: DataRequest): DataRequest.AsObject; - static extensions: { [key: number]: jspb.ExtensionFieldInfo }; - static extensionsBinary: { [key: number]: jspb.ExtensionFieldBinaryInfo }; - static serializeBinaryToWriter(message: DataRequest, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): DataRequest; - static deserializeBinaryFromReader( - message: DataRequest, - reader: jspb.BinaryReader, - ): DataRequest; -} - -export namespace DataRequest { - export type AsObject = { - apiId: string; - }; -} - -export class DataResponse extends jspb.Message { - getUserName(): string; - setUserName(value: string): void; - - getExp(): number; - setExp(value: number): void; - - getPoints(): number; - setPoints(value: number): void; - - clearFavoriteMusicEntriesList(): void; - getFavoriteMusicEntriesList(): Array; - setFavoriteMusicEntriesList(value: Array): void; - addFavoriteMusicEntries(value: number, index?: number): number; - - getVersionDataMap(): jspb.Map; - clearVersionDataMap(): void; - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): DataResponse.AsObject; - static toObject(includeInstance: boolean, msg: DataResponse): DataResponse.AsObject; - static extensions: { [key: number]: jspb.ExtensionFieldInfo }; - static extensionsBinary: { [key: number]: jspb.ExtensionFieldBinaryInfo }; - static serializeBinaryToWriter(message: DataResponse, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): DataResponse; - static deserializeBinaryFromReader( - message: DataResponse, - reader: jspb.BinaryReader, - ): DataResponse; -} - -export namespace DataResponse { - export type AsObject = { - userName: string; - exp: number; - points: number; - favoriteMusicEntriesList: Array; - versionDataMap: Array<[number, DataResponse.VersionData.AsObject]>; - }; - - export class VersionData extends jspb.Message { - getRank(): number; - setRank(value: number): void; - - getDanRank(): number; - setDanRank(value: number): void; - - getRating(): number; - setRating(value: number): void; - - clearTitlePartIdsList(): void; - getTitlePartIdsList(): Array; - setTitlePartIdsList(value: Array): void; - addTitlePartIds(value: number, index?: number): number; - - getLastAppVersion(): string; - setLastAppVersion(value: string): void; - - hasLastSong(): boolean; - clearLastSong(): void; - getLastSong(): DataResponse.VersionData.LastSong | undefined; - setLastSong(value?: DataResponse.VersionData.LastSong): void; - - getLastSongSort(): number; - setLastSongSort(value: number): void; - - getLastFolder(): number; - setLastFolder(value: number): void; - - getLastFolderSort(): number; - setLastFolderSort(value: number): void; - - getLastBrowsedSongList(): string; - setLastBrowsedSongList(value: string): void; - - getTutorialStateMap(): jspb.Map; - clearTutorialStateMap(): void; - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): VersionData.AsObject; - static toObject(includeInstance: boolean, msg: VersionData): VersionData.AsObject; - static extensions: { [key: number]: jspb.ExtensionFieldInfo }; - static extensionsBinary: { [key: number]: jspb.ExtensionFieldBinaryInfo }; - static serializeBinaryToWriter(message: VersionData, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): VersionData; - static deserializeBinaryFromReader( - message: VersionData, - reader: jspb.BinaryReader, - ): VersionData; - } - - export namespace VersionData { - export type AsObject = { - rank: number; - danRank: number; - rating: number; - titlePartIdsList: Array; - lastAppVersion: string; - lastSong?: DataResponse.VersionData.LastSong.AsObject; - lastSongSort: number; - lastFolder: number; - lastFolderSort: number; - lastBrowsedSongList: string; - tutorialStateMap: Array<[number, boolean]>; - }; - - export class LastSong extends jspb.Message { - getMusicId(): number; - setMusicId(value: number): void; - - getMusicDifficulty(): wacca_common_pb.WaccaMusicDifficultyMap[keyof wacca_common_pb.WaccaMusicDifficultyMap]; - setMusicDifficulty( - value: wacca_common_pb.WaccaMusicDifficultyMap[keyof wacca_common_pb.WaccaMusicDifficultyMap], - ): void; - - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): LastSong.AsObject; - static toObject(includeInstance: boolean, msg: LastSong): LastSong.AsObject; - static extensions: { [key: number]: jspb.ExtensionFieldInfo }; - static extensionsBinary: { [key: number]: jspb.ExtensionFieldBinaryInfo }; - static serializeBinaryToWriter(message: LastSong, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): LastSong; - static deserializeBinaryFromReader( - message: LastSong, - reader: jspb.BinaryReader, - ): LastSong; - } - - export namespace LastSong { - export type AsObject = { - musicId: number; - musicDifficulty: wacca_common_pb.WaccaMusicDifficultyMap[keyof wacca_common_pb.WaccaMusicDifficultyMap]; - }; - } - } -} - -export class PlaylogRequest extends jspb.Message { - getApiId(): string; - setApiId(value: string): void; - - hasMusicId(): boolean; - clearMusicId(): void; - getMusicId(): number; - setMusicId(value: number): void; - - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): PlaylogRequest.AsObject; - static toObject(includeInstance: boolean, msg: PlaylogRequest): PlaylogRequest.AsObject; - static extensions: { [key: number]: jspb.ExtensionFieldInfo }; - static extensionsBinary: { [key: number]: jspb.ExtensionFieldBinaryInfo }; - static serializeBinaryToWriter(message: PlaylogRequest, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): PlaylogRequest; - static deserializeBinaryFromReader( - message: PlaylogRequest, - reader: jspb.BinaryReader, - ): PlaylogRequest; -} - -export namespace PlaylogRequest { - export type AsObject = { - apiId: string; - musicId: number; - }; -} - -export class PlaylogStreamItem extends jspb.Message { - getPlaylogApiId(): string; - setPlaylogApiId(value: string): void; - - hasInfo(): boolean; - clearInfo(): void; - getInfo(): PlaylogInfo | undefined; - setInfo(value?: PlaylogInfo): void; - - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): PlaylogStreamItem.AsObject; - static toObject(includeInstance: boolean, msg: PlaylogStreamItem): PlaylogStreamItem.AsObject; - static extensions: { [key: number]: jspb.ExtensionFieldInfo }; - static extensionsBinary: { [key: number]: jspb.ExtensionFieldBinaryInfo }; - static serializeBinaryToWriter(message: PlaylogStreamItem, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): PlaylogStreamItem; - static deserializeBinaryFromReader( - message: PlaylogStreamItem, - reader: jspb.BinaryReader, - ): PlaylogStreamItem; -} - -export namespace PlaylogStreamItem { - export type AsObject = { - playlogApiId: string; - info?: PlaylogInfo.AsObject; - }; -} - -export class PlaylogInfo extends jspb.Message { - getMusicId(): number; - setMusicId(value: number): void; - - getMusicDifficulty(): wacca_common_pb.WaccaMusicDifficultyMap[keyof wacca_common_pb.WaccaMusicDifficultyMap]; - setMusicDifficulty( - value: wacca_common_pb.WaccaMusicDifficultyMap[keyof wacca_common_pb.WaccaMusicDifficultyMap], - ): void; - - getScore(): number; - setScore(value: number): void; - - getGrade(): wacca_common_pb.WaccaMusicScoreGradeMap[keyof wacca_common_pb.WaccaMusicScoreGradeMap]; - setGrade( - value: wacca_common_pb.WaccaMusicScoreGradeMap[keyof wacca_common_pb.WaccaMusicScoreGradeMap], - ): void; - - hasJudge(): boolean; - clearJudge(): void; - getJudge(): wacca_common_pb.WaccaJudge | undefined; - setJudge(value?: wacca_common_pb.WaccaJudge): void; - - hasClearStatus(): boolean; - clearClearStatus(): void; - getClearStatus(): wacca_common_pb.WaccaClearStatus | undefined; - setClearStatus(value?: wacca_common_pb.WaccaClearStatus): void; - - getIsNewRecord(): boolean; - setIsNewRecord(value: boolean): void; - - getCombo(): number; - setCombo(value: number): void; - - getSkillPoints(): number; - setSkillPoints(value: number): void; - - getFast(): number; - setFast(value: number): void; - - getLate(): number; - setLate(value: number): void; - - getUserPlayMode(): wacca_common_pb.WaccaPlayModeMap[keyof wacca_common_pb.WaccaPlayModeMap]; - setUserPlayMode( - value: wacca_common_pb.WaccaPlayModeMap[keyof wacca_common_pb.WaccaPlayModeMap], - ): void; - - getTrack(): number; - setTrack(value: number): void; - - getUserPlayDate(): string; - setUserPlayDate(value: string): void; - - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): PlaylogInfo.AsObject; - static toObject(includeInstance: boolean, msg: PlaylogInfo): PlaylogInfo.AsObject; - static extensions: { [key: number]: jspb.ExtensionFieldInfo }; - static extensionsBinary: { [key: number]: jspb.ExtensionFieldBinaryInfo }; - static serializeBinaryToWriter(message: PlaylogInfo, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): PlaylogInfo; - static deserializeBinaryFromReader( - message: PlaylogInfo, - reader: jspb.BinaryReader, - ): PlaylogInfo; -} - -export namespace PlaylogInfo { - export type AsObject = { - musicId: number; - musicDifficulty: wacca_common_pb.WaccaMusicDifficultyMap[keyof wacca_common_pb.WaccaMusicDifficultyMap]; - score: number; - grade: wacca_common_pb.WaccaMusicScoreGradeMap[keyof wacca_common_pb.WaccaMusicScoreGradeMap]; - judge?: wacca_common_pb.WaccaJudge.AsObject; - clearStatus?: wacca_common_pb.WaccaClearStatus.AsObject; - isNewRecord: boolean; - combo: number; - skillPoints: number; - fast: number; - late: number; - userPlayMode: wacca_common_pb.WaccaPlayModeMap[keyof wacca_common_pb.WaccaPlayModeMap]; - track: number; - userPlayDate: string; - }; -} diff --git a/typescript/server/src/proto/generated/wacca/user_pb.js b/typescript/server/src/proto/generated/wacca/user_pb.js deleted file mode 100644 index ce9bf0e58..000000000 --- a/typescript/server/src/proto/generated/wacca/user_pb.js +++ /dev/null @@ -1,2051 +0,0 @@ -// source: wacca/user.proto -/** - * @fileoverview - * @enhanceable - * @suppress {missingRequire} reports error on implicit type usages. - * @suppress {messageConventions} JS Compiler reports an error if a variable or - * field starts with 'MSG_' and isn't a translatable message. - * @public - */ -// GENERATED CODE -- DO NOT EDIT! -/* eslint-disable */ -// @ts-nocheck - -var jspb = require("google-protobuf"); -var goog = jspb; -var global = function () { - if (this) { - return this; - } - if (typeof window !== "undefined") { - return window; - } - if (typeof global !== "undefined") { - return global; - } - if (typeof self !== "undefined") { - return self; - } - return Function("return this")(); -}.call(null); - -var wacca_common_pb = require("../wacca/common_pb.js"); -goog.object.extend(proto, wacca_common_pb); -goog.exportSymbol("proto.mythos.wacca.v0.DataRequest", null, global); -goog.exportSymbol("proto.mythos.wacca.v0.DataResponse", null, global); -goog.exportSymbol("proto.mythos.wacca.v0.DataResponse.VersionData", null, global); -goog.exportSymbol("proto.mythos.wacca.v0.DataResponse.VersionData.LastSong", null, global); -goog.exportSymbol("proto.mythos.wacca.v0.PlaylogInfo", null, global); -goog.exportSymbol("proto.mythos.wacca.v0.PlaylogRequest", null, global); -goog.exportSymbol("proto.mythos.wacca.v0.PlaylogStreamItem", null, global); -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.mythos.wacca.v0.DataRequest = function (opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.mythos.wacca.v0.DataRequest, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.mythos.wacca.v0.DataRequest.displayName = "proto.mythos.wacca.v0.DataRequest"; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.mythos.wacca.v0.DataResponse = function (opt_data) { - jspb.Message.initialize( - this, - opt_data, - 0, - -1, - proto.mythos.wacca.v0.DataResponse.repeatedFields_, - null, - ); -}; -goog.inherits(proto.mythos.wacca.v0.DataResponse, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.mythos.wacca.v0.DataResponse.displayName = "proto.mythos.wacca.v0.DataResponse"; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.mythos.wacca.v0.DataResponse.VersionData = function (opt_data) { - jspb.Message.initialize( - this, - opt_data, - 0, - -1, - proto.mythos.wacca.v0.DataResponse.VersionData.repeatedFields_, - null, - ); -}; -goog.inherits(proto.mythos.wacca.v0.DataResponse.VersionData, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.mythos.wacca.v0.DataResponse.VersionData.displayName = - "proto.mythos.wacca.v0.DataResponse.VersionData"; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.mythos.wacca.v0.DataResponse.VersionData.LastSong = function (opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.mythos.wacca.v0.DataResponse.VersionData.LastSong, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.mythos.wacca.v0.DataResponse.VersionData.LastSong.displayName = - "proto.mythos.wacca.v0.DataResponse.VersionData.LastSong"; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.mythos.wacca.v0.PlaylogRequest = function (opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.mythos.wacca.v0.PlaylogRequest, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.mythos.wacca.v0.PlaylogRequest.displayName = "proto.mythos.wacca.v0.PlaylogRequest"; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.mythos.wacca.v0.PlaylogStreamItem = function (opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.mythos.wacca.v0.PlaylogStreamItem, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.mythos.wacca.v0.PlaylogStreamItem.displayName = "proto.mythos.wacca.v0.PlaylogStreamItem"; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.mythos.wacca.v0.PlaylogInfo = function (opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.mythos.wacca.v0.PlaylogInfo, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.mythos.wacca.v0.PlaylogInfo.displayName = "proto.mythos.wacca.v0.PlaylogInfo"; -} - -if (jspb.Message.GENERATE_TO_OBJECT) { - /** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ - proto.mythos.wacca.v0.DataRequest.prototype.toObject = function (opt_includeInstance) { - return proto.mythos.wacca.v0.DataRequest.toObject(opt_includeInstance, this); - }; - - /** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.mythos.wacca.v0.DataRequest} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ - proto.mythos.wacca.v0.DataRequest.toObject = function (includeInstance, msg) { - var f, - obj = { - apiId: jspb.Message.getFieldWithDefault(msg, 1, ""), - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; - }; -} - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.mythos.wacca.v0.DataRequest} - */ -proto.mythos.wacca.v0.DataRequest.deserializeBinary = function (bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.mythos.wacca.v0.DataRequest(); - return proto.mythos.wacca.v0.DataRequest.deserializeBinaryFromReader(msg, reader); -}; - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.mythos.wacca.v0.DataRequest} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.mythos.wacca.v0.DataRequest} - */ -proto.mythos.wacca.v0.DataRequest.deserializeBinaryFromReader = function (msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {string} */ (reader.readString()); - msg.setApiId(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.mythos.wacca.v0.DataRequest.prototype.serializeBinary = function () { - var writer = new jspb.BinaryWriter(); - proto.mythos.wacca.v0.DataRequest.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.mythos.wacca.v0.DataRequest} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.mythos.wacca.v0.DataRequest.serializeBinaryToWriter = function (message, writer) { - var f = undefined; - f = message.getApiId(); - if (f.length > 0) { - writer.writeString(1, f); - } -}; - -/** - * optional string api_id = 1; - * @return {string} - */ -proto.mythos.wacca.v0.DataRequest.prototype.getApiId = function () { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); -}; - -/** - * @param {string} value - * @return {!proto.mythos.wacca.v0.DataRequest} returns this - */ -proto.mythos.wacca.v0.DataRequest.prototype.setApiId = function (value) { - return jspb.Message.setProto3StringField(this, 1, value); -}; - -/** - * List of repeated fields within this message type. - * @private {!Array} - * @const - */ -proto.mythos.wacca.v0.DataResponse.repeatedFields_ = [4]; - -if (jspb.Message.GENERATE_TO_OBJECT) { - /** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ - proto.mythos.wacca.v0.DataResponse.prototype.toObject = function (opt_includeInstance) { - return proto.mythos.wacca.v0.DataResponse.toObject(opt_includeInstance, this); - }; - - /** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.mythos.wacca.v0.DataResponse} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ - proto.mythos.wacca.v0.DataResponse.toObject = function (includeInstance, msg) { - var f, - obj = { - userName: jspb.Message.getFieldWithDefault(msg, 1, ""), - exp: jspb.Message.getFieldWithDefault(msg, 2, 0), - points: jspb.Message.getFieldWithDefault(msg, 3, 0), - favoriteMusicEntriesList: - (f = jspb.Message.getRepeatedField(msg, 4)) == null ? undefined : f, - versionDataMap: (f = msg.getVersionDataMap()) - ? f.toObject( - includeInstance, - proto.mythos.wacca.v0.DataResponse.VersionData.toObject, - ) - : [], - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; - }; -} - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.mythos.wacca.v0.DataResponse} - */ -proto.mythos.wacca.v0.DataResponse.deserializeBinary = function (bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.mythos.wacca.v0.DataResponse(); - return proto.mythos.wacca.v0.DataResponse.deserializeBinaryFromReader(msg, reader); -}; - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.mythos.wacca.v0.DataResponse} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.mythos.wacca.v0.DataResponse} - */ -proto.mythos.wacca.v0.DataResponse.deserializeBinaryFromReader = function (msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {string} */ (reader.readString()); - msg.setUserName(value); - break; - case 2: - var value = /** @type {number} */ (reader.readInt32()); - msg.setExp(value); - break; - case 3: - var value = /** @type {number} */ (reader.readInt32()); - msg.setPoints(value); - break; - case 4: - var values = /** @type {!Array} */ ( - reader.isDelimited() ? reader.readPackedInt32() : [reader.readInt32()] - ); - for (var i = 0; i < values.length; i++) { - msg.addFavoriteMusicEntries(values[i]); - } - break; - case 5: - var value = msg.getVersionDataMap(); - reader.readMessage(value, function (message, reader) { - jspb.Map.deserializeBinary( - message, - reader, - jspb.BinaryReader.prototype.readInt32, - jspb.BinaryReader.prototype.readMessage, - proto.mythos.wacca.v0.DataResponse.VersionData.deserializeBinaryFromReader, - 0, - new proto.mythos.wacca.v0.DataResponse.VersionData(), - ); - }); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.mythos.wacca.v0.DataResponse.prototype.serializeBinary = function () { - var writer = new jspb.BinaryWriter(); - proto.mythos.wacca.v0.DataResponse.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.mythos.wacca.v0.DataResponse} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.mythos.wacca.v0.DataResponse.serializeBinaryToWriter = function (message, writer) { - var f = undefined; - f = message.getUserName(); - if (f.length > 0) { - writer.writeString(1, f); - } - f = message.getExp(); - if (f !== 0) { - writer.writeInt32(2, f); - } - f = message.getPoints(); - if (f !== 0) { - writer.writeInt32(3, f); - } - f = message.getFavoriteMusicEntriesList(); - if (f.length > 0) { - writer.writePackedInt32(4, f); - } - f = message.getVersionDataMap(true); - if (f && f.getLength() > 0) { - f.serializeBinary( - 5, - writer, - jspb.BinaryWriter.prototype.writeInt32, - jspb.BinaryWriter.prototype.writeMessage, - proto.mythos.wacca.v0.DataResponse.VersionData.serializeBinaryToWriter, - ); - } -}; - -/** - * List of repeated fields within this message type. - * @private {!Array} - * @const - */ -proto.mythos.wacca.v0.DataResponse.VersionData.repeatedFields_ = [4]; - -if (jspb.Message.GENERATE_TO_OBJECT) { - /** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ - proto.mythos.wacca.v0.DataResponse.VersionData.prototype.toObject = function ( - opt_includeInstance, - ) { - return proto.mythos.wacca.v0.DataResponse.VersionData.toObject(opt_includeInstance, this); - }; - - /** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.mythos.wacca.v0.DataResponse.VersionData} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ - proto.mythos.wacca.v0.DataResponse.VersionData.toObject = function (includeInstance, msg) { - var f, - obj = { - rank: jspb.Message.getFieldWithDefault(msg, 1, 0), - danRank: jspb.Message.getFieldWithDefault(msg, 2, 0), - rating: jspb.Message.getFieldWithDefault(msg, 3, 0), - titlePartIdsList: - (f = jspb.Message.getRepeatedField(msg, 4)) == null ? undefined : f, - lastAppVersion: jspb.Message.getFieldWithDefault(msg, 5, ""), - lastSong: - (f = msg.getLastSong()) && - proto.mythos.wacca.v0.DataResponse.VersionData.LastSong.toObject( - includeInstance, - f, - ), - lastSongSort: jspb.Message.getFieldWithDefault(msg, 7, 0), - lastFolder: jspb.Message.getFieldWithDefault(msg, 8, 0), - lastFolderSort: jspb.Message.getFieldWithDefault(msg, 9, 0), - lastBrowsedSongList: jspb.Message.getFieldWithDefault(msg, 10, ""), - tutorialStateMap: (f = msg.getTutorialStateMap()) - ? f.toObject(includeInstance, undefined) - : [], - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; - }; -} - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.mythos.wacca.v0.DataResponse.VersionData} - */ -proto.mythos.wacca.v0.DataResponse.VersionData.deserializeBinary = function (bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.mythos.wacca.v0.DataResponse.VersionData(); - return proto.mythos.wacca.v0.DataResponse.VersionData.deserializeBinaryFromReader(msg, reader); -}; - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.mythos.wacca.v0.DataResponse.VersionData} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.mythos.wacca.v0.DataResponse.VersionData} - */ -proto.mythos.wacca.v0.DataResponse.VersionData.deserializeBinaryFromReader = function ( - msg, - reader, -) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {number} */ (reader.readInt32()); - msg.setRank(value); - break; - case 2: - var value = /** @type {number} */ (reader.readInt32()); - msg.setDanRank(value); - break; - case 3: - var value = /** @type {number} */ (reader.readInt32()); - msg.setRating(value); - break; - case 4: - var values = /** @type {!Array} */ ( - reader.isDelimited() ? reader.readPackedInt32() : [reader.readInt32()] - ); - for (var i = 0; i < values.length; i++) { - msg.addTitlePartIds(values[i]); - } - break; - case 5: - var value = /** @type {string} */ (reader.readString()); - msg.setLastAppVersion(value); - break; - case 6: - var value = new proto.mythos.wacca.v0.DataResponse.VersionData.LastSong(); - reader.readMessage( - value, - proto.mythos.wacca.v0.DataResponse.VersionData.LastSong - .deserializeBinaryFromReader, - ); - msg.setLastSong(value); - break; - case 7: - var value = /** @type {number} */ (reader.readInt32()); - msg.setLastSongSort(value); - break; - case 8: - var value = /** @type {number} */ (reader.readInt32()); - msg.setLastFolder(value); - break; - case 9: - var value = /** @type {number} */ (reader.readInt32()); - msg.setLastFolderSort(value); - break; - case 10: - var value = /** @type {string} */ (reader.readString()); - msg.setLastBrowsedSongList(value); - break; - case 11: - var value = msg.getTutorialStateMap(); - reader.readMessage(value, function (message, reader) { - jspb.Map.deserializeBinary( - message, - reader, - jspb.BinaryReader.prototype.readInt32, - jspb.BinaryReader.prototype.readBool, - null, - 0, - false, - ); - }); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.mythos.wacca.v0.DataResponse.VersionData.prototype.serializeBinary = function () { - var writer = new jspb.BinaryWriter(); - proto.mythos.wacca.v0.DataResponse.VersionData.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.mythos.wacca.v0.DataResponse.VersionData} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.mythos.wacca.v0.DataResponse.VersionData.serializeBinaryToWriter = function ( - message, - writer, -) { - var f = undefined; - f = message.getRank(); - if (f !== 0) { - writer.writeInt32(1, f); - } - f = message.getDanRank(); - if (f !== 0) { - writer.writeInt32(2, f); - } - f = message.getRating(); - if (f !== 0) { - writer.writeInt32(3, f); - } - f = message.getTitlePartIdsList(); - if (f.length > 0) { - writer.writePackedInt32(4, f); - } - f = message.getLastAppVersion(); - if (f.length > 0) { - writer.writeString(5, f); - } - f = message.getLastSong(); - if (f != null) { - writer.writeMessage( - 6, - f, - proto.mythos.wacca.v0.DataResponse.VersionData.LastSong.serializeBinaryToWriter, - ); - } - f = message.getLastSongSort(); - if (f !== 0) { - writer.writeInt32(7, f); - } - f = message.getLastFolder(); - if (f !== 0) { - writer.writeInt32(8, f); - } - f = message.getLastFolderSort(); - if (f !== 0) { - writer.writeInt32(9, f); - } - f = message.getLastBrowsedSongList(); - if (f.length > 0) { - writer.writeString(10, f); - } - f = message.getTutorialStateMap(true); - if (f && f.getLength() > 0) { - f.serializeBinary( - 11, - writer, - jspb.BinaryWriter.prototype.writeInt32, - jspb.BinaryWriter.prototype.writeBool, - ); - } -}; - -if (jspb.Message.GENERATE_TO_OBJECT) { - /** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ - proto.mythos.wacca.v0.DataResponse.VersionData.LastSong.prototype.toObject = function ( - opt_includeInstance, - ) { - return proto.mythos.wacca.v0.DataResponse.VersionData.LastSong.toObject( - opt_includeInstance, - this, - ); - }; - - /** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.mythos.wacca.v0.DataResponse.VersionData.LastSong} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ - proto.mythos.wacca.v0.DataResponse.VersionData.LastSong.toObject = function ( - includeInstance, - msg, - ) { - var f, - obj = { - musicId: jspb.Message.getFieldWithDefault(msg, 1, 0), - musicDifficulty: jspb.Message.getFieldWithDefault(msg, 2, 0), - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; - }; -} - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.mythos.wacca.v0.DataResponse.VersionData.LastSong} - */ -proto.mythos.wacca.v0.DataResponse.VersionData.LastSong.deserializeBinary = function (bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.mythos.wacca.v0.DataResponse.VersionData.LastSong(); - return proto.mythos.wacca.v0.DataResponse.VersionData.LastSong.deserializeBinaryFromReader( - msg, - reader, - ); -}; - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.mythos.wacca.v0.DataResponse.VersionData.LastSong} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.mythos.wacca.v0.DataResponse.VersionData.LastSong} - */ -proto.mythos.wacca.v0.DataResponse.VersionData.LastSong.deserializeBinaryFromReader = function ( - msg, - reader, -) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {number} */ (reader.readInt32()); - msg.setMusicId(value); - break; - case 2: - var value = /** @type {!proto.mythos.wacca.v0.WaccaMusicDifficulty} */ ( - reader.readEnum() - ); - msg.setMusicDifficulty(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.mythos.wacca.v0.DataResponse.VersionData.LastSong.prototype.serializeBinary = function () { - var writer = new jspb.BinaryWriter(); - proto.mythos.wacca.v0.DataResponse.VersionData.LastSong.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.mythos.wacca.v0.DataResponse.VersionData.LastSong} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.mythos.wacca.v0.DataResponse.VersionData.LastSong.serializeBinaryToWriter = function ( - message, - writer, -) { - var f = undefined; - f = message.getMusicId(); - if (f !== 0) { - writer.writeInt32(1, f); - } - f = message.getMusicDifficulty(); - if (f !== 0.0) { - writer.writeEnum(2, f); - } -}; - -/** - * optional int32 music_id = 1; - * @return {number} - */ -proto.mythos.wacca.v0.DataResponse.VersionData.LastSong.prototype.getMusicId = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.wacca.v0.DataResponse.VersionData.LastSong} returns this - */ -proto.mythos.wacca.v0.DataResponse.VersionData.LastSong.prototype.setMusicId = function (value) { - return jspb.Message.setProto3IntField(this, 1, value); -}; - -/** - * optional WaccaMusicDifficulty music_difficulty = 2; - * @return {!proto.mythos.wacca.v0.WaccaMusicDifficulty} - */ -proto.mythos.wacca.v0.DataResponse.VersionData.LastSong.prototype.getMusicDifficulty = function () { - return /** @type {!proto.mythos.wacca.v0.WaccaMusicDifficulty} */ ( - jspb.Message.getFieldWithDefault(this, 2, 0) - ); -}; - -/** - * @param {!proto.mythos.wacca.v0.WaccaMusicDifficulty} value - * @return {!proto.mythos.wacca.v0.DataResponse.VersionData.LastSong} returns this - */ -proto.mythos.wacca.v0.DataResponse.VersionData.LastSong.prototype.setMusicDifficulty = function ( - value, -) { - return jspb.Message.setProto3EnumField(this, 2, value); -}; - -/** - * optional int32 rank = 1; - * @return {number} - */ -proto.mythos.wacca.v0.DataResponse.VersionData.prototype.getRank = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.wacca.v0.DataResponse.VersionData} returns this - */ -proto.mythos.wacca.v0.DataResponse.VersionData.prototype.setRank = function (value) { - return jspb.Message.setProto3IntField(this, 1, value); -}; - -/** - * optional int32 dan_rank = 2; - * @return {number} - */ -proto.mythos.wacca.v0.DataResponse.VersionData.prototype.getDanRank = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.wacca.v0.DataResponse.VersionData} returns this - */ -proto.mythos.wacca.v0.DataResponse.VersionData.prototype.setDanRank = function (value) { - return jspb.Message.setProto3IntField(this, 2, value); -}; - -/** - * optional int32 rating = 3; - * @return {number} - */ -proto.mythos.wacca.v0.DataResponse.VersionData.prototype.getRating = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.wacca.v0.DataResponse.VersionData} returns this - */ -proto.mythos.wacca.v0.DataResponse.VersionData.prototype.setRating = function (value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - -/** - * repeated int32 title_part_ids = 4; - * @return {!Array} - */ -proto.mythos.wacca.v0.DataResponse.VersionData.prototype.getTitlePartIdsList = function () { - return /** @type {!Array} */ (jspb.Message.getRepeatedField(this, 4)); -}; - -/** - * @param {!Array} value - * @return {!proto.mythos.wacca.v0.DataResponse.VersionData} returns this - */ -proto.mythos.wacca.v0.DataResponse.VersionData.prototype.setTitlePartIdsList = function (value) { - return jspb.Message.setField(this, 4, value || []); -}; - -/** - * @param {number} value - * @param {number=} opt_index - * @return {!proto.mythos.wacca.v0.DataResponse.VersionData} returns this - */ -proto.mythos.wacca.v0.DataResponse.VersionData.prototype.addTitlePartIds = function ( - value, - opt_index, -) { - return jspb.Message.addToRepeatedField(this, 4, value, opt_index); -}; - -/** - * Clears the list making it empty but non-null. - * @return {!proto.mythos.wacca.v0.DataResponse.VersionData} returns this - */ -proto.mythos.wacca.v0.DataResponse.VersionData.prototype.clearTitlePartIdsList = function () { - return this.setTitlePartIdsList([]); -}; - -/** - * optional string last_app_version = 5; - * @return {string} - */ -proto.mythos.wacca.v0.DataResponse.VersionData.prototype.getLastAppVersion = function () { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 5, "")); -}; - -/** - * @param {string} value - * @return {!proto.mythos.wacca.v0.DataResponse.VersionData} returns this - */ -proto.mythos.wacca.v0.DataResponse.VersionData.prototype.setLastAppVersion = function (value) { - return jspb.Message.setProto3StringField(this, 5, value); -}; - -/** - * optional LastSong last_song = 6; - * @return {?proto.mythos.wacca.v0.DataResponse.VersionData.LastSong} - */ -proto.mythos.wacca.v0.DataResponse.VersionData.prototype.getLastSong = function () { - return /** @type{?proto.mythos.wacca.v0.DataResponse.VersionData.LastSong} */ ( - jspb.Message.getWrapperField( - this, - proto.mythos.wacca.v0.DataResponse.VersionData.LastSong, - 6, - ) - ); -}; - -/** - * @param {?proto.mythos.wacca.v0.DataResponse.VersionData.LastSong|undefined} value - * @return {!proto.mythos.wacca.v0.DataResponse.VersionData} returns this - */ -proto.mythos.wacca.v0.DataResponse.VersionData.prototype.setLastSong = function (value) { - return jspb.Message.setWrapperField(this, 6, value); -}; - -/** - * Clears the message field making it undefined. - * @return {!proto.mythos.wacca.v0.DataResponse.VersionData} returns this - */ -proto.mythos.wacca.v0.DataResponse.VersionData.prototype.clearLastSong = function () { - return this.setLastSong(undefined); -}; - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.mythos.wacca.v0.DataResponse.VersionData.prototype.hasLastSong = function () { - return jspb.Message.getField(this, 6) != null; -}; - -/** - * optional int32 last_song_sort = 7; - * @return {number} - */ -proto.mythos.wacca.v0.DataResponse.VersionData.prototype.getLastSongSort = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 7, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.wacca.v0.DataResponse.VersionData} returns this - */ -proto.mythos.wacca.v0.DataResponse.VersionData.prototype.setLastSongSort = function (value) { - return jspb.Message.setProto3IntField(this, 7, value); -}; - -/** - * optional int32 last_folder = 8; - * @return {number} - */ -proto.mythos.wacca.v0.DataResponse.VersionData.prototype.getLastFolder = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 8, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.wacca.v0.DataResponse.VersionData} returns this - */ -proto.mythos.wacca.v0.DataResponse.VersionData.prototype.setLastFolder = function (value) { - return jspb.Message.setProto3IntField(this, 8, value); -}; - -/** - * optional int32 last_folder_sort = 9; - * @return {number} - */ -proto.mythos.wacca.v0.DataResponse.VersionData.prototype.getLastFolderSort = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 9, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.wacca.v0.DataResponse.VersionData} returns this - */ -proto.mythos.wacca.v0.DataResponse.VersionData.prototype.setLastFolderSort = function (value) { - return jspb.Message.setProto3IntField(this, 9, value); -}; - -/** - * optional string last_browsed_song_list = 10; - * @return {string} - */ -proto.mythos.wacca.v0.DataResponse.VersionData.prototype.getLastBrowsedSongList = function () { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 10, "")); -}; - -/** - * @param {string} value - * @return {!proto.mythos.wacca.v0.DataResponse.VersionData} returns this - */ -proto.mythos.wacca.v0.DataResponse.VersionData.prototype.setLastBrowsedSongList = function (value) { - return jspb.Message.setProto3StringField(this, 10, value); -}; - -/** - * map tutorial_state = 11; - * @param {boolean=} opt_noLazyCreate Do not create the map if - * empty, instead returning `undefined` - * @return {!jspb.Map} - */ -proto.mythos.wacca.v0.DataResponse.VersionData.prototype.getTutorialStateMap = function ( - opt_noLazyCreate, -) { - return /** @type {!jspb.Map} */ ( - jspb.Message.getMapField(this, 11, opt_noLazyCreate, null) - ); -}; - -/** - * Clears values from the map. The map will be non-null. - * @return {!proto.mythos.wacca.v0.DataResponse.VersionData} returns this - */ -proto.mythos.wacca.v0.DataResponse.VersionData.prototype.clearTutorialStateMap = function () { - this.getTutorialStateMap().clear(); - return this; -}; - -/** - * optional string user_name = 1; - * @return {string} - */ -proto.mythos.wacca.v0.DataResponse.prototype.getUserName = function () { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); -}; - -/** - * @param {string} value - * @return {!proto.mythos.wacca.v0.DataResponse} returns this - */ -proto.mythos.wacca.v0.DataResponse.prototype.setUserName = function (value) { - return jspb.Message.setProto3StringField(this, 1, value); -}; - -/** - * optional int32 exp = 2; - * @return {number} - */ -proto.mythos.wacca.v0.DataResponse.prototype.getExp = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.wacca.v0.DataResponse} returns this - */ -proto.mythos.wacca.v0.DataResponse.prototype.setExp = function (value) { - return jspb.Message.setProto3IntField(this, 2, value); -}; - -/** - * optional int32 points = 3; - * @return {number} - */ -proto.mythos.wacca.v0.DataResponse.prototype.getPoints = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.wacca.v0.DataResponse} returns this - */ -proto.mythos.wacca.v0.DataResponse.prototype.setPoints = function (value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - -/** - * repeated int32 favorite_music_entries = 4; - * @return {!Array} - */ -proto.mythos.wacca.v0.DataResponse.prototype.getFavoriteMusicEntriesList = function () { - return /** @type {!Array} */ (jspb.Message.getRepeatedField(this, 4)); -}; - -/** - * @param {!Array} value - * @return {!proto.mythos.wacca.v0.DataResponse} returns this - */ -proto.mythos.wacca.v0.DataResponse.prototype.setFavoriteMusicEntriesList = function (value) { - return jspb.Message.setField(this, 4, value || []); -}; - -/** - * @param {number} value - * @param {number=} opt_index - * @return {!proto.mythos.wacca.v0.DataResponse} returns this - */ -proto.mythos.wacca.v0.DataResponse.prototype.addFavoriteMusicEntries = function (value, opt_index) { - return jspb.Message.addToRepeatedField(this, 4, value, opt_index); -}; - -/** - * Clears the list making it empty but non-null. - * @return {!proto.mythos.wacca.v0.DataResponse} returns this - */ -proto.mythos.wacca.v0.DataResponse.prototype.clearFavoriteMusicEntriesList = function () { - return this.setFavoriteMusicEntriesList([]); -}; - -/** - * map version_data = 5; - * @param {boolean=} opt_noLazyCreate Do not create the map if - * empty, instead returning `undefined` - * @return {!jspb.Map} - */ -proto.mythos.wacca.v0.DataResponse.prototype.getVersionDataMap = function (opt_noLazyCreate) { - return /** @type {!jspb.Map} */ ( - jspb.Message.getMapField( - this, - 5, - opt_noLazyCreate, - proto.mythos.wacca.v0.DataResponse.VersionData, - ) - ); -}; - -/** - * Clears values from the map. The map will be non-null. - * @return {!proto.mythos.wacca.v0.DataResponse} returns this - */ -proto.mythos.wacca.v0.DataResponse.prototype.clearVersionDataMap = function () { - this.getVersionDataMap().clear(); - return this; -}; - -if (jspb.Message.GENERATE_TO_OBJECT) { - /** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ - proto.mythos.wacca.v0.PlaylogRequest.prototype.toObject = function (opt_includeInstance) { - return proto.mythos.wacca.v0.PlaylogRequest.toObject(opt_includeInstance, this); - }; - - /** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.mythos.wacca.v0.PlaylogRequest} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ - proto.mythos.wacca.v0.PlaylogRequest.toObject = function (includeInstance, msg) { - var f, - obj = { - apiId: jspb.Message.getFieldWithDefault(msg, 1, ""), - musicId: jspb.Message.getFieldWithDefault(msg, 2, 0), - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; - }; -} - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.mythos.wacca.v0.PlaylogRequest} - */ -proto.mythos.wacca.v0.PlaylogRequest.deserializeBinary = function (bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.mythos.wacca.v0.PlaylogRequest(); - return proto.mythos.wacca.v0.PlaylogRequest.deserializeBinaryFromReader(msg, reader); -}; - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.mythos.wacca.v0.PlaylogRequest} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.mythos.wacca.v0.PlaylogRequest} - */ -proto.mythos.wacca.v0.PlaylogRequest.deserializeBinaryFromReader = function (msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {string} */ (reader.readString()); - msg.setApiId(value); - break; - case 2: - var value = /** @type {number} */ (reader.readInt32()); - msg.setMusicId(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.mythos.wacca.v0.PlaylogRequest.prototype.serializeBinary = function () { - var writer = new jspb.BinaryWriter(); - proto.mythos.wacca.v0.PlaylogRequest.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.mythos.wacca.v0.PlaylogRequest} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.mythos.wacca.v0.PlaylogRequest.serializeBinaryToWriter = function (message, writer) { - var f = undefined; - f = message.getApiId(); - if (f.length > 0) { - writer.writeString(1, f); - } - f = /** @type {number} */ (jspb.Message.getField(message, 2)); - if (f != null) { - writer.writeInt32(2, f); - } -}; - -/** - * optional string api_id = 1; - * @return {string} - */ -proto.mythos.wacca.v0.PlaylogRequest.prototype.getApiId = function () { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); -}; - -/** - * @param {string} value - * @return {!proto.mythos.wacca.v0.PlaylogRequest} returns this - */ -proto.mythos.wacca.v0.PlaylogRequest.prototype.setApiId = function (value) { - return jspb.Message.setProto3StringField(this, 1, value); -}; - -/** - * optional int32 music_id = 2; - * @return {number} - */ -proto.mythos.wacca.v0.PlaylogRequest.prototype.getMusicId = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.wacca.v0.PlaylogRequest} returns this - */ -proto.mythos.wacca.v0.PlaylogRequest.prototype.setMusicId = function (value) { - return jspb.Message.setField(this, 2, value); -}; - -/** - * Clears the field making it undefined. - * @return {!proto.mythos.wacca.v0.PlaylogRequest} returns this - */ -proto.mythos.wacca.v0.PlaylogRequest.prototype.clearMusicId = function () { - return jspb.Message.setField(this, 2, undefined); -}; - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.mythos.wacca.v0.PlaylogRequest.prototype.hasMusicId = function () { - return jspb.Message.getField(this, 2) != null; -}; - -if (jspb.Message.GENERATE_TO_OBJECT) { - /** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ - proto.mythos.wacca.v0.PlaylogStreamItem.prototype.toObject = function (opt_includeInstance) { - return proto.mythos.wacca.v0.PlaylogStreamItem.toObject(opt_includeInstance, this); - }; - - /** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.mythos.wacca.v0.PlaylogStreamItem} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ - proto.mythos.wacca.v0.PlaylogStreamItem.toObject = function (includeInstance, msg) { - var f, - obj = { - playlogApiId: jspb.Message.getFieldWithDefault(msg, 1, ""), - info: - (f = msg.getInfo()) && - proto.mythos.wacca.v0.PlaylogInfo.toObject(includeInstance, f), - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; - }; -} - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.mythos.wacca.v0.PlaylogStreamItem} - */ -proto.mythos.wacca.v0.PlaylogStreamItem.deserializeBinary = function (bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.mythos.wacca.v0.PlaylogStreamItem(); - return proto.mythos.wacca.v0.PlaylogStreamItem.deserializeBinaryFromReader(msg, reader); -}; - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.mythos.wacca.v0.PlaylogStreamItem} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.mythos.wacca.v0.PlaylogStreamItem} - */ -proto.mythos.wacca.v0.PlaylogStreamItem.deserializeBinaryFromReader = function (msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {string} */ (reader.readString()); - msg.setPlaylogApiId(value); - break; - case 2: - var value = new proto.mythos.wacca.v0.PlaylogInfo(); - reader.readMessage( - value, - proto.mythos.wacca.v0.PlaylogInfo.deserializeBinaryFromReader, - ); - msg.setInfo(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.mythos.wacca.v0.PlaylogStreamItem.prototype.serializeBinary = function () { - var writer = new jspb.BinaryWriter(); - proto.mythos.wacca.v0.PlaylogStreamItem.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.mythos.wacca.v0.PlaylogStreamItem} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.mythos.wacca.v0.PlaylogStreamItem.serializeBinaryToWriter = function (message, writer) { - var f = undefined; - f = message.getPlaylogApiId(); - if (f.length > 0) { - writer.writeString(1, f); - } - f = message.getInfo(); - if (f != null) { - writer.writeMessage(2, f, proto.mythos.wacca.v0.PlaylogInfo.serializeBinaryToWriter); - } -}; - -/** - * optional string playlog_api_id = 1; - * @return {string} - */ -proto.mythos.wacca.v0.PlaylogStreamItem.prototype.getPlaylogApiId = function () { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); -}; - -/** - * @param {string} value - * @return {!proto.mythos.wacca.v0.PlaylogStreamItem} returns this - */ -proto.mythos.wacca.v0.PlaylogStreamItem.prototype.setPlaylogApiId = function (value) { - return jspb.Message.setProto3StringField(this, 1, value); -}; - -/** - * optional PlaylogInfo info = 2; - * @return {?proto.mythos.wacca.v0.PlaylogInfo} - */ -proto.mythos.wacca.v0.PlaylogStreamItem.prototype.getInfo = function () { - return /** @type{?proto.mythos.wacca.v0.PlaylogInfo} */ ( - jspb.Message.getWrapperField(this, proto.mythos.wacca.v0.PlaylogInfo, 2) - ); -}; - -/** - * @param {?proto.mythos.wacca.v0.PlaylogInfo|undefined} value - * @return {!proto.mythos.wacca.v0.PlaylogStreamItem} returns this - */ -proto.mythos.wacca.v0.PlaylogStreamItem.prototype.setInfo = function (value) { - return jspb.Message.setWrapperField(this, 2, value); -}; - -/** - * Clears the message field making it undefined. - * @return {!proto.mythos.wacca.v0.PlaylogStreamItem} returns this - */ -proto.mythos.wacca.v0.PlaylogStreamItem.prototype.clearInfo = function () { - return this.setInfo(undefined); -}; - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.mythos.wacca.v0.PlaylogStreamItem.prototype.hasInfo = function () { - return jspb.Message.getField(this, 2) != null; -}; - -if (jspb.Message.GENERATE_TO_OBJECT) { - /** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ - proto.mythos.wacca.v0.PlaylogInfo.prototype.toObject = function (opt_includeInstance) { - return proto.mythos.wacca.v0.PlaylogInfo.toObject(opt_includeInstance, this); - }; - - /** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.mythos.wacca.v0.PlaylogInfo} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ - proto.mythos.wacca.v0.PlaylogInfo.toObject = function (includeInstance, msg) { - var f, - obj = { - musicId: jspb.Message.getFieldWithDefault(msg, 1, 0), - musicDifficulty: jspb.Message.getFieldWithDefault(msg, 2, 0), - score: jspb.Message.getFieldWithDefault(msg, 3, 0), - grade: jspb.Message.getFieldWithDefault(msg, 4, 0), - judge: - (f = msg.getJudge()) && wacca_common_pb.WaccaJudge.toObject(includeInstance, f), - clearStatus: - (f = msg.getClearStatus()) && - wacca_common_pb.WaccaClearStatus.toObject(includeInstance, f), - isNewRecord: jspb.Message.getBooleanFieldWithDefault(msg, 7, false), - combo: jspb.Message.getFieldWithDefault(msg, 8, 0), - skillPoints: jspb.Message.getFieldWithDefault(msg, 9, 0), - fast: jspb.Message.getFieldWithDefault(msg, 10, 0), - late: jspb.Message.getFieldWithDefault(msg, 11, 0), - userPlayMode: jspb.Message.getFieldWithDefault(msg, 12, 0), - track: jspb.Message.getFieldWithDefault(msg, 13, 0), - userPlayDate: jspb.Message.getFieldWithDefault(msg, 14, ""), - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; - }; -} - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.mythos.wacca.v0.PlaylogInfo} - */ -proto.mythos.wacca.v0.PlaylogInfo.deserializeBinary = function (bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.mythos.wacca.v0.PlaylogInfo(); - return proto.mythos.wacca.v0.PlaylogInfo.deserializeBinaryFromReader(msg, reader); -}; - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.mythos.wacca.v0.PlaylogInfo} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.mythos.wacca.v0.PlaylogInfo} - */ -proto.mythos.wacca.v0.PlaylogInfo.deserializeBinaryFromReader = function (msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {number} */ (reader.readInt32()); - msg.setMusicId(value); - break; - case 2: - var value = /** @type {!proto.mythos.wacca.v0.WaccaMusicDifficulty} */ ( - reader.readEnum() - ); - msg.setMusicDifficulty(value); - break; - case 3: - var value = /** @type {number} */ (reader.readInt32()); - msg.setScore(value); - break; - case 4: - var value = /** @type {!proto.mythos.wacca.v0.WaccaMusicScoreGrade} */ ( - reader.readEnum() - ); - msg.setGrade(value); - break; - case 5: - var value = new wacca_common_pb.WaccaJudge(); - reader.readMessage(value, wacca_common_pb.WaccaJudge.deserializeBinaryFromReader); - msg.setJudge(value); - break; - case 6: - var value = new wacca_common_pb.WaccaClearStatus(); - reader.readMessage( - value, - wacca_common_pb.WaccaClearStatus.deserializeBinaryFromReader, - ); - msg.setClearStatus(value); - break; - case 7: - var value = /** @type {boolean} */ (reader.readBool()); - msg.setIsNewRecord(value); - break; - case 8: - var value = /** @type {number} */ (reader.readInt32()); - msg.setCombo(value); - break; - case 9: - var value = /** @type {number} */ (reader.readInt32()); - msg.setSkillPoints(value); - break; - case 10: - var value = /** @type {number} */ (reader.readInt32()); - msg.setFast(value); - break; - case 11: - var value = /** @type {number} */ (reader.readInt32()); - msg.setLate(value); - break; - case 12: - var value = /** @type {!proto.mythos.wacca.v0.WaccaPlayMode} */ (reader.readEnum()); - msg.setUserPlayMode(value); - break; - case 13: - var value = /** @type {number} */ (reader.readInt32()); - msg.setTrack(value); - break; - case 14: - var value = /** @type {string} */ (reader.readString()); - msg.setUserPlayDate(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.serializeBinary = function () { - var writer = new jspb.BinaryWriter(); - proto.mythos.wacca.v0.PlaylogInfo.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.mythos.wacca.v0.PlaylogInfo} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.mythos.wacca.v0.PlaylogInfo.serializeBinaryToWriter = function (message, writer) { - var f = undefined; - f = message.getMusicId(); - if (f !== 0) { - writer.writeInt32(1, f); - } - f = message.getMusicDifficulty(); - if (f !== 0.0) { - writer.writeEnum(2, f); - } - f = message.getScore(); - if (f !== 0) { - writer.writeInt32(3, f); - } - f = message.getGrade(); - if (f !== 0.0) { - writer.writeEnum(4, f); - } - f = message.getJudge(); - if (f != null) { - writer.writeMessage(5, f, wacca_common_pb.WaccaJudge.serializeBinaryToWriter); - } - f = message.getClearStatus(); - if (f != null) { - writer.writeMessage(6, f, wacca_common_pb.WaccaClearStatus.serializeBinaryToWriter); - } - f = message.getIsNewRecord(); - if (f) { - writer.writeBool(7, f); - } - f = message.getCombo(); - if (f !== 0) { - writer.writeInt32(8, f); - } - f = message.getSkillPoints(); - if (f !== 0) { - writer.writeInt32(9, f); - } - f = message.getFast(); - if (f !== 0) { - writer.writeInt32(10, f); - } - f = message.getLate(); - if (f !== 0) { - writer.writeInt32(11, f); - } - f = message.getUserPlayMode(); - if (f !== 0.0) { - writer.writeEnum(12, f); - } - f = message.getTrack(); - if (f !== 0) { - writer.writeInt32(13, f); - } - f = message.getUserPlayDate(); - if (f.length > 0) { - writer.writeString(14, f); - } -}; - -/** - * optional int32 music_id = 1; - * @return {number} - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.getMusicId = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.wacca.v0.PlaylogInfo} returns this - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.setMusicId = function (value) { - return jspb.Message.setProto3IntField(this, 1, value); -}; - -/** - * optional WaccaMusicDifficulty music_difficulty = 2; - * @return {!proto.mythos.wacca.v0.WaccaMusicDifficulty} - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.getMusicDifficulty = function () { - return /** @type {!proto.mythos.wacca.v0.WaccaMusicDifficulty} */ ( - jspb.Message.getFieldWithDefault(this, 2, 0) - ); -}; - -/** - * @param {!proto.mythos.wacca.v0.WaccaMusicDifficulty} value - * @return {!proto.mythos.wacca.v0.PlaylogInfo} returns this - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.setMusicDifficulty = function (value) { - return jspb.Message.setProto3EnumField(this, 2, value); -}; - -/** - * optional int32 score = 3; - * @return {number} - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.getScore = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.wacca.v0.PlaylogInfo} returns this - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.setScore = function (value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - -/** - * optional WaccaMusicScoreGrade grade = 4; - * @return {!proto.mythos.wacca.v0.WaccaMusicScoreGrade} - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.getGrade = function () { - return /** @type {!proto.mythos.wacca.v0.WaccaMusicScoreGrade} */ ( - jspb.Message.getFieldWithDefault(this, 4, 0) - ); -}; - -/** - * @param {!proto.mythos.wacca.v0.WaccaMusicScoreGrade} value - * @return {!proto.mythos.wacca.v0.PlaylogInfo} returns this - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.setGrade = function (value) { - return jspb.Message.setProto3EnumField(this, 4, value); -}; - -/** - * optional WaccaJudge judge = 5; - * @return {?proto.mythos.wacca.v0.WaccaJudge} - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.getJudge = function () { - return /** @type{?proto.mythos.wacca.v0.WaccaJudge} */ ( - jspb.Message.getWrapperField(this, wacca_common_pb.WaccaJudge, 5) - ); -}; - -/** - * @param {?proto.mythos.wacca.v0.WaccaJudge|undefined} value - * @return {!proto.mythos.wacca.v0.PlaylogInfo} returns this - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.setJudge = function (value) { - return jspb.Message.setWrapperField(this, 5, value); -}; - -/** - * Clears the message field making it undefined. - * @return {!proto.mythos.wacca.v0.PlaylogInfo} returns this - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.clearJudge = function () { - return this.setJudge(undefined); -}; - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.hasJudge = function () { - return jspb.Message.getField(this, 5) != null; -}; - -/** - * optional WaccaClearStatus clear_status = 6; - * @return {?proto.mythos.wacca.v0.WaccaClearStatus} - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.getClearStatus = function () { - return /** @type{?proto.mythos.wacca.v0.WaccaClearStatus} */ ( - jspb.Message.getWrapperField(this, wacca_common_pb.WaccaClearStatus, 6) - ); -}; - -/** - * @param {?proto.mythos.wacca.v0.WaccaClearStatus|undefined} value - * @return {!proto.mythos.wacca.v0.PlaylogInfo} returns this - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.setClearStatus = function (value) { - return jspb.Message.setWrapperField(this, 6, value); -}; - -/** - * Clears the message field making it undefined. - * @return {!proto.mythos.wacca.v0.PlaylogInfo} returns this - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.clearClearStatus = function () { - return this.setClearStatus(undefined); -}; - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.hasClearStatus = function () { - return jspb.Message.getField(this, 6) != null; -}; - -/** - * optional bool is_new_record = 7; - * @return {boolean} - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.getIsNewRecord = function () { - return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 7, false)); -}; - -/** - * @param {boolean} value - * @return {!proto.mythos.wacca.v0.PlaylogInfo} returns this - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.setIsNewRecord = function (value) { - return jspb.Message.setProto3BooleanField(this, 7, value); -}; - -/** - * optional int32 combo = 8; - * @return {number} - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.getCombo = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 8, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.wacca.v0.PlaylogInfo} returns this - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.setCombo = function (value) { - return jspb.Message.setProto3IntField(this, 8, value); -}; - -/** - * optional int32 skill_points = 9; - * @return {number} - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.getSkillPoints = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 9, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.wacca.v0.PlaylogInfo} returns this - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.setSkillPoints = function (value) { - return jspb.Message.setProto3IntField(this, 9, value); -}; - -/** - * optional int32 fast = 10; - * @return {number} - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.getFast = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 10, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.wacca.v0.PlaylogInfo} returns this - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.setFast = function (value) { - return jspb.Message.setProto3IntField(this, 10, value); -}; - -/** - * optional int32 late = 11; - * @return {number} - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.getLate = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 11, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.wacca.v0.PlaylogInfo} returns this - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.setLate = function (value) { - return jspb.Message.setProto3IntField(this, 11, value); -}; - -/** - * optional WaccaPlayMode user_play_mode = 12; - * @return {!proto.mythos.wacca.v0.WaccaPlayMode} - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.getUserPlayMode = function () { - return /** @type {!proto.mythos.wacca.v0.WaccaPlayMode} */ ( - jspb.Message.getFieldWithDefault(this, 12, 0) - ); -}; - -/** - * @param {!proto.mythos.wacca.v0.WaccaPlayMode} value - * @return {!proto.mythos.wacca.v0.PlaylogInfo} returns this - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.setUserPlayMode = function (value) { - return jspb.Message.setProto3EnumField(this, 12, value); -}; - -/** - * optional int32 track = 13; - * @return {number} - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.getTrack = function () { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 13, 0)); -}; - -/** - * @param {number} value - * @return {!proto.mythos.wacca.v0.PlaylogInfo} returns this - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.setTrack = function (value) { - return jspb.Message.setProto3IntField(this, 13, value); -}; - -/** - * optional string user_play_date = 14; - * @return {string} - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.getUserPlayDate = function () { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 14, "")); -}; - -/** - * @param {string} value - * @return {!proto.mythos.wacca.v0.PlaylogInfo} returns this - */ -proto.mythos.wacca.v0.PlaylogInfo.prototype.setUserPlayDate = function (value) { - return jspb.Message.setProto3StringField(this, 14, value); -}; - -goog.object.extend(exports, proto.mythos.wacca.v0); diff --git a/typescript/server/src/proto/generated/wacca/user_pb.ts b/typescript/server/src/proto/generated/wacca/user_pb.ts new file mode 100644 index 000000000..4d7208231 --- /dev/null +++ b/typescript/server/src/proto/generated/wacca/user_pb.ts @@ -0,0 +1,326 @@ +// @generated by protoc-gen-es v2.11.0 with parameter "target=ts" +// @generated from file wacca/user.proto (package mythos.wacca.v0, syntax proto3) +/* eslint-disable */ + +import type { GenFile, GenMessage, GenService } from "@bufbuild/protobuf/codegenv2"; +import { fileDesc, messageDesc, serviceDesc } from "@bufbuild/protobuf/codegenv2"; +import type { + WaccaClearStatus, + WaccaJudge, + WaccaMusicDifficulty, + WaccaMusicScoreGrade, + WaccaPlayMode, +} from "./common_pb"; +import { file_wacca_common } from "./common_pb"; +import type { Message } from "@bufbuild/protobuf"; + +/** + * Describes the file wacca/user.proto. + */ +export const file_wacca_user: GenFile = + /*@__PURE__*/ + fileDesc( + "ChB3YWNjYS91c2VyLnByb3RvEg9teXRob3Mud2FjY2EudjAiHQoLRGF0YVJlcXVlc3QSDgoGYXBpX2lkGAEgASgJIo4GCgxEYXRhUmVzcG9uc2USEQoJdXNlcl9uYW1lGAEgASgJEgsKA2V4cBgCIAEoBRIOCgZwb2ludHMYAyABKAUSHgoWZmF2b3JpdGVfbXVzaWNfZW50cmllcxgEIAMoBRJECgx2ZXJzaW9uX2RhdGEYBSADKAsyLi5teXRob3Mud2FjY2EudjAuRGF0YVJlc3BvbnNlLlZlcnNpb25EYXRhRW50cnkaiAQKC1ZlcnNpb25EYXRhEgwKBHJhbmsYASABKAUSEAoIZGFuX3JhbmsYAiABKAUSDgoGcmF0aW5nGAMgASgFEhYKDnRpdGxlX3BhcnRfaWRzGAQgAygFEhgKEGxhc3RfYXBwX3ZlcnNpb24YBSABKAkSRQoJbGFzdF9zb25nGAYgASgLMjIubXl0aG9zLndhY2NhLnYwLkRhdGFSZXNwb25zZS5WZXJzaW9uRGF0YS5MYXN0U29uZxIWCg5sYXN0X3Nvbmdfc29ydBgHIAEoBRITCgtsYXN0X2ZvbGRlchgIIAEoBRIYChBsYXN0X2ZvbGRlcl9zb3J0GAkgASgFEh4KFmxhc3RfYnJvd3NlZF9zb25nX2xpc3QYCiABKAkSVAoOdHV0b3JpYWxfc3RhdGUYCyADKAsyPC5teXRob3Mud2FjY2EudjAuRGF0YVJlc3BvbnNlLlZlcnNpb25EYXRhLlR1dG9yaWFsU3RhdGVFbnRyeRpdCghMYXN0U29uZxIQCghtdXNpY19pZBgBIAEoBRI/ChBtdXNpY19kaWZmaWN1bHR5GAIgASgOMiUubXl0aG9zLndhY2NhLnYwLldhY2NhTXVzaWNEaWZmaWN1bHR5GjQKElR1dG9yaWFsU3RhdGVFbnRyeRILCgNrZXkYASABKAUSDQoFdmFsdWUYAiABKAg6AjgBGl0KEFZlcnNpb25EYXRhRW50cnkSCwoDa2V5GAEgASgFEjgKBXZhbHVlGAIgASgLMikubXl0aG9zLndhY2NhLnYwLkRhdGFSZXNwb25zZS5WZXJzaW9uRGF0YToCOAEiRAoOUGxheWxvZ1JlcXVlc3QSDgoGYXBpX2lkGAEgASgJEhUKCG11c2ljX2lkGAIgASgFSACIAQFCCwoJX211c2ljX2lkIlcKEVBsYXlsb2dTdHJlYW1JdGVtEhYKDnBsYXlsb2dfYXBpX2lkGAEgASgJEioKBGluZm8YAiABKAsyHC5teXRob3Mud2FjY2EudjAuUGxheWxvZ0luZm8iwQMKC1BsYXlsb2dJbmZvEhAKCG11c2ljX2lkGAEgASgFEj8KEG11c2ljX2RpZmZpY3VsdHkYAiABKA4yJS5teXRob3Mud2FjY2EudjAuV2FjY2FNdXNpY0RpZmZpY3VsdHkSDQoFc2NvcmUYAyABKAUSNAoFZ3JhZGUYBCABKA4yJS5teXRob3Mud2FjY2EudjAuV2FjY2FNdXNpY1Njb3JlR3JhZGUSKgoFanVkZ2UYBSABKAsyGy5teXRob3Mud2FjY2EudjAuV2FjY2FKdWRnZRI3CgxjbGVhcl9zdGF0dXMYBiABKAsyIS5teXRob3Mud2FjY2EudjAuV2FjY2FDbGVhclN0YXR1cxIVCg1pc19uZXdfcmVjb3JkGAcgASgIEg0KBWNvbWJvGAggASgFEhQKDHNraWxsX3BvaW50cxgJIAEoBRIMCgRmYXN0GAogASgFEgwKBGxhdGUYCyABKAUSNgoOdXNlcl9wbGF5X21vZGUYDCABKA4yHi5teXRob3Mud2FjY2EudjAuV2FjY2FQbGF5TW9kZRINCgV0cmFjaxgNIAEoBRIWCg51c2VyX3BsYXlfZGF0ZRgOIAEoCTKoAQoJV2FjY2FVc2VyEkYKB0dldERhdGESHC5teXRob3Mud2FjY2EudjAuRGF0YVJlcXVlc3QaHS5teXRob3Mud2FjY2EudjAuRGF0YVJlc3BvbnNlElMKCkdldFBsYXlsb2cSHy5teXRob3Mud2FjY2EudjAuUGxheWxvZ1JlcXVlc3QaIi5teXRob3Mud2FjY2EudjAuUGxheWxvZ1N0cmVhbUl0ZW0wAWIGcHJvdG8z", + [file_wacca_common], + ); + +/** + * @generated from message mythos.wacca.v0.DataRequest + */ +export type DataRequest = Message<"mythos.wacca.v0.DataRequest"> & { + /** + * @generated from field: string api_id = 1; + */ + apiId: string; +}; + +/** + * Describes the message mythos.wacca.v0.DataRequest. + * Use `create(DataRequestSchema)` to create a new message. + */ +export const DataRequestSchema: GenMessage = + /*@__PURE__*/ + messageDesc(file_wacca_user, 0); + +/** + * @generated from message mythos.wacca.v0.DataResponse + */ +export type DataResponse = Message<"mythos.wacca.v0.DataResponse"> & { + /** + * @generated from field: string user_name = 1; + */ + userName: string; + + /** + * @generated from field: int32 exp = 2; + */ + exp: number; + + /** + * @generated from field: int32 points = 3; + */ + points: number; + + /** + * @generated from field: repeated int32 favorite_music_entries = 4; + */ + favoriteMusicEntries: number[]; + + /** + * @generated from field: map version_data = 5; + */ + versionData: { [key: number]: DataResponse_VersionData }; +}; + +/** + * Describes the message mythos.wacca.v0.DataResponse. + * Use `create(DataResponseSchema)` to create a new message. + */ +export const DataResponseSchema: GenMessage = + /*@__PURE__*/ + messageDesc(file_wacca_user, 1); + +/** + * @generated from message mythos.wacca.v0.DataResponse.VersionData + */ +export type DataResponse_VersionData = Message<"mythos.wacca.v0.DataResponse.VersionData"> & { + /** + * @generated from field: int32 rank = 1; + */ + rank: number; + + /** + * @generated from field: int32 dan_rank = 2; + */ + danRank: number; + + /** + * @generated from field: int32 rating = 3; + */ + rating: number; + + /** + * @generated from field: repeated int32 title_part_ids = 4; + */ + titlePartIds: number[]; + + /** + * @generated from field: string last_app_version = 5; + */ + lastAppVersion: string; + + /** + * @generated from field: mythos.wacca.v0.DataResponse.VersionData.LastSong last_song = 6; + */ + lastSong?: DataResponse_VersionData_LastSong; + + /** + * @generated from field: int32 last_song_sort = 7; + */ + lastSongSort: number; + + /** + * @generated from field: int32 last_folder = 8; + */ + lastFolder: number; + + /** + * @generated from field: int32 last_folder_sort = 9; + */ + lastFolderSort: number; + + /** + * @generated from field: string last_browsed_song_list = 10; + */ + lastBrowsedSongList: string; + + /** + * @generated from field: map tutorial_state = 11; + */ + tutorialState: { [key: number]: boolean }; +}; + +/** + * Describes the message mythos.wacca.v0.DataResponse.VersionData. + * Use `create(DataResponse_VersionDataSchema)` to create a new message. + */ +export const DataResponse_VersionDataSchema: GenMessage = + /*@__PURE__*/ + messageDesc(file_wacca_user, 1, 0); + +/** + * @generated from message mythos.wacca.v0.DataResponse.VersionData.LastSong + */ +export type DataResponse_VersionData_LastSong = + Message<"mythos.wacca.v0.DataResponse.VersionData.LastSong"> & { + /** + * @generated from field: int32 music_id = 1; + */ + musicId: number; + + /** + * @generated from field: mythos.wacca.v0.WaccaMusicDifficulty music_difficulty = 2; + */ + musicDifficulty: WaccaMusicDifficulty; + }; + +/** + * Describes the message mythos.wacca.v0.DataResponse.VersionData.LastSong. + * Use `create(DataResponse_VersionData_LastSongSchema)` to create a new message. + */ +export const DataResponse_VersionData_LastSongSchema: GenMessage = + /*@__PURE__*/ + messageDesc(file_wacca_user, 1, 0, 0); + +/** + * @generated from message mythos.wacca.v0.PlaylogRequest + */ +export type PlaylogRequest = Message<"mythos.wacca.v0.PlaylogRequest"> & { + /** + * @generated from field: string api_id = 1; + */ + apiId: string; + + /** + * @generated from field: optional int32 music_id = 2; + */ + musicId?: number; +}; + +/** + * Describes the message mythos.wacca.v0.PlaylogRequest. + * Use `create(PlaylogRequestSchema)` to create a new message. + */ +export const PlaylogRequestSchema: GenMessage = + /*@__PURE__*/ + messageDesc(file_wacca_user, 2); + +/** + * @generated from message mythos.wacca.v0.PlaylogStreamItem + */ +export type PlaylogStreamItem = Message<"mythos.wacca.v0.PlaylogStreamItem"> & { + /** + * @generated from field: string playlog_api_id = 1; + */ + playlogApiId: string; + + /** + * @generated from field: mythos.wacca.v0.PlaylogInfo info = 2; + */ + info?: PlaylogInfo; +}; + +/** + * Describes the message mythos.wacca.v0.PlaylogStreamItem. + * Use `create(PlaylogStreamItemSchema)` to create a new message. + */ +export const PlaylogStreamItemSchema: GenMessage = + /*@__PURE__*/ + messageDesc(file_wacca_user, 3); + +/** + * @generated from message mythos.wacca.v0.PlaylogInfo + */ +export type PlaylogInfo = Message<"mythos.wacca.v0.PlaylogInfo"> & { + /** + * @generated from field: int32 music_id = 1; + */ + musicId: number; + + /** + * @generated from field: mythos.wacca.v0.WaccaMusicDifficulty music_difficulty = 2; + */ + musicDifficulty: WaccaMusicDifficulty; + + /** + * @generated from field: int32 score = 3; + */ + score: number; + + /** + * @generated from field: mythos.wacca.v0.WaccaMusicScoreGrade grade = 4; + */ + grade: WaccaMusicScoreGrade; + + /** + * @generated from field: mythos.wacca.v0.WaccaJudge judge = 5; + */ + judge?: WaccaJudge; + + /** + * @generated from field: mythos.wacca.v0.WaccaClearStatus clear_status = 6; + */ + clearStatus?: WaccaClearStatus; + + /** + * @generated from field: bool is_new_record = 7; + */ + isNewRecord: boolean; + + /** + * @generated from field: int32 combo = 8; + */ + combo: number; + + /** + * @generated from field: int32 skill_points = 9; + */ + skillPoints: number; + + /** + * @generated from field: int32 fast = 10; + */ + fast: number; + + /** + * @generated from field: int32 late = 11; + */ + late: number; + + /** + * @generated from field: mythos.wacca.v0.WaccaPlayMode user_play_mode = 12; + */ + userPlayMode: WaccaPlayMode; + + /** + * @generated from field: int32 track = 13; + */ + track: number; + + /** + * @generated from field: string user_play_date = 14; + */ + userPlayDate: string; +}; + +/** + * Describes the message mythos.wacca.v0.PlaylogInfo. + * Use `create(PlaylogInfoSchema)` to create a new message. + */ +export const PlaylogInfoSchema: GenMessage = + /*@__PURE__*/ + messageDesc(file_wacca_user, 4); + +/** + * @generated from service mythos.wacca.v0.WaccaUser + */ +export const WaccaUser: GenService<{ + /** + * @generated from rpc mythos.wacca.v0.WaccaUser.GetData + */ + getData: { + methodKind: "unary"; + input: typeof DataRequestSchema; + output: typeof DataResponseSchema; + }; + /** + * + * + * @generated from rpc mythos.wacca.v0.WaccaUser.GetPlaylog + */ + getPlaylog: { + methodKind: "server_streaming"; + input: typeof PlaylogRequestSchema; + output: typeof PlaylogStreamItemSchema; + }; +}> = /*@__PURE__*/ serviceDesc(file_wacca_user, 0); diff --git a/typescript/server/src/scripts/invites/create-invite-code.ts b/typescript/server/src/scripts/invites/create-invite-code.ts index 2374f4263..8bf1ece6b 100644 --- a/typescript/server/src/scripts/invites/create-invite-code.ts +++ b/typescript/server/src/scripts/invites/create-invite-code.ts @@ -1,5 +1,5 @@ import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { Random20Hex } from "#utils/misc"; import { Command } from "commander"; @@ -16,7 +16,7 @@ if (options.code === undefined) { const code = options.code as string; -db.invites +MONGODB_KILL.invites .insert({ code, createdBy: 1, diff --git a/typescript/server/src/scripts/load-seeds-mongo.ts b/typescript/server/src/scripts/load-seeds-mongo.ts index 1d739fd7e..5269aa409 100644 --- a/typescript/server/src/scripts/load-seeds-mongo.ts +++ b/typescript/server/src/scripts/load-seeds-mongo.ts @@ -20,7 +20,7 @@ import { TachiConfig } from "#lib/setup/config"; import { RemoveStaleFolderShowcaseStats } from "#lib/showcase/showcase"; import { UpdateQuestSubscriptions } from "#lib/targets/quests"; /* eslint-disable no-await-in-loop */ -import db, { monkDB } from "#services/mongo/db"; +import MONGODB_KILL, { monkDB } from "#services/mongo/db"; import { RecalcAllScores } from "#utils/calculations/recalc-scores"; import { UpdateGameSongIDCounter } from "#utils/db"; import { InitaliseFolderChartLookup } from "#utils/folder"; @@ -276,7 +276,7 @@ const syncInstructions: Array = [ const allModifiedFolderIDs = r.changedFields as Array; - const keptFolderIDs = await db.folders.find( + const keptFolderIDs = await MONGODB_KILL.folders.find( { folderID: { $in: allModifiedFolderIDs }, }, diff --git a/typescript/server/src/scripts/make-user-admin.ts b/typescript/server/src/scripts/make-user-admin.ts index a72eba78b..0448d212d 100644 --- a/typescript/server/src/scripts/make-user-admin.ts +++ b/typescript/server/src/scripts/make-user-admin.ts @@ -1,5 +1,5 @@ import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { WrapScriptPromise } from "#utils/misc"; import { FormatUserDoc, ResolveUser } from "#utils/user"; import { UserAuthLevels } from "tachi-common"; @@ -14,7 +14,7 @@ async function MakeUserAdmin(userID: string) { throw new Error(`No such user '${userID}' exists.`); } - await db.users.update( + await MONGODB_KILL.users.update( { id: user.id, }, diff --git a/typescript/server/src/scripts/migrate-to-postgres.ts b/typescript/server/src/scripts/migrate-to-postgres.ts index 2d8b23a5c..57187803f 100644 --- a/typescript/server/src/scripts/migrate-to-postgres.ts +++ b/typescript/server/src/scripts/migrate-to-postgres.ts @@ -14,13 +14,11 @@ import type { PrivateUserInfoDocument } from "#utils/types"; import type { - AccountBadgeKind, AuthLevel, Database, GameGroup, ImportType, NewAccount, - NewAccountBadge, NewAccountFollowing, NewAccountSettings, NewAccountUsernameChange, @@ -305,6 +303,7 @@ async function streamCollection( const docs = (await col.find(query, { sort: { _id: 1 }, limit: batchSize, + // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any)) as unknown as Array<{ _id: unknown } & T>; if (docs.length === 0) { @@ -354,7 +353,6 @@ async function main(): Promise { // ── account + account_badge ─────────────────────────────────────────────── { console.log("\n[account / account_badge]"); - const VALID_BADGES = new Set(["alpha", "beta", "dev-team"]); const users = await mongoDB.get("users").find({}); const accountRows: Array = users.map((u) => ({ @@ -373,6 +371,9 @@ async function main(): Promise { custom_banner_location: u.customBannerLocation, last_seen: tsReq(u.lastSeen), auth_level: toAuthLevel(u.authLevel), + bd_alpha: u.badges.includes("alpha"), + bd_beta: u.badges.includes("beta"), + bd_dev_team: u.badges.includes("dev-team"), })); await batchInsert("account", accountRows); @@ -382,18 +383,7 @@ async function main(): Promise { pg, ); - const badgeRows: Array = []; - - for (const u of users) { - for (const badge of u.badges) { - if (VALID_BADGES.has(badge as AccountBadgeKind)) { - badgeRows.push({ user_id: u.id, badge: badge as AccountBadgeKind }); - } - } - } - - await batchInsert("account_badge", badgeRows); - console.log(` ${users.length} accounts, ${badgeRows.length} badges.`); + console.log(` ${users.length} accounts.`); } // ── orphan_chart + orphan_chart_user ────────────────────────────────────── diff --git a/typescript/server/src/scripts/state-sync/dedupe-score-ids.ts b/typescript/server/src/scripts/state-sync/dedupe-score-ids.ts index b69acb376..2dc097278 100644 --- a/typescript/server/src/scripts/state-sync/dedupe-score-ids.ts +++ b/typescript/server/src/scripts/state-sync/dedupe-score-ids.ts @@ -2,12 +2,12 @@ import type { IObjectID } from "monk"; import type { integer } from "tachi-common"; import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { WrapScriptPromise } from "#utils/misc"; async function DedupeScoreIDs() { const dups: Array<{ count: integer; dups: Array; id: string }> = - await db.scores.aggregate( + await MONGODB_KILL.scores.aggregate( [ { $group: { @@ -30,7 +30,7 @@ async function DedupeScoreIDs() { for (const dup of dups) { dup.dups.shift(); // eslint-disable-next-line no-await-in-loop - await db.scores.remove({ _id: { $in: dup.dups } }); + await MONGODB_KILL.scores.remove({ _id: { $in: dup.dups } }); } } diff --git a/typescript/server/src/scripts/state-sync/recalc-game-profiles.ts b/typescript/server/src/scripts/state-sync/recalc-game-profiles.ts index 7d4fc9ffd..ea9c2ca16 100644 --- a/typescript/server/src/scripts/state-sync/recalc-game-profiles.ts +++ b/typescript/server/src/scripts/state-sync/recalc-game-profiles.ts @@ -3,16 +3,16 @@ import type { GameGroup, Playtype, ScoreDocument } from "tachi-common"; import { log } from "#lib/log/log.js"; import { UpdateUsersGamePlaytypeStats } from "#lib/score-import/framework/ugpt-stats/update-ugpt-stats"; /* eslint-disable no-await-in-loop */ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { WrapScriptPromise } from "#utils/misc"; import { FormatUserDoc } from "#utils/user"; export async function RecalcGameProfiles() { - const users = await db.users.find({}); + const users = await MONGODB_KILL.users.find({}); for (const user of users) { const gpts: Array<{ _id: { game: GameGroup; playtype: Playtype } } & ScoreDocument> = - await db.scores.aggregate([ + await MONGODB_KILL.scores.aggregate([ { $match: { userID: user.id, diff --git a/typescript/server/src/scripts/state-sync/rederive-all-scores.ts b/typescript/server/src/scripts/state-sync/rederive-all-scores.ts index 263da1574..bb69da12d 100644 --- a/typescript/server/src/scripts/state-sync/rederive-all-scores.ts +++ b/typescript/server/src/scripts/state-sync/rederive-all-scores.ts @@ -1,6 +1,6 @@ import { log } from "#lib/log/log.js"; import UpdateScore from "#lib/score-mutation/update-score"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { UpdateAllPBs } from "#utils/calculations/recalc-scores"; import { EfficientDBIterate } from "#utils/efficient-db-iterate"; @@ -9,7 +9,7 @@ import { EfficientDBIterate } from "#utils/efficient-db-iterate"; */ async function main() { await EfficientDBIterate( - db.scores, + MONGODB_KILL.scores, async (score) => { // @ts-expect-error just incase delete score._id; diff --git a/typescript/server/src/server/middleware/auth.test.ts b/typescript/server/src/server/middleware/auth.oldtest.ts similarity index 90% rename from typescript/server/src/server/middleware/auth.test.ts rename to typescript/server/src/server/middleware/auth.oldtest.ts index c59409428..c98a31d4c 100644 --- a/typescript/server/src/server/middleware/auth.test.ts +++ b/typescript/server/src/server/middleware/auth.oldtest.ts @@ -1,5 +1,5 @@ import { SYMBOL_TACHI_API_AUTH } from "#lib/constants/tachi"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import mockApi from "#test-utils/mock-api"; import { expressRequestMock } from "#test-utils/mock-request"; import ResetDBState from "#test-utils/resets"; @@ -12,7 +12,7 @@ t.test("#SetRequestPermissions", (t) => { t.beforeEach(ResetDBState); t.test("Should assign valid APIKey information to req[SYMBOL_TachiAPIData]", async (t) => { - await db["api-tokens"].insert({ + await MONGODB_KILL["api-tokens"].insert({ userID: 1, identifier: "Mock API Token", permissions: { @@ -107,8 +107,8 @@ t.test("#SetRequestPermissions", (t) => { const { req } = await expressRequestMock(SetRequestPermissions, { session: { tachi: { - user: await db.users.findOne({ id: 1 }), - settings: await db["user-settings"].findOne({ userID: 1 }), + user: await MONGODB_KILL.users.findOne({ id: 1 }), + settings: await MONGODB_KILL["user-settings"].findOne({ userID: 1 }), }, }, }); @@ -128,9 +128,9 @@ t.test("#SetRequestPermissions", (t) => { t.test("#RejectIfBanned", (t) => { t.test("Should stop banned users from doing anything.", async (t) => { - await db.users.update({ id: 1 }, { $set: { authLevel: 0 } }); + await MONGODB_KILL.users.update({ id: 1 }, { $set: { authLevel: 0 } }); - await db["api-tokens"].insert({ + await MONGODB_KILL["api-tokens"].insert({ userID: 1, identifier: "Mock API Token", permissions: { diff --git a/typescript/server/src/server/middleware/auth.ts b/typescript/server/src/server/middleware/auth.ts index f6d837731..aa761995b 100644 --- a/typescript/server/src/server/middleware/auth.ts +++ b/typescript/server/src/server/middleware/auth.ts @@ -4,7 +4,7 @@ import type { Session, SessionData } from "express-session"; import { SYMBOL_TACHI_API_AUTH } from "#lib/constants/tachi"; import { log } from "#lib/log/log.js"; import { TachiConfig } from "#lib/setup/config"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { IsNullishOrEmptyStr, SplitAuthorizationHeader } from "#utils/misc"; import { ALL_PERMISSIONS, @@ -29,7 +29,7 @@ export const RejectIfBanned: RequestHandler = async (req, res, next) => { // we need to ignore this if auth doesn't exist and if auth is null. if (auth && auth.userID !== null) { - const isBanned = await db.users.findOne({ + const isBanned = await MONGODB_KILL.users.findOne({ id: req[SYMBOL_TACHI_API_AUTH].userID!, authLevel: UserAuthLevels.BANNED, }); @@ -108,7 +108,7 @@ function CreateSetRequestPermissions(errorKeyName: string): Array { if (req.session.tachi?.user.id === undefined) { @@ -14,7 +14,7 @@ export const UpdateLastSeen: RequestHandler = (req, _res, next) => { } // fire, but we have no reason to await it. - void db.users.update( + void MONGODB_KILL.users.update( { id: req.session.tachi.user.id }, { $set: { diff --git a/typescript/server/src/server/router/api/v1postgres/admin/router.test.ts b/typescript/server/src/server/router/api/v1/admin/router.oldtest.ts similarity index 84% rename from typescript/server/src/server/router/api/v1postgres/admin/router.test.ts rename to typescript/server/src/server/router/api/v1/admin/router.oldtest.ts index 3f67cf330..17482a545 100644 --- a/typescript/server/src/server/router/api/v1postgres/admin/router.test.ts +++ b/typescript/server/src/server/router/api/v1/admin/router.oldtest.ts @@ -1,7 +1,7 @@ import { ONE_MINUTE } from "#lib/constants/time"; import { ChangeRootLogLevel, GetLogLevel } from "#lib/log/log.js"; import { Env, ServerConfig } from "#lib/setup/config"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; @@ -16,13 +16,13 @@ const LOG_LEVEL = Env.LOG_LEVEL; t.test("POST /api/v1/admin/change-log-level", async (t) => { t.beforeEach(async () => { ChangeRootLogLevel(LOG_LEVEL); - await db.users.update({ id: 1 }, { $set: { authLevel: UserAuthLevels.ADMIN } }); + await MONGODB_KILL.users.update({ id: 1 }, { $set: { authLevel: UserAuthLevels.ADMIN } }); }); const auth = await CreateFakeAuthCookie(mockApi); t.test("Should require an admin authlevel", async (t) => { - await db.users.update({ id: 1 }, { $set: { authLevel: UserAuthLevels.USER } }); + await MONGODB_KILL.users.update({ id: 1 }, { $set: { authLevel: UserAuthLevels.USER } }); const res = await mockApi.post("/api/v1/admin/change-log-level").set("Cookie", auth).send({ noReset: true, @@ -81,13 +81,13 @@ t.test("POST /api/v1/admin/change-log-level", async (t) => { t.test("POST /api/v1/admin/delete-score", async (t) => { t.beforeEach(ResetDBState); t.beforeEach(async () => { - await db.users.update({ id: 1 }, { $set: { authLevel: UserAuthLevels.ADMIN } }); + await MONGODB_KILL.users.update({ id: 1 }, { $set: { authLevel: UserAuthLevels.ADMIN } }); }); const auth = await CreateFakeAuthCookie(mockApi); t.test("Should require an admin authlevel", async (t) => { - await db.users.update({ id: 1 }, { $set: { authLevel: UserAuthLevels.USER } }); + await MONGODB_KILL.users.update({ id: 1 }, { $set: { authLevel: UserAuthLevels.USER } }); const res = await mockApi .post("/api/v1/admin/delete-score") @@ -104,7 +104,7 @@ t.test("POST /api/v1/admin/delete-score", async (t) => { }); t.test("Should delete another user's score.", async (t) => { - await db.scores.insert( + await MONGODB_KILL.scores.insert( deepmerge(TestingIIDXSPScore, { scoreID: "deleteme", }), @@ -121,7 +121,7 @@ t.test("POST /api/v1/admin/delete-score", async (t) => { t.equal(res.statusCode, 200); - const dbScore = await db.scores.findOne({ scoreID: "deleteme" }); + const dbScore = await MONGODB_KILL.scores.findOne({ scoreID: "deleteme" }); t.equal(dbScore, null, "Should remove the score from the database."); diff --git a/typescript/server/src/server/router/api/v1/admin/router.test.ts b/typescript/server/src/server/router/api/v1/admin/router.test.ts deleted file mode 100644 index 3f67cf330..000000000 --- a/typescript/server/src/server/router/api/v1/admin/router.test.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { ONE_MINUTE } from "#lib/constants/time"; -import { ChangeRootLogLevel, GetLogLevel } from "#lib/log/log.js"; -import { Env, ServerConfig } from "#lib/setup/config"; -import db from "#services/mongo/db"; -import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import { TestingIIDXSPScore } from "#test-utils/test-data"; -import { Sleep } from "#utils/misc"; -import deepmerge from "deepmerge"; -import { type ScoreDocument, UserAuthLevels } from "tachi-common"; -import t from "tap"; - -const LOG_LEVEL = Env.LOG_LEVEL; - -t.test("POST /api/v1/admin/change-log-level", async (t) => { - t.beforeEach(async () => { - ChangeRootLogLevel(LOG_LEVEL); - await db.users.update({ id: 1 }, { $set: { authLevel: UserAuthLevels.ADMIN } }); - }); - - const auth = await CreateFakeAuthCookie(mockApi); - - t.test("Should require an admin authlevel", async (t) => { - await db.users.update({ id: 1 }, { $set: { authLevel: UserAuthLevels.USER } }); - - const res = await mockApi.post("/api/v1/admin/change-log-level").set("Cookie", auth).send({ - noReset: true, - logLevel: "crit", - }); - - t.equal(res.statusCode, 403); - - t.end(); - }); - - t.test("Should change the log level on the server.", async (t) => { - const res = await mockApi.post("/api/v1/admin/change-log-level").set("Cookie", auth).send({ - noReset: true, - logLevel: "crit", - }); - - t.equal(res.statusCode, 200); - t.equal(GetLogLevel(), "crit"); - - t.end(); - }); - - t.test("Should reject invalid log levels", async (t) => { - const res = await mockApi.post("/api/v1/admin/change-log-level").set("Cookie", auth).send({ - noReset: true, - logLevel: "invalid", - }); - - t.equal(res.statusCode, 400); - t.equal(GetLogLevel(), LOG_LEVEL); - - t.end(); - }); - - t.test("Should set a timer that lasts duration minutes.", async (t) => { - const res = await mockApi.post("/api/v1/admin/change-log-level").set("Cookie", auth).send({ - duration: 0.05, - logLevel: "warn", - }); - - t.equal(res.statusCode, 200); - t.equal(GetLogLevel(), "warn"); - - // wait a bit - await Sleep(ONE_MINUTE * 0.06); - - t.equal(GetLogLevel(), LOG_LEVEL); - - t.end(); - }); - - t.end(); -}); - -t.test("POST /api/v1/admin/delete-score", async (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(async () => { - await db.users.update({ id: 1 }, { $set: { authLevel: UserAuthLevels.ADMIN } }); - }); - - const auth = await CreateFakeAuthCookie(mockApi); - - t.test("Should require an admin authlevel", async (t) => { - await db.users.update({ id: 1 }, { $set: { authLevel: UserAuthLevels.USER } }); - - const res = await mockApi - .post("/api/v1/admin/delete-score") - .set({ - Cookie: auth, - }) - .send({ - scoreID: "deleteme", - }); - - t.equal(res.statusCode, 403); - - t.end(); - }); - - t.test("Should delete another user's score.", async (t) => { - await db.scores.insert( - deepmerge(TestingIIDXSPScore, { - scoreID: "deleteme", - }), - ); - - const res = await mockApi - .post("/api/v1/admin/delete-score") - .set({ - Cookie: auth, - }) - .send({ - scoreID: "deleteme", - }); - - t.equal(res.statusCode, 200); - - const dbScore = await db.scores.findOne({ scoreID: "deleteme" }); - - t.equal(dbScore, null, "Should remove the score from the database."); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1/admin/router.ts b/typescript/server/src/server/router/api/v1/admin/router.ts index 70c32e522..9db871c47 100644 --- a/typescript/server/src/server/router/api/v1/admin/router.ts +++ b/typescript/server/src/server/router/api/v1/admin/router.ts @@ -6,7 +6,7 @@ import { UpdateQuestsForUser } from "#lib/score-import/framework/quests/quests"; import { DeleteMultipleScores, DeleteScore } from "#lib/score-mutation/delete-scores"; import { TachiConfig } from "#lib/setup/config"; import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { RecalcAllScores, UpdateAllPBs } from "#utils/calculations/recalc-scores"; import { RecalcSessions } from "#utils/calculations/recalc-sessions"; import { IsValidPlaytype } from "#utils/misc"; @@ -98,7 +98,7 @@ router.post( router.post("/delete-score", prValidate({ scoreID: "string" }), async (req, res) => { const body = req.safeBody as { scoreID: string }; - const score = await db.scores.findOne({ scoreID: body.scoreID }); + const score = await MONGODB_KILL.scores.findOne({ scoreID: body.scoreID }); if (!score) { return res.status(404).json({ @@ -126,7 +126,7 @@ router.post("/delete-score", prValidate({ scoreID: "string" }), async (req, res) router.post("/delete-session", prValidate({ sessionID: "string" }), async (req, res) => { const body = req.safeBody as { sessionID: string }; - const session = await db.sessions.findOne({ scoreID: body.sessionID }); + const session = await MONGODB_KILL.sessions.findOne({ scoreID: body.sessionID }); if (!session) { return res.status(404).json({ @@ -179,7 +179,7 @@ router.post( userID: integer; }; - const ugpt = await db["game-stats"].findOne({ + const ugpt = await MONGODB_KILL["game-stats"].findOne({ userID, game, playtype, @@ -221,17 +221,17 @@ router.post( const { game, chartID } = body; - const scores = await db.scores.find({ + const scores = await MONGODB_KILL.scores.find({ chartID, }); await DeleteMultipleScores(scores); - await db.anyCharts[game].remove({ + await MONGODB_KILL.anyCharts[game].remove({ chartID, }); - await db["personal-bests"].remove({ + await MONGODB_KILL["personal-bests"].remove({ chartID, }); @@ -254,7 +254,7 @@ router.post("/recalc", async (req, res) => { await RecalcAllScores(filter); const scoreIDs = ( - await db.scores.find(filter, { + await MONGODB_KILL.scores.find(filter, { projection: { scoreID: 1, }, @@ -331,7 +331,7 @@ router.post("/supporter/:userID", async (req, res) => { }); } - await db.users.update({ id: user.id }, { $set: { isSupporter: true } }); + await MONGODB_KILL.users.update({ id: user.id }, { $set: { isSupporter: true } }); return res.status(200).json({ success: true, @@ -355,7 +355,7 @@ router.delete("/supporter/:userID", async (req, res) => { }); } - await db.users.update({ id: user.id }, { $set: { isSupporter: false } }); + await MONGODB_KILL.users.update({ id: user.id }, { $set: { isSupporter: false } }); return res.status(200).json({ success: true, @@ -372,13 +372,13 @@ router.delete("/supporter/:userID", async (req, res) => { * @name POST /api/v1/admin/reprocess-all-goals */ router.post("/reprocess-all-goals", async (req, res) => { - const ugpts = await db["game-stats"].find({}); + const ugpts = await MONGODB_KILL["game-stats"].find({}); const promises = []; for (const ugpt of ugpts) { promises.push(async () => { - const goalSubs = await db["goal-subs"].find({ + const goalSubs = await MONGODB_KILL["goal-subs"].find({ game: ugpt.game, playtype: ugpt.playtype, userID: ugpt.userID, @@ -390,19 +390,19 @@ router.post("/reprocess-all-goals", async (req, res) => { goalSubsMap.set(gSub.goalID, gSub); } - const goals = await db.goals.find({ + const goals = await MONGODB_KILL.goals.find({ goalID: { $in: goalSubs.map((e) => e.goalID) }, }); await UpdateGoalsForUser(goals, goalSubsMap, ugpt.userID, log); - const allQuestSubs = await db["quest-subs"].find({ + const allQuestSubs = await MONGODB_KILL["quest-subs"].find({ game: ugpt.game, playtype: ugpt.playtype, userID: ugpt.userID, }); - const quests = await db.quests.find({ + const quests = await MONGODB_KILL.quests.find({ questID: { $in: allQuestSubs.map((e) => e.questID) }, }); diff --git a/typescript/server/src/server/router/api/v1postgres/auth/auth.test.ts b/typescript/server/src/server/router/api/v1/auth/auth.oldtest.ts similarity index 90% rename from typescript/server/src/server/router/api/v1postgres/auth/auth.test.ts rename to typescript/server/src/server/router/api/v1/auth/auth.oldtest.ts index 0d3359bab..1e1e44ded 100644 --- a/typescript/server/src/server/router/api/v1postgres/auth/auth.test.ts +++ b/typescript/server/src/server/router/api/v1/auth/auth.oldtest.ts @@ -1,4 +1,4 @@ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { MockJSONFetch } from "#test-utils/mock-fetch"; import ResetDBState from "#test-utils/resets"; import t from "tap"; @@ -10,7 +10,7 @@ t.test("#ReinstateInvite", (t) => { t.test("Should change the 'consumed' property of an invite to true.", async (t) => { // mock insert - const inviteDoc = await db.invites.insert({ + const inviteDoc = await MONGODB_KILL.invites.insert({ code: "foobar", consumed: true, createdBy: 1, @@ -23,7 +23,7 @@ t.test("#ReinstateInvite", (t) => { t.equal(response.nModified, 1, "Should modify one document"); - const invite2 = await db.invites.findOne({ + const invite2 = await MONGODB_KILL.invites.findOne({ // lol code: inviteDoc.code, }); @@ -42,7 +42,7 @@ t.test("#AddNewInvite", (t) => { t.beforeEach(ResetDBState); t.test("Should create a new invite from a given user", async (t) => { - const userDoc = await db.users.findOne({ id: 1 }); + const userDoc = await MONGODB_KILL.users.findOne({ id: 1 }); const result = await AddNewInvite(userDoc!); diff --git a/typescript/server/src/server/router/api/v1/auth/auth.test.ts b/typescript/server/src/server/router/api/v1/auth/auth.test.ts index 0d3359bab..1508b3183 100644 --- a/typescript/server/src/server/router/api/v1/auth/auth.test.ts +++ b/typescript/server/src/server/router/api/v1/auth/auth.test.ts @@ -1,93 +1,395 @@ -import db from "#services/mongo/db"; -import { MockJSONFetch } from "#test-utils/mock-fetch"; -import ResetDBState from "#test-utils/resets"; -import t from "tap"; +import { ClearTestingRateLimitCache } from "#server/middleware/rate-limiter"; +import DB from "#services/pg/db.js"; +import mockApi, { CloseServerConnection } from "#test-utils/mock-api"; +import { afterAll, beforeEach, describe, expect, it } from "vitest"; -import { AddNewInvite, ReinstateInvite, ValidateCaptcha } from "./auth"; +import { HashPassword, PasswordCompare } from "./auth"; -t.test("#ReinstateInvite", (t) => { - t.beforeEach(ResetDBState); +afterAll(() => CloseServerConnection()); - t.test("Should change the 'consumed' property of an invite to true.", async (t) => { - // mock insert - const inviteDoc = await db.invites.insert({ - code: "foobar", - consumed: true, - createdBy: 1, - createdAt: 1, - consumedAt: 2, - consumedBy: 2, - }); +// ─── Helpers ───────────────────────────────────────────────────────────────── - const response = await ReinstateInvite(inviteDoc.code); +async function seedUser(opts?: { email?: string; password?: string; username?: string }) { + const username = opts?.username ?? "seed_user"; + const email = opts?.email ?? "seed@example.com"; + const password = opts?.password ?? "password123"; - t.equal(response.nModified, 1, "Should modify one document"); + const hashedPassword = await HashPassword(password); - const invite2 = await db.invites.findOne({ - // lol - code: inviteDoc.code, - }); + const { id } = await DB.insertInto("account") + .values({ + username, + about: "Seed user for tests.", + joined: new Date().toISOString(), + last_seen: new Date().toISOString(), + auth_level: "user", + custom_pfp_location: null, + custom_banner_location: null, + }) + .returning("id") + .executeTakeFirstOrThrow(); - t.equal(invite2!.consumed, false, "Should no longer be consumed"); - t.equal(invite2!.consumedAt, null, "Should revoke when it was consumed."); - t.equal(invite2!.consumedBy, null, "Should revoke who it was consumed by."); + await DB.insertInto("account_settings") + .values({ + user_id: id, + pf_invisible: false, + pf_developer_mode: false, + pf_advanced_mode: false, + pf_contentious_content: false, + pf_deletable_scores: false, + }) + .execute(); - t.end(); + await DB.insertInto("priv_account_credential") + .values({ user_id: id, email, password: hashedPassword }) + .execute(); + + return { id, username, email, password }; +} + +async function seedInvite(createdBy: number, code = "INVITE_CODE") { + await DB.insertInto("priv_invite") + .values({ + code, + created_by: createdBy, + created_at: new Date().toISOString(), + consumed: false, + consumed_by: null, + consumed_at: null, + }) + .execute(); + + return code; +} + +// ─── POST /api/v1/auth/register ────────────────────────────────────────────── + +describe("POST /api/v1/auth/register", () => { + let inviteCode: string; + + beforeEach(async () => { + ClearTestingRateLimitCache(); + const { id } = await seedUser(); + inviteCode = await seedInvite(id); }); - t.end(); -}); + it("creates a new user and sets a session cookie", async () => { + const res = await mockApi.post("/api/v1/auth/register").send({ + username: "newuser", + "!password": "securepassword", + email: "newuser@example.com", + captcha: "test", + inviteCode, + }); -t.test("#AddNewInvite", (t) => { - t.beforeEach(ResetDBState); + expect(res.status).toBe(200); + expect(res.body.success).toBe(true); + expect(res.body.body.username).toBe("newuser"); + expect(res.headers["set-cookie"]).toBeDefined(); - t.test("Should create a new invite from a given user", async (t) => { - const userDoc = await db.users.findOne({ id: 1 }); + const created = await DB.selectFrom("account") + .select("username") + .where("username", "=", "newuser") + .executeTakeFirst(); - const result = await AddNewInvite(userDoc!); - - t.equal(result.createdBy, userDoc!.id, "Invite should be created by the requesting user."); - t.equal(result.consumed, false, "Invite should not be consumed."); - - // was created +/- 6 seconds from now. This is perhaps too lenient, but we're only really testing its just around now ish. - t.ok(Math.abs(result.createdAt - Date.now()) <= 6000, "Invite was created roughly now."); - - t.match(result.code, /^[0-9a-f]{40}$/u, "Invite code should be a 40 character hex string."); + expect(created?.username).toBe("newuser"); }); - t.end(); + it("rejects a duplicate username (case-insensitive) with 409", async () => { + const res = await mockApi.post("/api/v1/auth/register").send({ + username: "Seed_User", + "!password": "securepassword", + email: "other@example.com", + captcha: "test", + inviteCode, + }); + + expect(res.status).toBe(409); + expect(res.body.success).toBe(false); + }); + + it("rejects a duplicate email with 409", async () => { + const res = await mockApi.post("/api/v1/auth/register").send({ + username: "brandnewuser", + "!password": "securepassword", + email: "seed@example.com", + captcha: "test", + inviteCode, + }); + + expect(res.status).toBe(409); + expect(res.body.success).toBe(false); + }); + + it("rejects an invalid email format with 400", async () => { + const res = await mockApi.post("/api/v1/auth/register").send({ + username: "brandnewuser", + "!password": "securepassword", + email: "not-an-email", + captcha: "test", + inviteCode, + }); + + expect(res.status).toBe(400); + expect(res.body.success).toBe(false); + }); + + it("rejects a password shorter than 8 characters with 400", async () => { + const res = await mockApi.post("/api/v1/auth/register").send({ + username: "brandnewuser", + "!password": "short", + email: "brandnewuser@example.com", + captcha: "test", + inviteCode, + }); + + expect(res.status).toBe(400); + expect(res.body.success).toBe(false); + }); + + it.each([ + ["starts with a number", "1badname"], + ["too short (< 3 chars)", "ab"], + ])("rejects username that %s with 400", async (_, username) => { + const res = await mockApi.post("/api/v1/auth/register").send({ + username, + "!password": "securepassword", + email: "someone@example.com", + captcha: "test", + inviteCode, + }); + + expect(res.status).toBe(400); + expect(res.body.success).toBe(false); + }); + + it("rejects a missing invite code with 400", async () => { + const res = await mockApi.post("/api/v1/auth/register").send({ + username: "brandnewuser", + "!password": "securepassword", + email: "brandnewuser@example.com", + captcha: "test", + }); + + expect(res.status).toBe(400); + expect(res.body.success).toBe(false); + }); + + it("does not consume the invite code on a failed registration", async () => { + await mockApi.post("/api/v1/auth/register").send({ + username: "seed_user", + "!password": "securepassword", + email: "other@example.com", + captcha: "test", + inviteCode, + }); + + const invite = await DB.selectFrom("priv_invite") + .select("consumed") + .where("code", "=", inviteCode) + .executeTakeFirstOrThrow(); + + expect(invite.consumed).toBe(false); + }); + + it("marks the invite code as consumed on success", async () => { + await mockApi.post("/api/v1/auth/register").send({ + username: "newuser", + "!password": "securepassword", + email: "newuser@example.com", + captcha: "test", + inviteCode, + }); + + const invite = await DB.selectFrom("priv_invite") + .select("consumed") + .where("code", "=", inviteCode) + .executeTakeFirstOrThrow(); + + expect(invite.consumed).toBe(true); + }); }); -t.test("#ValidateCaptcha", async (t) => { - t.equal( - await ValidateCaptcha( - "200", - "bar", - MockJSONFetch({ - "https://www.google.com/recaptcha/api/siteverify?secret=unused&response=200&remoteip=bar": - { - success: true, - }, - }), - ), - true, - "Validates captcha when sucess return is true", - ); +// ─── POST /api/v1/auth/login ───────────────────────────────────────────────── - t.equal( - await ValidateCaptcha( - "400", - "bar", - MockJSONFetch({ - "https://www.google.com/recaptcha/api/siteverify?secret=unused&response=400&remoteip=bar": - { - success: false, - }, - }), - ), - false, - "Invalidates captcha when success return is not true", - ); +describe("POST /api/v1/auth/login", () => { + beforeEach(async () => { + ClearTestingRateLimitCache(); + await seedUser(); + }); - t.end(); + it("returns 200 and a session cookie with correct credentials", async () => { + const res = await mockApi.post("/api/v1/auth/login").send({ + username: "seed_user", + "!password": "password123", + captcha: "test", + }); + + expect(res.status).toBe(200); + expect(res.body.success).toBe(true); + expect(res.body.body.userID).toBeDefined(); + expect(res.headers["set-cookie"]).toBeDefined(); + }); + + it("session cookie grants authenticated access", async () => { + const loginRes = await mockApi.post("/api/v1/auth/login").send({ + username: "seed_user", + "!password": "password123", + captcha: "test", + }); + + const cookie = loginRes.headers["set-cookie"] as unknown as string[]; + const userID = loginRes.body.body.userID; + + const statusRes = await mockApi.get("/api/v1/status").set("Cookie", cookie); + + expect(statusRes.status).toBe(200); + expect(statusRes.body.body.whoami).toBe(userID); + }); + + it("allows re-login when already logged in", async () => { + const first = await mockApi.post("/api/v1/auth/login").send({ + username: "seed_user", + "!password": "password123", + captcha: "test", + }); + + const second = await mockApi + .post("/api/v1/auth/login") + .set("Cookie", first.headers["set-cookie"] as unknown as string[]) + .send({ + username: "seed_user", + "!password": "password123", + captcha: "test", + }); + + expect(second.status).toBe(200); + }); + + it("returns 403 with incorrect password", async () => { + const res = await mockApi.post("/api/v1/auth/login").send({ + username: "seed_user", + "!password": "wrongpassword", + captcha: "test", + }); + + expect(res.status).toBe(403); + expect(res.body.success).toBe(false); + }); + + it("returns 404 for a non-existent username", async () => { + const res = await mockApi.post("/api/v1/auth/login").send({ + username: "nobody_here", + "!password": "password123", + captcha: "test", + }); + + expect(res.status).toBe(404); + expect(res.body.success).toBe(false); + }); + + it("returns 400 when password is missing", async () => { + const res = await mockApi.post("/api/v1/auth/login").send({ + username: "seed_user", + captcha: "test", + }); + + expect(res.status).toBe(400); + }); + + it("returns 400 when username is missing", async () => { + const res = await mockApi.post("/api/v1/auth/login").send({ + "!password": "password123", + captcha: "test", + }); + + expect(res.status).toBe(400); + }); + + it("returns 400 when captcha is missing", async () => { + const res = await mockApi.post("/api/v1/auth/login").send({ + username: "seed_user", + "!password": "password123", + }); + + expect(res.status).toBe(400); + }); +}); + +// ─── POST /api/v1/auth/logout ──────────────────────────────────────────────── + +describe("POST /api/v1/auth/logout", () => { + beforeEach(async () => { + ClearTestingRateLimitCache(); + await seedUser(); + }); + + it("returns 409 when not logged in", async () => { + const res = await mockApi.post("/api/v1/auth/logout"); + + expect(res.status).toBe(409); + expect(res.body.success).toBe(false); + }); + + it("returns 200 and destroys the session when logged in", async () => { + const loginRes = await mockApi.post("/api/v1/auth/login").send({ + username: "seed_user", + "!password": "password123", + captcha: "test", + }); + + const cookie = loginRes.headers["set-cookie"] as unknown as string[]; + + const logoutRes = await mockApi.post("/api/v1/auth/logout").set("Cookie", cookie); + + expect(logoutRes.status).toBe(200); + expect(logoutRes.body.success).toBe(true); + }); +}); + +// ─── POST /api/v1/auth/reset-password ──────────────────────────────────────── + +describe("POST /api/v1/auth/reset-password", () => { + beforeEach(async () => { + ClearTestingRateLimitCache(); + await seedUser(); + }); + + it("updates the password when a valid reset code is supplied", async () => { + const { id } = await DB.selectFrom("account") + .select("id") + .where("username", "=", "seed_user") + .executeTakeFirstOrThrow(); + + await DB.insertInto("priv_password_reset_token") + .values({ + token: "RESET_TOKEN", + user_id: id, + created_on: new Date().toISOString(), + }) + .execute(); + + const res = await mockApi.post("/api/v1/auth/reset-password").send({ + code: "RESET_TOKEN", + "!password": "brand_new_password", + }); + + expect(res.status).toBe(200); + expect(res.body.success).toBe(true); + + const cred = await DB.selectFrom("priv_account_credential") + .select("password") + .where("user_id", "=", id) + .executeTakeFirstOrThrow(); + + expect(await PasswordCompare("brand_new_password", cred.password)).toBe(true); + + const canLoginWithNewPassword = await mockApi.post("/api/v1/auth/login").send({ + username: "seed_user", + "!password": "brand_new_password", + captcha: "test", + }); + + expect(canLoginWithNewPassword.status).toBe(200); + }); }); diff --git a/typescript/server/src/server/router/api/v1/auth/auth.ts b/typescript/server/src/server/router/api/v1/auth/auth.ts index c07dc4368..d545c213a 100644 --- a/typescript/server/src/server/router/api/v1/auth/auth.ts +++ b/typescript/server/src/server/router/api/v1/auth/auth.ts @@ -1,20 +1,21 @@ -import type { PrivateUserInfoDocument } from "#utils/types"; - import { log } from "#lib/log/log.js"; -import { Env, ServerConfig } from "#lib/setup/config"; -import db from "#services/mongo/db"; +import { ServerConfig } from "#lib/setup/config"; +import DB from "#services/pg/db.js"; import nodeFetch from "#utils/fetch"; import { Random20Hex } from "#utils/misc"; import { CreateURLWithParams } from "#utils/url"; import { FormatUserDoc } from "#utils/user"; import bcrypt from "bcryptjs"; +import { type Transaction } from "kysely"; import { p } from "prudence"; import { type integer, + type InviteCodeDocument, UserAuthLevels, type UserDocument, type UserSettingsDocument, } from "tachi-common"; +import { type Database } from "tachi-db"; const BCRYPT_SALT_ROUNDS = 12; @@ -36,35 +37,41 @@ export function PasswordCompare(plaintext: string, password: string) { export function ReinstateInvite(code: string) { log.info(`Reinstated Invite ${code}`); - return db.invites.update( - { - code, - }, - { - $set: { - consumed: false, - consumedAt: null, - consumedBy: null, - }, - }, - ); + + return DB.updateTable("priv_invite") + .set({ + consumed: false, + consumed_at: null, + consumed_by: null, + }) + .where("code", "=", code) + .execute(); } -export async function AddNewInvite(user: UserDocument) { +export async function AddNewInvite(user: UserDocument): Promise { const code = Random20Hex(); - const result = await db.invites.insert({ - code, - consumed: false, - createdBy: user.id, - createdAt: Date.now(), - consumedAt: null, - consumedBy: null, - }); + await DB.insertInto("priv_invite") + .values({ + code, + consumed: false, + created_by: user.id, + created_at: new Date().toISOString(), + consumed_at: null, + consumed_by: null, + }) + .execute(); log.info(`User ${FormatUserDoc(user)} created an invite.`); - return result; + return { + code, + createdBy: user.id, + createdAt: Date.now(), + consumed: false, + consumedAt: null, + consumedBy: null, + }; } export const DEFAULT_USER_SETTINGS: UserSettingsDocument["preferences"] = { @@ -80,15 +87,37 @@ export function HashPassword(plaintext: string) { } export async function AddNewUser( + txn: Transaction, + username: string, plaintext: string, email: string, - userID: integer, -) { +): Promise<{ newSettings: UserSettingsDocument; newUser: UserDocument }> { const hashedPassword = await HashPassword(plaintext); log.debug(`Hashed password for ${username}.`); + // no - this is bad + // // all created users on a dev instance should be admins, for convenience. + // if (Env.NODE_ENV === "dev") { + // userDoc.authLevel = UserAuthLevels.ADMIN; + // } + + const userID = await txn + .insertInto("account") + .values({ + about: "I'm a fairly nondescript person.", + username, + joined: new Date().toISOString(), + last_seen: new Date().toISOString(), + auth_level: "user", + custom_pfp_location: null, + custom_banner_location: null, + }) + .returning("id") + .executeTakeFirstOrThrow() + .then((res) => res.id); + const userDoc: UserDocument = { id: userID, username, @@ -104,39 +133,52 @@ export async function AddNewUser( badges: [], }; - // all created users on a dev instance should be admins, for convenience. - if (Env.NODE_ENV === "dev") { - userDoc.authLevel = UserAuthLevels.ADMIN; - } + const settingsRes = await InsertDefaultUserSettings(txn, userID); - const res = await db.users.insert(userDoc); + await InsertPrivateUserInfo(txn, userID, hashedPassword, email); - const settingsRes = await InsertDefaultUserSettings(userID); - - await InsertPrivateUserInfo(userID, hashedPassword, email); - - return { newUser: res, newSettings: settingsRes }; + return { newUser: userDoc, newSettings: settingsRes }; } -export function InsertPrivateUserInfo(userID: integer, hashedPassword: string, email: string) { - const privateInfo: PrivateUserInfoDocument = { - userID, - email, - password: hashedPassword, - }; - - return db["user-private-information"].insert(privateInfo); +export function InsertPrivateUserInfo( + txn: Transaction, + userID: integer, + hashedPassword: string, + email: string, +) { + return txn + .insertInto("priv_account_credential") + .values({ + user_id: userID, + email, + password: hashedPassword, + }) + .execute(); } -export function InsertDefaultUserSettings(userID: integer) { +export async function InsertDefaultUserSettings( + txn: Transaction, + userID: integer, +): Promise { log.debug(`Inserting default settings for ${userID}.`); - const UserSettingsDocument: UserSettingsDocument = { + + await txn + .insertInto("account_settings") + .values({ + user_id: userID, + pf_invisible: DEFAULT_USER_SETTINGS.invisible, + pf_developer_mode: DEFAULT_USER_SETTINGS.developerMode, + pf_advanced_mode: DEFAULT_USER_SETTINGS.advancedMode, + pf_contentious_content: DEFAULT_USER_SETTINGS.contentiousContent, + pf_deletable_scores: DEFAULT_USER_SETTINGS.deletableScores, + }) + .execute(); + + return { userID, following: [], preferences: DEFAULT_USER_SETTINGS, }; - - return db["user-settings"].insert(UserSettingsDocument); } export async function ValidateCaptcha( diff --git a/typescript/server/src/server/router/api/v1postgres/auth/router.test.ts b/typescript/server/src/server/router/api/v1/auth/router.oldtest.ts similarity index 91% rename from typescript/server/src/server/router/api/v1postgres/auth/router.test.ts rename to typescript/server/src/server/router/api/v1/auth/router.oldtest.ts index 56c7ee714..3f491f8dd 100644 --- a/typescript/server/src/server/router/api/v1postgres/auth/router.test.ts +++ b/typescript/server/src/server/router/api/v1/auth/router.oldtest.ts @@ -1,5 +1,5 @@ import { ClearTestingRateLimitCache } from "#server/middleware/rate-limiter"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; import { Sleep } from "#utils/misc"; @@ -122,7 +122,7 @@ t.test("POST /api/v1/auth/register", (t) => { t.beforeEach(ClearTestingRateLimitCache); t.beforeEach(() => - db.invites.insert({ + MONGODB_KILL.invites.insert({ code: "code", createdBy: 1, createdAt: 0, @@ -145,7 +145,7 @@ t.test("POST /api/v1/auth/register", (t) => { t.equal(res.body.success, true); t.equal(res.body.body.username, "foo"); - const doc = await db.users.findOne({ username: "foo" }); + const doc = await MONGODB_KILL.users.findOne({ username: "foo" }); t.not(doc, null); @@ -257,7 +257,7 @@ t.test("POST /api/v1/auth/register", (t) => { t.test("Should recover from a fatal error without breaking state.", async (t) => { // this will cause a userID collision - await db.counters.update({ counterName: "users" }, { $set: { value: 1 } }); + await MONGODB_KILL.counters.update({ counterName: "users" }, { $set: { value: 1 } }); const res = await mockApi.post("/api/v1/auth/register").send({ username: "foo", @@ -269,12 +269,12 @@ t.test("POST /api/v1/auth/register", (t) => { t.equal(res.statusCode, 500); - const counter = await db.counters.findOne({ counterName: "users" }); + const counter = await MONGODB_KILL.counters.findOne({ counterName: "users" }); // value should not stay incremented t.equal(counter?.value, 1); - const invite = await db.invites.findOne({ code: "code" }); + const invite = await MONGODB_KILL.invites.findOne({ code: "code" }); // invite should not be consumed t.equal(invite?.consumed, false); @@ -303,7 +303,7 @@ t.test("POST /api/v1/auth/forgot-password", (t) => { // sorry! await Sleep(3_000); - const dbRes = await db["password-reset-codes"].findOne({ + const dbRes = await MONGODB_KILL["password-reset-codes"].findOne({ userID: 1, }); @@ -325,7 +325,7 @@ t.test("POST /api/v1/auth/forgot-password", (t) => { await Sleep(3_000); - const dbRes = await db["password-reset-codes"].findOne({ + const dbRes = await MONGODB_KILL["password-reset-codes"].findOne({ userID: 1, }); @@ -343,7 +343,7 @@ t.test("POST /api/v1/auth/reset-password", (t) => { t.beforeEach(ClearTestingRateLimitCache); t.test("Should reset a users password if they have a valid code.", async (t) => { - await db["password-reset-codes"].insert({ + await MONGODB_KILL["password-reset-codes"].insert({ code: "SECRET_CODE", createdOn: Date.now(), userID: 1, @@ -356,13 +356,13 @@ t.test("POST /api/v1/auth/reset-password", (t) => { t.equal(res.statusCode, 200); - const dbRes = await db["password-reset-codes"].findOne({ + const dbRes = await MONGODB_KILL["password-reset-codes"].findOne({ code: "SECRET_CODE", }); t.equal(dbRes, null, "Codes MUST be destroyed after use."); - const privateInfo = await db["user-private-information"].findOne({ + const privateInfo = await MONGODB_KILL["user-private-information"].findOne({ userID: 1, }); diff --git a/typescript/server/src/server/router/api/v1/auth/router.test.ts b/typescript/server/src/server/router/api/v1/auth/router.test.ts deleted file mode 100644 index 56c7ee714..000000000 --- a/typescript/server/src/server/router/api/v1/auth/router.test.ts +++ /dev/null @@ -1,378 +0,0 @@ -import { ClearTestingRateLimitCache } from "#server/middleware/rate-limiter"; -import db from "#services/mongo/db"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import { Sleep } from "#utils/misc"; -import t from "tap"; - -import { PasswordCompare } from "./auth"; - -t.test("POST /api/v1/auth/login", (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(ClearTestingRateLimitCache); - - t.test("Should log a user in with right credentials", async (t) => { - const res = await mockApi.post("/api/v1/auth/login").send({ - username: "test_zkldi", - "!password": "password", - captcha: "foo", - }); - - t.equal(res.status, 200); - t.equal(res.body.success, true); - t.strictSame(res.body.body, { - userID: 1, - }); - - const cookie = res.headers["set-cookie"]; - - const stat = await mockApi.get("/api/v1/status").set("Cookie", cookie); - - t.ok(stat.body.body.permissions.length > 0); - - t.end(); - }); - - t.test("Should return 200 if user already logged in", async (t) => { - const res = await mockApi.post("/api/v1/auth/login").send({ - username: "test_zkldi", - "!password": "password", - captcha: "foo", - }); - - const cookie = res.headers["set-cookie"]; - - const res2 = await mockApi - .post("/api/v1/auth/login") - .send({ - username: "test_zkldi", - "!password": "password", - captcha: "foo", - }) - .set("Cookie", cookie); - - // even if they have a login already going, just let them log in. - t.equal(res2.status, 200); - - t.end(); - }); - - t.test("Should return 403 if password invalid", async (t) => { - const res = await mockApi.post("/api/v1/auth/login").send({ - username: "test_zkldi", - "!password": "invalid_password", - captcha: "foo", - }); - - t.equal(res.status, 403); - - t.end(); - }); - - t.test("Should return 404 if user invalid", async (t) => { - const res = await mockApi.post("/api/v1/auth/login").send({ - username: "invalid_user", - "!password": "password", - captcha: "foo", - }); - - t.equal(res.status, 404); - - t.end(); - }); - - t.test("Should return 400 if no password", async (t) => { - const res = await mockApi.post("/api/v1/auth/login").send({ - username: "invalid_user", - captcha: "foo", - }); - - t.equal(res.status, 400); - - t.end(); - }); - - t.test("Should return 400 if no username", async (t) => { - const res = await mockApi.post("/api/v1/auth/login").send({ - "!password": "password", - captcha: "foo", - }); - - t.equal(res.status, 400); - - t.end(); - }); - - t.test("Should return 400 if no captcha", async (t) => { - const res = await mockApi.post("/api/v1/auth/login").send({ - "!password": "password", - username: "test_zkldi", - }); - - t.equal(res.status, 400); - - t.end(); - }); - - t.end(); -}); - -t.test("POST /api/v1/auth/register", (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(ClearTestingRateLimitCache); - - t.beforeEach(() => - db.invites.insert({ - code: "code", - createdBy: 1, - createdAt: 0, - consumed: false, - consumedAt: null, - consumedBy: null, - }), - ); - - t.test("Should register a new user.", async (t) => { - const res = await mockApi.post("/api/v1/auth/register").send({ - username: "foo", - "!password": "password", - email: "foo@bar.com", - captcha: "1", - inviteCode: "code", - }); - - t.equal(res.statusCode, 200); - t.equal(res.body.success, true); - t.equal(res.body.body.username, "foo"); - - const doc = await db.users.findOne({ username: "foo" }); - - t.not(doc, null); - - t.end(); - }); - - t.test("Should disallow users with matching names.", async (t) => { - const res = await mockApi.post("/api/v1/auth/register").send({ - username: "test_zkldi", - "!password": "password", - email: "foo@bar.com", - captcha: "1", - inviteCode: "code", - }); - - t.equal(res.statusCode, 409); - t.equal(res.body.success, false); - - t.end(); - }); - - t.test("Should disallow users with matching names case insensitively.", async (t) => { - const res = await mockApi.post("/api/v1/auth/register").send({ - username: "test_zKLdi", - "!password": "password", - email: "foo@bar.com", - captcha: "1", - inviteCode: "code", - }); - - t.equal(res.statusCode, 409); - t.equal(res.body.success, false); - - t.end(); - }); - - t.test("Should disallow email if it is already used.", async (t) => { - const res = await mockApi.post("/api/v1/auth/register").send({ - username: "foo", - "!password": "password", - - // this is our test docs email, apparently. - email: "thepasswordis@password.com", - captcha: "1", - inviteCode: "code", - }); - - t.equal(res.statusCode, 409); - t.equal(res.body.success, false); - - t.end(); - }); - - t.test("Should disallow invalid emails.", async (t) => { - const res = await mockApi.post("/api/v1/auth/register").send({ - username: "foo", - "!password": "password", - email: "nonsense+email", - captcha: "1", - inviteCode: "code", - }); - - t.equal(res.statusCode, 400); - t.equal(res.body.success, false); - - t.end(); - }); - - t.test("Should disallow short passwords.", async (t) => { - const res = await mockApi.post("/api/v1/auth/register").send({ - username: "foo", - "!password": "pass", - email: "foo@bar.com", - captcha: "1", - inviteCode: "code", - }); - - t.equal(res.statusCode, 400); - t.equal(res.body.success, false); - - t.end(); - }); - - t.test("Should disallow invalid usernames.", async (t) => { - const res = await mockApi.post("/api/v1/auth/register").send({ - username: "3foo", - "!password": "password", - email: "foo@bar.com", - captcha: "1", - inviteCode: "code", - }); - - t.equal(res.statusCode, 400); - t.equal(res.body.success, false); - - const res2 = await mockApi.post("/api/v1/auth/register").send({ - username: "f", - "!password": "password", - email: "foo@bar.com", - captcha: "1", - inviteCode: "code", - }); - - t.equal(res2.statusCode, 400); - t.equal(res2.body.success, false); - - t.end(); - }); - - t.test("Should recover from a fatal error without breaking state.", async (t) => { - // this will cause a userID collision - await db.counters.update({ counterName: "users" }, { $set: { value: 1 } }); - - const res = await mockApi.post("/api/v1/auth/register").send({ - username: "foo", - "!password": "password", - email: "foo@bar.com", - captcha: "1", - inviteCode: "code", - }); - - t.equal(res.statusCode, 500); - - const counter = await db.counters.findOne({ counterName: "users" }); - - // value should not stay incremented - t.equal(counter?.value, 1); - - const invite = await db.invites.findOne({ code: "code" }); - - // invite should not be consumed - t.equal(invite?.consumed, false); - - t.end(); - }); - - t.end(); -}); - -t.test("POST /api/v1/auth/forgot-password", (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(ClearTestingRateLimitCache); - - t.test("Should create a code to reset a password with.", async (t) => { - const res = await mockApi.post("/api/v1/auth/forgot-password").send({ - email: "thepasswordis@password.com", - }); - - t.equal(res.statusCode, 202, "Should return 202 immediately."); - - t.strictSame(res.body.body, {}, "Should have no body."); - - // We have to wait for this operation to complete, otherwise, this isn't going to work. - // Note that 3seconds is a bit excessive, but better safe than - // sorry! - await Sleep(3_000); - - const dbRes = await db["password-reset-codes"].findOne({ - userID: 1, - }); - - t.not(dbRes, null, "Should exist and save a code to the database."); - - t.end(); - }); - - t.test( - "Should not create a code to reset a password with if the email does not exist.", - async (t) => { - const res = await mockApi.post("/api/v1/auth/forgot-password").send({ - email: "bademail@example.com", - }); - - t.equal(res.statusCode, 202, "Should return 202 immediately."); - - t.strictSame(res.body.body, {}, "Should have no body."); - - await Sleep(3_000); - - const dbRes = await db["password-reset-codes"].findOne({ - userID: 1, - }); - - t.equal(dbRes, null, "Should not bother sending a code to the database."); - - t.end(); - }, - ); - - t.end(); -}); - -t.test("POST /api/v1/auth/reset-password", (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(ClearTestingRateLimitCache); - - t.test("Should reset a users password if they have a valid code.", async (t) => { - await db["password-reset-codes"].insert({ - code: "SECRET_CODE", - createdOn: Date.now(), - userID: 1, - }); - - const res = await mockApi.post("/api/v1/auth/reset-password").send({ - code: "SECRET_CODE", - "!password": "newpassword", - }); - - t.equal(res.statusCode, 200); - - const dbRes = await db["password-reset-codes"].findOne({ - code: "SECRET_CODE", - }); - - t.equal(dbRes, null, "Codes MUST be destroyed after use."); - - const privateInfo = await db["user-private-information"].findOne({ - userID: 1, - }); - - t.ok( - await PasswordCompare("newpassword", privateInfo!.password), - "Password must be updated to 'newpassword'", - ); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1/auth/router.ts b/typescript/server/src/server/router/api/v1/auth/router.ts index 18ba98810..18f05fe31 100644 --- a/typescript/server/src/server/router/api/v1/auth/router.ts +++ b/typescript/server/src/server/router/api/v1/auth/router.ts @@ -1,5 +1,3 @@ -import type { integer } from "tachi-common"; - import { SendEmail } from "#lib/email/client"; import { EmailFormatResetPassword, EmailFormatVerifyEmail } from "#lib/email/formats"; import { log } from "#lib/log/log.js"; @@ -9,9 +7,9 @@ import { AggressiveRateLimitMiddleware, HyperAggressiveRateLimitMiddleware, } from "#server/middleware/rate-limiter"; -import db from "#services/mongo/db"; -import { DecrementCounterValue, GetNextCounterValue } from "#utils/db"; +import DB from "#services/pg/db.js"; import { Random20Hex } from "#utils/misc"; +import { apiSuccess } from "#utils/response.js"; import { CheckIfEmailInUse, FormatUserDoc, @@ -23,14 +21,13 @@ import { } from "#utils/user"; import { Router } from "express"; import { p } from "prudence"; +import { type UserDocument } from "tachi-common"; import { AddNewUser, HashPassword, - InsertDefaultUserSettings, MountAuthCookie, PasswordCompare, - ReinstateInvite, ValidateCaptcha, ValidateEmail, ValidatePassword, @@ -137,12 +134,7 @@ router.post( }); } - let settings = await GetSettingsForUser(requestedUser.id); - - if (!settings) { - log.warn(`User ${FormatUserDoc(user)} has no settings. Inserting default settings.`); - settings = await InsertDefaultUserSettings(user.id); - } + const settings = await GetSettingsForUser(requestedUser.id); MountAuthCookie(req, user, settings); @@ -249,94 +241,84 @@ router.post( }); } - let hasInsertedUserID: integer | null = null; - try { - const userID = await GetNextCounterValue("users"); + const newUser = await DB.transaction().execute(async (txn): Promise => { + // if we get to this point, We're good to create the user. - if (ServerConfig.INVITE_CODE_CONFIG) { - const inviteCodeDoc = await db.invites.findOneAndUpdate( - { - code: body.inviteCode, - consumed: false, - }, - { - $set: { - consumed: true, - consumedAt: Date.now(), - consumedBy: userID, - }, - }, + const { newUser, newSettings } = await AddNewUser( + txn, + body.username, + body["!password"], + body.email, ); - if (!inviteCodeDoc) { - log.info(`Invalid invite code given: ${body.inviteCode}.`); - return res.status(401).json({ - success: false, - description: `This invite code is not valid.`, - }); + if (ServerConfig.INVITE_CODE_CONFIG) { + if (!body.inviteCode) { + throw new Error("No invite code given, yet the server uses invites."); + } + + const inviteCodeDoc = await txn + .selectFrom("priv_invite") + .select("code") + .where("code", "=", body.inviteCode) + .where("consumed", "=", false) + .executeTakeFirst(); + + if (!inviteCodeDoc) { + log.info(`Invalid invite code given: ${body.inviteCode}.`); + throw new Error(`Invalid invite code given: ${body.inviteCode}.`); + } + + log.info(`Consumed invite ${inviteCodeDoc.code}.`); + + await txn + .updateTable("priv_invite") + .set({ + consumed: true, + consumed_at: new Date().toISOString(), + consumed_by: newUser.id, + }) + .where("code", "=", body.inviteCode) + .execute(); } - log.info(`Consumed invite ${inviteCodeDoc.code}.`); - } + MountAuthCookie(req, newUser, newSettings); - // if we get to this point, We're good to create the user. + // If we have an EMAIL_CONFIG set, send out + // authentication emails. + // Otherwise, don't bother; this is equivalent to + // automatically verifying all users' emails. + if (ServerConfig.EMAIL_CONFIG) { + const resetEmailCode = Random20Hex(); - const { newUser, newSettings } = await AddNewUser( - body.username, - body["!password"], - body.email, - userID, - ); + await DB.insertInto("priv_verify_email_token") + .values({ + token: resetEmailCode, + user_id: newUser.id, + email: body.email, + }) + .execute(); - hasInsertedUserID = newUser.id; + // TODO: Put this on job queue + const { text, html } = EmailFormatVerifyEmail(newUser.username, resetEmailCode); - // re-fetch the user like this so we guaranteeably omit the private fields. - const user = await GetUserWithIDGuaranteed(newUser.id); + void SendEmail(body.email, "Email Verification", html, text); + } - MountAuthCookie(req, user, newSettings); - - // If we have an EMAIL_CONFIG set, send out - // authentication emails. - // Otherwise, don't bother; this is equivalent to - // automatically verifying all users' emails. - if (ServerConfig.EMAIL_CONFIG) { - const resetEmailCode = Random20Hex(); - - await db["verify-email-codes"].insert({ - code: resetEmailCode, - userID, - email: body.email, - }); - - const { text, html } = EmailFormatVerifyEmail(user.username, resetEmailCode); - - void SendEmail(body.email, "Email Verification", html, text); - } - - return res.status(200).json({ - success: true, - description: `Successfully created account ${body.username}!`, - body: user, + return newUser; }); + + return res + .status(200) + .json( + apiSuccess( + `Successfully created account ${body.username}!`, + newUser, + ), + ); } catch (err) { log.error({ err }, `Bailed on user creation ${body.username}.`); - if (ServerConfig.INVITE_CODE_CONFIG && body.inviteCode !== undefined) { - await ReinstateInvite(body.inviteCode); - } - - if (hasInsertedUserID !== null) { - log.warn( - `Removing user ${body.username} (#${hasInsertedUserID}), as their document was created, but creation still failed.`, - ); - await db.users.remove({ username: body.username }); - await db["user-settings"].remove({ userID: hasInsertedUserID }); - await db["user-private-information"].remove({ userID: hasInsertedUserID }); - } - - await DecrementCounterValue("users"); - return res.status(500).json({ success: false, description: "An internal server error has occured.", @@ -363,9 +345,10 @@ router.post( code: string; }; - const code = await db["verify-email-codes"].findOne({ - code: body.code, - }); + const code = await DB.selectFrom("priv_verify_email_token") + .select("user_id") + .where("token", "=", body.code) + .executeTakeFirstOrThrow(); if (!code) { return res.status(400).json({ @@ -374,9 +357,7 @@ router.post( }); } - await db["verify-email-codes"].remove({ - code: body.code, - }); + await DB.deleteFrom("priv_verify_email_token").where("token", "=", body.code).execute(); return res.status(200).json({ success: true, @@ -411,7 +392,10 @@ router.post("/resend-verify-email", HyperAggressiveRateLimitMiddleware, async (r const userID = user.id; - const verifyInfo = await db["verify-email-codes"].findOne({ userID }); + const verifyInfo = await DB.selectFrom("priv_verify_email_token") + .select(["email", "token"]) + .where("user_id", "=", userID) + .executeTakeFirstOrThrow(); if (!verifyInfo) { log.warn(`Attempted to send reset email to ${userID}, but no verifyInfo was set for them.`); @@ -420,7 +404,7 @@ router.post("/resend-verify-email", HyperAggressiveRateLimitMiddleware, async (r // Send the email again. - const { text, html } = EmailFormatVerifyEmail(user.username, verifyInfo.code); + const { text, html } = EmailFormatVerifyEmail(user.username, verifyInfo.token); void SendEmail(verifyInfo.email, "Email Verification", html, text); }); @@ -481,16 +465,17 @@ router.post( body: {}, }); - const userPrivateInfo = await db["user-private-information"].findOne({ - email: body.email, - }); + const userPrivateInfo = await DB.selectFrom("priv_account_credential") + .select(["user_id", "email"]) + .where("email", "=", body.email) + .executeTakeFirstOrThrow(); if (userPrivateInfo) { - const user = await db.users.findOne({ id: userPrivateInfo.userID }); + const user = await GetUserWithIDGuaranteed(userPrivateInfo.user_id); if (!user) { log.error( - `User ${userPrivateInfo.userID} has private information but no real account.`, + `User ${userPrivateInfo.user_id} has private information but no real account.`, ); return; } @@ -499,11 +484,13 @@ router.post( log.debug(`Created password reset code for ${FormatUserDoc(user)}.`); - await db["password-reset-codes"].insert({ - code, - userID: user.id, - createdOn: Date.now(), - }); + await DB.insertInto("priv_password_reset_token") + .values({ + token: code, + user_id: user.id, + created_on: new Date().toISOString(), + }) + .execute(); const { html, text } = EmailFormatResetPassword(user.username, code, req.ip); @@ -538,31 +525,28 @@ router.post( code: string; }; - const code = await db["password-reset-codes"].findOneAndDelete({ - code: body.code, - }); + const code = await DB.selectFrom("priv_password_reset_token") + .select("user_id") + .where("token", "=", body.code) + .executeTakeFirstOrThrow(); if (!code) { return res.status(404).json({ success: false, - description: `Invalid Reset Code.`, + description: `Invalid reset code.`, }); } - const encryptedPassword = await HashPassword(body["!password"]); + const hashedPassword = await HashPassword(body["!password"]); - await db["user-private-information"].update( - { - userID: code.userID, - }, - { - $set: { - password: encryptedPassword, - }, - }, - ); + await DB.updateTable("priv_account_credential") + .set({ + password: hashedPassword, + }) + .where("user_id", "=", code.user_id) + .execute(); - log.info(`User ${code.userID} reset their password.`); + log.info(`User ${code.user_id} reset their password.`); return res.status(200).json({ success: true, diff --git a/typescript/server/src/server/router/api/v1/clients/middleware.test.ts b/typescript/server/src/server/router/api/v1/clients/middleware.oldtest.ts similarity index 100% rename from typescript/server/src/server/router/api/v1/clients/middleware.test.ts rename to typescript/server/src/server/router/api/v1/clients/middleware.oldtest.ts diff --git a/typescript/server/src/server/router/api/v1/clients/middleware.ts b/typescript/server/src/server/router/api/v1/clients/middleware.ts index 93de2eb4e..182ab3a85 100644 --- a/typescript/server/src/server/router/api/v1/clients/middleware.ts +++ b/typescript/server/src/server/router/api/v1/clients/middleware.ts @@ -2,11 +2,11 @@ import type { RequestHandler } from "express"; import type { TachiAPIClientDocument } from "tachi-common"; import { Env } from "#lib/setup/config"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { AssignToReqTachiData, GetTachiData } from "#utils/req-tachi-data"; export const GetClientFromID: RequestHandler = async (req, res, next) => { - const client = await db["api-clients"].findOne( + const client = await MONGODB_KILL["api-clients"].findOne( { clientID: req.params.clientID, }, diff --git a/typescript/server/src/server/router/api/v1postgres/clients/router.test.ts b/typescript/server/src/server/router/api/v1/clients/router.oldtest.ts similarity index 90% rename from typescript/server/src/server/router/api/v1postgres/clients/router.test.ts rename to typescript/server/src/server/router/api/v1/clients/router.oldtest.ts index d0f9b08c9..b4359aa4a 100644 --- a/typescript/server/src/server/router/api/v1postgres/clients/router.test.ts +++ b/typescript/server/src/server/router/api/v1/clients/router.oldtest.ts @@ -1,7 +1,7 @@ import type { APITokenDocument, TachiAPIClientDocument } from "tachi-common"; import { ServerConfig } from "#lib/setup/config"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; @@ -46,8 +46,8 @@ const clientDataset: Array = [ t.test("GET /api/v1/clients", async (t) => { t.beforeEach(ResetDBState); t.beforeEach(async () => { - await db["api-clients"].remove({}); - await db["api-clients"].insert(clientDataset); + await MONGODB_KILL["api-clients"].remove({}); + await MONGODB_KILL["api-clients"].insert(clientDataset); }); const cookie = await CreateFakeAuthCookie(mockApi); @@ -128,7 +128,9 @@ t.test("POST /api/v1/clients/create", async (t) => { t.equal(res.statusCode, 200); - const dbRes = await db["api-clients"].findOne({ clientID: res.body.body.clientID }); + const dbRes = await MONGODB_KILL["api-clients"].findOne({ + clientID: res.body.body.clientID, + }); t.not(dbRes, null, "Should be saved in the database."); @@ -236,7 +238,7 @@ t.test("POST /api/v1/clients/create", async (t) => { t.equal(res.statusCode, 400); - const dbCount = await db["api-clients"].count({ author: 1 }); + const dbCount = await MONGODB_KILL["api-clients"].count({ author: 1 }); t.equal(dbCount, ServerConfig.OAUTH_CLIENT_CAP); @@ -282,8 +284,8 @@ t.test("GET /api/v1/clients/:clientID", (t) => { t.test("PATCH /api/v1/clients/:clientID", async (t) => { t.beforeEach(ResetDBState); t.beforeEach(async () => { - await db["api-clients"].remove({}); - await db["api-clients"].insert(clientDataset); + await MONGODB_KILL["api-clients"].remove({}); + await MONGODB_KILL["api-clients"].insert(clientDataset); }); const cookie = await CreateFakeAuthCookie(mockApi); @@ -298,7 +300,7 @@ t.test("PATCH /api/v1/clients/:clientID", async (t) => { t.equal(res.body.body.name, "NEW NAME"); - const dbRes = await db["api-clients"].findOne({ + const dbRes = await MONGODB_KILL["api-clients"].findOne({ clientID: "CLIENT_1", }); @@ -317,7 +319,7 @@ t.test("PATCH /api/v1/clients/:clientID", async (t) => { t.equal(res.body.body.webhookUri, "https://example.com"); - const dbRes = await db["api-clients"].findOne({ + const dbRes = await MONGODB_KILL["api-clients"].findOne({ clientID: "CLIENT_1", }); @@ -373,8 +375,8 @@ t.test("PATCH /api/v1/clients/:clientID", async (t) => { t.test("POST /api/v1/clients/:clientID/reset-secret", async (t) => { t.beforeEach(ResetDBState); t.beforeEach(async () => { - await db["api-clients"].remove({}); - await db["api-clients"].insert(clientDataset); + await MONGODB_KILL["api-clients"].remove({}); + await MONGODB_KILL["api-clients"].insert(clientDataset); }); const cookie = await CreateFakeAuthCookie(mockApi); @@ -388,7 +390,7 @@ t.test("POST /api/v1/clients/:clientID/reset-secret", async (t) => { t.not(res.body.body.clientSecret, "SECRET_1", "Should return the new secret."); - const dbRes = await db["api-clients"].findOne({ + const dbRes = await MONGODB_KILL["api-clients"].findOne({ clientID: "CLIENT_1", }); @@ -417,14 +419,14 @@ t.test("POST /api/v1/clients/:clientID/reset-secret", async (t) => { t.test("DELETE /api/v1/clients/:clientID", async (t) => { t.beforeEach(ResetDBState); t.beforeEach(async () => { - await db["api-clients"].remove({}); - await db["api-clients"].insert(clientDataset); + await MONGODB_KILL["api-clients"].remove({}); + await MONGODB_KILL["api-clients"].insert(clientDataset); }); const cookie = await CreateFakeAuthCookie(mockApi); t.test("Should destroy the client and all associated api keys.", async (t) => { - await db["api-tokens"].insert([ + await MONGODB_KILL["api-tokens"].insert([ { fromAPIClient: "CLIENT_1", token: "foo", @@ -441,11 +443,11 @@ t.test("DELETE /api/v1/clients/:clientID", async (t) => { t.equal(res.statusCode, 200); - const dbRes = await db["api-clients"].findOne({ clientID: "CLIENT_1" }); + const dbRes = await MONGODB_KILL["api-clients"].findOne({ clientID: "CLIENT_1" }); t.equal(dbRes, null, "Should no longer exist."); - const dbCount = await db["api-tokens"].count({ fromOAuth2Client: "CLIENT_1" }); + const dbCount = await MONGODB_KILL["api-tokens"].count({ fromOAuth2Client: "CLIENT_1" }); t.equal(dbCount, 0, "Should have destroyed all related api tokens."); diff --git a/typescript/server/src/server/router/api/v1/clients/router.test.ts b/typescript/server/src/server/router/api/v1/clients/router.test.ts deleted file mode 100644 index d0f9b08c9..000000000 --- a/typescript/server/src/server/router/api/v1/clients/router.test.ts +++ /dev/null @@ -1,468 +0,0 @@ -import type { APITokenDocument, TachiAPIClientDocument } from "tachi-common"; - -import { ServerConfig } from "#lib/setup/config"; -import db from "#services/mongo/db"; -import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import t from "tap"; - -const clientDataset: Array = [ - { - author: 1, - clientID: "CLIENT_1", - clientSecret: "SECRET_1", - name: "foo", - redirectUri: "example.com", - requestedPermissions: ["customise_profile"], - webhookUri: null, - apiKeyFilename: null, - apiKeyTemplate: null, - }, - { - author: 1, - clientID: "CLIENT_2", - clientSecret: "SECRET_2", - name: "bar", - redirectUri: "example.com", - requestedPermissions: ["customise_profile"], - webhookUri: null, - apiKeyFilename: null, - apiKeyTemplate: null, - }, - { - author: 2, - clientID: "CLIENT_3", - clientSecret: "SECRET_3", - name: "baz", - redirectUri: "example.com", - requestedPermissions: ["customise_profile"], - webhookUri: null, - apiKeyFilename: null, - apiKeyTemplate: null, - }, -]; - -t.test("GET /api/v1/clients", async (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(async () => { - await db["api-clients"].remove({}); - await db["api-clients"].insert(clientDataset); - }); - - const cookie = await CreateFakeAuthCookie(mockApi); - - t.test("Should retrieve your clients.", async (t) => { - const res = await mockApi.get("/api/v1/clients").set("Cookie", cookie); - - t.equal(res.statusCode, 200); - - // note: force sort alphabetically so this isn't dependent on - // mongodb natural order. - t.strictSame( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - res.body.body.sort((a: any, b: any) => a.name - b.name), - [ - { - author: 1, - clientID: "CLIENT_1", - clientSecret: "SECRET_1", - name: "foo", - redirectUri: "example.com", - requestedPermissions: ["customise_profile"], - webhookUri: null, - apiKeyFilename: null, - apiKeyTemplate: null, - }, - { - author: 1, - clientID: "CLIENT_2", - clientSecret: "SECRET_2", - name: "bar", - redirectUri: "example.com", - requestedPermissions: ["customise_profile"], - webhookUri: null, - apiKeyFilename: null, - apiKeyTemplate: null, - }, - ], - ); - - t.end(); - }); - - t.test("Requires self-key level authentication.", async (t) => { - const res = await mockApi.get("/api/v1/clients"); - - t.equal(res.statusCode, 401); - - const res2 = await mockApi - .get("/api/v1/clients") - .set("Authorization", "Bearer fake_api_token"); - - t.equal(res2.statusCode, 401); - - t.end(); - }); - - t.end(); -}); - -t.test("POST /api/v1/clients/create", async (t) => { - t.beforeEach(ResetDBState); - - const cookie = await CreateFakeAuthCookie(mockApi); - - t.test("Should create a new client.", async (t) => { - const res = await mockApi - .post("/api/v1/clients/create") - .send({ - name: "Hello World", - redirectUri: "https://example.com/callback", - permissions: ["customise_profile"], - webhookUri: null, - apiKeyTemplate: null, - apiKeyFilename: null, - }) - .set("Cookie", cookie); - - t.equal(res.statusCode, 200); - - const dbRes = await db["api-clients"].findOne({ clientID: res.body.body.clientID }); - - t.not(dbRes, null, "Should be saved in the database."); - - t.strictSame(dbRes?.requestedPermissions, ["customise_profile"]); - - t.end(); - }); - - t.test("Should validate names to be between 3 and 80 characters.", async (t) => { - const res = await mockApi - .post("/api/v1/clients/create") - .send({ - name: "2", - redirectUri: "https://example.com/callback", - permissions: ["customise_profile"], - webhookUri: null, - apiKeyTemplate: null, - apiKeyFilename: null, - }) - .set("Cookie", cookie); - - t.equal(res.statusCode, 400); - - const res2 = await mockApi - .post("/api/v1/clients/create") - .send({ - name: "2".repeat(100), - redirectUri: "https://example.com/callback", - permissions: ["customise_profile"], - webhookUri: null, - apiKeyTemplate: null, - apiKeyFilename: null, - }) - .set("Cookie", cookie); - - t.equal(res2.statusCode, 400); - - t.end(); - }); - - t.test("Should validate urls to be between 3 and 80 characters.", async (t) => { - const res = await mockApi - .post("/api/v1/clients/create") - .send({ - name: "Hello World", - redirectUri: "ftp://example.com/callback", - permissions: ["customise_profile"], - webhookUri: null, - apiKeyTemplate: null, - apiKeyFilename: null, - }) - .set("Cookie", cookie); - - t.equal(res.statusCode, 400); - - t.end(); - }); - - t.test("Should validate permissions.", async (t) => { - const res = await mockApi - .post("/api/v1/clients/create") - .send({ - name: "Hello World", - redirectUri: "http://example.com/callback", - permissions: ["permission_that_doesnt_exist"], - webhookUri: null, - apiKeyTemplate: null, - apiKeyFilename: null, - }) - .set("Cookie", cookie); - - t.equal(res.statusCode, 400); - - t.end(); - }); - - // Currently skipped as its difficult to mock user auth level. - t.skip("Should cap a user at OAUTH_CLIENT_CAP.", async (t) => { - for (let i = 0; i < ServerConfig.OAUTH_CLIENT_CAP; i++) { - // eslint-disable-next-line no-await-in-loop - await mockApi - .post("/api/v1/clients/create") - .send({ - name: "Hello World", - redirectUri: "https://example.com/callback", - permissions: ["customise_profile"], - webhookUri: null, - apiKeyTemplate: null, - apiKeyFilename: null, - }) - .set("Cookie", cookie); - } - - const res = await mockApi - .post("/api/v1/clients/create") - .send({ - name: "Hello World", - redirectUri: "https://example.com/callback", - permissions: ["customise_profile"], - webhookUri: null, - apiKeyTemplate: null, - apiKeyFilename: null, - }) - .set("Cookie", cookie); - - t.equal(res.statusCode, 400); - - const dbCount = await db["api-clients"].count({ author: 1 }); - - t.equal(dbCount, ServerConfig.OAUTH_CLIENT_CAP); - - t.end(); - }); - - t.end(); -}); - -t.test("GET /api/v1/clients/:clientID", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should return information about the client at that ID.", async (t) => { - const res = await mockApi.get("/api/v1/clients/OAUTH2_CLIENT_ID"); - - t.strictSame(res.body.body, { - clientID: "OAUTH2_CLIENT_ID", - - // clientSecret: "OAUTH2_CLIENT_SECRET", MUST NOT have secret! - name: "Test_Service", - author: 1, - requestedPermissions: ["customise_profile"], - redirectUri: "https://example.com/callback", - webhookUri: null, - apiKeyTemplate: null, - apiKeyFilename: null, - }); - - t.end(); - }); - - t.test("Should return 404 if client doesn't exist.", async (t) => { - const res = await mockApi.get("/api/v1/clients/BAD_CLIENT"); - - t.equal(res.statusCode, 404); - - t.end(); - }); - - t.end(); -}); - -t.test("PATCH /api/v1/clients/:clientID", async (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(async () => { - await db["api-clients"].remove({}); - await db["api-clients"].insert(clientDataset); - }); - - const cookie = await CreateFakeAuthCookie(mockApi); - - t.test("Should be able to modify a clients name.", async (t) => { - const res = await mockApi - .patch("/api/v1/clients/CLIENT_1") - .send({ name: "NEW NAME" }) - .set("Cookie", cookie); - - t.equal(res.statusCode, 200); - - t.equal(res.body.body.name, "NEW NAME"); - - const dbRes = await db["api-clients"].findOne({ - clientID: "CLIENT_1", - }); - - t.equal(dbRes?.name, "NEW NAME"); - - t.end(); - }); - - t.test("Should be able to modify a clients webhookUri.", async (t) => { - const res = await mockApi - .patch("/api/v1/clients/CLIENT_1") - .send({ webhookUri: "https://example.com" }) - .set("Cookie", cookie); - - t.equal(res.statusCode, 200); - - t.equal(res.body.body.webhookUri, "https://example.com"); - - const dbRes = await db["api-clients"].findOne({ - clientID: "CLIENT_1", - }); - - t.equal(dbRes?.webhookUri, "https://example.com"); - - t.end(); - }); - - t.test("Must validate name to be between 3 and 80 characters.", async (t) => { - const res = await mockApi - .patch("/api/v1/clients/CLIENT_1") - .send({ name: "2" }) - .set("Cookie", cookie); - - t.equal(res.statusCode, 400); - - const res2 = await mockApi - .patch("/api/v1/clients/CLIENT_1") - .send({ name: "2".repeat(100) }) - .set("Cookie", cookie); - - t.equal(res2.statusCode, 400); - - t.end(); - }); - - t.test("Must provide name to modify.", async (t) => { - const res = await mockApi.patch("/api/v1/clients/CLIENT_1").send({}).set("Cookie", cookie); - - t.equal(res.statusCode, 400); - - t.end(); - }); - - t.test("Must be owner of client.", async (t) => { - const res = await mockApi - .patch("/api/v1/clients/CLIENT_3") - .send({ name: "foo" }) - .set("Cookie", cookie); - - t.equal(res.statusCode, 403); - - const res2 = await mockApi.patch("/api/v1/clients/CLIENT_3").send({ name: "foo" }); - - t.equal(res2.statusCode, 401); - - t.end(); - }); - - t.end(); -}); - -t.test("POST /api/v1/clients/:clientID/reset-secret", async (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(async () => { - await db["api-clients"].remove({}); - await db["api-clients"].insert(clientDataset); - }); - - const cookie = await CreateFakeAuthCookie(mockApi); - - t.test("Should reset the client's secret.", async (t) => { - const res = await mockApi - .post("/api/v1/clients/CLIENT_1/reset-secret") - .set("Cookie", cookie); - - t.equal(res.statusCode, 200); - - t.not(res.body.body.clientSecret, "SECRET_1", "Should return the new secret."); - - const dbRes = await db["api-clients"].findOne({ - clientID: "CLIENT_1", - }); - - t.not(dbRes?.clientSecret, "SECRET_1", "Should change secret to anything else."); - - t.end(); - }); - - t.test("Must be owner of client.", async (t) => { - const res = await mockApi - .post("/api/v1/clients/CLIENT_3/reset-secret") - .set("Cookie", cookie); - - t.equal(res.statusCode, 403); - - const res2 = await mockApi.post("/api/v1/clients/CLIENT_3/reset-secret"); - - t.equal(res2.statusCode, 401); - - t.end(); - }); - - t.end(); -}); - -t.test("DELETE /api/v1/clients/:clientID", async (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(async () => { - await db["api-clients"].remove({}); - await db["api-clients"].insert(clientDataset); - }); - - const cookie = await CreateFakeAuthCookie(mockApi); - - t.test("Should destroy the client and all associated api keys.", async (t) => { - await db["api-tokens"].insert([ - { - fromAPIClient: "CLIENT_1", - token: "foo", - userID: 1, - }, - { - fromAPIClient: "CLIENT_1", - token: "bar", - userID: 1, - }, - ] as Array); - - const res = await mockApi.delete("/api/v1/clients/CLIENT_1").set("Cookie", cookie); - - t.equal(res.statusCode, 200); - - const dbRes = await db["api-clients"].findOne({ clientID: "CLIENT_1" }); - - t.equal(dbRes, null, "Should no longer exist."); - - const dbCount = await db["api-tokens"].count({ fromOAuth2Client: "CLIENT_1" }); - - t.equal(dbCount, 0, "Should have destroyed all related api tokens."); - - t.end(); - }); - - t.test("Must be owner of client.", async (t) => { - const res = await mockApi.delete("/api/v1/clients/CLIENT_3").set("Cookie", cookie); - - t.equal(res.statusCode, 403); - - const res2 = await mockApi.delete("/api/v1/clients/CLIENT_3"); - - t.equal(res2.statusCode, 401); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1/clients/router.ts b/typescript/server/src/server/router/api/v1/clients/router.ts index 6baea9023..817d3f781 100644 --- a/typescript/server/src/server/router/api/v1/clients/router.ts +++ b/typescript/server/src/server/router/api/v1/clients/router.ts @@ -1,7 +1,7 @@ import { log } from "#lib/log/log.js"; import { ServerConfig } from "#lib/setup/config"; import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { DedupeArr, DeleteUndefinedProps, IsValidURL, Random20Hex } from "#utils/misc"; import { optNull } from "#utils/prudence"; import { GetTachiData } from "#utils/req-tachi-data"; @@ -36,7 +36,7 @@ router.get("/", async (req, res) => { }); } - const clients = await db["api-clients"].find({ + const clients = await MONGODB_KILL["api-clients"].find({ author: user.id, }); @@ -101,7 +101,7 @@ router.post( webhookUri: string | null; }; - const existingClients = await db["api-clients"].find({ + const existingClients = await MONGODB_KILL["api-clients"].find({ author: req.session.tachi.user.id, }); @@ -154,7 +154,7 @@ router.post( apiKeyTemplate: body.apiKeyTemplate ?? null, }; - await db["api-clients"].insert(clientDoc); + await MONGODB_KILL["api-clients"].insert(clientDoc); log.info( `User ${FormatUserDoc(req.session.tachi.user)} created a new API Client ${ @@ -263,7 +263,7 @@ router.patch( }); } - const newClient = await db["api-clients"].findOneAndUpdate( + const newClient = await MONGODB_KILL["api-clients"].findOneAndUpdate( { clientID: client.clientID, }, @@ -302,7 +302,7 @@ router.post( const newSecret = Random20Hex(); - const newClient = await db["api-clients"].findOneAndUpdate( + const newClient = await MONGODB_KILL["api-clients"].findOneAndUpdate( { clientID: client.clientID, }, @@ -334,13 +334,13 @@ router.delete("/:clientID", GetClientFromID, RequireOwnershipOfClient, async (re log.info(`received request to destroy API Client ${client.name} (${client.clientID})`); log.debug(`Removing API Client ${clientName}.`); - await db["api-clients"].remove({ + await MONGODB_KILL["api-clients"].remove({ clientID: client.clientID, }); log.info(`Removed API Client ${clientName}.`); log.debug(`Removing all associated api tokens.`); - const result = await db["api-tokens"].remove({ + const result = await MONGODB_KILL["api-tokens"].remove({ fromOAuth2Client: client.clientID, }); diff --git a/typescript/server/src/server/router/api/v1/config/router.test.ts b/typescript/server/src/server/router/api/v1/config/router.oldtest.ts similarity index 100% rename from typescript/server/src/server/router/api/v1/config/router.test.ts rename to typescript/server/src/server/router/api/v1/config/router.oldtest.ts diff --git a/typescript/server/src/server/router/api/v1postgres/games/@gameSpecificRoutes/bms/router.test.ts b/typescript/server/src/server/router/api/v1/games/@gameSpecificRoutes/bms/router.oldtest.ts similarity index 94% rename from typescript/server/src/server/router/api/v1postgres/games/@gameSpecificRoutes/bms/router.test.ts rename to typescript/server/src/server/router/api/v1/games/@gameSpecificRoutes/bms/router.oldtest.ts index 06f7f4403..bf2dcbb90 100644 --- a/typescript/server/src/server/router/api/v1postgres/games/@gameSpecificRoutes/bms/router.test.ts +++ b/typescript/server/src/server/router/api/v1/games/@gameSpecificRoutes/bms/router.oldtest.ts @@ -1,10 +1,10 @@ import { ServerConfig } from "#lib/setup/config"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import mockApi from "#test-utils/mock-api"; import t from "tap"; t.test("GET /api/v1/games/bms/:playtype/custom-tables/:tableUrlName", async (t) => { - await db.tables.insert({ + await MONGODB_KILL.tables.insert({ default: false, description: "EC difficulties according to Sieglinde.", @@ -17,7 +17,7 @@ t.test("GET /api/v1/games/bms/:playtype/custom-tables/:tableUrlName", async (t) title: "Sieglinde EC", }); - await db.folders.insert({ + await MONGODB_KILL.folders.insert({ folderID: "sieglinde_folder", game: "bms", playtype: "7K", diff --git a/typescript/server/src/server/router/api/v1/games/@gameSpecificRoutes/bms/router.test.ts b/typescript/server/src/server/router/api/v1/games/@gameSpecificRoutes/bms/router.test.ts deleted file mode 100644 index 06f7f4403..000000000 --- a/typescript/server/src/server/router/api/v1/games/@gameSpecificRoutes/bms/router.test.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { ServerConfig } from "#lib/setup/config"; -import db from "#services/mongo/db"; -import mockApi from "#test-utils/mock-api"; -import t from "tap"; - -t.test("GET /api/v1/games/bms/:playtype/custom-tables/:tableUrlName", async (t) => { - await db.tables.insert({ - default: false, - description: "EC difficulties according to Sieglinde.", - - // stubbed folders - folders: ["sieglinde_folder"], - game: "bms", - inactive: false, - playtype: "7K", - tableID: "bms-7K-sgl-EC", - title: "Sieglinde EC", - }); - - await db.folders.insert({ - folderID: "sieglinde_folder", - game: "bms", - playtype: "7K", - inactive: false, - searchTerms: [], - title: "Mock Sieglinde Folder", - - // this table contains all charts. silly mock. - data: {}, - type: "charts", - }); - - t.test("html compatibility return should point to the right file", async (t) => { - const res = await mockApi.get("/api/v1/games/bms/7K/custom-tables/sieglindeEC"); - - const content = //u.exec(res.text); - - if (content) { - const maybeMatch = content[1]; - - t.equal( - maybeMatch, - `${ServerConfig.OUR_URL}/api/v1/games/bms/7K/custom-tables/sieglindeEC/header.json`, - ); - } else { - t.fail(`Didn't match regexp for having a bmstable header?`); - } - - t.end(); - }); - - t.test("Should point to the right file.", async (t) => { - const res = await mockApi.get("/api/v1/games/bms/7K/custom-tables/sieglindeEC/header.json"); - - t.equal( - res.body.data_url, - `${ServerConfig.OUR_URL}/api/v1/games/bms/7K/custom-tables/sieglindeEC/body.json`, - ); - - t.end(); - }); - - t.test("`body.json` should return an array.", async (t) => { - const res = await mockApi.get("/api/v1/games/bms/7K/custom-tables/sieglindeEC/body.json"); - - t.equal(Array.isArray(res.body), true); - - t.end(); - }); - - t.test("Invalid Table", async (t) => { - const res = await mockApi.get("/api/v1/games/bms/7K/custom-tables/fake-table"); - - t.equal(res.statusCode, 404); - - t.end(); - }); - - t.test("Invalid Playtype", async (t) => { - const res = await mockApi.get("/api/v1/games/bms/14K/custom-tables/sieglindeEC"); - - t.equal(res.statusCode, 404); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1/games/@gameSpecificRoutes/bms/router.ts b/typescript/server/src/server/router/api/v1/games/@gameSpecificRoutes/bms/router.ts index 450a30175..d5eb48b45 100644 --- a/typescript/server/src/server/router/api/v1/games/@gameSpecificRoutes/bms/router.ts +++ b/typescript/server/src/server/router/api/v1/games/@gameSpecificRoutes/bms/router.ts @@ -7,7 +7,7 @@ import { HandleBMSTableHTMLRequest, type TachiBMSTable, } from "#lib/game-specific/custom-bms-tables"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { AssignToReqTachiData, GetTachiData } from "#utils/req-tachi-data"; import { type RequestHandler, Router } from "express"; @@ -138,12 +138,12 @@ router.get( router.get("/:playtype/sieglinde-charts", ValidatePlaytypeFromParamFor("bms"), async (req, res) => { const playtype = req.params.playtype as Playtypes["bms"]; - const charts = await db.charts.bms.find({ + const charts = await MONGODB_KILL.charts.bms.find({ playtype, $or: [{ "data.sglEC": { $gt: 0 } }, { "data.sglHC": { $gt: 0 } }], }); - const songs = await db.songs.bms.find({ id: { $in: charts.map((e) => e.songID) } }); + const songs = await MONGODB_KILL.songs.bms.find({ id: { $in: charts.map((e) => e.songID) } }); return res.status(200).json({ success: true, diff --git a/typescript/server/src/server/router/api/v1/games/_game/_playtype/charts/_chartID/middleware.ts b/typescript/server/src/server/router/api/v1/games/_game/_playtype/charts/_chartID/middleware.ts index f51db76e0..18d01aa4c 100644 --- a/typescript/server/src/server/router/api/v1/games/_game/_playtype/charts/_chartID/middleware.ts +++ b/typescript/server/src/server/router/api/v1/games/_game/_playtype/charts/_chartID/middleware.ts @@ -1,12 +1,12 @@ import type { RequestHandler } from "express"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { AssignToReqTachiData, GetGPT } from "#utils/req-tachi-data"; export const ValidateAndGetChart: RequestHandler = async (req, res, next) => { const { game, playtype } = GetGPT(req); - const chart = await db.anyCharts[game].findOne({ + const chart = await MONGODB_KILL.anyCharts[game].findOne({ chartID: req.params.chartID, // technically redundant, but we're under playtypes here URL wise. diff --git a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/charts/_chartID/router.test.ts b/typescript/server/src/server/router/api/v1/games/_game/_playtype/charts/_chartID/router.oldtest.ts similarity index 92% rename from typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/charts/_chartID/router.test.ts rename to typescript/server/src/server/router/api/v1/games/_game/_playtype/charts/_chartID/router.oldtest.ts index 8d591af51..bf2b10e7d 100644 --- a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/charts/_chartID/router.test.ts +++ b/typescript/server/src/server/router/api/v1/games/_game/_playtype/charts/_chartID/router.oldtest.ts @@ -1,6 +1,6 @@ import type { PBScoreDocument, UserDocument } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { dmf } from "#test-utils/misc.js"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; @@ -39,7 +39,7 @@ t.test("GET /api/v1/games/:game/:playtype/charts/:chartID/pbs", (t) => { t.beforeEach(ResetDBState); t.test("Should return the best PBs on that chart.", async (t) => { - await db["personal-bests"].insert([ + await MONGODB_KILL["personal-bests"].insert([ TestingIIDXSPScorePB, dmf(TestingIIDXSPScorePB, { userID: 2, @@ -51,7 +51,7 @@ t.test("GET /api/v1/games/:game/:playtype/charts/:chartID/pbs", (t) => { }) as PBScoreDocument, ]); - await db.users.insert({ + await MONGODB_KILL.users.insert({ id: 2, username: "foo", usernameLowercase: "foo", @@ -83,7 +83,7 @@ t.test("GET /api/v1/games/:game/:playtype/charts/:chartID/playcount", (t) => { pbs.push(dmf(TestingIIDXSPScorePB, { userID: i }) as PBScoreDocument); } - await db["personal-bests"].insert(pbs); + await MONGODB_KILL["personal-bests"].insert(pbs); const res = await mockApi.get( `/api/v1/games/iidx/SP/charts/${Testing511SPA.chartID}/playcount`, diff --git a/typescript/server/src/server/router/api/v1/games/_game/_playtype/charts/_chartID/router.test.ts b/typescript/server/src/server/router/api/v1/games/_game/_playtype/charts/_chartID/router.test.ts deleted file mode 100644 index 8d591af51..000000000 --- a/typescript/server/src/server/router/api/v1/games/_game/_playtype/charts/_chartID/router.test.ts +++ /dev/null @@ -1,98 +0,0 @@ -import type { PBScoreDocument, UserDocument } from "tachi-common"; - -import db from "#services/mongo/db"; -import { dmf } from "#test-utils/misc.js"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import { Testing511SPA, TestingIIDXSPScorePB } from "#test-utils/test-data"; -import t from "tap"; - -t.test("GET /api/v1/games/:game/:playtype/charts/:chartID", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should return the chart at this ID", async (t) => { - const res = await mockApi.get(`/api/v1/games/iidx/SP/charts/${Testing511SPA.chartID}`); - - t.hasStrict(res.body.body, { - song: { - id: 1, - }, - chart: { - chartID: Testing511SPA.chartID, - }, - }); - t.end(); - }); - - t.test("Should return 404 if the chart doesnt exist", async (t) => { - const res = await mockApi.get(`/api/v1/games/iidx/SP/charts/FAKECHART`); - - t.equal(res.statusCode, 404); - - t.end(); - }); - - t.end(); -}); - -t.test("GET /api/v1/games/:game/:playtype/charts/:chartID/pbs", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should return the best PBs on that chart.", async (t) => { - await db["personal-bests"].insert([ - TestingIIDXSPScorePB, - dmf(TestingIIDXSPScorePB, { - userID: 2, - rankingData: { rank: 2 }, - scoreData: { score: 123 }, - }), - dmf(TestingIIDXSPScorePB, { - chartID: "other_chart", - }) as PBScoreDocument, - ]); - - await db.users.insert({ - id: 2, - username: "foo", - usernameLowercase: "foo", - } as UserDocument); - - const res = await mockApi.get(`/api/v1/games/iidx/SP/charts/${Testing511SPA.chartID}/pbs`); - - t.equal(res.body.body.pbs.length, 2); - t.equal(res.body.body.users.length, 2); - - t.strictSame( - res.body.body.pbs.map((e: PBScoreDocument) => e.chartID), - [Testing511SPA.chartID, Testing511SPA.chartID], - ); - - t.end(); - }); - - t.end(); -}); - -t.test("GET /api/v1/games/:game/:playtype/charts/:chartID/playcount", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should return the total playcount on this chart.", async (t) => { - const pbs: Array = []; - - for (let i = 1; i <= 132; i++) { - pbs.push(dmf(TestingIIDXSPScorePB, { userID: i }) as PBScoreDocument); - } - - await db["personal-bests"].insert(pbs); - - const res = await mockApi.get( - `/api/v1/games/iidx/SP/charts/${Testing511SPA.chartID}/playcount`, - ); - - t.equal(res.body.body.count, 132); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1/games/_game/_playtype/charts/_chartID/router.ts b/typescript/server/src/server/router/api/v1/games/_game/_playtype/charts/_chartID/router.ts index 1ca909242..34dd7dd44 100644 --- a/typescript/server/src/server/router/api/v1/games/_game/_playtype/charts/_chartID/router.ts +++ b/typescript/server/src/server/router/api/v1/games/_game/_playtype/charts/_chartID/router.ts @@ -2,13 +2,19 @@ import type { FilterQuery } from "mongodb"; import { log } from "#lib/log/log.js"; import { SearchUsersRegExp } from "#lib/search/search"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { IsString } from "#utils/misc"; import { GetTachiData } from "#utils/req-tachi-data"; +import { apiSuccess } from "#utils/response.js"; import { ParseStrPositiveNonZeroInt } from "#utils/string-checks"; import { GetUsersWithIDs } from "#utils/user"; import { Router } from "express"; -import { type FolderDocument, FormatChart } from "tachi-common"; +import { + type FolderDocument, + FormatChart, + type PBScoreDocument, + type UserDocument, +} from "tachi-common"; import { ValidateAndGetChart } from "./middleware"; @@ -25,7 +31,7 @@ router.get("/", async (req, res) => { const chart = GetTachiData(req, "chartDoc"); const game = GetTachiData(req, "game"); - const song = await db.anySongs[game].findOne({ + const song = await MONGODB_KILL.anySongs[game].findOne({ id: chart.songID, }); @@ -60,7 +66,7 @@ router.get("/", async (req, res) => { router.get("/folders", async (req, res) => { const chart = GetTachiData(req, "chartDoc"); - const folderIDs = await db["folder-chart-lookup"].find( + const folderIDs = await MONGODB_KILL["folder-chart-lookup"].find( { chartID: chart.chartID, }, @@ -79,7 +85,7 @@ router.get("/folders", async (req, res) => { query.inactive = false; } - const folders = await db.folders.find(query); + const folders = await MONGODB_KILL.folders.find(query); return res.status(200).json({ success: true, @@ -96,7 +102,7 @@ router.get("/folders", async (req, res) => { router.get("/playcount", async (req, res) => { const chart = GetTachiData(req, "chartDoc"); - const count = await db["personal-bests"].count({ chartID: chart.chartID }); + const count = await MONGODB_KILL["personal-bests"].count({ chartID: chart.chartID }); return res.status(200).json({ success: true, @@ -120,7 +126,7 @@ router.get("/pbs", async (req, res) => { const startRanking = ParseStrPositiveNonZeroInt(req.query.startRanking) ?? 1; - const pbs = await db["personal-bests"].find( + const pbs = await MONGODB_KILL["personal-bests"].find( { chartID: chart.chartID, "rankingData.rank": { $gte: startRanking }, @@ -164,19 +170,20 @@ router.get("/pbs/search", async (req, res) => { const users = await SearchUsersRegExp(req.query.search); - const pbs = await db["personal-bests"].find({ + const pbs = await MONGODB_KILL["personal-bests"].find({ chartID: chart.chartID, userID: { $in: users.map((e) => e.id) }, }); - return res.status(200).json({ - success: true, - description: `Returned ${pbs.length} scores.`, - body: { - pbs, - users, - }, - }); + return res.status(200).json( + apiSuccess<{ pbs: Array; users: Array }>( + `Returned ${pbs.length} scores.`, + { + pbs, + users, + }, + ), + ); }); export default router; diff --git a/typescript/server/src/server/router/api/v1/games/_game/_playtype/charts/router.test.ts b/typescript/server/src/server/router/api/v1/games/_game/_playtype/charts/router.oldtest.ts similarity index 95% rename from typescript/server/src/server/router/api/v1/games/_game/_playtype/charts/router.test.ts rename to typescript/server/src/server/router/api/v1/games/_game/_playtype/charts/router.oldtest.ts index 291187b2d..22804ff59 100644 --- a/typescript/server/src/server/router/api/v1/games/_game/_playtype/charts/router.test.ts +++ b/typescript/server/src/server/router/api/v1/games/_game/_playtype/charts/router.oldtest.ts @@ -1,4 +1,4 @@ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { mkFakePBIIDXSP } from "#test-utils/misc"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; @@ -10,7 +10,7 @@ t.test("GET /api/v1/games/:game/:playtype/charts", (t) => { t.beforeEach(LoadTachiIIDXData); t.test("Should return the most popular charts if no param is set.", async (t) => { - await db["personal-bests"].insert([ + await MONGODB_KILL["personal-bests"].insert([ mkFakePBIIDXSP({ chartID: Testing511SPA.chartID, userID: 1, @@ -49,7 +49,7 @@ t.test("GET /api/v1/games/:game/:playtype/charts", (t) => { }); t.test("Should search charts if a search param is set.", async (t) => { - await db["personal-bests"].insert([ + await MONGODB_KILL["personal-bests"].insert([ mkFakePBIIDXSP({ // gambol hyper songID: 7, @@ -74,7 +74,7 @@ t.test("GET /api/v1/games/:game/:playtype/charts", (t) => { t.test( "Should only return charts the requester has played if requesterHasPlayed is set.", async (t) => { - await db["personal-bests"].insert([ + await MONGODB_KILL["personal-bests"].insert([ mkFakePBIIDXSP({ // gambol hyper songID: 7, @@ -108,7 +108,7 @@ t.test("GET /api/v1/games/:game/:playtype/charts", (t) => { t.test( "Should only return charts the requester has played if requesterHasPlayed is set, and work with searches at the same time.", async (t) => { - await db["personal-bests"].insert([ + await MONGODB_KILL["personal-bests"].insert([ mkFakePBIIDXSP({ // gambol hyper songID: 7, diff --git a/typescript/server/src/server/router/api/v1/games/_game/_playtype/charts/router.ts b/typescript/server/src/server/router/api/v1/games/_game/_playtype/charts/router.ts index 547cde4b9..29ceafdf5 100644 --- a/typescript/server/src/server/router/api/v1/games/_game/_playtype/charts/router.ts +++ b/typescript/server/src/server/router/api/v1/games/_game/_playtype/charts/router.ts @@ -3,7 +3,7 @@ import { log } from "#lib/log/log.js"; import { ResolveSongAndChart } from "#lib/score-import/import-types/common/batch-manual/converter"; import { SearchSpecificGameSongs } from "#lib/search/search"; import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { IsString } from "#utils/misc"; import { FindChartsOnPopularity } from "#utils/queries/charts"; import { GetGPT } from "#utils/req-tachi-data"; @@ -55,7 +55,7 @@ router.get("/", async (req, res) => { } const playedSongs = ( - await db["personal-bests"].find( + await MONGODB_KILL["personal-bests"].find( { userID, game, playtype }, { projection: { songID: 1 } }, ) @@ -85,7 +85,7 @@ router.get("/", async (req, res) => { // @optimisable // could use songIDs from above instead of refetching // but this is not very expensive. - const songs = await db.anySongs[game].find({ + const songs = await MONGODB_KILL.anySongs[game].find({ id: { $in: charts.map((e) => e.songID) }, }); @@ -102,7 +102,7 @@ router.get("/", async (req, res) => { (e) => (e as ChartDocument<"iidx:DP" | "iidx:SP">).data["2dxtraSet"] === null, ); } else { - const iidxSettings = (await db["game-settings"].findOne({ + const iidxSettings = (await MONGODB_KILL["game-settings"].findOne({ userID: req[SYMBOL_TACHI_API_AUTH].userID, game, playtype, diff --git a/typescript/server/src/server/router/api/v1/games/_game/_playtype/folders/middleware.ts b/typescript/server/src/server/router/api/v1/games/_game/_playtype/folders/middleware.ts index 8d0c5a2b6..51a6b10aa 100644 --- a/typescript/server/src/server/router/api/v1/games/_game/_playtype/folders/middleware.ts +++ b/typescript/server/src/server/router/api/v1/games/_game/_playtype/folders/middleware.ts @@ -1,12 +1,16 @@ import type { RequestHandler } from "express"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { AssignToReqTachiData, GetGPT } from "#utils/req-tachi-data"; export const GetFolderFromParam: RequestHandler = async (req, res, next) => { const { game, playtype } = GetGPT(req); - const folder = await db.folders.findOne({ folderID: req.params.folderID, game, playtype }); + const folder = await MONGODB_KILL.folders.findOne({ + folderID: req.params.folderID, + game, + playtype, + }); if (!folder) { return res.status(404).json({ diff --git a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/folders/router.test.ts b/typescript/server/src/server/router/api/v1/games/_game/_playtype/folders/router.oldtest.ts similarity index 92% rename from typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/folders/router.test.ts rename to typescript/server/src/server/router/api/v1/games/_game/_playtype/folders/router.oldtest.ts index 95a89b2c6..2efe3dfd4 100644 --- a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/folders/router.test.ts +++ b/typescript/server/src/server/router/api/v1/games/_game/_playtype/folders/router.oldtest.ts @@ -1,6 +1,6 @@ import type { FolderDocument } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; import { Testing511SPA } from "#test-utils/test-data"; @@ -25,7 +25,7 @@ t.test("GET /api/v1/games/:game/:playtype/folders", (t) => { t.beforeEach(ResetDBState); t.test("Should search the folders for this game.", async (t) => { - await db.folders.insert([ + await MONGODB_KILL.folders.insert([ deepmerge(mockFolder, {}), deepmerge(mockFolder, { folderID: "bar", playtype: "DP" }), deepmerge(mockFolder, { folderID: "baz", game: "bms" }), @@ -54,7 +54,7 @@ t.test("GET /api/v1/games/:game/:playtype/folders/:folderID", (t) => { t.beforeEach(ResetDBState); t.test("Should return the folder at this ID.", async (t) => { - await db.folders.insert(deepmerge(mockFolder, {})); + await MONGODB_KILL.folders.insert(deepmerge(mockFolder, {})); await CreateFolderChartLookup(mockFolder, true); const res = await mockApi.get("/api/v1/games/iidx/SP/folders/foo"); diff --git a/typescript/server/src/server/router/api/v1/games/_game/_playtype/folders/router.test.ts b/typescript/server/src/server/router/api/v1/games/_game/_playtype/folders/router.test.ts deleted file mode 100644 index 95a89b2c6..000000000 --- a/typescript/server/src/server/router/api/v1/games/_game/_playtype/folders/router.test.ts +++ /dev/null @@ -1,80 +0,0 @@ -import type { FolderDocument } from "tachi-common"; - -import db from "#services/mongo/db"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import { Testing511SPA } from "#test-utils/test-data"; -import { CreateFolderChartLookup } from "#utils/folder"; -import deepmerge from "deepmerge"; -import t from "tap"; - -const mockFolder: FolderDocument = { - folderID: "foo", - game: "iidx", - playtype: "SP", - title: "12", - data: { - level: "10", - }, - type: "charts", - searchTerms: [], - inactive: false, -}; - -t.test("GET /api/v1/games/:game/:playtype/folders", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should search the folders for this game.", async (t) => { - await db.folders.insert([ - deepmerge(mockFolder, {}), - deepmerge(mockFolder, { folderID: "bar", playtype: "DP" }), - deepmerge(mockFolder, { folderID: "baz", game: "bms" }), - ]); - - const res = await mockApi.get("/api/v1/games/iidx/SP/folders?search=12"); - - t.equal(res.body.body.length, 1); - t.equal(res.body.body[0].folderID, "foo"); - - t.end(); - }); - - t.test("Should return 400 if no search parameter is given.", async (t) => { - const res = await mockApi.get("/api/v1/games/iidx/SP/folders"); - - t.equal(res.statusCode, 400); - - t.end(); - }); - - t.end(); -}); - -t.test("GET /api/v1/games/:game/:playtype/folders/:folderID", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should return the folder at this ID.", async (t) => { - await db.folders.insert(deepmerge(mockFolder, {})); - await CreateFolderChartLookup(mockFolder, true); - - const res = await mockApi.get("/api/v1/games/iidx/SP/folders/foo"); - - t.equal(res.body.body.folder.folderID, "foo"); - t.equal(res.body.body.songs.length, 1); - t.equal(res.body.body.charts.length, 1); - t.equal(res.body.body.songs[0].id, 1); - t.equal(res.body.body.charts[0].chartID, Testing511SPA.chartID); - - t.end(); - }); - - t.test("Should return 404 if the folder does not exist.", async (t) => { - const res = await mockApi.get("/api/v1/games/iidx/SP/folders/bar"); - - t.equal(res.statusCode, 404); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1/games/_game/_playtype/folders/router.ts b/typescript/server/src/server/router/api/v1/games/_game/_playtype/folders/router.ts index 84f50e565..60b081f39 100644 --- a/typescript/server/src/server/router/api/v1/games/_game/_playtype/folders/router.ts +++ b/typescript/server/src/server/router/api/v1/games/_game/_playtype/folders/router.ts @@ -1,5 +1,5 @@ import { SearchCollection } from "#lib/search/search"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetFolderCharts } from "#utils/folder"; import { IsString } from "#utils/misc"; import { GetGPT, GetTachiData } from "#utils/req-tachi-data"; @@ -33,7 +33,7 @@ router.get("/", async (req, res) => { const inactive = req.query.inactive === undefined ? false : undefined; const folders = await SearchCollection( - db.folders, + MONGODB_KILL.folders, req.query.search, "folders", { game, playtype, inactive }, diff --git a/typescript/server/src/server/router/api/v1/games/_game/_playtype/router.test.ts b/typescript/server/src/server/router/api/v1/games/_game/_playtype/router.oldtest.ts similarity index 94% rename from typescript/server/src/server/router/api/v1/games/_game/_playtype/router.test.ts rename to typescript/server/src/server/router/api/v1/games/_game/_playtype/router.oldtest.ts index d8ba31ba6..79ae4df9b 100644 --- a/typescript/server/src/server/router/api/v1/games/_game/_playtype/router.test.ts +++ b/typescript/server/src/server/router/api/v1/games/_game/_playtype/router.oldtest.ts @@ -1,4 +1,4 @@ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { mkFakeGameStats, mkFakeUser } from "#test-utils/misc"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; @@ -68,7 +68,7 @@ t.test("GET /api/v1/games/:game/:playtype/leaderboard", (t) => { }); t.test("Should use provided algorithm to resort results.", async (t) => { - await db["game-stats"].insert([ + await MONGODB_KILL["game-stats"].insert([ { userID: 2, game: "iidx", @@ -87,7 +87,7 @@ t.test("GET /api/v1/games/:game/:playtype/leaderboard", (t) => { }, ] as Array); - await db.users.insert([ + await MONGODB_KILL.users.insert([ FakeOtherUser, dm(FakeOtherUser, { username: "foo", @@ -112,14 +112,14 @@ t.test("GET /api/v1/games/:game/:playtype/leaderboard", (t) => { t.test("GET /api/v1/games/:game/:playtype/players", (t) => { t.beforeEach(ResetDBState); t.beforeEach(async () => { - await db.users.insert([ + await MONGODB_KILL.users.insert([ mkFakeUser(2, { usernameLowercase: "scrimblo" }), mkFakeUser(3, { usernameLowercase: "scrimblo_2" }), mkFakeUser(4, { usernameLowercase: "scrimblo_3" }), mkFakeUser(5, { usernameLowercase: "cloudy" }), ]); - await db["game-stats"].insert([ + await MONGODB_KILL["game-stats"].insert([ mkFakeGameStats(2), mkFakeGameStats(3, { game: "iidx", playtype: "DP" }), mkFakeGameStats(4, { game: "bms", playtype: "7K" }), diff --git a/typescript/server/src/server/router/api/v1/games/_game/_playtype/router.ts b/typescript/server/src/server/router/api/v1/games/_game/_playtype/router.ts index a1488b1fb..44f64359b 100644 --- a/typescript/server/src/server/router/api/v1/games/_game/_playtype/router.ts +++ b/typescript/server/src/server/router/api/v1/games/_game/_playtype/router.ts @@ -4,7 +4,7 @@ import { CreateActivityRouteHandler } from "#lib/activity/activity"; import { ONE_HOUR } from "#lib/constants/time"; import { SearchUsersRegExp } from "#lib/search/search"; import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetRelevantSongsAndCharts } from "#utils/db"; import { IsString } from "#utils/misc"; import { GetGPT } from "#utils/req-tachi-data"; @@ -46,15 +46,15 @@ async function GetGameStats( if (cacheRes === undefined) { const [scoreCount, playerCount, chartCount] = await Promise.all([ - db.scores.count({ + MONGODB_KILL.scores.count({ game, playtype, }), - db["game-stats"].count({ + MONGODB_KILL["game-stats"].count({ game, playtype, }), - db.anyCharts[game].count({ playtype }), + MONGODB_KILL.anyCharts[game].count({ playtype }), ]); gptStatCache.set(`${game}:${playtype}`, { scoreCount, playerCount, chartCount }, ONE_HOUR); @@ -133,7 +133,7 @@ router.get("/leaderboard", async (req, res) => { limit, }; - const gameStats = await db["game-stats"].find( + const gameStats = await MONGODB_KILL["game-stats"].find( { game, playtype, @@ -191,7 +191,7 @@ router.get("/pb-leaderboard", async (req, res) => { alg = temp; } - const pbs = await db["personal-bests"].find( + const pbs = await MONGODB_KILL["personal-bests"].find( { game, playtype, @@ -241,7 +241,7 @@ router.get( const users = await SearchUsersRegExp(search); - const gameStats = await db["game-stats"].find({ + const gameStats = await MONGODB_KILL["game-stats"].find({ userID: { $in: users.map((e) => e.id) }, game, playtype, diff --git a/typescript/server/src/server/router/api/v1/games/_game/_playtype/songs/_songID/middleware.ts b/typescript/server/src/server/router/api/v1/games/_game/_playtype/songs/_songID/middleware.ts index 304c51276..0c0cf4d77 100644 --- a/typescript/server/src/server/router/api/v1/games/_game/_playtype/songs/_songID/middleware.ts +++ b/typescript/server/src/server/router/api/v1/games/_game/_playtype/songs/_songID/middleware.ts @@ -1,6 +1,6 @@ import type { RequestHandler } from "express"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { AssignToReqTachiData, GetTachiData } from "#utils/req-tachi-data"; import { ParseStrPositiveInt } from "#utils/string-checks"; @@ -16,7 +16,7 @@ export const ValidateAndGetSong: RequestHandler = async (req, res, next) => { const game = GetTachiData(req, "game"); - const song = await db.anySongs[game].findOne({ id: songID }); + const song = await MONGODB_KILL.anySongs[game].findOne({ id: songID }); if (!song) { return res.status(404).json({ diff --git a/typescript/server/src/server/router/api/v1/games/_game/_playtype/songs/_songID/router.test.ts b/typescript/server/src/server/router/api/v1/games/_game/_playtype/songs/_songID/router.oldtest.ts similarity index 100% rename from typescript/server/src/server/router/api/v1/games/_game/_playtype/songs/_songID/router.test.ts rename to typescript/server/src/server/router/api/v1/games/_game/_playtype/songs/_songID/router.oldtest.ts diff --git a/typescript/server/src/server/router/api/v1/games/_game/_playtype/songs/_songID/router.ts b/typescript/server/src/server/router/api/v1/games/_game/_playtype/songs/_songID/router.ts index 3470a592c..9261a29c7 100644 --- a/typescript/server/src/server/router/api/v1/games/_game/_playtype/songs/_songID/router.ts +++ b/typescript/server/src/server/router/api/v1/games/_game/_playtype/songs/_songID/router.ts @@ -1,4 +1,4 @@ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetGPT, GetTachiData } from "#utils/req-tachi-data"; import { Router } from "express"; @@ -17,7 +17,7 @@ router.get("/", async (req, res) => { const song = GetTachiData(req, "songDoc"); const { game, playtype } = GetGPT(req); - const charts = await db.anyCharts[game].find({ + const charts = await MONGODB_KILL.anyCharts[game].find({ songID: song.id, playtype, }); diff --git a/typescript/server/src/server/router/api/v1/games/_game/_playtype/tables/middleware.ts b/typescript/server/src/server/router/api/v1/games/_game/_playtype/tables/middleware.ts index a3574f1d9..aab331553 100644 --- a/typescript/server/src/server/router/api/v1/games/_game/_playtype/tables/middleware.ts +++ b/typescript/server/src/server/router/api/v1/games/_game/_playtype/tables/middleware.ts @@ -1,13 +1,17 @@ import type { RequestHandler } from "express"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { AssignToReqTachiData, GetTachiData } from "#utils/req-tachi-data"; export const GetTableFromParam: RequestHandler = async (req, res, next) => { const game = GetTachiData(req, "game"); const playtype = GetTachiData(req, "playtype"); - const table = await db.tables.findOne({ tableID: req.params.tableID, game, playtype }); + const table = await MONGODB_KILL.tables.findOne({ + tableID: req.params.tableID, + game, + playtype, + }); if (!table) { return res.status(404).json({ diff --git a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/tables/router.test.ts b/typescript/server/src/server/router/api/v1/games/_game/_playtype/tables/router.oldtest.ts similarity index 94% rename from typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/tables/router.test.ts rename to typescript/server/src/server/router/api/v1/games/_game/_playtype/tables/router.oldtest.ts index 18313352f..0fc18ad95 100644 --- a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/tables/router.test.ts +++ b/typescript/server/src/server/router/api/v1/games/_game/_playtype/tables/router.oldtest.ts @@ -1,6 +1,6 @@ import type { FolderDocument } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; import { TestingIIDXFolderSP10 } from "#test-utils/test-data"; @@ -26,7 +26,7 @@ t.test("GET /api/v1/games/:game/:playtype/tables/:tableID", (t) => { t.beforeEach(ResetDBState); t.test("Should return the table at this ID and its folders.", async (t) => { - await db.folders.insert( + await MONGODB_KILL.folders.insert( deepmerge(TestingIIDXFolderSP10, { folderID: "testing_folder" }) as FolderDocument, ); diff --git a/typescript/server/src/server/router/api/v1/games/_game/_playtype/tables/router.test.ts b/typescript/server/src/server/router/api/v1/games/_game/_playtype/tables/router.test.ts deleted file mode 100644 index 18313352f..000000000 --- a/typescript/server/src/server/router/api/v1/games/_game/_playtype/tables/router.test.ts +++ /dev/null @@ -1,51 +0,0 @@ -import type { FolderDocument } from "tachi-common"; - -import db from "#services/mongo/db"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import { TestingIIDXFolderSP10 } from "#test-utils/test-data"; -import deepmerge from "deepmerge"; -import t from "tap"; - -t.test("GET /api/v1/games/:game/:playtype/tables", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should return all folders for this game.", async (t) => { - const res = await mockApi.get("/api/v1/games/iidx/SP/tables"); - - t.equal(res.body.body.length, 1); - t.equal(res.body.body[0].tableID, "mock_table"); - - t.end(); - }); - - t.end(); -}); - -t.test("GET /api/v1/games/:game/:playtype/tables/:tableID", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should return the table at this ID and its folders.", async (t) => { - await db.folders.insert( - deepmerge(TestingIIDXFolderSP10, { folderID: "testing_folder" }) as FolderDocument, - ); - - const res = await mockApi.get("/api/v1/games/iidx/SP/tables/mock_table"); - - t.equal(res.body.body.folders.length, 1); - t.equal(res.body.body.folders[0].folderID, "testing_folder"); - t.equal(res.body.body.table.tableID, "mock_table"); - - t.end(); - }); - - t.test("Should return 404 if the table does not exist.", async (t) => { - const res = await mockApi.get("/api/v1/games/iidx/SP/tables/non_existent_table"); - - t.equal(res.statusCode, 404); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1/games/_game/_playtype/tables/router.ts b/typescript/server/src/server/router/api/v1/games/_game/_playtype/tables/router.ts index 2e8217fd0..357feb778 100644 --- a/typescript/server/src/server/router/api/v1/games/_game/_playtype/tables/router.ts +++ b/typescript/server/src/server/router/api/v1/games/_game/_playtype/tables/router.ts @@ -1,7 +1,7 @@ import type { FilterQuery } from "mongodb"; import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetFoldersFromTable } from "#utils/folder"; import { GetGPT, GetTachiData } from "#utils/req-tachi-data"; import { Router } from "express"; @@ -27,7 +27,7 @@ router.get("/", async (req, res) => { query.inactive = false; } - const tables = await db.tables.find(query); + const tables = await MONGODB_KILL.tables.find(query); if (tables.length === 0) { log.error( diff --git a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/targets/goals/router.test.ts b/typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/goals/router.oldtest.ts similarity index 91% rename from typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/targets/goals/router.test.ts rename to typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/goals/router.oldtest.ts index 1600b50b1..a99a94ae4 100644 --- a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/targets/goals/router.test.ts +++ b/typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/goals/router.oldtest.ts @@ -1,6 +1,6 @@ import type { GoalDocument, GoalSubscriptionDocument } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; import { @@ -14,9 +14,9 @@ import t from "tap"; // this is my lazy sample data for these tests. const LoadLazySampleData = async () => { - await db.users.insert(FakeOtherUser); - await db.goals.insert(IIDXSPQuestGoals); - await db["goal-subs"].insert([ + await MONGODB_KILL.users.insert(FakeOtherUser); + await MONGODB_KILL.goals.insert(IIDXSPQuestGoals); + await MONGODB_KILL["goal-subs"].insert([ ...IIDXSPQuestGoalSubs, dm(IIDXSPQuestGoalSubs[0]!, { userID: 2, @@ -71,7 +71,7 @@ t.test("GET /api/v1/games/:game/:playtype/targets/goals/:goalID", (t) => { t.beforeEach(LoadLazySampleData); t.test("Should return information about the specified goal.", async (t) => { - await db.quests.insert(TestingIIDXSPQuest); + await MONGODB_KILL.quests.insert(TestingIIDXSPQuest); const res = await mockApi.get("/api/v1/games/iidx/SP/targets/goals/eg_goal_1"); diff --git a/typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/goals/router.test.ts b/typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/goals/router.test.ts deleted file mode 100644 index 1600b50b1..000000000 --- a/typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/goals/router.test.ts +++ /dev/null @@ -1,94 +0,0 @@ -import type { GoalDocument, GoalSubscriptionDocument } from "tachi-common"; - -import db from "#services/mongo/db"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import { - FakeOtherUser, - IIDXSPQuestGoals, - IIDXSPQuestGoalSubs, - TestingIIDXSPQuest, -} from "#test-utils/test-data"; -import dm from "deepmerge"; -import t from "tap"; - -// this is my lazy sample data for these tests. -const LoadLazySampleData = async () => { - await db.users.insert(FakeOtherUser); - await db.goals.insert(IIDXSPQuestGoals); - await db["goal-subs"].insert([ - ...IIDXSPQuestGoalSubs, - dm(IIDXSPQuestGoalSubs[0]!, { - userID: 2, - }), - ] as Array); -}; - -t.test("GET /api/v1/games/:game/:playtype/targets/goals/popular", (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(LoadLazySampleData); - - t.test("Should return the most popular subscribed goals for this game.", async (t) => { - const res = await mockApi.get("/api/v1/games/iidx/SP/targets/goals/popular"); - - t.equal(res.statusCode, 200, "Should return 200."); - - // note: we have to sort the output here such that it's deterministic. - t.strictSame( - (res.body.body as Array).sort((a, b) => a.goalID.localeCompare(b.goalID)), - ( - [ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - dm(IIDXSPQuestGoals[0] as any, { __subscriptions: 2 }), - // eslint-disable-next-line @typescript-eslint/no-explicit-any - dm(IIDXSPQuestGoals[1] as any, { __subscriptions: 1 }), - // eslint-disable-next-line @typescript-eslint/no-explicit-any - dm(IIDXSPQuestGoals[2] as any, { __subscriptions: 1 }), - // eslint-disable-next-line @typescript-eslint/no-explicit-any - dm(IIDXSPQuestGoals[3] as any, { __subscriptions: 1 }), - ] as unknown as Array - ).sort((a, b) => a.goalID.localeCompare(b.goalID)), - "Should return the most subscribed goals.", - ); - - t.end(); - }); - - t.test("Should return an empty array if nobody has done anything.", async (t) => { - const res = await mockApi.get("/api/v1/games/chunithm/Single/targets/goals/popular"); - - t.equal(res.statusCode, 200, "Should return 200."); - t.strictSame(res.body.body, []); - - t.end(); - }); - - t.end(); -}); - -t.test("GET /api/v1/games/:game/:playtype/targets/goals/:goalID", (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(LoadLazySampleData); - - t.test("Should return information about the specified goal.", async (t) => { - await db.quests.insert(TestingIIDXSPQuest); - - const res = await mockApi.get("/api/v1/games/iidx/SP/targets/goals/eg_goal_1"); - - t.hasStrict(res.body.body, { - goal: { - goalID: "eg_goal_1", - }, - goalSubs: [ - { userID: 1, goalID: "eg_goal_1" }, - { userID: 2, goalID: "eg_goal_1" }, - ], - users: [{ id: 1 }, { id: 2 }], - parentQuests: [{ questID: TestingIIDXSPQuest.questID }], - }); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/goals/router.ts b/typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/goals/router.ts index f46d0200b..342b2a5c9 100644 --- a/typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/goals/router.ts +++ b/typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/goals/router.ts @@ -4,7 +4,7 @@ import { log } from "#lib/log/log.js"; import { CreateGoalTitle, ValidateGoalChartsAndCriteria } from "#lib/targets/goal-utils"; import { GetQuestsThatContainGoal } from "#lib/targets/goals"; import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetMostSubscribedGoals } from "#utils/db"; import { AssignToReqTachiData, GetGPT, GetTachiData } from "#utils/req-tachi-data"; import { GetUsersWithIDs } from "#utils/user"; @@ -131,7 +131,7 @@ const ResolveGoalID: RequestHandler = async (req, res, next) => { const { game, playtype } = GetGPT(req); const goalID = req.params.goalID; - const goal = await db.goals.findOne({ + const goal = await MONGODB_KILL.goals.findOne({ goalID, game, playtype, @@ -157,7 +157,7 @@ const ResolveGoalID: RequestHandler = async (req, res, next) => { router.get("/:goalID", ResolveGoalID, async (req, res) => { const goal = GetTachiData(req, "goalDoc"); - const goalSubs = await db["goal-subs"].find({ + const goalSubs = await MONGODB_KILL["goal-subs"].find({ goalID: goal.goalID, }); diff --git a/typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/questlines/router.test.ts b/typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/questlines/router.oldtest.ts similarity index 93% rename from typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/questlines/router.test.ts rename to typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/questlines/router.oldtest.ts index ee1ec2d14..aa0a21e7e 100644 --- a/typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/questlines/router.test.ts +++ b/typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/questlines/router.oldtest.ts @@ -1,6 +1,6 @@ import type { QuestDocument, QuestlineDocument } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; import { TestingIIDXSPQuest } from "#test-utils/test-data"; @@ -24,7 +24,7 @@ t.test("GET /api/v1/games/:game/:playtype/targets/questlines", (t) => { } t.test("Should return all questlines for this game.", async (t) => { - await db.questlines.insert([ + await MONGODB_KILL.questlines.insert([ mkSet({ name: "Testing Set", questlineID: "name" }), mkSet({ name: "Testing Other Set", questlineID: "similar_name" }), mkSet({ name: "Different Name", questlineID: "radically_different_name" }), @@ -58,8 +58,8 @@ t.test("GET /api/v1/games/:game/:playtype/targets/questlines", (t) => { t.test("GET /api/v1/games/:game/:playtype/targets/questlines/:questlineID", (t) => { t.beforeEach(ResetDBState); t.beforeEach(async () => { - await db.questlines.insert(TestingIIDXSPQuestline); - await db.quests.insert([ + await MONGODB_KILL.questlines.insert(TestingIIDXSPQuestline); + await MONGODB_KILL.quests.insert([ TestingIIDXSPQuest, dm(TestingIIDXSPQuest, { questID: "other_quest" }) as QuestDocument, ]); diff --git a/typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/questlines/router.ts b/typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/questlines/router.ts index cff4d51b9..b41ea2567 100644 --- a/typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/questlines/router.ts +++ b/typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/questlines/router.ts @@ -1,5 +1,5 @@ import { FindStandaloneQuests, GetGoalsInQuests } from "#lib/targets/quests"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetChildQuests } from "#utils/db"; import { IsString } from "#utils/misc"; import { AssignToReqTachiData, GetGPT, GetTachiData } from "#utils/req-tachi-data"; @@ -11,7 +11,7 @@ const ResolveQuestlineID: RequestHandler = async (req, res, next) => { const { game, playtype } = GetGPT(req); const questlineID = req.params.questlineID; - const questline = await db.questlines.findOne({ + const questline = await MONGODB_KILL.questlines.findOne({ questlineID, game, playtype, @@ -37,7 +37,7 @@ const ResolveQuestlineID: RequestHandler = async (req, res, next) => { router.get("/", async (req, res) => { const { game, playtype } = GetGPT(req); - const questlines = await db.questlines.find({ game, playtype }); + const questlines = await MONGODB_KILL.questlines.find({ game, playtype }); const standalone = await FindStandaloneQuests(game, playtype); const standaloneGoals = await GetGoalsInQuests(standalone); diff --git a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/targets/quests/router.test.ts b/typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/quests/router.oldtest.ts similarity index 89% rename from typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/targets/quests/router.test.ts rename to typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/quests/router.oldtest.ts index 89a228eb2..58a13e2f4 100644 --- a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/targets/quests/router.test.ts +++ b/typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/quests/router.oldtest.ts @@ -1,6 +1,6 @@ import type { QuestDocument, QuestlineDocument, QuestSubscriptionDocument } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; import { @@ -21,7 +21,7 @@ t.test("GET /api/v1/games/:game/:playtype/targets/quests", (t) => { } t.test("Should search quests.", async (t) => { - await db.quests.insert([ + await MONGODB_KILL.quests.insert([ mkQuest({ name: "Testing Set", questID: "name" }), mkQuest({ name: "Testing Other Set", questID: "similar_name" }), mkQuest({ name: "Different Name", questID: "radically_different_name" }), @@ -52,13 +52,13 @@ t.test("GET /api/v1/games/:game/:playtype/targets/quests", (t) => { // this is my lazy sample data for these tests. const LoadLazySampleData = async () => { - await db.users.insert(FakeOtherUser); - await db.goals.insert(IIDXSPQuestGoals); - await db.quests.insert([ + await MONGODB_KILL.users.insert(FakeOtherUser); + await MONGODB_KILL.goals.insert(IIDXSPQuestGoals); + await MONGODB_KILL.quests.insert([ TestingIIDXSPQuest, dm(TestingIIDXSPQuest, { questID: "other_quest" }) as QuestDocument, ]); - await db["quest-subs"].insert([ + await MONGODB_KILL["quest-subs"].insert([ TestingIIDXSPQuestSub, dm(TestingIIDXSPQuestSub, { questID: "other_quest" }), dm(TestingIIDXSPQuestSub, { @@ -71,8 +71,8 @@ t.test("GET /api/v1/games/:game/:playtype/targets/quests/:questID", (t) => { t.beforeEach(ResetDBState); t.beforeEach(async () => { await Promise.all([ - db["goal-subs"].insert(IIDXSPQuestGoalSubs), - db.questlines.insert({ + MONGODB_KILL["goal-subs"].insert(IIDXSPQuestGoalSubs), + MONGODB_KILL.questlines.insert({ questlineID: "set_id", quests: [TestingIIDXSPQuest.questID], } as QuestlineDocument), diff --git a/typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/quests/router.test.ts b/typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/quests/router.test.ts deleted file mode 100644 index 89a228eb2..000000000 --- a/typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/quests/router.test.ts +++ /dev/null @@ -1,124 +0,0 @@ -import type { QuestDocument, QuestlineDocument, QuestSubscriptionDocument } from "tachi-common"; - -import db from "#services/mongo/db"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import { - FakeOtherUser, - IIDXSPQuestGoals, - IIDXSPQuestGoalSubs, - TestingIIDXSPQuest, - TestingIIDXSPQuestSub, -} from "#test-utils/test-data"; -import dm from "deepmerge"; -import t from "tap"; - -t.test("GET /api/v1/games/:game/:playtype/targets/quests", (t) => { - t.beforeEach(ResetDBState); - - function mkQuest(merge: any) { - return dm(TestingIIDXSPQuest, merge) as QuestDocument; - } - - t.test("Should search quests.", async (t) => { - await db.quests.insert([ - mkQuest({ name: "Testing Set", questID: "name" }), - mkQuest({ name: "Testing Other Set", questID: "similar_name" }), - mkQuest({ name: "Different Name", questID: "radically_different_name" }), - mkQuest({ - game: "chunithm", - playtype: "Single", - questID: "matching name but different gpt", - }), - mkQuest({ playtype: "DP", questID: "matching name but different playtype" }), - ]); - - const res = await mockApi.get("/api/v1/games/iidx/SP/targets/quests?search=Testing"); - - t.equal(res.statusCode, 200); - - t.hasStrict( - (res.body.body.quests as Array).sort((a, b) => - a.name.localeCompare(b.name), - ), - [{ questID: "similar_name" }, { questID: "name" }], - ); - - t.end(); - }); - - t.end(); -}); - -// this is my lazy sample data for these tests. -const LoadLazySampleData = async () => { - await db.users.insert(FakeOtherUser); - await db.goals.insert(IIDXSPQuestGoals); - await db.quests.insert([ - TestingIIDXSPQuest, - dm(TestingIIDXSPQuest, { questID: "other_quest" }) as QuestDocument, - ]); - await db["quest-subs"].insert([ - TestingIIDXSPQuestSub, - dm(TestingIIDXSPQuestSub, { questID: "other_quest" }), - dm(TestingIIDXSPQuestSub, { - userID: 2, - }), - ] as Array); -}; - -t.test("GET /api/v1/games/:game/:playtype/targets/quests/:questID", (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(async () => { - await Promise.all([ - db["goal-subs"].insert(IIDXSPQuestGoalSubs), - db.questlines.insert({ - questlineID: "set_id", - quests: [TestingIIDXSPQuest.questID], - } as QuestlineDocument), - ]); - }); - - t.beforeEach(LoadLazySampleData); - - t.test("Should return the quest and its goals.", async (t) => { - const res = await mockApi.get( - `/api/v1/games/iidx/SP/targets/quests/${TestingIIDXSPQuest.questID}`, - ); - - t.hasStrict(res.body.body, { - quest: { questID: TestingIIDXSPQuest.questID }, - questSubs: [{ userID: 1, questID: TestingIIDXSPQuest.questID }], - users: [{ id: 1 }, { id: 2 }], - goals: [ - { goalID: "eg_goal_1" }, - { goalID: "eg_goal_2" }, - { goalID: "eg_goal_3" }, - { goalID: "eg_goal_4" }, - ], - parentQuestlines: [{ questlineID: "set_id" }], - }); - - t.end(); - }); - - t.test("Should return 404 if the requested quest doesn't exist.", async (t) => { - const res = await mockApi.get("/api/v1/games/iidx/SP/targets/quests/fake_quest"); - - t.equal(res.statusCode, 404); - - t.end(); - }); - - t.test("Should return 404 if the quest exists but for a different GPT.", async (t) => { - const res = await mockApi.get( - `/api/v1/games/iidx/DP/targets/quests/${TestingIIDXSPQuest.questID}`, - ); - - t.equal(res.statusCode, 404); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/quests/router.ts b/typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/quests/router.ts index 3421a2f75..abc790bef 100644 --- a/typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/quests/router.ts +++ b/typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/quests/router.ts @@ -1,6 +1,6 @@ import { SearchCollection } from "#lib/search/search"; import { GetGoalsInQuest, GetGoalsInQuests } from "#lib/targets/quests"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { IsString } from "#utils/misc"; import { AssignToReqTachiData, GetGPT, GetTachiData } from "#utils/req-tachi-data"; import { GetUsersWithIDs } from "#utils/user"; @@ -12,7 +12,7 @@ const ResolveQuestID: RequestHandler = async (req, res, next) => { const { game, playtype } = GetGPT(req); const questID = req.params.questID; - const quest = await db.quests.findOne({ + const quest = await MONGODB_KILL.quests.findOne({ questID, game, playtype, @@ -48,7 +48,7 @@ router.get("/", async (req, res) => { } const quests = await SearchCollection( - db.quests, + MONGODB_KILL.quests, req.query.search, "quests", { game, playtype }, @@ -71,7 +71,7 @@ router.get("/", async (req, res) => { router.get("/:questID", ResolveQuestID, async (req, res) => { const quest = GetTachiData(req, "questDoc"); - const questSubs = await db["quest-subs"].find({ + const questSubs = await MONGODB_KILL["quest-subs"].find({ questID: quest.questID, }); @@ -79,7 +79,7 @@ router.get("/:questID", ResolveQuestID, async (req, res) => { const goals = await GetGoalsInQuest(quest); - const parentQuestlines = await db.questlines.find({ + const parentQuestlines = await MONGODB_KILL.questlines.find({ quests: quest.questID, }); diff --git a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/targets/router.test.ts b/typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/router.oldtest.ts similarity index 93% rename from typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/targets/router.test.ts rename to typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/router.oldtest.ts index 1c5c4533c..4afc80139 100644 --- a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/targets/router.test.ts +++ b/typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/router.oldtest.ts @@ -1,6 +1,6 @@ import type { GoalSubscriptionDocument } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { mkFakeGoal, mkFakeGoalSub } from "#test-utils/misc"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; @@ -11,13 +11,13 @@ t.test("GET /api/v1/games/:game/:playtype/targets/recently-achieved", (t) => { t.beforeEach(ResetDBState); t.test("Should return some recently achieved goals.", async (t) => { - await db.goals.insert([ + await MONGODB_KILL.goals.insert([ mkFakeGoal({ goalID: "achieved" }), mkFakeGoal({ goalID: "achieved_more_recently" }), mkFakeGoal({ goalID: "achieved_instantly" }), ]); - await db["goal-subs"].insert([ + await MONGODB_KILL["goal-subs"].insert([ // not achieved HC511UserGoal, mkFakeGoalSub({ goalID: "achieved", achieved: true, timeAchieved: 1000 }), @@ -51,14 +51,14 @@ t.test("GET /api/v1/games/:game/:playtype/targets/recently-raised", (t) => { t.beforeEach(ResetDBState); t.test("Should return some recently interacted goals.", async (t) => { - await db.goals.insert([ + await MONGODB_KILL.goals.insert([ mkFakeGoal({ goalID: "interacted" }), mkFakeGoal({ goalID: "interacted_more_recently" }), mkFakeGoal({ goalID: "achieved" }), mkFakeGoal({ goalID: "achieved_instantly" }), ]); - await db["goal-subs"].insert([ + await MONGODB_KILL["goal-subs"].insert([ // not achieved HC511UserGoal, mkFakeGoalSub({ goalID: "interacted", achieved: false, lastInteraction: 1000 }), diff --git a/typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/router.test.ts b/typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/router.test.ts deleted file mode 100644 index 1c5c4533c..000000000 --- a/typescript/server/src/server/router/api/v1/games/_game/_playtype/targets/router.test.ts +++ /dev/null @@ -1,102 +0,0 @@ -import type { GoalSubscriptionDocument } from "tachi-common"; - -import db from "#services/mongo/db"; -import { mkFakeGoal, mkFakeGoalSub } from "#test-utils/misc"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import { HC511UserGoal } from "#test-utils/test-data"; -import t from "tap"; - -t.test("GET /api/v1/games/:game/:playtype/targets/recently-achieved", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should return some recently achieved goals.", async (t) => { - await db.goals.insert([ - mkFakeGoal({ goalID: "achieved" }), - mkFakeGoal({ goalID: "achieved_more_recently" }), - mkFakeGoal({ goalID: "achieved_instantly" }), - ]); - - await db["goal-subs"].insert([ - // not achieved - HC511UserGoal, - mkFakeGoalSub({ goalID: "achieved", achieved: true, timeAchieved: 1000 }), - mkFakeGoalSub({ goalID: "achieved_more_recently", achieved: true, timeAchieved: 2000 }), - mkFakeGoalSub({ - goalID: "achieved_instantly", - achieved: true, - timeAchieved: 1000, - wasInstantlyAchieved: true, - }), - ]); - - const res = await mockApi.get("/api/v1/games/iidx/SP/targets/recently-achieved"); - - t.equal(res.statusCode, 200); - - // Shouldn't have the unachieved goal, shouldn't have the instantly achieved goal. - // should also have them in the right order. - t.strictSame( - res.body.body.goalSubs.map((e: GoalSubscriptionDocument) => e.goalID), - ["achieved_more_recently", "achieved"], - ); - - t.end(); - }); - - t.end(); -}); - -t.test("GET /api/v1/games/:game/:playtype/targets/recently-raised", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should return some recently interacted goals.", async (t) => { - await db.goals.insert([ - mkFakeGoal({ goalID: "interacted" }), - mkFakeGoal({ goalID: "interacted_more_recently" }), - mkFakeGoal({ goalID: "achieved" }), - mkFakeGoal({ goalID: "achieved_instantly" }), - ]); - - await db["goal-subs"].insert([ - // not achieved - HC511UserGoal, - mkFakeGoalSub({ goalID: "interacted", achieved: false, lastInteraction: 1000 }), - - // happened more recently - mkFakeGoalSub({ - goalID: "interacted_more_recently", - achieved: false, - lastInteraction: 2000, - }), - - // shouldnt be included -- just recently-raised. - mkFakeGoalSub({ - goalID: "achieved", - achieved: true, - lastInteraction: 1000, - }), - mkFakeGoalSub({ - goalID: "achieved_instantly", - achieved: true, - lastInteraction: 1000, - wasInstantlyAchieved: true, - }), - ]); - - const res = await mockApi.get("/api/v1/games/iidx/SP/targets/recently-raised"); - - t.equal(res.statusCode, 200); - - // Shouldn't have the uninteracted goal, shouldn't have the instantly achieved goal. - // should also have them in the right order. - t.strictSame( - res.body.body.goalSubs.map((e: GoalSubscriptionDocument) => e.goalID), - ["interacted_more_recently", "interacted"], - ); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1/games/_game/router.test.ts b/typescript/server/src/server/router/api/v1/games/_game/router.oldtest.ts similarity index 100% rename from typescript/server/src/server/router/api/v1/games/_game/router.test.ts rename to typescript/server/src/server/router/api/v1/games/_game/router.oldtest.ts diff --git a/typescript/server/src/server/router/api/v1/games/router.test.ts b/typescript/server/src/server/router/api/v1/games/router.oldtest.ts similarity index 100% rename from typescript/server/src/server/router/api/v1/games/router.test.ts rename to typescript/server/src/server/router/api/v1/games/router.oldtest.ts diff --git a/typescript/server/src/server/router/api/v1postgres/import/router.test.ts b/typescript/server/src/server/router/api/v1/import/router.oldtest.ts similarity index 93% rename from typescript/server/src/server/router/api/v1postgres/import/router.test.ts rename to typescript/server/src/server/router/api/v1/import/router.oldtest.ts index 39602518c..b9d41f4f7 100644 --- a/typescript/server/src/server/router/api/v1postgres/import/router.test.ts +++ b/typescript/server/src/server/router/api/v1/import/router.oldtest.ts @@ -1,4 +1,4 @@ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { RequireAuthPerms } from "#test-utils/api-common"; import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; import mockApi from "#test-utils/mock-api"; @@ -39,7 +39,7 @@ t.test("POST /api/v1/import/file", async (t) => { t.equal(res.body.body.scoreIDs.length, 2, "Should have 2 successful scores."); - const scoreCount = await db.scores.find({ + const scoreCount = await MONGODB_KILL.scores.find({ scoreID: { $in: res.body.body.scoreIDs }, }); @@ -64,7 +64,7 @@ t.test("POST /api/v1/import/file", async (t) => { t.equal(res.body.body.errors.length, 0, "Should have 0 failed scores."); - const scoreCount = await db.scores.find({ + const scoreCount = await MONGODB_KILL.scores.find({ scoreID: { $in: res.body.body.scoreIDs }, }); @@ -89,7 +89,7 @@ t.test("POST /api/v1/import/file", async (t) => { t.strictSame(res.body.body.errors, [], "Should have 0 failed scores."); - const scoreCount = await db.scores.find({ + const scoreCount = await MONGODB_KILL.scores.find({ scoreID: { $in: res.body.body.scoreIDs }, }); @@ -127,7 +127,7 @@ t.test("POST /api/v1/import/file", async (t) => { t.equal(res.body.body.scoreIDs.length, 2, "Should have 2 successful scores."); - const scoreCount = await db.scores.find({ + const scoreCount = await MONGODB_KILL.scores.find({ scoreID: { $in: res.body.body.scoreIDs }, }); @@ -152,7 +152,7 @@ t.test("POST /api/v1/import/file", async (t) => { t.equal(res.body.body.errors.length, 0, "Should have 0 failed scores."); - const scoreCount = await db.scores.find({ + const scoreCount = await MONGODB_KILL.scores.find({ scoreID: { $in: res.body.body.scoreIDs }, }); @@ -177,7 +177,7 @@ t.test("POST /api/v1/import/file", async (t) => { t.equal(res.body.body.errors.length, 0, "Should have 0 failed scores."); - const scoreCount = await db.scores.find({ + const scoreCount = await MONGODB_KILL.scores.find({ scoreID: { $in: res.body.body.scoreIDs }, }); @@ -211,7 +211,7 @@ t.test("POST /api/v1/import/file", async (t) => { t.equal(res.body.body.scoreIDs.length, 0, "Should have 0 successful scores."); - const scoreCount = await db.scores.find({ + const scoreCount = await MONGODB_KILL.scores.find({ scoreID: { $in: res.body.body.scoreIDs }, }); @@ -255,7 +255,7 @@ t.test("POST /api/v1/import/file", async (t) => { t.equal(res.body.body.scoreIDs.length, 1, "Should have 1 successful score."); - const scoreCount = await db.scores.find({ + const scoreCount = await MONGODB_KILL.scores.find({ scoreID: { $in: res.body.body.scoreIDs }, }); @@ -285,7 +285,7 @@ t.test("POST /api/v1/import/file", async (t) => { t.equal(res.body.body.scoreIDs.length, 1, "Should have 1 successful score."); - const scoreCount = await db.scores.find({ + const scoreCount = await MONGODB_KILL.scores.find({ scoreID: { $in: res.body.body.scoreIDs }, }); @@ -329,7 +329,7 @@ t.test("POST /api/v1/import/orphans", async (t) => { t.beforeEach(ResetDBState); t.test("Should force a reprocessing of orphan scores.", async (t) => { - await db["orphan-scores"].insert([ + await MONGODB_KILL["orphan-scores"].insert([ { userID: 1, timeInserted: 1000, @@ -382,7 +382,7 @@ t.test("POST /api/v1/import/orphans", async (t) => { t.equal(res.body.body.processed, 2, "Should reprocess two orphans."); t.equal(res.body.body.failed, 1, "Should fail in de-orphaning one orphan."); - const dbCount = await db["orphan-scores"].count({}); + const dbCount = await MONGODB_KILL["orphan-scores"].count({}); t.equal(dbCount, 1, "Should only leave one orphan-score in the database."); diff --git a/typescript/server/src/server/router/api/v1/import/router.test.ts b/typescript/server/src/server/router/api/v1/import/router.test.ts deleted file mode 100644 index 39602518c..000000000 --- a/typescript/server/src/server/router/api/v1/import/router.test.ts +++ /dev/null @@ -1,393 +0,0 @@ -import db from "#services/mongo/db"; -import { RequireAuthPerms } from "#test-utils/api-common"; -import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import { - GetKTDataBuffer, - LoadTachiIIDXData, - TestingIIDXEamusementCSV26, - TestingIIDXEamusementCSV27, -} from "#test-utils/test-data"; -import t from "tap"; - -t.test("POST /api/v1/import/file", async (t) => { - const cookie = await CreateFakeAuthCookie(mockApi); - - t.beforeEach(ResetDBState); - - RequireAuthPerms("/api/v1/import/file", "submit_score", "POST"); - - t.test("file/eamusement-iidx-csv", (t) => { - t.beforeEach(LoadTachiIIDXData); - - t.test("Mini HV import", async (t) => { - const res = await mockApi - .post("/api/v1/import/file") - .set("Cookie", cookie) - .attach( - "scoreData", - GetKTDataBuffer("./eamusement-iidx-csv/small-hv-file.csv"), - "my_csv.csv", - ) - .field("importType", "file/eamusement-iidx-csv") - .field("playtype", "SP"); - - t.equal(res.body.success, true, "Should be successful."); - - t.equal(res.body.body.errors.length, 0, "Mini HV Import Should have 0 failed scores."); - - t.equal(res.body.body.scoreIDs.length, 2, "Should have 2 successful scores."); - - const scoreCount = await db.scores.find({ - scoreID: { $in: res.body.body.scoreIDs }, - }); - - t.equal( - scoreCount.length, - res.body.body.scoreIDs.length, - "All returned scoreIDs should be inserted to the DB.", - ); - - t.end(); - }); - - t.test("Valid Rootage CSV import", async (t) => { - const res = await mockApi - .post("/api/v1/import/file") - .set("Cookie", cookie) - .attach("scoreData", TestingIIDXEamusementCSV26, "my_csv.csv") - .field("importType", "file/eamusement-iidx-csv") - .field("playtype", "SP"); - - t.equal(res.body.success, true, "Should be successful."); - - t.equal(res.body.body.errors.length, 0, "Should have 0 failed scores."); - - const scoreCount = await db.scores.find({ - scoreID: { $in: res.body.body.scoreIDs }, - }); - - t.equal( - scoreCount.length, - res.body.body.scoreIDs.length, - "All returned scoreIDs should be inserted to the DB.", - ); - - t.end(); - }); - - t.test("Valid Heroic Verse CSV import", async (t) => { - const res = await mockApi - .post("/api/v1/import/file") - .set("Cookie", cookie) - .attach("scoreData", TestingIIDXEamusementCSV27, "my_csv.csv") - .field("importType", "file/eamusement-iidx-csv") - .field("playtype", "SP"); - - t.equal(res.body.success, true, "Should be successful."); - - t.strictSame(res.body.body.errors, [], "Should have 0 failed scores."); - - const scoreCount = await db.scores.find({ - scoreID: { $in: res.body.body.scoreIDs }, - }); - - t.equal( - scoreCount.length, - res.body.body.scoreIDs.length, - "All returned scoreIDs should be inserted to the DB.", - ); - - t.end(); - }); - - t.end(); - }); - - // thats right i literally just copied it - t.test("file/pli-iidx-csv", (t) => { - t.beforeEach(LoadTachiIIDXData); - - t.test("Mini HV import", async (t) => { - const res = await mockApi - .post("/api/v1/import/file") - .set("Cookie", cookie) - .attach( - "scoreData", - GetKTDataBuffer("./eamusement-iidx-csv/small-hv-file.csv"), - "my_csv.csv", - ) - .field("importType", "file/pli-iidx-csv") - .field("playtype", "SP"); - - t.equal(res.body.success, true, "Should be successful."); - - t.equal(res.body.body.errors.length, 0, "Mini HV Import Should have 0 failed scores."); - - t.equal(res.body.body.scoreIDs.length, 2, "Should have 2 successful scores."); - - const scoreCount = await db.scores.find({ - scoreID: { $in: res.body.body.scoreIDs }, - }); - - t.equal( - scoreCount.length, - res.body.body.scoreIDs.length, - "All returned scoreIDs should be inserted to the DB.", - ); - - t.end(); - }); - - t.test("Valid Rootage CSV import", async (t) => { - const res = await mockApi - .post("/api/v1/import/file") - .set("Cookie", cookie) - .attach("scoreData", TestingIIDXEamusementCSV26, "my_csv.csv") - .field("importType", "file/pli-iidx-csv") - .field("playtype", "SP"); - - t.equal(res.body.success, true, "Should be successful."); - - t.equal(res.body.body.errors.length, 0, "Should have 0 failed scores."); - - const scoreCount = await db.scores.find({ - scoreID: { $in: res.body.body.scoreIDs }, - }); - - t.equal( - scoreCount.length, - res.body.body.scoreIDs.length, - "All returned scoreIDs should be inserted to the DB.", - ); - - t.end(); - }); - - t.test("Valid Heroic Verse CSV import", async (t) => { - const res = await mockApi - .post("/api/v1/import/file") - .set("Cookie", cookie) - .attach("scoreData", TestingIIDXEamusementCSV27, "my_csv.csv") - .field("importType", "file/pli-iidx-csv") - .field("playtype", "SP"); - - t.equal(res.body.success, true, "Should be successful."); - - t.equal(res.body.body.errors.length, 0, "Should have 0 failed scores."); - - const scoreCount = await db.scores.find({ - scoreID: { $in: res.body.body.scoreIDs }, - }); - - t.equal( - scoreCount.length, - res.body.body.scoreIDs.length, - "All returned scoreIDs should be inserted to the DB.", - ); - - t.end(); - }); - - t.end(); - }); - - t.test("file/batch-manual", (t) => { - t.test("Empty import", async (t) => { - const res = await mockApi - .post("/api/v1/import/file") - .set("Cookie", cookie) - .attach( - "scoreData", - GetKTDataBuffer("./batch-manual/empty-file.json"), - "empty-file.json", - ) - .field("importType", "file/batch-manual"); - - t.equal(res.body.success, true, "Should be successful."); - - t.equal(res.body.body.errors.length, 0, "Import Should have 0 failed scores."); - - t.equal(res.body.body.scoreIDs.length, 0, "Should have 0 successful scores."); - - const scoreCount = await db.scores.find({ - scoreID: { $in: res.body.body.scoreIDs }, - }); - - t.equal( - scoreCount.length, - res.body.body.scoreIDs.length, - "All returned scoreIDs should be inserted to the DB.", - ); - - t.end(); - }); - - t.test("Invalid JSON", async (t) => { - const res = await mockApi - .post("/api/v1/import/file") - .set("Cookie", cookie) - .attach("scoreData", Buffer.from("{invalid JSON")) - .field("importType", "file/batch-manual"); - - t.equal(res.body.success, false, "Should not be successful."); - - t.equal(res.statusCode, 400); - - t.end(); - }); - - t.test("Single import", async (t) => { - const res = await mockApi - .post("/api/v1/import/file") - .set("Cookie", cookie) - .attach( - "scoreData", - GetKTDataBuffer("./batch-manual/small-file.json"), - "small-file.json", - ) - .field("importType", "file/batch-manual"); - - t.equal(res.body.success, true, "Should be successful."); - - t.equal(res.body.body.errors.length, 0, "Import Should have 0 failed scores."); - - t.equal(res.body.body.scoreIDs.length, 1, "Should have 1 successful score."); - - const scoreCount = await db.scores.find({ - scoreID: { $in: res.body.body.scoreIDs }, - }); - - t.equal( - scoreCount.length, - res.body.body.scoreIDs.length, - "All returned scoreIDs should be inserted to the DB.", - ); - - t.end(); - }); - - t.test("Single sdvxInGameID import", async (t) => { - const res = await mockApi - .post("/api/v1/import/file") - .set("Cookie", cookie) - .attach( - "scoreData", - GetKTDataBuffer("./batch-manual/sdvx-in-game-id.json"), - "small-file.json", - ) - .field("importType", "file/batch-manual"); - - t.equal(res.body.success, true, "Should be successful."); - - t.equal(res.body.body.errors.length, 0, "Import Should have 0 failed scores."); - - t.equal(res.body.body.scoreIDs.length, 1, "Should have 1 successful score."); - - const scoreCount = await db.scores.find({ - scoreID: { $in: res.body.body.scoreIDs }, - }); - - t.equal( - scoreCount.length, - res.body.body.scoreIDs.length, - "All returned scoreIDs should be inserted to the DB.", - ); - - t.end(); - }); - - t.end(); - }); - - t.skip("file/solid-state-squad", (t) => { - t.beforeEach(LoadTachiIIDXData); - - t.test("Large Import", async (t) => { - const res = await mockApi - .post("/api/v1/import/file") - .set("Cookie", cookie) - .attach("scoreData", GetKTDataBuffer("./s3/large-example.xml"), "large.xml") - .field("importType", "file/solid-state-squad"); - - t.equal(res.body.success, true, "Should be successful"); - t.equal(res.body.body.scoreIDs.length, null, "Should parse N scores."); - - t.end(); - }); - - t.end(); - }); - - t.end(); -}); - -t.test("POST /api/v1/import/orphans", async (t) => { - const cookie = await CreateFakeAuthCookie(mockApi); - - t.beforeEach(ResetDBState); - - t.test("Should force a reprocessing of orphan scores.", async (t) => { - await db["orphan-scores"].insert([ - { - userID: 1, - timeInserted: 1000, - orphanID: "asdf", - importType: "ir/direct-manual", - errMsg: "foo", - context: { - game: "iidx", - playtype: "SP", - service: "foo", - version: null, - }, - data: { - score: 500, - lamp: "HARD CLEAR", - matchType: "songTitle", - identifier: "5.1.1.", - difficulty: "ANOTHER", - }, - game: "iidx", - }, - { - userID: 1, - timeInserted: 1000, - orphanID: "asdf2", - importType: "ir/direct-manual", - errMsg: "foo", - context: { - game: "iidx", - playtype: "SP", - service: "foo", - version: null, - }, - data: { - score: 500, - lamp: "HARD CLEAR", - matchType: "songTitle", - identifier: "TITLE NOBODY WILL USE", - difficulty: "ANOTHER", - }, - game: "iidx", - }, - ]); - - const res = await mockApi.post("/api/v1/import/orphans").set("Cookie", cookie); - - t.equal(res.statusCode, 200, "Should return 200."); - - t.equal(res.body.body.success, 1, "Should successfully reprocess one orphan."); - t.equal(res.body.body.processed, 2, "Should reprocess two orphans."); - t.equal(res.body.body.failed, 1, "Should fail in de-orphaning one orphan."); - - const dbCount = await db["orphan-scores"].count({}); - - t.equal(dbCount, 1, "Should only leave one orphan-score in the database."); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1/imports/middleware.ts b/typescript/server/src/server/router/api/v1/imports/middleware.ts index e4a1b2670..2f28207b7 100644 --- a/typescript/server/src/server/router/api/v1/imports/middleware.ts +++ b/typescript/server/src/server/router/api/v1/imports/middleware.ts @@ -2,12 +2,12 @@ import type { RequestHandler } from "express"; import { SYMBOL_TACHI_API_AUTH } from "#lib/constants/tachi"; import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { AssignToReqTachiData, GetTachiData } from "#utils/req-tachi-data"; import { IsRequesterAdmin } from "#utils/user"; export const GetImportFromParam: RequestHandler = async (req, res, next) => { - const importDoc = await db.imports.findOne({ importID: req.params.importID }); + const importDoc = await MONGODB_KILL.imports.findOne({ importID: req.params.importID }); if (!importDoc) { return res.status(404).json({ diff --git a/typescript/server/src/server/router/api/v1/imports/router.test.ts b/typescript/server/src/server/router/api/v1/imports/router.oldtest.ts similarity index 88% rename from typescript/server/src/server/router/api/v1/imports/router.test.ts rename to typescript/server/src/server/router/api/v1/imports/router.oldtest.ts index f953744e4..b5b52478d 100644 --- a/typescript/server/src/server/router/api/v1/imports/router.test.ts +++ b/typescript/server/src/server/router/api/v1/imports/router.oldtest.ts @@ -1,4 +1,4 @@ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; import { mkFakeImport } from "#test-utils/misc"; import mockApi from "#test-utils/mock-api"; @@ -9,7 +9,7 @@ import t from "tap"; t.test("GET /api/v1/imports/:importID", (t) => { t.beforeEach(ResetDBState); - t.beforeEach(() => db.imports.insert(FakeImport)); + t.beforeEach(() => MONGODB_KILL.imports.insert(FakeImport)); t.test("Should return the import at this ID.", async (t) => { const res = await mockApi.get(`/api/v1/imports/${FakeImport.importID}`); @@ -60,7 +60,7 @@ t.test("GET /api/v1/imports/:importID", (t) => { t.test("POST /api/v1/imports/:importID/revert", async (t) => { t.beforeEach(ResetDBState); - t.beforeEach(() => db.imports.insert(FakeImport)); + t.beforeEach(() => MONGODB_KILL.imports.insert(FakeImport)); const cookie = await CreateFakeAuthCookie(mockApi); @@ -74,7 +74,7 @@ t.test("POST /api/v1/imports/:importID/revert", async (t) => { t.strictSame(res.body.body, {}, "The response body should be empty."); t.resolveMatch( - db.scores.findOne({ scoreID: FakeImport.scoreIDs[0] }), + MONGODB_KILL.scores.findOne({ scoreID: FakeImport.scoreIDs[0] }), // @ts-expect-error https://github.com/DefinitelyTyped/DefinitelyTyped/pull/60020 null, @@ -101,7 +101,7 @@ t.test("POST /api/v1/imports/:importID/revert", async (t) => { }); t.test("Should return 403 if the user is authed as someone else.", async (t) => { - await db.users.update( + await MONGODB_KILL.users.update( { id: 1, }, @@ -112,7 +112,7 @@ t.test("POST /api/v1/imports/:importID/revert", async (t) => { const someoneElsesImport = mkFakeImport({ userID: 2, importID: "someone_elses" }); - await db.imports.insert(someoneElsesImport); + await MONGODB_KILL.imports.insert(someoneElsesImport); const res = await mockApi .post(`/api/v1/imports/${someoneElsesImport.importID}/revert`) @@ -126,7 +126,7 @@ t.test("POST /api/v1/imports/:importID/revert", async (t) => { t.test( "Should allow reverting another users import if the requester is an admin.", async (t) => { - await db.users.update( + await MONGODB_KILL.users.update( { id: 1, }, @@ -137,7 +137,7 @@ t.test("POST /api/v1/imports/:importID/revert", async (t) => { const someoneElsesImport = mkFakeImport({ userID: 2, importID: "someone_elses" }); - await db.imports.insert(someoneElsesImport); + await MONGODB_KILL.imports.insert(someoneElsesImport); const res = await mockApi .post(`/api/v1/imports/${someoneElsesImport.importID}/revert`) @@ -148,7 +148,7 @@ t.test("POST /api/v1/imports/:importID/revert", async (t) => { t.strictSame(res.body.body, {}, "The response body should be empty."); t.resolveMatch( - db.scores.findOne({ scoreID: FakeImport.scoreIDs[0] }), + MONGODB_KILL.scores.findOne({ scoreID: FakeImport.scoreIDs[0] }), // @ts-expect-error https://github.com/DefinitelyTyped/DefinitelyTyped/pull/60020 null, diff --git a/typescript/server/src/server/router/api/v1/imports/router.ts b/typescript/server/src/server/router/api/v1/imports/router.ts index fdc08a5ee..434597b76 100644 --- a/typescript/server/src/server/router/api/v1/imports/router.ts +++ b/typescript/server/src/server/router/api/v1/imports/router.ts @@ -9,7 +9,7 @@ import ScoreImportQueue, { ScoreImportQueueEvents } from "#lib/score-import/work import { ServerConfig, TachiConfig } from "#lib/setup/config"; import { RequirePermissions } from "#server/middleware/auth"; import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetRelevantSongsAndCharts } from "#utils/db"; import { DeleteUndefinedProps } from "#utils/misc"; import { GetTachiData } from "#utils/req-tachi-data"; @@ -50,7 +50,7 @@ router.get( DeleteUndefinedProps(query); - const imports = await db.imports.find(query, { + const imports = await MONGODB_KILL.imports.find(query, { sort: { timeFinished: -1 }, limit: 500, }); @@ -103,7 +103,7 @@ router.get( DeleteUndefinedProps(query); - const trackers = await db["import-trackers"].find(query, { + const trackers = await MONGODB_KILL["import-trackers"].find(query, { sort: { timeStarted: -1 }, limit: 500, }); @@ -130,13 +130,13 @@ router.get( router.get("/:importID", GetImportFromParam, async (req, res) => { const importDoc = GetTachiData(req, "importDoc"); - const scores = await db.scores.find({ + const scores = await MONGODB_KILL.scores.find({ scoreID: { $in: importDoc.scoreIDs }, }); const { songs, charts } = await GetRelevantSongsAndCharts(scores, importDoc.game); - const sessions = await db.sessions.find({ + const sessions = await MONGODB_KILL.sessions.find({ sessionID: { $in: importDoc.createdSessions.map((e) => e.sessionID) }, }); @@ -243,7 +243,7 @@ router.get("/:importID/poll-status", async (req, res) => { }); } - const importDoc = await db.imports.findOne({ importID: req.params.importID }); + const importDoc = await MONGODB_KILL.imports.findOne({ importID: req.params.importID }); if (importDoc) { return res.status(200).json({ @@ -259,7 +259,7 @@ router.get("/:importID/poll-status", async (req, res) => { const job = await FindImportJob(req.params.importID); if (!job) { - const tracker = await db["import-trackers"].findOne({ + const tracker = await MONGODB_KILL["import-trackers"].findOne({ importID: req.params.importID, }); diff --git a/typescript/server/src/server/router/api/v1postgres/oauth/router.test.ts b/typescript/server/src/server/router/api/v1/oauth/router.oldtest.ts similarity index 92% rename from typescript/server/src/server/router/api/v1postgres/oauth/router.test.ts rename to typescript/server/src/server/router/api/v1/oauth/router.oldtest.ts index a066e0e55..d7dc5a4e9 100644 --- a/typescript/server/src/server/router/api/v1postgres/oauth/router.test.ts +++ b/typescript/server/src/server/router/api/v1/oauth/router.oldtest.ts @@ -1,4 +1,4 @@ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; import t from "tap"; @@ -17,7 +17,7 @@ t.test("POST /api/v1/oauth/token", (t) => { t.equal(res.statusCode, 200); - const tokenDoc = await db["api-tokens"].findOne({ + const tokenDoc = await MONGODB_KILL["api-tokens"].findOne({ token: res.body.body.token, }); @@ -32,7 +32,7 @@ t.test("POST /api/v1/oauth/token", (t) => { "Should assign the permissions this client has configured.", ); - const exists = await db["oauth2-auth-codes"].findOne({ code: "AUTH_CODE" }); + const exists = await MONGODB_KILL["oauth2-auth-codes"].findOne({ code: "AUTH_CODE" }); t.equal(exists, null, "Should remove the code from the database after being used."); diff --git a/typescript/server/src/server/router/api/v1/oauth/router.test.ts b/typescript/server/src/server/router/api/v1/oauth/router.test.ts deleted file mode 100644 index a066e0e55..000000000 --- a/typescript/server/src/server/router/api/v1/oauth/router.test.ts +++ /dev/null @@ -1,99 +0,0 @@ -import db from "#services/mongo/db"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import t from "tap"; - -t.test("POST /api/v1/oauth/token", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should grant and create an api token.", async (t) => { - const res = await mockApi.post(`/api/v1/oauth/token`).send({ - client_id: "OAUTH2_CLIENT_ID", - client_secret: "OAUTH2_CLIENT_SECRET", - grant_type: "authorization_code", - redirect_uri: "https://example.com/callback", - code: "AUTH_CODE", - }); - - t.equal(res.statusCode, 200); - - const tokenDoc = await db["api-tokens"].findOne({ - token: res.body.body.token, - }); - - t.not(tokenDoc, null); - t.equal(tokenDoc?.userID, 1); - t.equal(tokenDoc?.fromAPIClient, "OAUTH2_CLIENT_ID"); - t.strictSame( - tokenDoc?.permissions, - { - customise_profile: true, - }, - "Should assign the permissions this client has configured.", - ); - - const exists = await db["oauth2-auth-codes"].findOne({ code: "AUTH_CODE" }); - - t.equal(exists, null, "Should remove the code from the database after being used."); - - t.end(); - }); - - t.test("Requires a valid code.", async (t) => { - const res = await mockApi.post(`/api/v1/oauth/token`).send({ - client_id: "OAUTH2_CLIENT_ID", - client_secret: "OAUTH2_CLIENT_SECRET", - grant_type: "authorization_code", - redirect_uri: "https://example.com/callback", - code: "invalidcode", - }); - - t.equal(res.statusCode, 404); - - t.end(); - }); - - t.test("Requires a valid clientID.", async (t) => { - const res = await mockApi.post(`/api/v1/oauth/token`).send({ - client_id: "INVALID_CLIENT_ID", - client_secret: "OAUTH2_CLIENT_SECRET", - grant_type: "authorization_code", - redirect_uri: "https://example.com/callback", - code: "AUTH_CODE", - }); - - t.equal(res.statusCode, 404); - - t.end(); - }); - - t.test("Requires a valid client secret.", async (t) => { - const res = await mockApi.post(`/api/v1/oauth/token`).send({ - client_id: "OAUTH2_CLIENT_ID", - client_secret: "INVALID_SECRET", - grant_type: "authorization_code", - redirect_uri: "https://example.com/callback", - code: "AUTH_CODE", - }); - - t.equal(res.statusCode, 403); - - t.end(); - }); - - t.test("Requires an identical redirect_uri.", async (t) => { - const res = await mockApi.post(`/api/v1/oauth/token`).send({ - client_id: "OAUTH2_CLIENT_ID", - client_secret: "OAUTH2_CLIENT_SECRET", - grant_type: "authorization_code", - redirect_uri: "https://invalid.example.com/callback", - code: "AUTH_CODE", - }); - - t.equal(res.statusCode, 400); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1/oauth/router.ts b/typescript/server/src/server/router/api/v1/oauth/router.ts index e5a595d46..3ead2c326 100644 --- a/typescript/server/src/server/router/api/v1/oauth/router.ts +++ b/typescript/server/src/server/router/api/v1/oauth/router.ts @@ -1,5 +1,5 @@ import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { Random20Hex } from "#utils/misc"; import { Router } from "express"; import { p } from "prudence"; @@ -39,7 +39,7 @@ router.post( redirect_uri: string; }; - const client = await db["api-clients"].findOne({ + const client = await MONGODB_KILL["api-clients"].findOne({ clientID: body.client_id, }); @@ -66,7 +66,7 @@ router.post( }); } - const codeDoc = await db["oauth2-auth-codes"].findOne({ code: body.code }); + const codeDoc = await MONGODB_KILL["oauth2-auth-codes"].findOne({ code: body.code }); if (!codeDoc) { return res.status(404).json({ @@ -76,7 +76,7 @@ router.post( } // don't let people auth with the same code multiple times. - await db["oauth2-auth-codes"].remove({ code: body.code }); + await MONGODB_KILL["oauth2-auth-codes"].remove({ code: body.code }); const apiDoc = { userID: codeDoc.userID, @@ -89,7 +89,7 @@ router.post( }; // Now we can actually register the api key (lol) - await db["api-tokens"].insert(apiDoc); + await MONGODB_KILL["api-tokens"].insert(apiDoc); return res.status(200).json({ success: true, @@ -116,7 +116,7 @@ router.post("/create-code", async (req, res) => { const doc = { code, userID: req.session.tachi.user.id, createdOn: Date.now() }; - await db["oauth2-auth-codes"].insert(doc); + await MONGODB_KILL["oauth2-auth-codes"].insert(doc); return res.status(200).json({ success: true, diff --git a/typescript/server/src/server/router/api/v1/router.test.ts b/typescript/server/src/server/router/api/v1/router.oldtest.ts similarity index 100% rename from typescript/server/src/server/router/api/v1/router.test.ts rename to typescript/server/src/server/router/api/v1/router.oldtest.ts diff --git a/typescript/server/src/server/router/api/v1/scores/_scoreID/middleware.ts b/typescript/server/src/server/router/api/v1/scores/_scoreID/middleware.ts index 6a2e8e72f..5b76975e3 100644 --- a/typescript/server/src/server/router/api/v1/scores/_scoreID/middleware.ts +++ b/typescript/server/src/server/router/api/v1/scores/_scoreID/middleware.ts @@ -2,12 +2,12 @@ import type { RequestHandler } from "express"; import { SYMBOL_TACHI_API_AUTH } from "#lib/constants/tachi"; import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { AssignToReqTachiData, GetTachiData } from "#utils/req-tachi-data"; import { IsRequesterAdmin } from "#utils/user"; export const GetScoreFromParam: RequestHandler = async (req, res, next) => { - const score = await db.scores.findOne({ scoreID: req.params.scoreID }); + const score = await MONGODB_KILL.scores.findOne({ scoreID: req.params.scoreID }); if (!score) { return res.status(404).json({ diff --git a/typescript/server/src/server/router/api/v1postgres/scores/_scoreID/router.test.ts b/typescript/server/src/server/router/api/v1/scores/_scoreID/router.oldtest.ts similarity index 89% rename from typescript/server/src/server/router/api/v1postgres/scores/_scoreID/router.test.ts rename to typescript/server/src/server/router/api/v1/scores/_scoreID/router.oldtest.ts index 121b8a0ad..2c0fce4f6 100644 --- a/typescript/server/src/server/router/api/v1postgres/scores/_scoreID/router.test.ts +++ b/typescript/server/src/server/router/api/v1/scores/_scoreID/router.oldtest.ts @@ -1,4 +1,4 @@ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; import { mkFakeScoreIIDXSP } from "#test-utils/misc"; import mockApi from "#test-utils/mock-api"; @@ -53,7 +53,7 @@ t.test("PATCH /api/v1/scores/:scoreID", (t) => { t.equal(res.body.body.comment, "hello_world"); - const score = await db.scores.findOne({ scoreID: "TESTING_SCORE_ID" }); + const score = await MONGODB_KILL.scores.findOne({ scoreID: "TESTING_SCORE_ID" }); t.equal(score?.comment, "hello_world"); @@ -70,7 +70,7 @@ t.test("PATCH /api/v1/scores/:scoreID", (t) => { t.equal(res.body.body.highlight, true); - const score = await db.scores.findOne({ scoreID: "TESTING_SCORE_ID" }); + const score = await MONGODB_KILL.scores.findOne({ scoreID: "TESTING_SCORE_ID" }); t.equal(score?.highlight, true); @@ -87,7 +87,7 @@ t.test("PATCH /api/v1/scores/:scoreID", (t) => { t.equal(res.body.body.comment, null); - const score = await db.scores.findOne({ scoreID: "TESTING_SCORE_ID" }); + const score = await MONGODB_KILL.scores.findOne({ scoreID: "TESTING_SCORE_ID" }); t.equal(score?.comment, null); @@ -130,7 +130,7 @@ t.test("PATCH /api/v1/scores/:scoreID", (t) => { }); t.test("Should require authorisation as this user.", async (t) => { - await db["api-tokens"].insert({ + await MONGODB_KILL["api-tokens"].insert({ token: "some_dude", userID: 2, identifier: "Fake Token", @@ -155,7 +155,7 @@ t.test("PATCH /api/v1/scores/:scoreID", (t) => { }); t.test("Should require the customise_score permission", async (t) => { - await db["api-tokens"].insert({ + await MONGODB_KILL["api-tokens"].insert({ token: "some_token", userID: 1, identifier: "another fake token", @@ -184,7 +184,7 @@ t.test("DELETE /api/v1/scores/:scoreID", (t) => { t.beforeEach(ResetDBState); t.test("Should delete a score if the requester can.", async (t) => { - await db["api-tokens"].insert({ + await MONGODB_KILL["api-tokens"].insert({ userID: 1, identifier: "foo", permissions: { @@ -200,7 +200,7 @@ t.test("DELETE /api/v1/scores/:scoreID", (t) => { t.equal(res.statusCode, 200); - const dbScore = await db.scores.findOne({ scoreID: "TESTING_SCORE_ID" }); + const dbScore = await MONGODB_KILL.scores.findOne({ scoreID: "TESTING_SCORE_ID" }); t.equal(dbScore, null, "Should remove the score from the database."); @@ -208,7 +208,7 @@ t.test("DELETE /api/v1/scores/:scoreID", (t) => { }); t.test("Should require authorisation as this user.", async (t) => { - await db["api-tokens"].insert({ + await MONGODB_KILL["api-tokens"].insert({ token: "some_dude", userID: 2, identifier: "Fake Token", @@ -233,7 +233,7 @@ t.test("DELETE /api/v1/scores/:scoreID", (t) => { }); t.test("Should require the delete_score permission", async (t) => { - await db["api-tokens"].insert({ + await MONGODB_KILL["api-tokens"].insert({ token: "some_token", userID: 1, identifier: "another fake token", @@ -256,7 +256,7 @@ t.test("DELETE /api/v1/scores/:scoreID", (t) => { t.test("Should allow admins to delete other's scores", async (t) => { const cookie = await CreateFakeAuthCookie(mockApi); - await db.users.update( + await MONGODB_KILL.users.update( { id: 1, }, @@ -267,7 +267,7 @@ t.test("DELETE /api/v1/scores/:scoreID", (t) => { const someoneElsesScore = mkFakeScoreIIDXSP({ userID: 2, scoreID: "someone_elses" }); - await db.scores.insert(someoneElsesScore); + await MONGODB_KILL.scores.insert(someoneElsesScore); const res = await mockApi .delete(`/api/v1/scores/${someoneElsesScore.scoreID}`) @@ -278,7 +278,7 @@ t.test("DELETE /api/v1/scores/:scoreID", (t) => { t.strictSame(res.body.body, {}, "The response body should be empty."); t.resolveMatch( - db.scores.findOne({ scoreID: someoneElsesScore.scoreID }), + MONGODB_KILL.scores.findOne({ scoreID: someoneElsesScore.scoreID }), // @ts-expect-error https://github.com/DefinitelyTyped/DefinitelyTyped/pull/60020 null, diff --git a/typescript/server/src/server/router/api/v1/scores/_scoreID/router.test.ts b/typescript/server/src/server/router/api/v1/scores/_scoreID/router.test.ts deleted file mode 100644 index 121b8a0ad..000000000 --- a/typescript/server/src/server/router/api/v1/scores/_scoreID/router.test.ts +++ /dev/null @@ -1,292 +0,0 @@ -import db from "#services/mongo/db"; -import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; -import { mkFakeScoreIIDXSP } from "#test-utils/misc"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import { Testing511SPA } from "#test-utils/test-data"; -import { UserAuthLevels } from "tachi-common"; -import t from "tap"; - -t.test("GET /api/v1/scores/:scoreID", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should return the score at that ID.", async (t) => { - const res = await mockApi.get("/api/v1/scores/TESTING_SCORE_ID"); - - t.equal(res.body.body.score.scoreID, "TESTING_SCORE_ID"); - - t.end(); - }); - - t.test("Should return the associated data if the param is set.", async (t) => { - const res = await mockApi.get("/api/v1/scores/TESTING_SCORE_ID?getRelated=true"); - - t.equal(res.body.body.score.scoreID, "TESTING_SCORE_ID"); - t.equal(res.body.body.user.id, 1); - t.equal(res.body.body.chart.chartID, Testing511SPA.chartID); - t.equal(res.body.body.song.id, 1); - - t.end(); - }); - - t.test("Should return 404 if the score does not exist.", async (t) => { - const res = await mockApi.get("/api/v1/scores/not_real"); - - t.equal(res.statusCode, 404); - - t.end(); - }); - - t.end(); -}); - -t.test("PATCH /api/v1/scores/:scoreID", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should modify the session if the user has permission to.", async (t) => { - const res = await mockApi - .patch("/api/v1/scores/TESTING_SCORE_ID") - .set("Authorization", "Bearer fake_api_token") - .send({ - comment: "hello_world", - }); - - t.equal(res.body.body.comment, "hello_world"); - - const score = await db.scores.findOne({ scoreID: "TESTING_SCORE_ID" }); - - t.equal(score?.comment, "hello_world"); - - t.end(); - }); - - t.test("Should modify highlighted status.", async (t) => { - const res = await mockApi - .patch("/api/v1/scores/TESTING_SCORE_ID") - .set("Authorization", "Bearer fake_api_token") - .send({ - highlight: true, - }); - - t.equal(res.body.body.highlight, true); - - const score = await db.scores.findOne({ scoreID: "TESTING_SCORE_ID" }); - - t.equal(score?.highlight, true); - - t.end(); - }); - - t.test("Should set comment status to null.", async (t) => { - const res = await mockApi - .patch("/api/v1/scores/TESTING_SCORE_ID") - .set("Authorization", "Bearer fake_api_token") - .send({ - comment: null, - }); - - t.equal(res.body.body.comment, null); - - const score = await db.scores.findOne({ scoreID: "TESTING_SCORE_ID" }); - - t.equal(score?.comment, null); - - t.end(); - }); - - t.test("Should restrict comments to those between 1 and 120 characters.", async (t) => { - const res = await mockApi - .patch("/api/v1/scores/TESTING_SCORE_ID") - .set("Authorization", "Bearer fake_api_token") - .send({ - comment: "", - }); - - t.equal(res.statusCode, 400); - t.match(res.body.description, /\[comment\]/u); - - const res2 = await mockApi - .patch("/api/v1/scores/TESTING_SCORE_ID") - .set("Authorization", "Bearer fake_api_token") - .send({ - comment: "a".repeat(121), - }); - - t.equal(res2.statusCode, 400); - t.match(res2.body.description, /\[comment\]/u); - - t.end(); - }); - - t.test("Should reject empty bodies.", async (t) => { - const res = await mockApi - .patch("/api/v1/scores/TESTING_SCORE_ID") - .set("Authorization", "Bearer fake_api_token") - .send({}); - - t.equal(res.statusCode, 400); - - t.end(); - }); - - t.test("Should require authorisation as this user.", async (t) => { - await db["api-tokens"].insert({ - token: "some_dude", - userID: 2, - identifier: "Fake Token", - permissions: { - customise_score: true, - }, - fromAPIClient: null, - }); - - const res = await mockApi - .patch("/api/v1/scores/TESTING_SCORE_ID") - .set("Authorization", "Bearer some_dude") - .send({ - comment: "foo", - }); - - t.equal(res.statusCode, 403); - - t.match(res.body.description, /You are not authorised/u); - - t.end(); - }); - - t.test("Should require the customise_score permission", async (t) => { - await db["api-tokens"].insert({ - token: "some_token", - userID: 1, - identifier: "another fake token", - permissions: {}, - fromAPIClient: null, - }); - - const res = await mockApi - .patch("/api/v1/scores/TESTING_SCORE_ID") - .set("Authorization", "Bearer some_token") - .send({ - comment: "foo", - }); - - t.equal(res.statusCode, 403); - - t.match(res.body.description, /customise_score/u); - - t.end(); - }); - - t.end(); -}); - -t.test("DELETE /api/v1/scores/:scoreID", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should delete a score if the requester can.", async (t) => { - await db["api-tokens"].insert({ - userID: 1, - identifier: "foo", - permissions: { - delete_score: true, - }, - token: "foo", - fromAPIClient: null, - }); - - const res = await mockApi - .delete("/api/v1/scores/TESTING_SCORE_ID") - .set("Authorization", "Bearer foo"); - - t.equal(res.statusCode, 200); - - const dbScore = await db.scores.findOne({ scoreID: "TESTING_SCORE_ID" }); - - t.equal(dbScore, null, "Should remove the score from the database."); - - t.end(); - }); - - t.test("Should require authorisation as this user.", async (t) => { - await db["api-tokens"].insert({ - token: "some_dude", - userID: 2, - identifier: "Fake Token", - permissions: { - delete_score: true, - }, - fromAPIClient: null, - }); - - const res = await mockApi - .delete("/api/v1/scores/TESTING_SCORE_ID") - .set("Authorization", "Bearer some_dude") - .send({ - comment: "foo", - }); - - t.equal(res.statusCode, 403); - - t.match(res.body.description, /You are not authorised/u); - - t.end(); - }); - - t.test("Should require the delete_score permission", async (t) => { - await db["api-tokens"].insert({ - token: "some_token", - userID: 1, - identifier: "another fake token", - permissions: {}, - fromAPIClient: null, - }); - - const res = await mockApi - .delete("/api/v1/scores/TESTING_SCORE_ID") - .set("Authorization", "Bearer some_token") - .send(); - - t.equal(res.statusCode, 403); - - t.match(res.body.description, /delete_score/u); - - t.end(); - }); - - t.test("Should allow admins to delete other's scores", async (t) => { - const cookie = await CreateFakeAuthCookie(mockApi); - - await db.users.update( - { - id: 1, - }, - { - $set: { authLevel: UserAuthLevels.ADMIN }, - }, - ); - - const someoneElsesScore = mkFakeScoreIIDXSP({ userID: 2, scoreID: "someone_elses" }); - - await db.scores.insert(someoneElsesScore); - - const res = await mockApi - .delete(`/api/v1/scores/${someoneElsesScore.scoreID}`) - .set("Cookie", cookie); - - t.equal(res.statusCode, 200, "Should return 200."); - - t.strictSame(res.body.body, {}, "The response body should be empty."); - - t.resolveMatch( - db.scores.findOne({ scoreID: someoneElsesScore.scoreID }), - - // @ts-expect-error https://github.com/DefinitelyTyped/DefinitelyTyped/pull/60020 - null, - "The score should be deleted.", - ); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1/scores/_scoreID/router.ts b/typescript/server/src/server/router/api/v1/scores/_scoreID/router.ts index 77a950c91..a6a791174 100644 --- a/typescript/server/src/server/router/api/v1/scores/_scoreID/router.ts +++ b/typescript/server/src/server/router/api/v1/scores/_scoreID/router.ts @@ -2,7 +2,7 @@ import { log } from "#lib/log/log.js"; import { DeleteScore } from "#lib/score-mutation/delete-scores"; import { RequirePermissions } from "#server/middleware/auth"; import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetTachiData } from "#utils/req-tachi-data"; import { GetUserWithID } from "#utils/user"; import { Router } from "express"; @@ -27,8 +27,8 @@ router.get("/", async (req, res) => { if (req.query.getRelated !== undefined) { const [user, chart, song] = await Promise.all([ GetUserWithID(score.userID), - db.anyCharts[score.game].findOne({ chartID: score.chartID }), - db.anySongs[score.game].findOne({ id: score.songID }), + MONGODB_KILL.anyCharts[score.game].findOne({ chartID: score.chartID }), + MONGODB_KILL.anySongs[score.game].findOne({ id: score.songID }), ]); if (!user || !chart || !song) { @@ -110,13 +110,13 @@ router.patch( }); } - const newScore = await db.scores.findOneAndUpdate( + const newScore = await MONGODB_KILL.scores.findOneAndUpdate( { scoreID: score.scoreID }, { $set: modifyOption }, ); if (modifyOption.highlight === true || modifyOption.highlight === false) { - await db["personal-bests"].findOneAndUpdate( + await MONGODB_KILL["personal-bests"].findOneAndUpdate( { chartID: score.chartID, userID: score.userID, diff --git a/typescript/server/src/server/router/api/v1/search/router.test.ts b/typescript/server/src/server/router/api/v1/search/router.oldtest.ts similarity index 84% rename from typescript/server/src/server/router/api/v1/search/router.test.ts rename to typescript/server/src/server/router/api/v1/search/router.oldtest.ts index 81e659d01..ebfe6165b 100644 --- a/typescript/server/src/server/router/api/v1/search/router.test.ts +++ b/typescript/server/src/server/router/api/v1/search/router.oldtest.ts @@ -1,6 +1,6 @@ import type { ChartDocument, SongDocument } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { mkFakeGameSettings, mkFakeUser } from "#test-utils/misc"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; @@ -23,12 +23,14 @@ t.test("GET /api/v1/search", (t) => { }); t.test("Should highlight users as rivals if they're rivals of the requester", async (t) => { - await db.users.insert( + await MONGODB_KILL.users.insert( mkFakeUser(2, { username: "scoobydoo", usernameLowercase: "scoobydoo" }), ); - await db["game-settings"].remove({}); - await db["game-settings"].insert(mkFakeGameSettings(1, "iidx", "SP", { rivals: [2] })); + await MONGODB_KILL["game-settings"].remove({}); + await MONGODB_KILL["game-settings"].insert( + mkFakeGameSettings(1, "iidx", "SP", { rivals: [2] }), + ); const res = await mockApi .get("/api/v1/search?search=scoobydoo") diff --git a/typescript/server/src/server/router/api/v1/sessions/_sessionID/middleware.ts b/typescript/server/src/server/router/api/v1/sessions/_sessionID/middleware.ts index 581cab34b..425cf5078 100644 --- a/typescript/server/src/server/router/api/v1/sessions/_sessionID/middleware.ts +++ b/typescript/server/src/server/router/api/v1/sessions/_sessionID/middleware.ts @@ -2,11 +2,11 @@ import type { RequestHandler } from "express"; import { SYMBOL_TACHI_API_AUTH } from "#lib/constants/tachi"; import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { AssignToReqTachiData, GetTachiData } from "#utils/req-tachi-data"; export const GetSessionFromParam: RequestHandler = async (req, res, next) => { - const session = await db.sessions.findOne({ + const session = await MONGODB_KILL.sessions.findOne({ sessionID: req.params.sessionID, }); diff --git a/typescript/server/src/server/router/api/v1postgres/sessions/_sessionID/router.test.ts b/typescript/server/src/server/router/api/v1/sessions/_sessionID/router.oldtest.ts similarity index 91% rename from typescript/server/src/server/router/api/v1postgres/sessions/_sessionID/router.test.ts rename to typescript/server/src/server/router/api/v1/sessions/_sessionID/router.oldtest.ts index ddc7471ae..6dd5f4d09 100644 --- a/typescript/server/src/server/router/api/v1postgres/sessions/_sessionID/router.test.ts +++ b/typescript/server/src/server/router/api/v1/sessions/_sessionID/router.oldtest.ts @@ -1,4 +1,4 @@ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; import { Testing511SPA } from "#test-utils/test-data"; @@ -52,7 +52,7 @@ t.test("PATCH /api/v1/sessions/:sessionID", (t) => { t.equal(res.body.body.name, "hello_world"); - const session = await db.sessions.findOne({ sessionID: TESTING_SESSION_ID }); + const session = await MONGODB_KILL.sessions.findOne({ sessionID: TESTING_SESSION_ID }); t.equal(session?.name, "hello_world", "Should update the session in the database."); @@ -69,7 +69,7 @@ t.test("PATCH /api/v1/sessions/:sessionID", (t) => { t.equal(res.body.body.highlight, true); - const session = await db.sessions.findOne({ sessionID: TESTING_SESSION_ID }); + const session = await MONGODB_KILL.sessions.findOne({ sessionID: TESTING_SESSION_ID }); t.equal(session?.highlight, true); @@ -86,7 +86,7 @@ t.test("PATCH /api/v1/sessions/:sessionID", (t) => { t.equal(res.body.body.desc, "foobar"); - const session = await db.sessions.findOne({ sessionID: TESTING_SESSION_ID }); + const session = await MONGODB_KILL.sessions.findOne({ sessionID: TESTING_SESSION_ID }); t.equal(session?.desc, "foobar"); @@ -153,7 +153,7 @@ t.test("PATCH /api/v1/sessions/:sessionID", (t) => { }); t.test("Should require authorisation as this user.", async (t) => { - await db["api-tokens"].insert({ + await MONGODB_KILL["api-tokens"].insert({ token: "some_dude", userID: 2, identifier: "Fake Token", @@ -178,7 +178,7 @@ t.test("PATCH /api/v1/sessions/:sessionID", (t) => { }); t.test("Should require the customise_session permission", async (t) => { - await db["api-tokens"].insert({ + await MONGODB_KILL["api-tokens"].insert({ token: "some_token", userID: 1, identifier: "another fake token", diff --git a/typescript/server/src/server/router/api/v1/sessions/_sessionID/router.test.ts b/typescript/server/src/server/router/api/v1/sessions/_sessionID/router.test.ts deleted file mode 100644 index ddc7471ae..000000000 --- a/typescript/server/src/server/router/api/v1/sessions/_sessionID/router.test.ts +++ /dev/null @@ -1,204 +0,0 @@ -import db from "#services/mongo/db"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import { Testing511SPA } from "#test-utils/test-data"; -import t from "tap"; - -const TESTING_SESSION_ID = "Qe7b00261b1d3ba8e5c9ee4e76e77ea9f07d9493b"; - -t.test("GET /api/v1/sessions/:sessionID", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should return the session at this ID", async (t) => { - const res = await mockApi.get(`/api/v1/sessions/${TESTING_SESSION_ID}`); - - t.equal(res.body.body.session.sessionID, TESTING_SESSION_ID); - - t.equal(res.body.body.charts.length, 1); - t.equal(res.body.body.charts[0].chartID, Testing511SPA.chartID); - - t.equal(res.body.body.songs.length, 1); - t.equal(res.body.body.songs[0].id, 1); - - t.equal(res.body.body.scores.length, 1); - t.equal(res.body.body.scores[0].scoreID, "TESTING_SCORE_ID"); - - t.equal(res.body.body.user.id, 1); - - t.end(); - }); - - t.test("Should return 404 if the session does not exist.", async (t) => { - const res = await mockApi.get("/api/v1/sessions/fake_session"); - - t.equal(res.statusCode, 404); - - t.end(); - }); - - t.end(); -}); - -t.test("PATCH /api/v1/sessions/:sessionID", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should modify the session if the user has permission to.", async (t) => { - const res = await mockApi - .patch(`/api/v1/sessions/${TESTING_SESSION_ID}`) - .set("Authorization", "Bearer fake_api_token") - .send({ - name: "hello_world", - }); - - t.equal(res.body.body.name, "hello_world"); - - const session = await db.sessions.findOne({ sessionID: TESTING_SESSION_ID }); - - t.equal(session?.name, "hello_world", "Should update the session in the database."); - - t.end(); - }); - - t.test("Should modify highlighted status.", async (t) => { - const res = await mockApi - .patch(`/api/v1/sessions/${TESTING_SESSION_ID}`) - .set("Authorization", "Bearer fake_api_token") - .send({ - highlight: true, - }); - - t.equal(res.body.body.highlight, true); - - const session = await db.sessions.findOne({ sessionID: TESTING_SESSION_ID }); - - t.equal(session?.highlight, true); - - t.end(); - }); - - t.test("Should set description.", async (t) => { - const res = await mockApi - .patch(`/api/v1/sessions/${TESTING_SESSION_ID}`) - .set("Authorization", "Bearer fake_api_token") - .send({ - desc: "foobar", - }); - - t.equal(res.body.body.desc, "foobar"); - - const session = await db.sessions.findOne({ sessionID: TESTING_SESSION_ID }); - - t.equal(session?.desc, "foobar"); - - t.end(); - }); - - t.test("Should restrict names to those between 3 and 80 chars.", async (t) => { - const res = await mockApi - .patch(`/api/v1/sessions/${TESTING_SESSION_ID}`) - .set("Authorization", "Bearer fake_api_token") - .send({ - name: "a", - }); - - t.equal(res.statusCode, 400); - t.match(res.body.description, /\[name\]/u); - - const res2 = await mockApi - .patch(`/api/v1/sessions/${TESTING_SESSION_ID}`) - .set("Authorization", "Bearer fake_api_token") - .send({ - name: "a".repeat(81), - }); - - t.equal(res2.statusCode, 400); - t.match(res2.body.description, /\[name\]/u); - - t.end(); - }); - - t.test("Should restrict descs to those between 3 and 120 chars.", async (t) => { - const res = await mockApi - .patch(`/api/v1/sessions/${TESTING_SESSION_ID}`) - .set("Authorization", "Bearer fake_api_token") - .send({ - desc: "a", - }); - - t.equal(res.statusCode, 400); - t.match(res.body.description, /\[desc\]/u); - - const res2 = await mockApi - .patch(`/api/v1/sessions/${TESTING_SESSION_ID}`) - .set("Authorization", "Bearer fake_api_token") - .send({ - desc: "a".repeat(121), - }); - - t.equal(res2.statusCode, 400); - t.match(res2.body.description, /\[desc\]/u); - - t.end(); - }); - - t.test("Should reject empty bodies.", async (t) => { - const res = await mockApi - .patch(`/api/v1/sessions/${TESTING_SESSION_ID}`) - .set("Authorization", "Bearer fake_api_token") - .send({}); - - t.equal(res.statusCode, 400); - - t.end(); - }); - - t.test("Should require authorisation as this user.", async (t) => { - await db["api-tokens"].insert({ - token: "some_dude", - userID: 2, - identifier: "Fake Token", - permissions: { - customise_session: true, - }, - fromAPIClient: null, - }); - - const res = await mockApi - .patch(`/api/v1/sessions/${TESTING_SESSION_ID}`) - .set("Authorization", "Bearer some_dude") - .send({ - comment: "foo", - }); - - t.equal(res.statusCode, 403); - - t.match(res.body.description, /You are not authorised/u); - - t.end(); - }); - - t.test("Should require the customise_session permission", async (t) => { - await db["api-tokens"].insert({ - token: "some_token", - userID: 1, - identifier: "another fake token", - permissions: {}, - fromAPIClient: null, - }); - - const res = await mockApi - .patch(`/api/v1/sessions/${TESTING_SESSION_ID}`) - .set("Authorization", "Bearer some_token") - .send({ - comment: "foo", - }); - - t.equal(res.statusCode, 403); - - t.match(res.body.description, /customise_session/u); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1/sessions/_sessionID/router.ts b/typescript/server/src/server/router/api/v1/sessions/_sessionID/router.ts index eb2e762cb..cfdbdd7be 100644 --- a/typescript/server/src/server/router/api/v1/sessions/_sessionID/router.ts +++ b/typescript/server/src/server/router/api/v1/sessions/_sessionID/router.ts @@ -1,7 +1,7 @@ import { GetSessionScoreInfo } from "#lib/score-import/framework/sessions/sessions"; import { RequirePermissions } from "#server/middleware/auth"; import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetEnumDistForFolderAsOf } from "#utils/folder"; import { AddToSetInRecord } from "#utils/misc"; import { GetTachiData } from "#utils/req-tachi-data"; @@ -32,15 +32,15 @@ router.use(GetSessionFromParam); router.get("/", async (req, res) => { const session = GetTachiData(req, "sessionDoc"); - const scores = await db.scores.find({ + const scores = await MONGODB_KILL.scores.find({ scoreID: { $in: session.scoreIDs }, }); const [songs, charts, user, scoreInfo] = await Promise.all([ - db.anySongs[session.game].find({ + MONGODB_KILL.anySongs[session.game].find({ id: { $in: scores.map((e) => e.songID) }, }), - db.anyCharts[session.game].find({ + MONGODB_KILL.anyCharts[session.game].find({ chartID: { $in: scores.map((e) => e.chartID) }, }), GetUserWithID(session.userID), @@ -105,7 +105,7 @@ router.get("/folder-raises", async (req, res) => { const enumScoreMetrics = GetScoreEnumConfs(gptConfig); - const relevantScores = await db.scores.find({ + const relevantScores = await MONGODB_KILL.scores.find({ scoreID: { $in: session.scoreIDs }, }); @@ -113,7 +113,7 @@ router.get("/folder-raises", async (req, res) => { // what folderIDs were involved in this session? const affectedFolderIDs = ( - await db["folder-chart-lookup"].find( + await MONGODB_KILL["folder-chart-lookup"].find( { chartID: { $in: chartIDs }, }, @@ -124,7 +124,7 @@ router.get("/folder-raises", async (req, res) => { ).map((e) => e.folderID); // find all the active folder documents raised in this session. - const folders = await db.folders.find({ + const folders = await MONGODB_KILL.folders.find({ folderID: { $in: affectedFolderIDs }, inactive: false, }); @@ -349,7 +349,7 @@ router.patch( }); } - const newSession = await db.sessions.findOneAndUpdate( + const newSession = await MONGODB_KILL.sessions.findOneAndUpdate( { sessionID: session.sessionID }, { $set: updateExp }, ); diff --git a/typescript/server/src/server/router/api/v1/status/router.test.ts b/typescript/server/src/server/router/api/v1/status/router.oldtest.ts similarity index 100% rename from typescript/server/src/server/router/api/v1/status/router.test.ts rename to typescript/server/src/server/router/api/v1/status/router.oldtest.ts diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/api-tokens/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/api-tokens/router.oldtest.ts similarity index 93% rename from typescript/server/src/server/router/api/v1postgres/users/_userID/api-tokens/router.test.ts rename to typescript/server/src/server/router/api/v1/users/_userID/api-tokens/router.oldtest.ts index 0c82f1eb5..a07420790 100644 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/api-tokens/router.test.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/api-tokens/router.oldtest.ts @@ -1,6 +1,6 @@ import type { UserDocument } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; @@ -9,8 +9,8 @@ import t from "tap"; t.test("GET /api/v1/users/:userID/api-tokens", async (t) => { t.beforeEach(ResetDBState); t.beforeEach(async () => { - await db["api-tokens"].remove({}); - await db["api-tokens"].insert([ + await MONGODB_KILL["api-tokens"].remove({}); + await MONGODB_KILL["api-tokens"].insert([ { userID: 1, identifier: "foo", @@ -72,7 +72,7 @@ t.test("GET /api/v1/users/:userID/api-tokens", async (t) => { t.equal(res.statusCode, 401); - await db.users.insert({ + await MONGODB_KILL.users.insert({ username: "test", usernameLowercase: "test", id: 2, @@ -99,7 +99,7 @@ t.test("DELETE /api/v1/users/:userID/api-tokens/:token", async (t) => { t.equal(res.statusCode, 200); - const dbRes = await db["api-tokens"].findOne({ + const dbRes = await MONGODB_KILL["api-tokens"].findOne({ token: "fake_api_token", }); @@ -119,7 +119,7 @@ t.test("DELETE /api/v1/users/:userID/api-tokens/:token", async (t) => { }); t.test("Must return 404 if the token belongs to another user.", async (t) => { - await db["api-tokens"].insert({ + await MONGODB_KILL["api-tokens"].insert({ identifier: "foo", permissions: {}, token: "foo", @@ -132,7 +132,7 @@ t.test("DELETE /api/v1/users/:userID/api-tokens/:token", async (t) => { // so as not to reveal that this token exists. t.equal(res.statusCode, 404); - const dbRes = await db["api-tokens"].findOne({ token: "foo" }); + const dbRes = await MONGODB_KILL["api-tokens"].findOne({ token: "foo" }); t.not(dbRes, null, "Should not have deleted the token."); @@ -177,7 +177,7 @@ t.test("POST /api/v1/users/:userID/api-tokens/create", async (t) => { "Should return a conforming API Token.", ); - const dbRes = await db["api-tokens"].findOne({ + const dbRes = await MONGODB_KILL["api-tokens"].findOne({ identifier: "Hello World", }); @@ -218,7 +218,7 @@ t.test("POST /api/v1/users/:userID/api-tokens/create", async (t) => { "Should return a conforming API Token, with the permissions from that client.", ); - const dbRes = await db["api-tokens"].findOne({ + const dbRes = await MONGODB_KILL["api-tokens"].findOne({ identifier: "Test_Service", }); diff --git a/typescript/server/src/server/router/api/v1/users/_userID/api-tokens/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/api-tokens/router.test.ts deleted file mode 100644 index 0c82f1eb5..000000000 --- a/typescript/server/src/server/router/api/v1/users/_userID/api-tokens/router.test.ts +++ /dev/null @@ -1,289 +0,0 @@ -import type { UserDocument } from "tachi-common"; - -import db from "#services/mongo/db"; -import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import t from "tap"; - -t.test("GET /api/v1/users/:userID/api-tokens", async (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(async () => { - await db["api-tokens"].remove({}); - await db["api-tokens"].insert([ - { - userID: 1, - identifier: "foo", - permissions: {}, - token: "tfoo", - fromAPIClient: null, - }, - { - userID: 1, - identifier: "bar", - permissions: {}, - token: "tbar", - fromAPIClient: null, - }, - { - userID: 2, - identifier: "baz", - permissions: {}, - token: "tbaz", - fromAPIClient: null, - }, - ]); - }); - - const cookie = await CreateFakeAuthCookie(mockApi); - - t.test("Should return this users tokens alone.", async (t) => { - const res = await mockApi.get("/api/v1/users/1/api-tokens").set("Cookie", cookie); - - t.equal(res.statusCode, 200); - - // sort these alphabetically so that strictsame can work properly - t.strictSame( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - res.body.body.sort((a: any, b: any) => a.identifier - b.identifier), - [ - { - userID: 1, - identifier: "foo", - permissions: {}, - token: "tfoo", - fromAPIClient: null, - }, - { - userID: 1, - identifier: "bar", - permissions: {}, - token: "tbar", - fromAPIClient: null, - }, - ], - ); - - t.end(); - }); - - t.test("Should require authentication as that user.", async (t) => { - const res = await mockApi.get("/api/v1/users/1/api-tokens"); - - t.equal(res.statusCode, 401); - - await db.users.insert({ - username: "test", - usernameLowercase: "test", - id: 2, - } as UserDocument); - - const res2 = await mockApi.get("/api/v1/users/2/api-tokens").set("Cookie", cookie); - - t.equal(res2.statusCode, 403); - - t.end(); - }); - - t.end(); -}); - -t.test("DELETE /api/v1/users/:userID/api-tokens/:token", async (t) => { - t.beforeEach(ResetDBState); - const cookie = await CreateFakeAuthCookie(mockApi); - - t.test("Should delete the cookie at that token.", async (t) => { - const res = await mockApi - .delete("/api/v1/users/1/api-tokens/fake_api_token") - .set("Cookie", cookie); - - t.equal(res.statusCode, 200); - - const dbRes = await db["api-tokens"].findOne({ - token: "fake_api_token", - }); - - t.equal(dbRes, null, "Should delete the token in the database"); - - t.end(); - }); - - t.test("Returns 404 if token doesn't exist.", async (t) => { - const res = await mockApi - .delete("/api/v1/users/1/api-tokens/non_exist_token") - .set("Cookie", cookie); - - t.equal(res.statusCode, 404); - - t.end(); - }); - - t.test("Must return 404 if the token belongs to another user.", async (t) => { - await db["api-tokens"].insert({ - identifier: "foo", - permissions: {}, - token: "foo", - userID: 2, - fromAPIClient: null, - }); - - const res = await mockApi.delete("/api/v1/users/1/api-tokens/foo").set("Cookie", cookie); - - // so as not to reveal that this token exists. - t.equal(res.statusCode, 404); - - const dbRes = await db["api-tokens"].findOne({ token: "foo" }); - - t.not(dbRes, null, "Should not have deleted the token."); - - t.end(); - }); - - t.test("Must require authentication as that user.", async (t) => { - const res = await mockApi.delete("/api/v1/users/1/api-tokens/foo"); - - t.equal(res.statusCode, 401); - - t.end(); - }); - - t.end(); -}); - -t.test("POST /api/v1/users/:userID/api-tokens/create", async (t) => { - t.beforeEach(ResetDBState); - - const cookie = await CreateFakeAuthCookie(mockApi); - - t.test("Should create a new API Key for this user with provided permissions.", async (t) => { - const res = await mockApi - .post("/api/v1/users/1/api-tokens/create") - .set("Cookie", cookie) - .send({ - identifier: "Hello World", - permissions: ["submit_score", "customise_profile"], - }); - - t.equal(res.statusCode, 200, "Should return 200."); - - t.hasStrict( - res.body.body, - { - identifier: "Hello World", - permissions: { submit_score: true, customise_profile: true }, - userID: 1, - fromAPIClient: null, - }, - "Should return a conforming API Token.", - ); - - const dbRes = await db["api-tokens"].findOne({ - identifier: "Hello World", - }); - - t.hasStrict( - dbRes, - { - identifier: "Hello World", - permissions: { submit_score: true, customise_profile: true }, - userID: 1, - fromAPIClient: null, - }, - "Should insert a conforming API Token into the database.", - ); - - t.end(); - }); - - t.test( - "Should create a new API Key for this user according to an existing clientID.", - async (t) => { - const res = await mockApi - .post("/api/v1/users/1/api-tokens/create") - .set("Cookie", cookie) - .send({ - clientID: "OAUTH2_CLIENT_ID", - }); - - t.equal(res.statusCode, 200, "Should return 200."); - - t.hasStrict( - res.body.body, - { - identifier: "Test_Service", - permissions: { customise_profile: true }, - userID: 1, - fromAPIClient: "OAUTH2_CLIENT_ID", - }, - "Should return a conforming API Token, with the permissions from that client.", - ); - - const dbRes = await db["api-tokens"].findOne({ - identifier: "Test_Service", - }); - - t.hasStrict( - dbRes, - { - identifier: "Test_Service", - permissions: { customise_profile: true }, - userID: 1, - fromAPIClient: "OAUTH2_CLIENT_ID", - }, - "Should insert a conforming API Token into the database.", - ); - - t.end(); - }, - ); - - t.test("Should reject requests that use both provided permissions and clientID.", async (t) => { - const res = await mockApi - .post("/api/v1/users/1/api-tokens/create") - .set("Cookie", cookie) - .send({ - identifier: "Hello World", - permissions: ["submit_score", "customise_profile"], - clientID: "OAUTH2_CLIENT_ID", - }); - - t.equal(res.statusCode, 400, "Should return 400."); - t.match( - res.body.description, - /clientID creation and permissions creation at the same time/iu, - ); - - t.end(); - }); - - t.test("Should reject requests with no provided permissions or clientID.", async (t) => { - const res = await mockApi - .post("/api/v1/users/1/api-tokens/create") - .set("Cookie", cookie) - .send({ - identifier: "Hello World", - }); - - t.equal(res.statusCode, 400, "Should return 400."); - t.match(res.body.description, /must specify either clientID or permissions/iu); - - t.end(); - }); - - t.test("Should reject invalid permissions.", async (t) => { - const res = await mockApi - .post("/api/v1/users/1/api-tokens/create") - .set("Cookie", cookie) - .send({ - identifier: "Hello World", - permissions: ["submit_score", "invalid_permission"], - }); - - t.equal(res.statusCode, 400, "Should return 400."); - t.match(res.body.description, /invalid_permission/iu); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1/users/_userID/api-tokens/router.ts b/typescript/server/src/server/router/api/v1/users/_userID/api-tokens/router.ts index 6cf7e692b..54c5679b1 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/api-tokens/router.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/api-tokens/router.ts @@ -1,6 +1,6 @@ import { log } from "#lib/log/log.js"; import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { Random20Hex } from "#utils/misc"; import { GetTachiData } from "#utils/req-tachi-data"; import { FormatUserDoc } from "#utils/user"; @@ -23,7 +23,7 @@ router.use(RequireSelfRequestFromUser); router.get("/", async (req, res) => { const user = GetTachiData(req, "requestedUser"); - const keys = await db["api-tokens"].find({ + const keys = await MONGODB_KILL["api-tokens"].find({ userID: user.id, }); @@ -73,7 +73,7 @@ router.post( let fromAPIClient = null; if (body.clientID !== undefined) { - const client = await db["api-clients"].findOne( + const client = await MONGODB_KILL["api-clients"].findOne( { clientID: body.clientID, }, @@ -91,7 +91,7 @@ router.post( }); } - const exists = await db["api-tokens"].findOne({ + const exists = await MONGODB_KILL["api-tokens"].findOne({ userID: user.id, fromAPIClient: client.clientID, }); @@ -133,7 +133,7 @@ router.post( fromAPIClient, }; - await db["api-tokens"].insert(apiTokenDocument); + await MONGODB_KILL["api-tokens"].insert(apiTokenDocument); log.info(`Inserted new API Key for ${FormatUserDoc(user)}.`); @@ -155,7 +155,7 @@ router.delete("/:token", async (req, res) => { log.info(`received request from ${FormatUserDoc(user)} to delete token ${req.params.token}.`); - const token = await db["api-tokens"].findOne({ + const token = await MONGODB_KILL["api-tokens"].findOne({ token: req.params.token, userID: user.id, }); @@ -167,7 +167,7 @@ router.delete("/:token", async (req, res) => { }); } - await db["api-tokens"].remove({ token: req.params.token }); + await MONGODB_KILL["api-tokens"].remove({ token: req.params.token }); log.info(`Deleted ${req.params.token}, which belonged to ${FormatUserDoc(user)}.`); diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/banner/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/banner/router.oldtest.ts similarity index 90% rename from typescript/server/src/server/router/api/v1postgres/users/_userID/banner/router.test.ts rename to typescript/server/src/server/router/api/v1/users/_userID/banner/router.oldtest.ts index 60b46c217..2ccafec86 100644 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/banner/router.test.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/banner/router.oldtest.ts @@ -1,7 +1,7 @@ import { CDNStoreOrOverwrite } from "#lib/cdn/cdn"; import { GetProfileBannerURL } from "#lib/cdn/url-format"; import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; import { GetKTDataBuffer } from "#test-utils/test-data"; @@ -23,7 +23,7 @@ t.test("GET /api/v1/users/:userID/banner", (t) => { t.test("Should return a custom profile banner if one is set", async (t) => { await CDNStoreOrOverwrite(GetProfileBannerURL(1, "checksum"), "foo"); - await db.users.update({ id: 1 }, { $set: { customBannerLocation: "checksum" } }); + await MONGODB_KILL.users.update({ id: 1 }, { $set: { customBannerLocation: "checksum" } }); const res = await mockApi.get("/api/v1/users/1/banner").redirects(1); t.equal(res.statusCode, 200, "Should return 200."); @@ -66,7 +66,7 @@ t.test("PUT /api/v1/users/:userID/banner", (t) => { }); t.test("Should set a profile banner if user has custom banner", async (t) => { - await db.users.update({ id: 1 }, { $set: { customBannerLocation: "checksum" } }); + await MONGODB_KILL.users.update({ id: 1 }, { $set: { customBannerLocation: "checksum" } }); const img = GetKTDataBuffer("/images/acorn.png"); diff --git a/typescript/server/src/server/router/api/v1/users/_userID/banner/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/banner/router.test.ts deleted file mode 100644 index 60b46c217..000000000 --- a/typescript/server/src/server/router/api/v1/users/_userID/banner/router.test.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { CDNStoreOrOverwrite } from "#lib/cdn/cdn"; -import { GetProfileBannerURL } from "#lib/cdn/url-format"; -import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import { GetKTDataBuffer } from "#test-utils/test-data"; -import t from "tap"; - -t.test("GET /api/v1/users/:userID/banner", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should return the default profile banner if user has no custom banner", async (t) => { - await CDNStoreOrOverwrite("/users/default/banner", "test"); - const res = await mockApi.get("/api/v1/users/1/banner").redirects(1); - - t.equal(res.statusCode, 200, "Should return 200."); - - t.equal(res.body.toString(), "test"); - - t.end(); - }); - - t.test("Should return a custom profile banner if one is set", async (t) => { - await CDNStoreOrOverwrite(GetProfileBannerURL(1, "checksum"), "foo"); - await db.users.update({ id: 1 }, { $set: { customBannerLocation: "checksum" } }); - const res = await mockApi.get("/api/v1/users/1/banner").redirects(1); - - t.equal(res.statusCode, 200, "Should return 200."); - - if (res.statusCode !== 200) { - log.info( - { - body: res.body, - }, - "Unexpected non-200 in CDN tests, received this as a body.", - ); - } - - t.equal(res.body.toString(), "foo"); - - t.end(); - }); - - t.end(); -}); - -t.test("PUT /api/v1/users/:userID/banner", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should set a profile banner if user has no custom banner", async (t) => { - const img = GetKTDataBuffer("/images/acorn.png"); - - const res = await mockApi - .put("/api/v1/users/1/banner") - .set("Authorization", "Bearer fake_api_token") - .attach("banner", img, "file.jpg"); - - t.equal(res.statusCode, 200); - - const get = await mockApi.get(res.body.body.get).redirects(1); - - t.strictSame(img, get.body, "Profile banner should be stored."); - - t.end(); - }); - - t.test("Should set a profile banner if user has custom banner", async (t) => { - await db.users.update({ id: 1 }, { $set: { customBannerLocation: "checksum" } }); - - const img = GetKTDataBuffer("/images/acorn.png"); - - const res = await mockApi - .put("/api/v1/users/1/banner") - .set("Authorization", "Bearer fake_api_token") - .attach("banner", img, "file.jpg"); - - t.equal(res.statusCode, 200); - - const get = await mockApi.get(res.body.body.get).redirects(1); - - t.strictSame(img, get.body, "Profile banner should be stored."); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1/users/_userID/banner/router.ts b/typescript/server/src/server/router/api/v1/users/_userID/banner/router.ts index 634139227..a74d0595d 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/banner/router.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/banner/router.ts @@ -4,7 +4,7 @@ import { ONE_MEGABYTE } from "#lib/constants/filesize"; import { log } from "#lib/log/log.js"; import { RequirePermissions } from "#server/middleware/auth"; import { CreateMulterSingleUploadMiddleware } from "#server/middleware/multer-upload"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { HashSHA256 } from "#utils/crypto"; import { GetTachiData } from "#utils/req-tachi-data"; import { FormatUserDoc } from "#utils/user"; @@ -70,7 +70,10 @@ router.put( req.session.tachi.user.customBannerLocation = contentHash; } - await db.users.update({ id: user.id }, { $set: { customBannerLocation: contentHash } }); + await MONGODB_KILL.users.update( + { id: user.id }, + { $set: { customBannerLocation: contentHash } }, + ); return res.status(200).json({ success: true, @@ -121,7 +124,7 @@ router.delete( } await CDNDelete(GetProfileBannerURL(user.id, user.customBannerLocation)); - await db.users.update({ id: user.id }, { $set: { customBannerLocation: null } }); + await MONGODB_KILL.users.update({ id: user.id }, { $set: { customBannerLocation: null } }); return res.status(200).json({ success: true, diff --git a/typescript/server/src/server/router/api/v1/users/_userID/following/router.ts b/typescript/server/src/server/router/api/v1/users/_userID/following/router.ts index 4c6dc8651..1edaf6aa4 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/following/router.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/following/router.ts @@ -3,7 +3,7 @@ import type { integer } from "tachi-common"; import { log } from "#lib/log/log.js"; import { ServerConfig } from "#lib/setup/config"; import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetUser } from "#utils/req-tachi-data"; import { FormatUserDoc, GetUsersWithIDs, GetUserWithID } from "#utils/user"; import { Router } from "express"; @@ -23,7 +23,7 @@ const router: Router = Router({ mergeParams: true }); router.get("/", async (req, res) => { const user = GetUser(req); - const settings = await db["user-settings"].findOne({ userID: user.id }); + const settings = await MONGODB_KILL["user-settings"].findOne({ userID: user.id }); if (!settings) { log.error({ user }, `User ${FormatUserDoc(user)} has no settings?`); @@ -68,7 +68,7 @@ router.post( }); } - const settings = await db["user-settings"].findOne({ userID: user.id }); + const settings = await MONGODB_KILL["user-settings"].findOne({ userID: user.id }); if (!settings) { log.error({ user }, `User ${FormatUserDoc(user)} has no settings?`); @@ -107,7 +107,7 @@ router.post( // in a race condition. const following = [...settings.following, toFollow]; - await db["user-settings"].update( + await MONGODB_KILL["user-settings"].update( { userID: user.id, }, @@ -142,7 +142,7 @@ router.post( const { userID: toFollow } = req.safeBody as { userID: integer }; - const settings = await db["user-settings"].findOne({ userID: user.id }); + const settings = await MONGODB_KILL["user-settings"].findOne({ userID: user.id }); if (!settings) { log.error({ user }, `User ${FormatUserDoc(user)} has no settings?`); @@ -174,7 +174,7 @@ router.post( // in a race condition. const following = settings.following.filter((e) => e !== toFollow); - await db["user-settings"].update( + await MONGODB_KILL["user-settings"].update( { userID: user.id, }, diff --git a/typescript/server/src/server/router/api/v1/users/_userID/games/@gameSpecificRoutes/bms/router.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/@gameSpecificRoutes/bms/router.ts index 334400566..426cb1522 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/games/@gameSpecificRoutes/bms/router.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/games/@gameSpecificRoutes/bms/router.ts @@ -5,7 +5,7 @@ import { HandleBMSTableHTMLRequest, } from "#lib/game-specific/custom-bms-tables"; import { ValidatePlaytypeFromParamFor } from "#server/router/api/v1/games/_game/_playtype/middleware.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { AssignToReqTachiData, GetTachiData, GetUGPT, GetUser } from "#utils/req-tachi-data"; import { type RequestHandler, Router } from "express"; @@ -142,7 +142,7 @@ router.get( }); } - const chart = await db.charts.bms.findOne({ + const chart = await MONGODB_KILL.charts.bms.findOne({ ...query, }); @@ -152,7 +152,7 @@ router.get( .json({ success: false, description: "No chart found with the given checksum." }); } - const pb = await db["personal-bests"].findOne({ + const pb = await MONGODB_KILL["personal-bests"].findOne({ game: "bms", playtype: chart.playtype, userID: user.id, diff --git a/typescript/server/src/server/router/api/v1/users/_userID/games/@gameSpecificRoutes/iidx/router.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/@gameSpecificRoutes/iidx/router.ts index 7574c74af..98e22ed8c 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/games/@gameSpecificRoutes/iidx/router.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/games/@gameSpecificRoutes/iidx/router.ts @@ -15,7 +15,7 @@ import { ResolveSongAndChart } from "#lib/score-import/import-types/common/batch import { EAM_VERSION_NAMES } from "#lib/score-import/import-types/common/eamusement-iidx-csv/parser"; import { AggressiveRateLimitMiddleware } from "#server/middleware/rate-limiter"; import { ValidatePlaytypeFromParamFor } from "#server/router/api/v1/games/_game/_playtype/middleware.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetUser } from "#utils/req-tachi-data"; import { Router } from "express"; @@ -59,7 +59,7 @@ router.get( _id: integer; pbs: Array>; song: SongDocument<"iidx">; - }> = await db["personal-bests"].aggregate([ + }> = await MONGODB_KILL["personal-bests"].aggregate([ { $match: { userID: user.id, @@ -91,7 +91,7 @@ router.get( const rows = [EAMUSEMENT_CSV_HEADER]; // get all relevant charts - const charts = await db.charts.iidx.find({ + const charts = await MONGODB_KILL.charts.iidx.find({ songID: { $in: pbData.map((e) => e.song.id) }, difficulty: { $in: ["NORMAL", "HYPER", "ANOTHER", "LEGGENDARIA"] }, }); diff --git a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/folders/_folderID/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/folders/_folderID/router.oldtest.ts similarity index 90% rename from typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/folders/_folderID/router.test.ts rename to typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/folders/_folderID/router.oldtest.ts index 79bf24df6..1d70c4e2f 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/folders/_folderID/router.test.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/folders/_folderID/router.oldtest.ts @@ -1,6 +1,6 @@ import type { ChartDocument, FolderDocument, ScoreDocument, SongDocument } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; import { @@ -21,9 +21,9 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/folders/:folderID", (t) folderID: "testing_folder", }) as FolderDocument; - await db.folders.insert(folder); + await MONGODB_KILL.folders.insert(folder); await CreateFolderChartLookup(folder, true); - await db["personal-bests"].insert(deepmerge(TestingIIDXSPScorePB, {})); + await MONGODB_KILL["personal-bests"].insert(deepmerge(TestingIIDXSPScorePB, {})); }); t.test("Should return a user's PBs on a given folder.", async (t) => { @@ -57,20 +57,20 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/folders/:folderID/timeli t.beforeEach(ResetDBState); t.beforeEach(async () => { - await db.folders.insert(folder); + await MONGODB_KILL.folders.insert(folder); await CreateFolderChartLookup(folder, true); - await db["personal-bests"].insert(deepmerge(TestingIIDXSPScorePB, {})); + await MONGODB_KILL["personal-bests"].insert(deepmerge(TestingIIDXSPScorePB, {})); }); t.test("Should return the users scores on this folder in timeline order", async (t) => { // set up a more realistic scenario so we can fire more data at it. - await db.songs.iidx.remove({}); - await db.charts.iidx.remove({}); - await db["folder-chart-lookup"].remove({}); - await db.songs.iidx.insert( + await MONGODB_KILL.songs.iidx.remove({}); + await MONGODB_KILL.charts.iidx.remove({}); + await MONGODB_KILL["folder-chart-lookup"].remove({}); + await MONGODB_KILL.songs.iidx.insert( GetKTDataJSON("./tachi/tachi-songs-iidx.json") as Array>, ); - await db.charts.iidx.insert( + await MONGODB_KILL.charts.iidx.insert( GetKTDataJSON("./tachi/tachi-charts-iidx.json") as Array< ChartDocument<"iidx:DP" | "iidx:SP"> >, @@ -78,7 +78,7 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/folders/:folderID/timeli await CreateFolderChartLookup(folder, true); - await db.scores.insert([ + await MONGODB_KILL.scores.insert([ deepmerge(TestingIIDXSPScore, { scoreID: "OTHER_SCORE_ID", timeAchieved: 500, diff --git a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/folders/_folderID/router.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/folders/_folderID/router.ts index de24abad5..352476552 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/folders/_folderID/router.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/folders/_folderID/router.ts @@ -1,7 +1,7 @@ import type { FilterQuery } from "mongodb"; import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetEnumDistForFolder, GetFolderCharts, GetPBsOnFolder } from "#utils/folder"; import { GetTachiData, GetUGPT } from "#utils/req-tachi-data"; import { ParseStrPositiveInt } from "#utils/string-checks"; @@ -78,7 +78,7 @@ router.post("/viewed", RequireSelfRequestFromUser, async (req, res) => { const folder = GetTachiData(req, "folderDoc"); - await db["recent-folder-views"].update( + await MONGODB_KILL["recent-folder-views"].update( { userID: user.id, game: folder.game, @@ -166,7 +166,7 @@ router.get( // Returns a unique score per-chart that was the first score to achieve // this criteria on that chart. - const scoresAgg: Array<{ doc: ScoreDocument }> = await db.scores.aggregate([ + const scoresAgg: Array<{ doc: ScoreDocument }> = await MONGODB_KILL.scores.aggregate([ { $match: matchCriteria, }, diff --git a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/folders/router.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/folders/router.ts index 19ba009ac..bac74d41e 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/folders/router.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/folders/router.ts @@ -1,5 +1,5 @@ import { SearchCollection } from "#lib/search/search"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetEnumDistForFolders, GetRecentlyViewedFolders } from "#utils/folder"; import { IsString } from "#utils/misc"; import { GetUGPT } from "#utils/req-tachi-data"; @@ -37,7 +37,7 @@ router.get("/", async (req, res) => { const inactive = req.query.inactive === undefined ? false : undefined; const folders = await SearchCollection( - db.folders, + MONGODB_KILL.folders, req.query.search, "folders", { game, playtype, inactive }, diff --git a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/middleware.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/middleware.ts index 863ff2c69..0c8f8a8be 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/middleware.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/middleware.ts @@ -1,6 +1,6 @@ import type { RequestHandler } from "express"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { IsValidGame, IsValidPlaytype } from "#utils/misc"; import { AssignToReqTachiData, GetTachiData } from "#utils/req-tachi-data"; @@ -36,7 +36,7 @@ export const CheckUserPlayedGamePlaytype: RequestHandler = async (req, res, next }); } - const stats = await db["game-stats"].findOne({ + const stats = await MONGODB_KILL["game-stats"].findOne({ userID: user.id, game, playtype, diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/pbs/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/pbs/router.oldtest.ts similarity index 94% rename from typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/pbs/router.test.ts rename to typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/pbs/router.oldtest.ts index 5d740eb4f..973989628 100644 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/pbs/router.test.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/pbs/router.oldtest.ts @@ -1,6 +1,6 @@ import type { ChartDocument, PBScoreDocument } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; import { @@ -35,7 +35,7 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/pbs/best", (t) => { } as PBScoreDocument); } - await db["personal-bests"].insert(mockPBs); + await MONGODB_KILL["personal-bests"].insert(mockPBs); const res = await mockApi.get("/api/v1/users/test_zkldi/games/iidx/SP/pbs/best"); @@ -113,7 +113,7 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/pbs", (t) => { } as PBScoreDocument); } - await db["personal-bests"].insert(mockPBs); + await MONGODB_KILL["personal-bests"].insert(mockPBs); const res = await mockApi.get("/api/v1/users/test_zkldi/games/iidx/SP/pbs?search=5.1.1."); @@ -167,7 +167,7 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/pbs/:chartID", (t) => { t.beforeEach(ResetDBState); t.test("Should retrieve the PB at this chart ID.", async (t) => { - await db["personal-bests"].insert(TestingIIDXSPScorePB); + await MONGODB_KILL["personal-bests"].insert(TestingIIDXSPScorePB); const res = await mockApi.get(`/api/v1/users/1/games/iidx/SP/pbs/${Testing511SPA.chartID}`); @@ -178,7 +178,7 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/pbs/:chartID", (t) => { }); t.test("Should retrieve composed scores if param is set.", async (t) => { - await db["personal-bests"].insert(TestingIIDXSPScorePB); + await MONGODB_KILL["personal-bests"].insert(TestingIIDXSPScorePB); const res = await mockApi.get( `/api/v1/users/1/games/iidx/SP/pbs/${Testing511SPA.chartID}?getComposition=true`, @@ -202,7 +202,7 @@ t.test("POST /api/v1/users/:userID/games/:game/:playtype/pbs/resolve", (t) => { t.test( "Should resolve a chart and return user's PB using tachiSongID matchType.", async (t) => { - await db["personal-bests"].insert(TestingIIDXSPScorePB); + await MONGODB_KILL["personal-bests"].insert(TestingIIDXSPScorePB); const res = await mockApi.post("/api/v1/users/1/games/iidx/SP/pbs/resolve").send({ matchType: "tachiSongID", @@ -222,7 +222,7 @@ t.test("POST /api/v1/users/:userID/games/:game/:playtype/pbs/resolve", (t) => { ); t.test("Should resolve a chart and return user's PB using songTitle matchType.", async (t) => { - await db["personal-bests"].insert(TestingIIDXSPScorePB); + await MONGODB_KILL["personal-bests"].insert(TestingIIDXSPScorePB); const res = await mockApi.post("/api/v1/users/1/games/iidx/SP/pbs/resolve").send({ matchType: "songTitle", diff --git a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/pbs/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/pbs/router.test.ts deleted file mode 100644 index 5d740eb4f..000000000 --- a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/pbs/router.test.ts +++ /dev/null @@ -1,297 +0,0 @@ -import type { ChartDocument, PBScoreDocument } from "tachi-common"; - -import db from "#services/mongo/db"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import { - GetKTDataJSON, - LoadTachiIIDXData, - Testing511Song, - Testing511SPA, - TestingIIDXSPScorePB, -} from "#test-utils/test-data"; -import t from "tap"; - -t.test("GET /api/v1/users/:userID/games/:game/:playtype/pbs/best", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should return a user's best 100 personal bests.", async (t) => { - const mockPBs: Array = []; - - for (let i = 0; i < 200; i++) { - mockPBs.push({ - userID: 1, - game: "iidx", - playtype: "SP", - isPrimary: true, - - // hack to generate some random chartIDs - chartID: i.toString(), - - songID: Testing511Song.id, - calculatedData: { - ktLampRating: i, - }, - } as PBScoreDocument); - } - - await db["personal-bests"].insert(mockPBs); - - const res = await mockApi.get("/api/v1/users/test_zkldi/games/iidx/SP/pbs/best"); - - t.hasStrict(res.body, { - success: true, - description: "Retrieved 100 personal bests.", - body: { - pbs: mockPBs.slice(100).reverse(), - songs: [Testing511Song], - charts: [], - }, - }); - - t.end(); - }); - - t.end(); -}); - -t.test("GET /api/v1/users/:userID/games/:game/:playtype/pbs", (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(LoadTachiIIDXData); - - t.test("Should return 400 if no search param is given", async (t) => { - const res = await mockApi.get("/api/v1/users/test_zkldi/games/iidx/SP/pbs"); - - t.equal(res.statusCode, 400); - t.equal(res.body.success, false); - - t.end(); - }); - - t.test("Should return 400 if invalid search param is given", async (t) => { - const res = await mockApi.get( - "/api/v1/users/test_zkldi/games/iidx/SP/pbs?search=foo&search=bar", - ); - - t.equal(res.statusCode, 400); - t.equal(res.body.success, false); - - const res2 = await mockApi.get( - "/api/v1/users/test_zkldi/games/iidx/SP/pbs?search[$where]=process.exit(1)", - ); - - t.equal(res2.statusCode, 400); - t.equal(res2.body.success, false); - - t.end(); - }); - - t.test("Should search a user's personal bests.", async (t) => { - const mockPBs: Array = []; - - const charts = GetKTDataJSON("./tachi/tachi-charts-iidx.json") as Array; - - for (let i = 0; i < 200; i++) { - const chart = charts[i]; - - if (!chart) { - return t.fail( - `Not enough charts in tachi-charts-iidx.json to mock pb data? Failed at index ${i}.`, - ); - } - - mockPBs.push({ - userID: 1, - game: "iidx", - playtype: "SP", - isPrimary: true, - chartID: chart.chartID, - songID: chart.songID, - calculatedData: { - ktLampRating: i, - }, - } as PBScoreDocument); - } - - await db["personal-bests"].insert(mockPBs); - - const res = await mockApi.get("/api/v1/users/test_zkldi/games/iidx/SP/pbs?search=5.1.1."); - - t.hasStrict(res.body, { - success: true, - description: "Retrieved 4 personal bests.", - body: { - pbs: [ - { - chartID: "71865a2b6d3581decf076ae83c6621302c4bb271", - }, - { - chartID: "952805894d7d78257e87019426fa1d87aec834a5", - }, - { - chartID: "c2311194e3897ddb5745b1760d2c0141f933e683", - }, - { - chartID: "c641238220d73faf82659513ba03bde71b0b45f0", - }, - ], - songs: [ - { - title: "5.1.1.", - }, - ], - charts: [ - { - chartID: "c2311194e3897ddb5745b1760d2c0141f933e683", - }, - { - chartID: "71865a2b6d3581decf076ae83c6621302c4bb271", - }, - { - chartID: "c641238220d73faf82659513ba03bde71b0b45f0", - }, - { - chartID: "952805894d7d78257e87019426fa1d87aec834a5", - }, - ], - }, - }); - - t.end(); - }); - - t.end(); -}); - -t.test("GET /api/v1/users/:userID/games/:game/:playtype/pbs/:chartID", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should retrieve the PB at this chart ID.", async (t) => { - await db["personal-bests"].insert(TestingIIDXSPScorePB); - - const res = await mockApi.get(`/api/v1/users/1/games/iidx/SP/pbs/${Testing511SPA.chartID}`); - - t.equal(res.body.body.pb.chartID, Testing511SPA.chartID); - t.equal(res.body.body.chart.chartID, Testing511SPA.chartID); - - t.end(); - }); - - t.test("Should retrieve composed scores if param is set.", async (t) => { - await db["personal-bests"].insert(TestingIIDXSPScorePB); - - const res = await mockApi.get( - `/api/v1/users/1/games/iidx/SP/pbs/${Testing511SPA.chartID}?getComposition=true`, - ); - - t.equal(res.body.body.pb.chartID, Testing511SPA.chartID); - t.equal(res.body.body.chart.chartID, Testing511SPA.chartID); - t.equal(res.body.body.scores.length, 1); - t.equal(res.body.body.scores[0].scoreID, "TESTING_SCORE_ID"); - - t.end(); - }); - - t.end(); -}); - -t.test("POST /api/v1/users/:userID/games/:game/:playtype/pbs/resolve", (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(LoadTachiIIDXData); - - t.test( - "Should resolve a chart and return user's PB using tachiSongID matchType.", - async (t) => { - await db["personal-bests"].insert(TestingIIDXSPScorePB); - - const res = await mockApi.post("/api/v1/users/1/games/iidx/SP/pbs/resolve").send({ - matchType: "tachiSongID", - identifier: "1", - difficulty: "ANOTHER", - }); - - t.equal(res.statusCode, 200); - t.equal(res.body.success, true); - t.equal(res.body.body.pb.chartID, Testing511SPA.chartID); - t.equal(res.body.body.chart.chartID, Testing511SPA.chartID); - t.equal(res.body.body.song.id, 1); - t.equal(res.body.body.song.title, "5.1.1."); - - t.end(); - }, - ); - - t.test("Should resolve a chart and return user's PB using songTitle matchType.", async (t) => { - await db["personal-bests"].insert(TestingIIDXSPScorePB); - - const res = await mockApi.post("/api/v1/users/1/games/iidx/SP/pbs/resolve").send({ - matchType: "songTitle", - identifier: "5.1.1.", - difficulty: "ANOTHER", - }); - - t.equal(res.statusCode, 200); - t.equal(res.body.success, true); - t.equal(res.body.body.pb.chartID, Testing511SPA.chartID); - t.equal(res.body.body.chart.chartID, Testing511SPA.chartID); - t.equal(res.body.body.song.id, 1); - - t.end(); - }); - - t.test("Should return 404 when chart cannot be resolved.", async (t) => { - const res = await mockApi.post("/api/v1/users/1/games/iidx/SP/pbs/resolve").send({ - matchType: "tachiSongID", - identifier: "99999", - difficulty: "ANOTHER", - }); - - t.equal(res.statusCode, 404); - t.equal(res.body.success, false); - t.match(res.body.description, /Could not resolve this chart/u); - - t.end(); - }); - - t.test("Should return 404 when user has no PB on resolved chart.", async (t) => { - // Don't insert any PBs - user hasn't played this chart - - const res = await mockApi.post("/api/v1/users/1/games/iidx/SP/pbs/resolve").send({ - matchType: "tachiSongID", - identifier: "1", - difficulty: "ANOTHER", - }); - - t.equal(res.statusCode, 404); - t.equal(res.body.success, false); - t.match(res.body.description, /has not played this chart/u); - - t.end(); - }); - - t.test("Should return 400 for invalid request body.", async (t) => { - const res = await mockApi.post("/api/v1/users/1/games/iidx/SP/pbs/resolve").send({ - matchType: "invalidMatchType", - identifier: "1", - }); - - t.equal(res.statusCode, 400); - t.equal(res.body.success, false); - - t.end(); - }); - - t.test("Should return 400 when required fields are missing.", async (t) => { - const res = await mockApi.post("/api/v1/users/1/games/iidx/SP/pbs/resolve").send({ - matchType: "tachiSongID", - // missing identifier - }); - - t.equal(res.statusCode, 400); - t.equal(res.body.success, false); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/pbs/router.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/pbs/router.ts index c55642dd3..14536b773 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/pbs/router.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/pbs/router.ts @@ -4,7 +4,7 @@ import { ResolveSongAndChart } from "#lib/score-import/import-types/common/batch import { SearchSpecificGameSongsAndCharts } from "#lib/search/search"; import prValidate from "#server/middleware/prudence-validate"; import { AggressiveRateLimitMiddleware } from "#server/middleware/rate-limiter"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetRelevantSongsAndCharts } from "#utils/db"; import { IsValidScoreAlg } from "#utils/misc"; import { GetAdjacentAbove, GetAdjacentBelow } from "#utils/queries/pbs"; @@ -40,7 +40,7 @@ router.get("/", async (req, res) => { playtype, ); - const pbs = await db["personal-bests"].find( + const pbs = await MONGODB_KILL["personal-bests"].find( { chartID: { $in: allCharts.map((e) => e.chartID) }, userID: user.id, @@ -77,7 +77,7 @@ router.get("/", async (req, res) => { router.get("/all", AggressiveRateLimitMiddleware, async (req, res) => { const { user, game, playtype } = GetUGPT(req); - const pbs = await db["personal-bests"].find({ + const pbs = await MONGODB_KILL["personal-bests"].find({ userID: user.id, game, playtype, @@ -117,7 +117,7 @@ router.get("/best", prValidate({ alg: "*string" }), async (req, res) => { const alg = (req.query.alg as string | undefined) ?? gptConfig.defaultScoreRatingAlg; - const pbs = await db["personal-bests"].find( + const pbs = await MONGODB_KILL["personal-bests"].find( { userID: user.id, game, @@ -156,7 +156,7 @@ router.get("/best", prValidate({ alg: "*string" }), async (req, res) => { router.get("/:chartID", async (req, res) => { const { user, game, playtype } = GetUGPT(req); - const chart = await db.anyCharts[game].findOne({ + const chart = await MONGODB_KILL.anyCharts[game].findOne({ chartID: req.params.chartID, playtype, }); @@ -168,7 +168,7 @@ router.get("/:chartID", async (req, res) => { }); } - const pb = await db["personal-bests"].findOne({ + const pb = await MONGODB_KILL["personal-bests"].findOne({ chartID: req.params.chartID, userID: user.id, }); @@ -183,7 +183,7 @@ router.get("/:chartID", async (req, res) => { if (req.query.getComposition !== undefined) { const scoreIDs = GetScoreIDsFromComposed(pb); - const scores = await db.scores.find({ + const scores = await MONGODB_KILL.scores.find({ scoreID: { $in: scoreIDs }, }); @@ -218,7 +218,7 @@ router.get("/:chartID/rivals", async (req, res) => { const rivals = await GetRivalUsers(user.id, game, playtype); - const pbs = await db["personal-bests"].find({ + const pbs = await MONGODB_KILL["personal-bests"].find({ userID: { $in: rivals.map((e) => e.id) }, chartID: req.params.chartID, }); @@ -248,7 +248,7 @@ router.get("/:chartID/rivals", async (req, res) => { router.get("/:chartID/leaderboard-adjacent", async (req, res) => { const { user, game, playtype } = GetUGPT(req); - const chart = await db.anyCharts[game].findOne({ + const chart = await MONGODB_KILL.anyCharts[game].findOne({ chartID: req.params.chartID, playtype, }); @@ -260,7 +260,7 @@ router.get("/:chartID/leaderboard-adjacent", async (req, res) => { }); } - const pb = await db["personal-bests"].findOne({ + const pb = await MONGODB_KILL["personal-bests"].findOne({ chartID: req.params.chartID, userID: user.id, }); diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/rivals/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/rivals/router.oldtest.ts similarity index 85% rename from typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/rivals/router.test.ts rename to typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/rivals/router.oldtest.ts index 55ab44c85..9422cb6a0 100644 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/rivals/router.test.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/rivals/router.oldtest.ts @@ -1,6 +1,6 @@ import type { UserDocument } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; import { mkFakeGameSettings, mkFakeUser } from "#test-utils/misc"; import mockApi from "#test-utils/mock-api"; @@ -9,13 +9,13 @@ import { FakeOtherUser } from "#test-utils/test-data"; import t from "tap"; const SetupRivals = async () => { - await db.users.insert([FakeOtherUser, mkFakeUser(3), mkFakeUser(4)]); - await db["game-settings"].insert([ + await MONGODB_KILL.users.insert([FakeOtherUser, mkFakeUser(3), mkFakeUser(4)]); + await MONGODB_KILL["game-settings"].insert([ mkFakeGameSettings(2, "iidx", "SP"), mkFakeGameSettings(3, "iidx", "SP"), ]); - await db["game-settings"].update( + await MONGODB_KILL["game-settings"].update( { userID: 1, }, @@ -46,7 +46,7 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/rivals", (t) => { }); t.test("Should panic if your rival doesn't exist.", async (t) => { - await db.users.remove({ id: 3 }); + await MONGODB_KILL.users.remove({ id: 3 }); const res = await mockApi.get("/api/v1/users/1/games/iidx/SP/rivals"); @@ -74,7 +74,7 @@ t.test("PUT /api/v1/users/:userID/games/:game/:playtype/rivals", async (t) => { t.equal(res.statusCode, 200); - const dbRes = await db["game-settings"].findOne({ + const dbRes = await MONGODB_KILL["game-settings"].findOne({ userID: 1, game: "iidx", playtype: "SP", @@ -99,7 +99,7 @@ t.test("PUT /api/v1/users/:userID/games/:game/:playtype/rivals", async (t) => { }); t.test("Should return 400 if the user tries to rival > 5 people.", async (t) => { - await db.users.insert([mkFakeUser(5), mkFakeUser(6)]); + await MONGODB_KILL.users.insert([mkFakeUser(5), mkFakeUser(6)]); const res = await mockApi .put("/api/v1/users/1/games/iidx/SP/rivals") @@ -129,7 +129,7 @@ t.test("PUT /api/v1/users/:userID/games/:game/:playtype/rivals", async (t) => { t.test( "Should return 400 if the user tries to rival someone who exists but hasn't played the GPT.", async (t) => { - await db.users.insert(mkFakeUser(5)); + await MONGODB_KILL.users.insert(mkFakeUser(5)); const res = await mockApi .put("/api/v1/users/1/games/iidx/SP/rivals") @@ -162,12 +162,12 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/rivals/challengers", (t) t.beforeEach(SetupRivals); t.test("Should return all of the users that rival this UGPT.", async (t) => { - await db.users.insert(mkFakeUser(5)); + await MONGODB_KILL.users.insert(mkFakeUser(5)); - await db["game-settings"].remove({ userID: 3 }); - await db["game-settings"].remove({ userID: 4 }); + await MONGODB_KILL["game-settings"].remove({ userID: 3 }); + await MONGODB_KILL["game-settings"].remove({ userID: 4 }); - await db["game-settings"].insert([ + await MONGODB_KILL["game-settings"].insert([ mkFakeGameSettings(2, "chunithm", "Single", { rivals: [1] }), mkFakeGameSettings(3, "iidx", "SP", { rivals: [1, 3] }), mkFakeGameSettings(4, "iidx", "SP", { rivals: [1, 3, 5] }), diff --git a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/rivals/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/rivals/router.test.ts deleted file mode 100644 index 55ab44c85..000000000 --- a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/rivals/router.test.ts +++ /dev/null @@ -1,190 +0,0 @@ -import type { UserDocument } from "tachi-common"; - -import db from "#services/mongo/db"; -import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; -import { mkFakeGameSettings, mkFakeUser } from "#test-utils/misc"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import { FakeOtherUser } from "#test-utils/test-data"; -import t from "tap"; - -const SetupRivals = async () => { - await db.users.insert([FakeOtherUser, mkFakeUser(3), mkFakeUser(4)]); - await db["game-settings"].insert([ - mkFakeGameSettings(2, "iidx", "SP"), - mkFakeGameSettings(3, "iidx", "SP"), - ]); - - await db["game-settings"].update( - { - userID: 1, - }, - { - $set: { - rivals: [2, 3], - }, - }, - ); -}; - -t.test("GET /api/v1/users/:userID/games/:game/:playtype/rivals", (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(SetupRivals); - - t.test("Should return the userdocs of your rivals.", async (t) => { - const res = await mockApi.get("/api/v1/users/1/games/iidx/SP/rivals"); - - t.equal(res.statusCode, 200); - - t.strictSame( - res.body.body.map((e: UserDocument) => e.id), - [2, 3], - "Should have the users rivals and no more.", - ); - - t.end(); - }); - - t.test("Should panic if your rival doesn't exist.", async (t) => { - await db.users.remove({ id: 3 }); - - const res = await mockApi.get("/api/v1/users/1/games/iidx/SP/rivals"); - - t.equal(res.statusCode, 500); - - t.end(); - }); - - t.end(); -}); - -t.test("PUT /api/v1/users/:userID/games/:game/:playtype/rivals", async (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(SetupRivals); - - const cookie = await CreateFakeAuthCookie(mockApi); - - t.test("Should replace the users rivals.", async (t) => { - const res = await mockApi - .put("/api/v1/users/1/games/iidx/SP/rivals") - .set("Cookie", cookie) - .send({ - rivalIDs: [2], - }); - - t.equal(res.statusCode, 200); - - const dbRes = await db["game-settings"].findOne({ - userID: 1, - game: "iidx", - playtype: "SP", - }); - - t.strictSame(dbRes?.rivals, [2]); - - t.end(); - }); - - t.test("Should return 400 if the user tries to rival themselves.", async (t) => { - const res = await mockApi - .put("/api/v1/users/1/games/iidx/SP/rivals") - .set("Cookie", cookie) - .send({ - rivalIDs: [1], - }); - - t.equal(res.statusCode, 400); - - t.end(); - }); - - t.test("Should return 400 if the user tries to rival > 5 people.", async (t) => { - await db.users.insert([mkFakeUser(5), mkFakeUser(6)]); - - const res = await mockApi - .put("/api/v1/users/1/games/iidx/SP/rivals") - .set("Cookie", cookie) - .send({ - rivalIDs: [2, 3, 4, 5, 6], - }); - - t.equal(res.statusCode, 400); - - t.end(); - }); - - t.test("Should return 400 if the user tries to rival someone who doesn't exist.", async (t) => { - const res = await mockApi - .put("/api/v1/users/1/games/iidx/SP/rivals") - .set("Cookie", cookie) - .send({ - rivalIDs: [5], - }); - - t.equal(res.statusCode, 400); - - t.end(); - }); - - t.test( - "Should return 400 if the user tries to rival someone who exists but hasn't played the GPT.", - async (t) => { - await db.users.insert(mkFakeUser(5)); - - const res = await mockApi - .put("/api/v1/users/1/games/iidx/SP/rivals") - .set("Cookie", cookie) - .send({ - rivalIDs: [5], - }); - - t.equal(res.statusCode, 400); - - t.end(); - }, - ); - - t.test("Should return 401 if user has no auth.", async (t) => { - const res = await mockApi.put("/api/v1/users/1/games/iidx/SP/rivals").send({ - rivalIDs: [2], - }); - - t.equal(res.statusCode, 401); - - t.end(); - }); - - t.end(); -}); - -t.test("GET /api/v1/users/:userID/games/:game/:playtype/rivals/challengers", (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(SetupRivals); - - t.test("Should return all of the users that rival this UGPT.", async (t) => { - await db.users.insert(mkFakeUser(5)); - - await db["game-settings"].remove({ userID: 3 }); - await db["game-settings"].remove({ userID: 4 }); - - await db["game-settings"].insert([ - mkFakeGameSettings(2, "chunithm", "Single", { rivals: [1] }), - mkFakeGameSettings(3, "iidx", "SP", { rivals: [1, 3] }), - mkFakeGameSettings(4, "iidx", "SP", { rivals: [1, 3, 5] }), - mkFakeGameSettings(5, "iidx", "SP", { rivals: [1, 4] }), - ]); - - const res = await mockApi.get("/api/v1/users/1/games/iidx/SP/rivals/challengers"); - - t.equal(res.statusCode, 200); - - t.hasStrict( - res.body.body.map((e: UserDocument) => e.id), - [3, 4, 5], - ); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/rivals/router.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/rivals/router.ts index c97f16c09..949b5b367 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/rivals/router.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/rivals/router.ts @@ -3,7 +3,7 @@ import { SetRivalsFailReasons } from "#lib/constants/err-codes"; import { GetChallengerUsers, GetRivalIDs, GetRivalUsers, SetRivals } from "#lib/rivals/rivals"; import { RequirePermissions } from "#server/middleware/auth"; import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetRelevantSongsAndCharts } from "#utils/db"; import { IsString } from "#utils/misc"; import { GetUGPT } from "#utils/req-tachi-data"; @@ -138,7 +138,7 @@ router.get("/pb-leaderboard", async (req, res) => { const rivalIDs = await GetRivalIDs(user.id, game, playtype); const userSet = [...rivalIDs, user.id]; - const pbs = await db["personal-bests"].find( + const pbs = await MONGODB_KILL["personal-bests"].find( { game, playtype, diff --git a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/router.oldtest.ts similarity index 89% rename from typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/router.test.ts rename to typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/router.oldtest.ts index c2ecf9068..fa07710d8 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/router.test.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/router.oldtest.ts @@ -1,7 +1,7 @@ import type { ScoreDocument } from "tachi-common"; import { ONE_DAY } from "#lib/constants/time"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; @@ -46,7 +46,7 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype", (t) => { }); t.test("Should return a users first score if one exists.", async (t) => { - await db.scores.insert([ + await MONGODB_KILL.scores.insert([ { userID: 1, timeAchieved: 100, @@ -115,7 +115,7 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/history", (t) => { t.beforeEach(ResetDBState); t.test("Should return a users history snapshots for that gpt.", async (t) => { - await db["game-stats-snapshots"].insert([ + await MONGODB_KILL["game-stats-snapshots"].insert([ { userID: 1, game: "iidx", @@ -185,7 +185,7 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/most-played", (t) => { t.beforeEach(ResetDBState); t.test("Should return the users most played scores.", async (t) => { - await db.scores.insert([ + await MONGODB_KILL.scores.insert([ // @ts-expect-error nonsense deepmerge(TestingIIDXSPScore, { scoreID: "something_else" }), @@ -193,7 +193,7 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/most-played", (t) => { deepmerge(TestingIIDXSPScore, { scoreID: "something_else2" }), ]); - await db["personal-bests"].insert(TestingIIDXSPScorePB); + await MONGODB_KILL["personal-bests"].insert(TestingIIDXSPScorePB); const res = await mockApi.get("/api/v1/users/1/games/iidx/SP/most-played"); @@ -227,7 +227,11 @@ t.test("DELETE /api/v1/users/:userID/games/:game/:playtype", async (t) => { t.equal(res.status, 200, "Should return 200."); - const dbRes = await db["game-stats"].findOne({ userID: 1, game: "iidx", playtype: "SP" }); + const dbRes = await MONGODB_KILL["game-stats"].findOne({ + userID: 1, + game: "iidx", + playtype: "SP", + }); t.equal(dbRes, null, "Should remove the UGPT profile from the database."); @@ -244,7 +248,11 @@ t.test("DELETE /api/v1/users/:userID/games/:game/:playtype", async (t) => { t.equal(res.status, 403, "Should return 403."); - const dbRes = await db["game-stats"].findOne({ userID: 1, game: "iidx", playtype: "SP" }); + const dbRes = await MONGODB_KILL["game-stats"].findOne({ + userID: 1, + game: "iidx", + playtype: "SP", + }); t.not(dbRes, null, "Should NOT remove the UGPT profile from the database."); @@ -259,7 +267,11 @@ t.test("DELETE /api/v1/users/:userID/games/:game/:playtype", async (t) => { t.equal(res.status, 400, "Should return 400."); - const dbRes = await db["game-stats"].findOne({ userID: 1, game: "iidx", playtype: "SP" }); + const dbRes = await MONGODB_KILL["game-stats"].findOne({ + userID: 1, + game: "iidx", + playtype: "SP", + }); t.not(dbRes, null, "Should NOT remove the UGPT profile from the database."); @@ -273,7 +285,11 @@ t.test("DELETE /api/v1/users/:userID/games/:game/:playtype", async (t) => { t.equal(res.status, 401, "Should return 401."); - const dbRes = await db["game-stats"].findOne({ userID: 1, game: "iidx", playtype: "SP" }); + const dbRes = await MONGODB_KILL["game-stats"].findOne({ + userID: 1, + game: "iidx", + playtype: "SP", + }); t.not(dbRes, null, "Should NOT remove the UGPT profile from the database."); diff --git a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/router.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/router.ts index 89f23f3f2..9dea561ad 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/router.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/router.ts @@ -3,7 +3,7 @@ import { ONE_MONTH, ONE_WEEK, ONE_YEAR } from "#lib/constants/time"; import { log } from "#lib/log/log.js"; import prValidate from "#server/middleware/prudence-validate"; import { PasswordCompare, ValidatePassword } from "#server/router/api/v1/auth/auth.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { IsString } from "#utils/misc"; import { GetTachiData, GetUGPT } from "#utils/req-tachi-data"; import DestroyUserGamePlaytypeData from "#utils/reset-state/destroy-ugpt"; @@ -54,12 +54,12 @@ router.get("/", async (req, res) => { const stats = GetTachiData(req, "requestedUserGameStats"); const [totalScores, firstScore, mostRecentScore, rankingData, playtime] = await Promise.all([ - db.scores.count({ + MONGODB_KILL.scores.count({ userID: user.id, game, playtype, }), - db.scores.findOne( + MONGODB_KILL.scores.findOne( { userID: user.id, game, @@ -72,7 +72,7 @@ router.get("/", async (req, res) => { }, }, ), - db.scores.findOne( + MONGODB_KILL.scores.findOne( { userID: user.id, game, @@ -86,7 +86,7 @@ router.get("/", async (req, res) => { }, ), GetAllRankings(stats), - db.sessions.aggregate<[] | [{ playtime: number }]>([ + MONGODB_KILL.sessions.aggregate<[] | [{ playtime: number }]>([ { $match: { userID: user.id, @@ -163,7 +163,7 @@ router.get( const stats = GetTachiData(req, "requestedUserGameStats"); - const snapshots = (await db["game-stats-snapshots"].find( + const snapshots = (await MONGODB_KILL["game-stats-snapshots"].find( { userID: user.id, game, @@ -212,7 +212,7 @@ router.get("/most-played", async (req, res) => { const { game, playtype, user } = GetUGPT(req); const mostPlayed: Array<{ _id: string; playcount: integer; songID: integer }> = - await db.scores.aggregate([ + await MONGODB_KILL.scores.aggregate([ { $match: { userID: user.id, @@ -243,9 +243,9 @@ router.get("/most-played", async (req, res) => { const songIDs = mostPlayed.map((e) => e.songID); const [songs, charts, pbs] = await Promise.all([ - await db.anySongs[game].find({ id: { $in: songIDs } }), - await db.anyCharts[game].find({ chartID: { $in: chartIDs } }), - await db["personal-bests"].find({ chartID: { $in: chartIDs }, userID: user.id }), + await MONGODB_KILL.anySongs[game].find({ id: { $in: songIDs } }), + await MONGODB_KILL.anyCharts[game].find({ chartID: { $in: chartIDs } }), + await MONGODB_KILL["personal-bests"].find({ chartID: { $in: chartIDs }, userID: user.id }), ]); const playcountMap = new Map(); @@ -304,7 +304,11 @@ router.get("/leaderboard-adjacent", async (req, res) => { alg = temp; } - const thisUsersStats = await db["game-stats"].findOne({ game, playtype, userID: user.id }); + const thisUsersStats = await MONGODB_KILL["game-stats"].findOne({ + game, + playtype, + userID: user.id, + }); if (!thisUsersStats) { return res.status(400).json({ @@ -314,7 +318,7 @@ router.get("/leaderboard-adjacent", async (req, res) => { } const [above, below] = await Promise.all([ - db["game-stats"].find( + MONGODB_KILL["game-stats"].find( { game, playtype, @@ -328,7 +332,7 @@ router.get("/leaderboard-adjacent", async (req, res) => { }, }, ), - db["game-stats"].find( + MONGODB_KILL["game-stats"].find( { game, playtype, diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/scores/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/scores/router.oldtest.ts similarity index 94% rename from typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/scores/router.test.ts rename to typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/scores/router.oldtest.ts index 5c1e8dcaf..8b9cf8e01 100644 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/scores/router.test.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/scores/router.oldtest.ts @@ -1,6 +1,6 @@ import type { ChartDocument, ScoreDocument } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { mkFakeScoreIIDXSP, mkFakeScoreSDVX } from "#test-utils/misc"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; @@ -30,7 +30,7 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/scores/recent", (t) => { } as ScoreDocument); } - await db.scores.insert(mockScores); + await MONGODB_KILL.scores.insert(mockScores); const res = await mockApi.get("/api/v1/users/1/games/iidx/SP/scores/recent"); @@ -112,7 +112,7 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/scores", (t) => { } as ScoreDocument); } - await db.scores.insert(mockScores); + await MONGODB_KILL.scores.insert(mockScores); const res = await mockApi.get( "/api/v1/users/test_zkldi/games/iidx/SP/scores?search=5.1.1.", @@ -163,8 +163,8 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/scores/all", (t) => { t.beforeEach(ResetDBState); t.test("Should return every score from a user.", async (t) => { - await db.scores.remove({}); - await db.scores.insert([ + await MONGODB_KILL.scores.remove({}); + await MONGODB_KILL.scores.insert([ mkFakeScoreIIDXSP({ scoreID: "score_1" }), mkFakeScoreIIDXSP({ scoreID: "score_2" }), mkFakeScoreIIDXSP({ scoreID: "score_3" }), @@ -192,8 +192,8 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/scores/:chartID", (t) => t.beforeEach(LoadTachiIIDXData); t.test("Should return a users score history on a given chart.", async (t) => { - await db.scores.remove({}); - await db.scores.insert([ + await MONGODB_KILL.scores.remove({}); + await MONGODB_KILL.scores.insert([ { chartID: Testing511SPA.chartID, userID: 1, diff --git a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/scores/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/scores/router.test.ts deleted file mode 100644 index 5c1e8dcaf..000000000 --- a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/scores/router.test.ts +++ /dev/null @@ -1,239 +0,0 @@ -import type { ChartDocument, ScoreDocument } from "tachi-common"; - -import db from "#services/mongo/db"; -import { mkFakeScoreIIDXSP, mkFakeScoreSDVX } from "#test-utils/misc"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import { - GetKTDataJSON, - LoadTachiIIDXData, - Testing511Song, - Testing511SPA, -} from "#test-utils/test-data"; -import t from "tap"; - -t.test("GET /api/v1/users/:userID/games/:game/:playtype/scores/recent", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should return a users 100 most recent scores.", async (t) => { - const mockScores: Array = []; - - for (let i = 0; i < 200; i++) { - mockScores.push({ - userID: 1, - game: "iidx", - playtype: "SP", - timeAchieved: i * 100, - scoreID: `scoreID_${i}`, - chartID: Testing511SPA.chartID, - songID: Testing511Song.id, - } as ScoreDocument); - } - - await db.scores.insert(mockScores); - - const res = await mockApi.get("/api/v1/users/1/games/iidx/SP/scores/recent"); - - t.hasStrict(res.body, { - success: true, - description: "Retrieved 100 scores.", - body: { - scores: mockScores.slice(100).reverse(), - songs: [Testing511Song], - charts: [Testing511SPA], - }, - }); - - t.end(); - }); - - t.end(); -}); - -t.test("GET /api/v1/users/:userID/games/:game/:playtype/scores", (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(LoadTachiIIDXData); - - t.test("Should return 400 if no search param is given", async (t) => { - const res = await mockApi.get("/api/v1/users/test_zkldi/games/iidx/SP/scores"); - - t.equal(res.statusCode, 400); - t.equal(res.body.success, false); - - t.end(); - }); - - t.test("Should return 400 if invalid search param is given", async (t) => { - const res = await mockApi.get( - "/api/v1/users/test_zkldi/games/iidx/SP/scores?search=foo&search=bar", - ); - - t.equal(res.statusCode, 400); - t.equal(res.body.success, false); - - // evil eval attempts - const res2 = await mockApi.get( - "/api/v1/users/test_zkldi/games/iidx/SP/scores?search[$where]=process.exit(1)", - ); - - t.equal(res2.statusCode, 400); - t.equal(res2.body.success, false); - - t.end(); - }); - - t.test("Should search a user's scores.", async (t) => { - const mockScores: Array = []; - - const charts = GetKTDataJSON("./tachi/tachi-charts-iidx.json") as Array< - ChartDocument<"iidx:DP" | "iidx:SP"> - >; - - for (let i = 0; i < 200; i++) { - const chart = charts[i]; - - if (!chart) { - return t.fail( - `tachi-charts-iidx.json doesn't have enough mock data for testing. Needed atleast 200 entries, but failed to retrieve one at index ${i}.`, - ); - } - - mockScores.push({ - scoreID: i.toString(), - userID: 1, - game: "iidx", - playtype: "SP", - isPrimary: true, - chartID: chart.chartID, - songID: chart.songID, - calculatedData: { - ktLampRating: i, - }, - } as ScoreDocument); - } - - await db.scores.insert(mockScores); - - const res = await mockApi.get( - "/api/v1/users/test_zkldi/games/iidx/SP/scores?search=5.1.1.", - ); - - t.equal(res.body.body.scores.length, 5); - t.equal(res.body.body.songs.length, 1); - t.equal(res.body.body.charts.length, 4); - - t.hasStrict(res.body, { - success: true, - description: "Retrieved 5 scores.", - body: { - scores: [ - { - songID: 1, - }, - { - songID: 1, - }, - { - songID: 1, - }, - ], - songs: [ - { - title: "5.1.1.", - }, - ], - charts: [ - { - songID: 1, - }, - { - songID: 1, - }, - ], - }, - }); - - t.end(); - }); - - t.end(); -}); - -t.test("GET /api/v1/users/:userID/games/:game/:playtype/scores/all", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should return every score from a user.", async (t) => { - await db.scores.remove({}); - await db.scores.insert([ - mkFakeScoreIIDXSP({ scoreID: "score_1" }), - mkFakeScoreIIDXSP({ scoreID: "score_2" }), - mkFakeScoreIIDXSP({ scoreID: "score_3" }), - mkFakeScoreIIDXSP({ scoreID: "other_score", userID: 2 }), - mkFakeScoreSDVX({ scoreID: "other_game_same_user" }), - ]); - - const res = await mockApi.get("/api/v1/users/1/games/iidx/SP/scores/all"); - - t.equal(res.statusCode, 200, "Should return 200"); - - t.strictSame( - res.body.body.scores.map((e: ScoreDocument) => e.scoreID), - ["score_1", "score_2", "score_3"], - ); - - t.end(); - }); - - t.end(); -}); - -t.test("GET /api/v1/users/:userID/games/:game/:playtype/scores/:chartID", (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(LoadTachiIIDXData); - - t.test("Should return a users score history on a given chart.", async (t) => { - await db.scores.remove({}); - await db.scores.insert([ - { - chartID: Testing511SPA.chartID, - userID: 1, - scoreID: "foo", - }, - { - chartID: Testing511SPA.chartID, - userID: 1, - scoreID: "bar", - }, - { - chartID: Testing511SPA.chartID, - userID: 2, - scoreID: "baz", - }, - ] as Array); - - const res = await mockApi.get( - `/api/v1/users/1/games/iidx/SP/scores/${Testing511SPA.chartID}`, - ); - - const scoreIDs = res.body.body.map((e: ScoreDocument) => e.scoreID); - - t.equal(scoreIDs.length, 2); - t.ok(scoreIDs.includes("foo")); - t.ok(scoreIDs.includes("bar")); - - t.end(); - }); - - t.test("Should reject a chart that does not exist.", async (t) => { - const res = await mockApi.get( - `/api/v1/users/1/games/iidx/SP/scores/CHART_THAT_DOESNT_EXIST`, - ); - - t.equal(res.statusCode, 404); - t.match(res.body.description, /chart does not exist/iu); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/scores/router.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/scores/router.ts index 7778462ca..33f8a9263 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/scores/router.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/scores/router.ts @@ -1,6 +1,6 @@ import { SearchSpecificGameSongsAndCharts } from "#lib/search/search"; import { HyperAggressiveRateLimitMiddleware } from "#server/middleware/rate-limiter"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetRelevantSongsAndCharts } from "#utils/db"; import { GetUGPT } from "#utils/req-tachi-data"; import { FilterChartsAndSongs } from "#utils/scores"; @@ -29,7 +29,7 @@ router.get("/", async (req, res) => { playtype, ); - const scores = await db.scores.find( + const scores = await MONGODB_KILL.scores.find( { chartID: { $in: allCharts.map((e) => e.chartID) }, userID: user.id, @@ -65,7 +65,7 @@ router.get("/", async (req, res) => { router.get("/all", HyperAggressiveRateLimitMiddleware, async (req, res) => { const { user, game, playtype } = GetUGPT(req); - const scores = await db.scores.find({ + const scores = await MONGODB_KILL.scores.find({ userID: user.id, game, playtype, @@ -89,7 +89,7 @@ router.get("/all", HyperAggressiveRateLimitMiddleware, async (req, res) => { router.get("/recent", async (req, res) => { const { user, game, playtype } = GetUGPT(req); - const recentScores = await db.scores.find( + const recentScores = await MONGODB_KILL.scores.find( { userID: user.id, game, @@ -124,7 +124,7 @@ router.get("/recent", async (req, res) => { router.get("/:chartID", async (req, res) => { const { user, game, playtype } = GetUGPT(req); - const chart = await db.anyCharts[game].findOne({ + const chart = await MONGODB_KILL.anyCharts[game].findOne({ chartID: req.params.chartID, playtype, }); @@ -136,7 +136,7 @@ router.get("/:chartID", async (req, res) => { }); } - const scores = await db.scores.find({ + const scores = await MONGODB_KILL.scores.find({ userID: user.id, chartID: chart.chartID, }); diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/sessions/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/sessions/router.oldtest.ts similarity index 92% rename from typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/sessions/router.test.ts rename to typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/sessions/router.oldtest.ts index ec0261464..2731115d4 100644 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/sessions/router.test.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/sessions/router.oldtest.ts @@ -1,6 +1,6 @@ import type { SessionDocument } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; import { LoadTachiIIDXData } from "#test-utils/test-data"; @@ -39,7 +39,7 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/sessions", (t) => { }); t.test("Should search a user's sessions.", async (t) => { - await db.sessions.insert( + await MONGODB_KILL.sessions.insert( ["Epic Session", "Session Of Epic", "Epic Gaming", "something else", "bad session"].map( (e) => ({ userID: 1, @@ -96,8 +96,8 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/sessions/best", (t) => { }); } - await db.sessions.remove({}); - await db.sessions.insert(sessions); + await MONGODB_KILL.sessions.remove({}); + await MONGODB_KILL.sessions.insert(sessions); const res = await mockApi.get("/api/v1/users/test_zkldi/games/iidx/SP/sessions/best"); @@ -142,8 +142,8 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/sessions/best", (t) => { }); } - await db.sessions.remove({}); - await db.sessions.insert(sessions); + await MONGODB_KILL.sessions.remove({}); + await MONGODB_KILL.sessions.insert(sessions); const res = await mockApi.get( "/api/v1/users/test_zkldi/games/iidx/SP/sessions/best?alg=BPI", @@ -173,7 +173,7 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/sessions/highlighted", ( t.beforeEach(ResetDBState); t.test("Should return only highlighted sessions.", async (t) => { - await db.sessions.insert({ + await MONGODB_KILL.sessions.insert({ highlight: true, userID: 1, game: "iidx", @@ -197,8 +197,8 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/sessions/recent", (t) => t.beforeEach(ResetDBState); t.test("Should return the users most recent sessions.", async (t) => { - await db.sessions.remove({}); - await db.sessions.insert([ + await MONGODB_KILL.sessions.remove({}); + await MONGODB_KILL.sessions.insert([ { highlight: false, userID: 1, diff --git a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/sessions/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/sessions/router.test.ts deleted file mode 100644 index ec0261464..000000000 --- a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/sessions/router.test.ts +++ /dev/null @@ -1,244 +0,0 @@ -import type { SessionDocument } from "tachi-common"; - -import db from "#services/mongo/db"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import { LoadTachiIIDXData } from "#test-utils/test-data"; -import t from "tap"; - -t.test("GET /api/v1/users/:userID/games/:game/:playtype/sessions", (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(LoadTachiIIDXData); - - t.test("Should return 400 if no search param is given", async (t) => { - const res = await mockApi.get("/api/v1/users/test_zkldi/games/iidx/SP/sessions"); - - t.equal(res.statusCode, 400); - t.equal(res.body.success, false); - - t.end(); - }); - - t.test("Should return 400 if invalid search param is given", async (t) => { - const res = await mockApi.get( - "/api/v1/users/test_zkldi/games/iidx/SP/sessions?search=foo&search=bar", - ); - - t.equal(res.statusCode, 400); - t.equal(res.body.success, false); - - // evil eval attempts - const res2 = await mockApi.get( - "/api/v1/users/test_zkldi/games/iidx/SP/sessions?search[$where]=process.exit(1)", - ); - - t.equal(res2.statusCode, 400); - t.equal(res2.body.success, false); - - t.end(); - }); - - t.test("Should search a user's sessions.", async (t) => { - await db.sessions.insert( - ["Epic Session", "Session Of Epic", "Epic Gaming", "something else", "bad session"].map( - (e) => ({ - userID: 1, - game: "iidx", - playtype: "SP", - name: e, - desc: "something", - - // hack to avoid db nonsense - sessionID: e, - }), - ) as Array, - ); - - const res = await mockApi.get( - "/api/v1/users/test_zkldi/games/iidx/SP/sessions?search=Epic", - ); - - t.hasStrict(res.body, { - success: true, - description: "Retrieved 3 sessions.", - body: [], - }); - - t.end(); - }); - - t.end(); -}); - -t.test("GET /api/v1/users/:userID/games/:game/:playtype/sessions/best", (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(LoadTachiIIDXData); - - t.test("Should return a user's best 100 sessions.", async (t) => { - const sessions: Array = []; - - for (let i = 0; i < 200; i++) { - sessions.push({ - sessionID: i.toString(), - game: "iidx", - playtype: "SP", - userID: 1, - scoreIDs: [], - calculatedData: { - ktLampRating: i, - }, - desc: null, - name: i.toString(), - highlight: false, - timeEnded: 0, - timeInserted: 0, - timeStarted: 0, - }); - } - - await db.sessions.remove({}); - await db.sessions.insert(sessions); - - const res = await mockApi.get("/api/v1/users/test_zkldi/games/iidx/SP/sessions/best"); - - t.hasStrict(res.body, { - success: true, - description: "Retrieved 100 sessions.", - }); - - t.strictSame( - res.body.body.map((e: SessionDocument) => e.sessionID), - sessions - .slice(100) - .reverse() - .map((e) => e.sessionID), - ); - - t.end(); - }); - - t.test( - "Should return a user's best 100 sessions according to the provided rating alg.", - async (t) => { - const sessions: Array = []; - - for (let i = 0; i < 200; i++) { - sessions.push({ - sessionID: i.toString(), - game: "iidx", - playtype: "SP", - userID: 1, - scoreIDs: [], - calculatedData: { - ktLampRating: 200 - i, - BPI: i, - }, - desc: null, - name: i.toString(), - highlight: false, - timeEnded: 0, - timeInserted: 0, - timeStarted: 0, - }); - } - - await db.sessions.remove({}); - await db.sessions.insert(sessions); - - const res = await mockApi.get( - "/api/v1/users/test_zkldi/games/iidx/SP/sessions/best?alg=BPI", - ); - - t.hasStrict(res.body, { - success: true, - description: "Retrieved 100 sessions.", - }); - - t.strictSame( - res.body.body.map((e: SessionDocument) => e.sessionID), - sessions - .slice(100) - .reverse() - .map((e) => e.sessionID), - ); - - t.end(); - }, - ); - - t.end(); -}); - -t.test("GET /api/v1/users/:userID/games/:game/:playtype/sessions/highlighted", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should return only highlighted sessions.", async (t) => { - await db.sessions.insert({ - highlight: true, - userID: 1, - game: "iidx", - playtype: "SP", - sessionID: "highlighted_id", - scoreIDs: [""], - } as SessionDocument); - - const res = await mockApi.get("/api/v1/users/1/games/iidx/SP/sessions/highlighted"); - - t.equal(res.body.body.length, 1); - t.equal(res.body.body[0].sessionID, "highlighted_id"); - - t.end(); - }); - - t.end(); -}); - -t.test("GET /api/v1/users/:userID/games/:game/:playtype/sessions/recent", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should return the users most recent sessions.", async (t) => { - await db.sessions.remove({}); - await db.sessions.insert([ - { - highlight: false, - userID: 1, - game: "iidx", - playtype: "SP", - sessionID: "recent_id1", - timeEnded: 1, - scoreIDs: [""], - }, - { - highlight: false, - userID: 1, - game: "iidx", - playtype: "SP", - sessionID: "recent_id2", - timeEnded: 3, - scoreIDs: [""], - }, - { - highlight: false, - userID: 1, - game: "iidx", - playtype: "SP", - sessionID: "recent_id3", - timeEnded: 2, - scoreIDs: [""], - }, - ] as Array); - - const res = await mockApi.get("/api/v1/users/1/games/iidx/SP/sessions/recent"); - - t.equal(res.body.body.length, 3); - t.strictSame( - // @ts-expect-error temporary type hack - res.body.body.map((e) => e.sessionID), - ["recent_id2", "recent_id3", "recent_id1"], - ); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/sessions/router.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/sessions/router.ts index 0e5ad7903..963db80ff 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/sessions/router.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/sessions/router.ts @@ -1,6 +1,6 @@ import { GetSessionScoreInfo } from "#lib/score-import/framework/sessions/sessions"; import { SearchSessions } from "#lib/search/search"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetUGPT } from "#utils/req-tachi-data"; import { CheckStrSessionAlg } from "#utils/string-checks"; import { Router } from "express"; @@ -68,7 +68,7 @@ router.get("/best", async (req, res) => { alg = userAlg; } - const sessions = await db.sessions.find( + const sessions = await MONGODB_KILL.sessions.find( { userID: user.id, game, @@ -115,7 +115,7 @@ router.get("/best", async (req, res) => { router.get("/highlighted", async (req, res) => { const { user, game, playtype } = GetUGPT(req); - const sessions = await db.sessions.find( + const sessions = await MONGODB_KILL.sessions.find( { userID: user.id, game, playtype, highlight: true }, { sort: { timeEnded: -1 }, limit: 100 }, ); @@ -149,7 +149,7 @@ router.get("/highlighted", async (req, res) => { router.get("/recent", async (req, res) => { const { user, game, playtype } = GetUGPT(req); - const sessions = await db.sessions.find( + const sessions = await MONGODB_KILL.sessions.find( { userID: user.id, game, playtype }, { sort: { timeEnded: -1 }, limit: 100 }, ); @@ -185,7 +185,7 @@ router.get("/recent", async (req, res) => { router.get("/last", async (req, res) => { const { user, game, playtype } = GetUGPT(req); - const session = await db.sessions.findOne( + const session = await MONGODB_KILL.sessions.findOne( { userID: user.id, game, playtype }, { sort: { timeEnded: -1 } }, ); @@ -218,7 +218,7 @@ router.get("/last", async (req, res) => { router.get("/calendar", async (req, res) => { const { user, game, playtype } = GetUGPT(req); - const sessions = await db.sessions.find( + const sessions = await MONGODB_KILL.sessions.find( { userID: user.id, game, diff --git a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/settings/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/settings/router.oldtest.ts similarity index 90% rename from typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/settings/router.test.ts rename to typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/settings/router.oldtest.ts index 257bcbc07..8dee47e68 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/settings/router.test.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/settings/router.oldtest.ts @@ -1,7 +1,7 @@ import type { UGPTSettingsDocument } from "tachi-common"; /* eslint-disable no-await-in-loop */ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { mkFakeUser } from "#test-utils/misc"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; @@ -43,7 +43,7 @@ t.test("PATCH /api/v1/users/:userID/games/:game/:playtype/settings", (t) => { t.beforeEach(ResetDBState); t.test("Should update a user's settings.", async (t) => { - await db["api-tokens"].insert({ + await MONGODB_KILL["api-tokens"].insert({ userID: 1, identifier: "api_token", permissions: { @@ -84,7 +84,7 @@ t.test("PATCH /api/v1/users/:userID/games/:game/:playtype/settings", (t) => { "Should only update the mutated properties.", ); - const data = await db["game-settings"].findOne({ + const data = await MONGODB_KILL["game-settings"].findOne({ userID: 1, game: "iidx", playtype: "SP", @@ -96,7 +96,7 @@ t.test("PATCH /api/v1/users/:userID/games/:game/:playtype/settings", (t) => { }); t.test("Should update a user's game specific settings.", async (t) => { - await db["api-tokens"].insert({ + await MONGODB_KILL["api-tokens"].insert({ userID: 1, identifier: "api_token", permissions: { @@ -141,7 +141,7 @@ t.test("PATCH /api/v1/users/:userID/games/:game/:playtype/settings", (t) => { "Should only update the mutated properties.", ); - const data = await db["game-settings"].findOne({ + const data = await MONGODB_KILL["game-settings"].findOne({ userID: 1, game: "iidx", playtype: "SP", @@ -154,7 +154,7 @@ t.test("PATCH /api/v1/users/:userID/games/:game/:playtype/settings", (t) => { t.test("Should update an IIDX player's bpiTarget settings.", (t) => { t.beforeEach(async () => { - await db["api-tokens"].insert({ + await MONGODB_KILL["api-tokens"].insert({ userID: 1, identifier: "api_token", permissions: { @@ -202,7 +202,7 @@ t.test("PATCH /api/v1/users/:userID/games/:game/:playtype/settings", (t) => { "Should only update the mutated properties.", ); - const data = (await db["game-settings"].findOne({ + const data = (await MONGODB_KILL["game-settings"].findOne({ userID: 1, game: "iidx", playtype: "SP", @@ -248,7 +248,7 @@ t.test("PATCH /api/v1/users/:userID/games/:game/:playtype/settings", (t) => { }); t.test("Should reject arbitrary things on game specific settings.", async (t) => { - await db["api-tokens"].insert({ + await MONGODB_KILL["api-tokens"].insert({ userID: 1, identifier: "api_token", permissions: { @@ -275,8 +275,8 @@ t.test("PATCH /api/v1/users/:userID/games/:game/:playtype/settings", (t) => { }); t.test("Requires the user to be authed as the requested user.", async (t) => { - await db.users.insert(mkFakeUser(2)); - await db["api-tokens"].insert({ + await MONGODB_KILL.users.insert(mkFakeUser(2)); + await MONGODB_KILL["api-tokens"].insert({ userID: 2, identifier: "api_token", permissions: { @@ -296,7 +296,7 @@ t.test("PATCH /api/v1/users/:userID/games/:game/:playtype/settings", (t) => { }); t.test("Requires the permission customise_profile", async (t) => { - await db["api-tokens"].insert({ + await MONGODB_KILL["api-tokens"].insert({ userID: 1, identifier: "api_token", permissions: { @@ -316,7 +316,7 @@ t.test("PATCH /api/v1/users/:userID/games/:game/:playtype/settings", (t) => { }); t.test("Should reject invalid values.", async (t) => { - await db["api-tokens"].insert({ + await MONGODB_KILL["api-tokens"].insert({ userID: 1, identifier: "foo", permissions: { @@ -340,7 +340,7 @@ t.test("PATCH /api/v1/users/:userID/games/:game/:playtype/settings", (t) => { t.equal(res.statusCode, 400); - const data = await db["game-settings"].findOne({ + const data = await MONGODB_KILL["game-settings"].findOne({ userID: 1, game: "iidx", playtype: "SP", @@ -353,7 +353,7 @@ t.test("PATCH /api/v1/users/:userID/games/:game/:playtype/settings", (t) => { }); t.test("Should update a user's default table.", async (t) => { - await db["api-tokens"].insert({ + await MONGODB_KILL["api-tokens"].insert({ userID: 1, identifier: "api_token", permissions: { @@ -394,7 +394,7 @@ t.test("PATCH /api/v1/users/:userID/games/:game/:playtype/settings", (t) => { "Should only update the mutated properties.", ); - const data = await db["game-settings"].findOne({ + const data = await MONGODB_KILL["game-settings"].findOne({ userID: 1, game: "iidx", playtype: "SP", @@ -406,7 +406,7 @@ t.test("PATCH /api/v1/users/:userID/games/:game/:playtype/settings", (t) => { }); t.test("Should not allow updating to a table that doesn't exist.", async (t) => { - await db["api-tokens"].insert({ + await MONGODB_KILL["api-tokens"].insert({ userID: 1, identifier: "api_token", permissions: { @@ -425,7 +425,7 @@ t.test("PATCH /api/v1/users/:userID/games/:game/:playtype/settings", (t) => { t.equal(res.statusCode, 400); - const data = await db["game-settings"].findOne({ + const data = await MONGODB_KILL["game-settings"].findOne({ userID: 1, game: "iidx", playtype: "SP", diff --git a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/settings/router.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/settings/router.ts index 12470c396..c8aae0c05 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/settings/router.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/settings/router.ts @@ -1,6 +1,6 @@ import { log } from "#lib/log/log.js"; import { RequirePermissions } from "#server/middleware/auth"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { FormatPrError, optNull } from "#utils/prudence"; import { GetUGPT } from "#utils/req-tachi-data"; import { FormatUserDoc } from "#utils/user"; @@ -62,7 +62,7 @@ router.patch( const body = req.safeBody as Partial; if (typeof body.defaultTable === "string") { - const table = await db.tables.findOne({ + const table = await MONGODB_KILL.tables.findOne({ game, playtype, tableID: body.defaultTable, @@ -108,7 +108,7 @@ router.patch( } if (Object.keys(updateQuery).length === 0) { - const settings = await db["game-settings"].findOne({ + const settings = await MONGODB_KILL["game-settings"].findOne({ userID: user.id, game, playtype, @@ -121,7 +121,7 @@ router.patch( }); } - await db["game-settings"].update( + await MONGODB_KILL["game-settings"].update( { userID: user.id, game, @@ -132,7 +132,7 @@ router.patch( }, ); - const settings = await db["game-settings"].findOne({ + const settings = await MONGODB_KILL["game-settings"].findOne({ userID: user.id, game, playtype, @@ -167,7 +167,7 @@ router.patch( router.get("/", async (req, res) => { const { user, game, playtype } = GetUGPT(req); - const settings = await db["game-settings"].findOne({ + const settings = await MONGODB_KILL["game-settings"].findOne({ userID: user.id, game, playtype, diff --git a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/showcase/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/showcase/router.oldtest.ts similarity index 92% rename from typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/showcase/router.test.ts rename to typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/showcase/router.oldtest.ts index 5e9968063..634c2064b 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/showcase/router.test.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/showcase/router.oldtest.ts @@ -1,4 +1,4 @@ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { mkFakeUser } from "#test-utils/misc"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; @@ -9,12 +9,12 @@ import { type ChartDocument, IIDX_GRADES, IIDX_LAMPS, type UserDocument } from " import t from "tap"; const SetFolders = async () => { - await db.folders.insert(TestingIIDXFolderSP10); + await MONGODB_KILL.folders.insert(TestingIIDXFolderSP10); await CreateFolderChartLookup(TestingIIDXFolderSP10, true); - await db["game-settings"].remove({}); + await MONGODB_KILL["game-settings"].remove({}); - await db["game-settings"].insert({ + await MONGODB_KILL["game-settings"].insert({ userID: 1, game: "iidx", playtype: "SP", @@ -46,7 +46,7 @@ const SetFolders = async () => { rivals: [], }); - await db["personal-bests"].insert(deepmerge(TestingIIDXSPScorePB, {})); + await MONGODB_KILL["personal-bests"].insert(deepmerge(TestingIIDXSPScorePB, {})); }; t.test("GET /api/v1/users/:userID/games/:game/:playtype/showcase", (t) => { @@ -80,7 +80,7 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/showcase", (t) => { }); t.test("Should project another users stats over this one if projectUser is set.", async (t) => { - await db["game-settings"].insert({ + await MONGODB_KILL["game-settings"].insert({ userID: 2, game: "iidx", playtype: "SP", @@ -108,7 +108,7 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/showcase", (t) => { }); // hack - await db.users.insert({ + await MONGODB_KILL.users.insert({ username: "test_acc", usernameLowercase: "test_acc", id: 2, @@ -214,7 +214,7 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/showcase/custom", (t) => t.equal(res.statusCode, 400); - await db.charts.iidx.insert({ + await MONGODB_KILL.charts.iidx.insert({ chartID: "testing_dp_chart", playtype: "DP", } as ChartDocument<"iidx:DP">); @@ -264,7 +264,7 @@ t.test("PUT /api/v1/users/:userID/games/:game/:playtype/showcase", (t) => { t.beforeEach( async () => // eslint-disable-next-line no-return-await - await db["api-tokens"].insert({ + await MONGODB_KILL["api-tokens"].insert({ userID: 1, identifier: "alt_token", permissions: { @@ -276,8 +276,8 @@ t.test("PUT /api/v1/users/:userID/games/:game/:playtype/showcase", (t) => { ); t.test("Requires the user to be authed as the requested user.", async (t) => { - await db.users.insert(mkFakeUser(2)); - await db["api-tokens"].insert({ + await MONGODB_KILL.users.insert(mkFakeUser(2)); + await MONGODB_KILL["api-tokens"].insert({ userID: 2, identifier: "alt_token", permissions: { @@ -297,7 +297,7 @@ t.test("PUT /api/v1/users/:userID/games/:game/:playtype/showcase", (t) => { }); t.test("Requires the permission customise_profile", async (t) => { - await db["api-tokens"].insert({ + await MONGODB_KILL["api-tokens"].insert({ userID: 1, identifier: "alt_token", permissions: { @@ -330,7 +330,11 @@ t.test("PUT /api/v1/users/:userID/games/:game/:playtype/showcase", (t) => { t.equal(res.statusCode, 200); - const data = await db["game-settings"].findOne({ userID: 1, game: "iidx", playtype: "SP" }); + const data = await MONGODB_KILL["game-settings"].findOne({ + userID: 1, + game: "iidx", + playtype: "SP", + }); t.hasStrict( data?.preferences.stats, @@ -373,7 +377,7 @@ t.test("PUT /api/v1/users/:userID/games/:game/:playtype/showcase", (t) => { t.equal(res.statusCode, 400); - await db.charts.iidx.insert({ + await MONGODB_KILL.charts.iidx.insert({ chartID: "testing_dp_chart", playtype: "DP", } as ChartDocument<"iidx:DP">); diff --git a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/showcase/router.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/showcase/router.ts index 4caee3adb..e5d94aa84 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/showcase/router.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/showcase/router.ts @@ -3,7 +3,7 @@ import { GetRelatedStatDocuments } from "#lib/showcase/get-related"; import { EvaluateUsersStatsShowcase } from "#lib/showcase/get-stats"; import { RequirePermissions } from "#server/middleware/auth"; import { RequireAuthedAsUser } from "#server/router/api/v1/users/_userID/middleware.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { IsRecord } from "#utils/misc"; import { FormatPrError } from "#utils/prudence"; import { GetUGPT } from "#utils/req-tachi-data"; @@ -99,7 +99,7 @@ router.get("/custom", async (req, res) => { const folderID = req.query.folderID as string; - const folder = await db.folders.findOne({ folderID }); + const folder = await MONGODB_KILL.folders.findOne({ folderID }); if (!folder || folder.game !== game || folder.playtype !== playtype) { return res.status(400).json({ @@ -136,7 +136,9 @@ router.get("/custom", async (req, res) => { }); } - const chart = await db.anyCharts[game].findOne({ chartID: req.query.chartID as string }); + const chart = await MONGODB_KILL.anyCharts[game].findOne({ + chartID: req.query.chartID as string, + }); if (!chart || chart.playtype !== playtype) { return res.status(400).json({ @@ -280,7 +282,7 @@ router.put("/", RequireAuthedAsUser, RequirePermissions("customise_profile"), as if (stat.mode === "chart") { // eslint-disable-next-line no-await-in-loop - const chart = await db.anyCharts[game].findOne({ chartID: stat.chartID }); + const chart = await MONGODB_KILL.anyCharts[game].findOne({ chartID: stat.chartID }); if (!chart || chart.playtype !== playtype) { return res.status(400).json({ @@ -294,7 +296,7 @@ router.put("/", RequireAuthedAsUser, RequirePermissions("customise_profile"), as : [unvalidatedStat.folderID]; // eslint-disable-next-line no-await-in-loop - const folders = await db.folders.find({ folderID: { $in: folderIDs } }); + const folders = await MONGODB_KILL.folders.find({ folderID: { $in: folderIDs } }); if ( folders.length !== folderIDs.length || @@ -310,7 +312,7 @@ router.put("/", RequireAuthedAsUser, RequirePermissions("customise_profile"), as } } - await db["game-settings"].update( + await MONGODB_KILL["game-settings"].update( { userID: user.id, game, @@ -323,7 +325,7 @@ router.put("/", RequireAuthedAsUser, RequirePermissions("customise_profile"), as }, ); - const newSettings = await db["game-settings"].findOne({ + const newSettings = await MONGODB_KILL["game-settings"].findOne({ userID: user.id, game, playtype, diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/tables/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/tables/router.oldtest.ts similarity index 89% rename from typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/tables/router.test.ts rename to typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/tables/router.oldtest.ts index c2b3d0e24..ce93ab806 100644 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/tables/router.test.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/tables/router.oldtest.ts @@ -1,6 +1,6 @@ import type { FolderDocument } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; import { TestingIIDXFolderSP10, TestingIIDXSPScorePB } from "#test-utils/test-data"; @@ -16,11 +16,11 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/tables/:tableID", (t) => folderID: "testing_folder", }) as FolderDocument; - await db.folders.insert(folder); + await MONGODB_KILL.folders.insert(folder); await CreateFolderChartLookup(folder, true); - await db["personal-bests"].insert(TestingIIDXSPScorePB); + await MONGODB_KILL["personal-bests"].insert(TestingIIDXSPScorePB); const res = await mockApi.get("/api/v1/users/1/games/iidx/SP/tables/mock_table"); diff --git a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/tables/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/tables/router.test.ts deleted file mode 100644 index c2b3d0e24..000000000 --- a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/tables/router.test.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type { FolderDocument } from "tachi-common"; - -import db from "#services/mongo/db"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import { TestingIIDXFolderSP10, TestingIIDXSPScorePB } from "#test-utils/test-data"; -import { CreateFolderChartLookup } from "#utils/folder"; -import deepmerge from "deepmerge"; -import t from "tap"; - -t.test("GET /api/v1/users/:userID/games/:game/:playtype/tables/:tableID", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should return a users statistics for this table", async (t) => { - const folder = deepmerge(TestingIIDXFolderSP10, { - folderID: "testing_folder", - }) as FolderDocument; - - await db.folders.insert(folder); - - await CreateFolderChartLookup(folder, true); - - await db["personal-bests"].insert(TestingIIDXSPScorePB); - - const res = await mockApi.get("/api/v1/users/1/games/iidx/SP/tables/mock_table"); - - t.equal(res.body.body.folders.length, 1); - t.equal(res.body.body.folders[0].folderID, "testing_folder"); - t.equal(res.body.body.table.tableID, "mock_table"); - t.equal(res.body.body.stats.length, 1); - - t.equal(res.body.body.stats[0].stats.lamp["EX HARD CLEAR"], 1); - t.equal(res.body.body.stats[0].stats.grade.AAA, 1); - - t.end(); - }); - - t.test("Should return 404 if the table does not exist.", async (t) => { - const res = await mockApi.get("/api/v1/users/1/games/iidx/SP/tables/bad_table"); - - t.equal(res.statusCode, 404); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.oldtest.ts similarity index 91% rename from typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.test.ts rename to typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.oldtest.ts index aebf89ebb..4fd6562bb 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.test.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.oldtest.ts @@ -1,4 +1,4 @@ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; @@ -44,8 +44,8 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/targets/goals", (t) => { t.beforeEach(ResetDBState); t.test("Should retrieve this user's goal subscriptions.", async (t) => { - await db.goals.insert(dupedGoal); - await db["goal-subs"].insert(dupedGoalSub); + await MONGODB_KILL.goals.insert(dupedGoal); + await MONGODB_KILL["goal-subs"].insert(dupedGoalSub); const res = await mockApi.get("/api/v1/users/1/games/iidx/SP/targets/goals"); @@ -72,8 +72,8 @@ t.test("POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal", asyn }) as PBScoreDocument; t.beforeEach(async () => { - await db["personal-bests"].remove({}); - await db["personal-bests"].insert(customScorePB); + await MONGODB_KILL["personal-bests"].remove({}); + await MONGODB_KILL["personal-bests"].insert(customScorePB); }); const cookie = await CreateFakeAuthCookie(mockApi); @@ -104,10 +104,10 @@ t.test("POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal", asyn return input; } - t.beforeEach(() => db.charts.iidx.insert(anotherFakeChart)); + t.beforeEach(() => MONGODB_KILL.charts.iidx.insert(anotherFakeChart)); // @ts-expect-error weird type issues - t.beforeEach(() => db.folders.insert(dm(TestingIIDXFolderSP10, {}))); + t.beforeEach(() => MONGODB_KILL.folders.insert(dm(TestingIIDXFolderSP10, {}))); t.beforeEach(() => CreateFolderChartLookup(TestingIIDXFolderSP10, true)); @@ -286,14 +286,14 @@ t.test("POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal", asyn return; } - const existsInDb = await db.goals.findOne({ + const existsInDb = await MONGODB_KILL.goals.findOne({ charts: input.content.charts, criteria: input.content.criteria, }); t.not(existsInDb, null, "A new goal should be added to the database."); - const subscribed = await db["goal-subs"].findOne({ + const subscribed = await MONGODB_KILL["goal-subs"].findOne({ userID: 1, goalID: existsInDb?.goalID ?? "edge_case", }); @@ -566,8 +566,8 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/targets/goals/:goalID", t.beforeEach(ResetDBState); t.test("Should retrieve this user's goal subscription.", async (t) => { - await db.goals.insert(dupedGoal); - await db["goal-subs"].insert(dupedGoalSub); + await MONGODB_KILL.goals.insert(dupedGoal); + await MONGODB_KILL["goal-subs"].insert(dupedGoalSub); const res = await mockApi.get( `/api/v1/users/1/games/iidx/SP/targets/goals/${HC511Goal.goalID}`, @@ -588,9 +588,9 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/targets/goals/:goalID", }); t.test("Should return parent quests if goal has any.", async (t) => { - await db.goals.insert(IIDXSPQuestGoals); - await db["goal-subs"].insert(IIDXSPQuestGoalSubs); - await db.quests.insert(TestingIIDXSPQuest); + await MONGODB_KILL.goals.insert(IIDXSPQuestGoals); + await MONGODB_KILL["goal-subs"].insert(IIDXSPQuestGoalSubs); + await MONGODB_KILL.quests.insert(TestingIIDXSPQuest); if (!IIDXSPQuestGoalSubs[0] || !IIDXSPQuestGoals[0]) { throw new Error(`Expected atleast one quest goal or sub to work with?`); @@ -631,8 +631,8 @@ t.test("DELETE /api/v1/users/:userID/games/:game/:playtype/targets/goals/:goalID const cookie = await CreateFakeAuthCookie(mockApi); t.test("Should delete a goal subscription if subscribed.", async (t) => { - await db.goals.insert(dupedGoal); - await db["goal-subs"].insert(dupedGoalSub); + await MONGODB_KILL.goals.insert(dupedGoal); + await MONGODB_KILL["goal-subs"].insert(dupedGoalSub); const res = await mockApi .delete(`/api/v1/users/1/games/iidx/SP/targets/goals/${dupedGoalSub.goalID}`) @@ -640,7 +640,7 @@ t.test("DELETE /api/v1/users/:userID/games/:game/:playtype/targets/goals/:goalID t.equal(res.statusCode, 200); - const dbRes = await db["goal-subs"].findOne({ + const dbRes = await MONGODB_KILL["goal-subs"].findOne({ userID: 1, goalID: dupedGoal.goalID, }); @@ -651,10 +651,10 @@ t.test("DELETE /api/v1/users/:userID/games/:game/:playtype/targets/goals/:goalID }); t.test("Should reject a goal deletion if goal has parent quests.", async (t) => { - await db.quests.insert(TestingIIDXSPQuest); - await db["quest-subs"].insert(TestingIIDXSPQuestSub); - await db.goals.insert(IIDXSPQuestGoals); - await db["goal-subs"].insert( + await MONGODB_KILL.quests.insert(TestingIIDXSPQuest); + await MONGODB_KILL["quest-subs"].insert(TestingIIDXSPQuestSub); + await MONGODB_KILL.goals.insert(IIDXSPQuestGoals); + await MONGODB_KILL["goal-subs"].insert( dm(dupedGoalSub, { goalID: "eg_goal_1" }) as GoalSubscriptionDocument, ); @@ -668,7 +668,7 @@ t.test("DELETE /api/v1/users/:userID/games/:game/:playtype/targets/goals/:goalID `This goal is part of a quest you are subscribed to. It can only be removed by unsubscribing from the relevant quests: '${TestingIIDXSPQuest.name}'.`, ); - const dbRes = await db["goal-subs"].findOne({ + const dbRes = await MONGODB_KILL["goal-subs"].findOne({ userID: 1, goalID: "eg_goal_1", }); @@ -679,8 +679,8 @@ t.test("DELETE /api/v1/users/:userID/games/:game/:playtype/targets/goals/:goalID }); t.test("Should reject a goal deletion if not subscribed.", async (t) => { - await db.goals.insert(dupedGoal); - await db["goal-subs"].insert(dupedGoalSub); + await MONGODB_KILL.goals.insert(dupedGoal); + await MONGODB_KILL["goal-subs"].insert(dupedGoalSub); const res = await mockApi .delete(`/api/v1/users/1/games/iidx/SP/targets/goals/INVALID_GOAL`) @@ -692,8 +692,8 @@ t.test("DELETE /api/v1/users/:userID/games/:game/:playtype/targets/goals/:goalID }); t.test("Should reject a goal deletion if not authed.", async (t) => { - await db.goals.insert(dupedGoal); - await db["goal-subs"].insert(dupedGoalSub); + await MONGODB_KILL.goals.insert(dupedGoal); + await MONGODB_KILL["goal-subs"].insert(dupedGoalSub); const res = await mockApi.delete( `/api/v1/users/1/games/iidx/SP/targets/goals/${dupedGoalSub.goalID}`, diff --git a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.ts index 9ad6ff471..2e918bf69 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.ts @@ -10,7 +10,7 @@ import { import { GetParentQuests } from "#lib/targets/quests"; import { RequirePermissions } from "#server/middleware/auth"; import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetGoalForIDGuaranteed } from "#utils/db"; import { AssignToReqTachiData, GetTachiData, GetUGPT } from "#utils/req-tachi-data"; import { type RequestHandler, Router } from "express"; @@ -29,19 +29,19 @@ const router: Router = Router({ mergeParams: true }); router.get("/", async (req, res) => { const { user, game, playtype } = GetUGPT(req); - const goalSubs = await db["goal-subs"].find({ + const goalSubs = await MONGODB_KILL["goal-subs"].find({ userID: user.id, game, playtype, }); - const goals = await db.goals.find({ + const goals = await MONGODB_KILL.goals.find({ goalID: { $in: goalSubs.map((e) => e.goalID) }, }); const allQuests = await GetParentQuests(user.id, game, playtype, goalSubs); - const questSubs = await db["quest-subs"].find({ userID: user.id, game, playtype }); + const questSubs = await MONGODB_KILL["quest-subs"].find({ userID: user.id, game, playtype }); const questSubIDs = questSubs.map((e) => e.questID); @@ -136,7 +136,7 @@ router.post( async (req, res) => { const { user, game, playtype } = GetUGPT(req); - const existingGoalsCount = await db["goal-subs"].count({ + const existingGoalsCount = await MONGODB_KILL["goal-subs"].count({ userID: user.id, game, playtype, @@ -212,7 +212,7 @@ router.post( const GetGoalSubscription: RequestHandler = async (req, res, next) => { const { user, game, playtype } = GetUGPT(req); - const goalSub = await db["goal-subs"].findOne({ + const goalSub = await MONGODB_KILL["goal-subs"].findOne({ userID: user.id, game, playtype, diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/targets/quests/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/quests/router.oldtest.ts similarity index 81% rename from typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/targets/quests/router.test.ts rename to typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/quests/router.oldtest.ts index 3ee697f4f..dfeee744e 100644 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/targets/quests/router.test.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/quests/router.oldtest.ts @@ -1,6 +1,6 @@ import type { UserDocument, UserGameStats } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; @@ -14,11 +14,11 @@ import t from "tap"; t.test("GET /api/v1/users/:userID/games/:game/:playtype/targets/quests", (t) => { t.beforeEach(ResetDBState); - t.beforeEach(() => db.quests.insert(TestingIIDXSPQuest)); + t.beforeEach(() => MONGODB_KILL.quests.insert(TestingIIDXSPQuest)); t.test("Should return all quest subscriptions.", async (t) => { - await db["quest-subs"].insert(TestingIIDXSPQuestSub); - await db.goals.insert(IIDXSPQuestGoals); + await MONGODB_KILL["quest-subs"].insert(TestingIIDXSPQuestSub); + await MONGODB_KILL.goals.insert(IIDXSPQuestGoals); const res = await mockApi.get("/api/v1/users/1/games/iidx/SP/targets/quests"); @@ -32,8 +32,8 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/targets/quests", (t) => }); t.test("Should panic if quest subs are parentless.", async (t) => { - await db.quests.remove({ questID: TestingIIDXSPQuest.questID }); - await db["quest-subs"].insert(TestingIIDXSPQuestSub); + await MONGODB_KILL.quests.remove({ questID: TestingIIDXSPQuest.questID }); + await MONGODB_KILL["quest-subs"].insert(TestingIIDXSPQuestSub); const res = await mockApi.get("/api/v1/users/1/games/iidx/SP/targets/quests"); @@ -47,13 +47,13 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/targets/quests", (t) => t.test("GET /api/v1/users/:userID/games/:game/:playtype/targets/quests/:questID", (t) => { t.beforeEach(ResetDBState); - t.beforeEach(() => db.quests.insert(TestingIIDXSPQuest)); + t.beforeEach(() => MONGODB_KILL.quests.insert(TestingIIDXSPQuest)); t.test("Should return the user's specific quest sub.", async (t) => { - await db["quest-subs"].insert(TestingIIDXSPQuestSub); + await MONGODB_KILL["quest-subs"].insert(TestingIIDXSPQuestSub); - await db.goals.insert(IIDXSPQuestGoals); - await db["goal-subs"].insert(IIDXSPQuestGoalSubs); + await MONGODB_KILL.goals.insert(IIDXSPQuestGoals); + await MONGODB_KILL["goal-subs"].insert(IIDXSPQuestGoalSubs); const res = await mockApi.get( `/api/v1/users/1/games/iidx/SP/targets/quests/${TestingIIDXSPQuest.questID}`, @@ -91,7 +91,7 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/targets/quests/:questID" t.test("PUT /api/v1/users/:userID/games/:game/:playtype/targets/quests/:questID", async (t) => { t.beforeEach(ResetDBState); - t.beforeEach(() => db.quests.insert(TestingIIDXSPQuest)); + t.beforeEach(() => MONGODB_KILL.quests.insert(TestingIIDXSPQuest)); const cookie = await CreateFakeAuthCookie(mockApi); @@ -106,7 +106,7 @@ t.test("PUT /api/v1/users/:userID/games/:game/:playtype/targets/quests/:questID" }); t.test("Should return 409 if user is already subscribed to this quest.", async (t) => { - await db["quest-subs"].insert(TestingIIDXSPQuestSub); + await MONGODB_KILL["quest-subs"].insert(TestingIIDXSPQuestSub); const res = await mockApi .put(`/api/v1/users/1/games/iidx/SP/targets/quests/${TestingIIDXSPQuest.questID}`) @@ -118,7 +118,7 @@ t.test("PUT /api/v1/users/:userID/games/:game/:playtype/targets/quests/:questID" }); t.test("Should subscribe to a quest.", async (t) => { - await db.goals.insert(IIDXSPQuestGoals); + await MONGODB_KILL.goals.insert(IIDXSPQuestGoals); const res = await mockApi .put(`/api/v1/users/1/games/iidx/SP/targets/quests/${TestingIIDXSPQuest.questID}`) @@ -126,7 +126,7 @@ t.test("PUT /api/v1/users/:userID/games/:game/:playtype/targets/quests/:questID" t.equal(res.statusCode, 200); - const dbRes = await db["quest-subs"].findOne({ + const dbRes = await MONGODB_KILL["quest-subs"].findOne({ userID: 1, questID: TestingIIDXSPQuest.questID, }); @@ -149,13 +149,13 @@ t.test("PUT /api/v1/users/:userID/games/:game/:playtype/targets/quests/:questID" t.test("Should return 403 if the user is authed as someone else.", async (t) => { // We have to make sure that the user exists, otherwise we'll just get // a normal 404. - await db.users.insert({ + await MONGODB_KILL.users.insert({ id: 2, username: "fake_person", usernameLowercase: "fake_person", } as UserDocument); - await db["game-stats"].insert({ + await MONGODB_KILL["game-stats"].insert({ game: "iidx", playtype: "SP", userID: 2, @@ -175,7 +175,7 @@ t.test("PUT /api/v1/users/:userID/games/:game/:playtype/targets/quests/:questID" t.test("DELETE /api/v1/users/:userID/games/:game/:playtype/targets/quests/:questID", async (t) => { t.beforeEach(ResetDBState); - t.beforeEach(() => db.quests.insert(TestingIIDXSPQuest)); + t.beforeEach(() => MONGODB_KILL.quests.insert(TestingIIDXSPQuest)); const cookie = await CreateFakeAuthCookie(mockApi); @@ -202,13 +202,13 @@ t.test("DELETE /api/v1/users/:userID/games/:game/:playtype/targets/quests/:quest t.test("Should return 403 if the user is authed as someone else.", async (t) => { // We have to make sure that the user exists, otherwise we'll just get // a normal 404. - await db.users.insert({ + await MONGODB_KILL.users.insert({ id: 2, username: "fake_person", usernameLowercase: "fake_person", } as UserDocument); - await db["game-stats"].insert({ + await MONGODB_KILL["game-stats"].insert({ game: "iidx", playtype: "SP", userID: 2, @@ -224,7 +224,7 @@ t.test("DELETE /api/v1/users/:userID/games/:game/:playtype/targets/quests/:quest }); t.test("Should unsubscribe from a quest.", async (t) => { - await db["quest-subs"].insert(TestingIIDXSPQuestSub); + await MONGODB_KILL["quest-subs"].insert(TestingIIDXSPQuestSub); const res = await mockApi .delete(`/api/v1/users/1/games/iidx/SP/targets/quests/${TestingIIDXSPQuest.questID}`) @@ -234,7 +234,7 @@ t.test("DELETE /api/v1/users/:userID/games/:game/:playtype/targets/quests/:quest t.strictSame(res.body.body.quest, TestingIIDXSPQuest); - const dbRes = await db["quest-subs"].findOne({ + const dbRes = await MONGODB_KILL["quest-subs"].findOne({ userID: 1, questID: TestingIIDXSPQuest.questID, }); diff --git a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/quests/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/quests/router.test.ts deleted file mode 100644 index 3ee697f4f..000000000 --- a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/quests/router.test.ts +++ /dev/null @@ -1,248 +0,0 @@ -import type { UserDocument, UserGameStats } from "tachi-common"; - -import db from "#services/mongo/db"; -import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import { - IIDXSPQuestGoals, - IIDXSPQuestGoalSubs, - TestingIIDXSPQuest, - TestingIIDXSPQuestSub, -} from "#test-utils/test-data"; -import t from "tap"; - -t.test("GET /api/v1/users/:userID/games/:game/:playtype/targets/quests", (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(() => db.quests.insert(TestingIIDXSPQuest)); - - t.test("Should return all quest subscriptions.", async (t) => { - await db["quest-subs"].insert(TestingIIDXSPQuestSub); - await db.goals.insert(IIDXSPQuestGoals); - - const res = await mockApi.get("/api/v1/users/1/games/iidx/SP/targets/quests"); - - t.strictSame(res.body.body, { - quests: [TestingIIDXSPQuest], - questSubs: [TestingIIDXSPQuestSub], - goals: IIDXSPQuestGoals, - }); - - t.end(); - }); - - t.test("Should panic if quest subs are parentless.", async (t) => { - await db.quests.remove({ questID: TestingIIDXSPQuest.questID }); - await db["quest-subs"].insert(TestingIIDXSPQuestSub); - - const res = await mockApi.get("/api/v1/users/1/games/iidx/SP/targets/quests"); - - t.equal(res.statusCode, 500); - - t.end(); - }); - - t.end(); -}); - -t.test("GET /api/v1/users/:userID/games/:game/:playtype/targets/quests/:questID", (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(() => db.quests.insert(TestingIIDXSPQuest)); - - t.test("Should return the user's specific quest sub.", async (t) => { - await db["quest-subs"].insert(TestingIIDXSPQuestSub); - - await db.goals.insert(IIDXSPQuestGoals); - await db["goal-subs"].insert(IIDXSPQuestGoalSubs); - - const res = await mockApi.get( - `/api/v1/users/1/games/iidx/SP/targets/quests/${TestingIIDXSPQuest.questID}`, - ); - - t.strictSame(res.body.body, { - quest: TestingIIDXSPQuest, - questSub: TestingIIDXSPQuestSub, - results: IIDXSPQuestGoalSubs.map((e) => ({ - achieved: e.achieved, - progress: e.progress, - progressHuman: e.progressHuman, - outOf: e.outOf, - outOfHuman: e.outOfHuman, - goalID: e.goalID, - })), - goals: IIDXSPQuestGoals, - }); - - t.end(); - }); - - t.test("Should return 404 if the user is not subscribed to this quest.", async (t) => { - const res = await mockApi.get( - `/api/v1/users/1/games/iidx/SP/targets/quests/${TestingIIDXSPQuest.questID}`, - ); - - t.equal(res.statusCode, 404); - - t.end(); - }); - - t.end(); -}); - -t.test("PUT /api/v1/users/:userID/games/:game/:playtype/targets/quests/:questID", async (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(() => db.quests.insert(TestingIIDXSPQuest)); - - const cookie = await CreateFakeAuthCookie(mockApi); - - t.test("Should return 404 if this quest doesn't exist.", async (t) => { - const res = await mockApi - .put(`/api/v1/users/1/games/iidx/SP/targets/quests/fake_quest`) - .set("Cookie", cookie); - - t.equal(res.statusCode, 404); - - t.end(); - }); - - t.test("Should return 409 if user is already subscribed to this quest.", async (t) => { - await db["quest-subs"].insert(TestingIIDXSPQuestSub); - - const res = await mockApi - .put(`/api/v1/users/1/games/iidx/SP/targets/quests/${TestingIIDXSPQuest.questID}`) - .set("Cookie", cookie); - - t.equal(res.statusCode, 409); - - t.end(); - }); - - t.test("Should subscribe to a quest.", async (t) => { - await db.goals.insert(IIDXSPQuestGoals); - - const res = await mockApi - .put(`/api/v1/users/1/games/iidx/SP/targets/quests/${TestingIIDXSPQuest.questID}`) - .set("Cookie", cookie); - - t.equal(res.statusCode, 200); - - const dbRes = await db["quest-subs"].findOne({ - userID: 1, - questID: TestingIIDXSPQuest.questID, - }); - - t.not(dbRes, null, "Should have subscribed the user to the quest."); - - t.end(); - }); - - t.test("Should return 401 if the user is not authed.", async (t) => { - const res = await mockApi.put( - `/api/v1/users/1/games/iidx/SP/targets/quests/${TestingIIDXSPQuest.questID}`, - ); - - t.equal(res.statusCode, 401); - - t.end(); - }); - - t.test("Should return 403 if the user is authed as someone else.", async (t) => { - // We have to make sure that the user exists, otherwise we'll just get - // a normal 404. - await db.users.insert({ - id: 2, - username: "fake_person", - usernameLowercase: "fake_person", - } as UserDocument); - - await db["game-stats"].insert({ - game: "iidx", - playtype: "SP", - userID: 2, - } as UserGameStats); - - const res = await mockApi - .put(`/api/v1/users/2/games/iidx/SP/targets/quests/${TestingIIDXSPQuest.questID}`) - .set("Cookie", cookie); - - t.equal(res.statusCode, 403); - - t.end(); - }); - - t.end(); -}); - -t.test("DELETE /api/v1/users/:userID/games/:game/:playtype/targets/quests/:questID", async (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(() => db.quests.insert(TestingIIDXSPQuest)); - - const cookie = await CreateFakeAuthCookie(mockApi); - - t.test("Should return 409 if the user is not subscribed to this quest.", async (t) => { - const res = await mockApi - .delete(`/api/v1/users/1/games/iidx/SP/targets/quests/${TestingIIDXSPQuest.questID}`) - .set("Cookie", cookie); - - t.equal(res.statusCode, 409); - - t.end(); - }); - - t.test("Should return 401 if the user is not authed.", async (t) => { - const res = await mockApi.delete( - `/api/v1/users/1/games/iidx/SP/targets/quests/${TestingIIDXSPQuest.questID}`, - ); - - t.equal(res.statusCode, 401); - - t.end(); - }); - - t.test("Should return 403 if the user is authed as someone else.", async (t) => { - // We have to make sure that the user exists, otherwise we'll just get - // a normal 404. - await db.users.insert({ - id: 2, - username: "fake_person", - usernameLowercase: "fake_person", - } as UserDocument); - - await db["game-stats"].insert({ - game: "iidx", - playtype: "SP", - userID: 2, - } as UserGameStats); - - const res = await mockApi - .delete(`/api/v1/users/2/games/iidx/SP/targets/quests/${TestingIIDXSPQuest.questID}`) - .set("Cookie", cookie); - - t.equal(res.statusCode, 403); - - t.end(); - }); - - t.test("Should unsubscribe from a quest.", async (t) => { - await db["quest-subs"].insert(TestingIIDXSPQuestSub); - - const res = await mockApi - .delete(`/api/v1/users/1/games/iidx/SP/targets/quests/${TestingIIDXSPQuest.questID}`) - .set("Cookie", cookie); - - t.equal(res.statusCode, 200); - - t.strictSame(res.body.body.quest, TestingIIDXSPQuest); - - const dbRes = await db["quest-subs"].findOne({ - userID: 1, - questID: TestingIIDXSPQuest.questID, - }); - - t.equal(dbRes, null, "Should have removed the quest from the database."); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/quests/router.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/quests/router.ts index d6c3d5a73..cd36e6153 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/quests/router.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/quests/router.ts @@ -8,7 +8,7 @@ import { UnsubscribeFromQuest, } from "#lib/targets/quests"; import { RequirePermissions } from "#server/middleware/auth"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { AssignToReqTachiData, GetGPT, GetTachiData, GetUGPT } from "#utils/req-tachi-data"; import { FormatUserDoc } from "#utils/user"; import { type RequestHandler, Router } from "express"; @@ -25,13 +25,13 @@ const router: Router = Router({ mergeParams: true }); router.get("/", async (req, res) => { const { user, game, playtype } = GetUGPT(req); - const questSubs = await db["quest-subs"].find({ + const questSubs = await MONGODB_KILL["quest-subs"].find({ userID: user.id, game, playtype, }); - const quests = await db.quests.find({ + const quests = await MONGODB_KILL.quests.find({ questID: { $in: questSubs.map((e) => e.questID) }, }); @@ -58,7 +58,7 @@ router.get("/", async (req, res) => { const GetQuestSubscription: RequestHandler = async (req, res, next) => { const { user, game, playtype } = GetUGPT(req); - const questSub = await db["quest-subs"].findOne({ + const questSub = await MONGODB_KILL["quest-subs"].findOne({ userID: user.id, game, playtype, @@ -80,7 +80,7 @@ const GetQuestSubscription: RequestHandler = async (req, res, next) => { const GetQuest: RequestHandler = async (req, res, next) => { const { game, playtype } = GetGPT(req); - const quest = await db.quests.findOne({ + const quest = await MONGODB_KILL.quests.findOne({ game, playtype, questID: req.params.questID, @@ -142,7 +142,7 @@ router.put( async (req, res) => { const { user, game, playtype } = GetUGPT(req); - const existingQuestsCount = await db["quest-subs"].count({ + const existingQuestsCount = await MONGODB_KILL["quest-subs"].count({ userID: user.id, game, playtype, @@ -157,7 +157,7 @@ router.put( const quest = GetTachiData(req, "questDoc"); - const alreadySubscibed = await db["quest-subs"].findOne({ + const alreadySubscibed = await MONGODB_KILL["quest-subs"].findOne({ userID: user.id, questID: quest.questID, }); @@ -218,7 +218,7 @@ router.delete( `User ${FormatUserDoc(user)} is unsubscribing from quest '${quest.name}'.`, ); - const questSub = await db["quest-subs"].findOne({ + const questSub = await MONGODB_KILL["quest-subs"].findOne({ userID: user.id, questID: quest.questID, }); diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/targets/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/router.oldtest.ts similarity index 89% rename from typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/targets/router.test.ts rename to typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/router.oldtest.ts index db4d5e46b..e4684d002 100644 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/targets/router.test.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/router.oldtest.ts @@ -5,7 +5,7 @@ import type { QuestSubscriptionDocument, } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; import { @@ -37,7 +37,7 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/targets/recently-achieve t.beforeEach(ResetDBState); t.test("Should return subscriptions that were recently achieved by this user.", async (t) => { - await db.goals.insert([ + await MONGODB_KILL.goals.insert([ mkGoal({ goalID: "achieved_goal", }), @@ -46,7 +46,7 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/targets/recently-achieve }), ]); - await db["goal-subs"].insert([ + await MONGODB_KILL["goal-subs"].insert([ mkGoalSub({ achieved: true, timeAchieved: 1000, @@ -58,14 +58,14 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/targets/recently-achieve }), ]); - await db.quests.insert([ + await MONGODB_KILL.quests.insert([ mkQuest({}), mkQuest({ questID: "not_achieved_quest", }), ]); - await db["quest-subs"].insert([ + await MONGODB_KILL["quest-subs"].insert([ mkQuestSub({ achieved: true, timeAchieved: 2000, @@ -103,7 +103,7 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/targets/recently-raised" t.beforeEach(ResetDBState); t.test("Should return subscriptions that were recently achieved by this user.", async (t) => { - await db.goals.insert([ + await MONGODB_KILL.goals.insert([ mkGoal({ goalID: "achieved_goal", }), @@ -112,7 +112,7 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/targets/recently-raised" }), ]); - await db["goal-subs"].insert([ + await MONGODB_KILL["goal-subs"].insert([ mkGoalSub({ achieved: true, timeAchieved: 1000, @@ -125,14 +125,14 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/targets/recently-raised" }), ]); - await db.quests.insert([ + await MONGODB_KILL.quests.insert([ mkQuest({}), mkQuest({ questID: "not_achieved_quest", }), ]); - await db["quest-subs"].insert([ + await MONGODB_KILL["quest-subs"].insert([ mkQuestSub({ achieved: true, timeAchieved: 2000, diff --git a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/router.test.ts deleted file mode 100644 index db4d5e46b..000000000 --- a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/router.test.ts +++ /dev/null @@ -1,163 +0,0 @@ -import type { - GoalDocument, - GoalSubscriptionDocument, - QuestDocument, - QuestSubscriptionDocument, -} from "tachi-common"; - -import db from "#services/mongo/db"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import { - HC511Goal, - HC511UserGoal, - TestingIIDXSPQuest, - TestingIIDXSPQuestSub, -} from "#test-utils/test-data"; -import dm from "deepmerge"; -import t from "tap"; - -function mkGoalSub(merge: Partial) { - return dm(HC511UserGoal, merge); -} - -function mkGoal(merge: Partial) { - return dm(HC511Goal, merge); -} - -function mkQuest(merge: Partial) { - return dm(TestingIIDXSPQuest, merge); -} - -function mkQuestSub(merge: Partial) { - return dm(TestingIIDXSPQuestSub, merge); -} - -t.test("GET /api/v1/users/:userID/games/:game/:playtype/targets/recently-achieved", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should return subscriptions that were recently achieved by this user.", async (t) => { - await db.goals.insert([ - mkGoal({ - goalID: "achieved_goal", - }), - mkGoal({ - goalID: "not_achieved_goal", - }), - ]); - - await db["goal-subs"].insert([ - mkGoalSub({ - achieved: true, - timeAchieved: 1000, - goalID: "achieved_goal", - }), - mkGoalSub({ - achieved: false, - goalID: "not_achieved_goal", - }), - ]); - - await db.quests.insert([ - mkQuest({}), - mkQuest({ - questID: "not_achieved_quest", - }), - ]); - - await db["quest-subs"].insert([ - mkQuestSub({ - achieved: true, - timeAchieved: 2000, - }), - mkQuestSub({ - achieved: false, - questID: "not_achieved_quest", - }), - ]); - - const res = await mockApi.get("/api/v1/users/1/games/iidx/SP/targets/recently-achieved"); - - t.equal(res.statusCode, 200); - - t.hasStrict(res.body.body, { - quests: [{ questID: "example_quest_id" }], - goals: [{ goalID: "achieved_goal" }], - goalSubs: [{ goalID: "achieved_goal" }], - questSubs: [{ questID: "example_quest_id" }], - user: { id: 1 }, - }); - - t.equal(res.body.body.goals.length, 1); - t.equal(res.body.body.quests.length, 1); - t.equal(res.body.body.goalSubs.length, 1); - t.equal(res.body.body.questSubs.length, 1); - - t.end(); - }); - - t.end(); -}); - -t.test("GET /api/v1/users/:userID/games/:game/:playtype/targets/recently-raised", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should return subscriptions that were recently achieved by this user.", async (t) => { - await db.goals.insert([ - mkGoal({ - goalID: "achieved_goal", - }), - mkGoal({ - goalID: "not_achieved_goal", - }), - ]); - - await db["goal-subs"].insert([ - mkGoalSub({ - achieved: true, - timeAchieved: 1000, - goalID: "achieved_goal", - }), - mkGoalSub({ - achieved: false, - lastInteraction: 1000, - goalID: "not_achieved_goal", - }), - ]); - - await db.quests.insert([ - mkQuest({}), - mkQuest({ - questID: "not_achieved_quest", - }), - ]); - - await db["quest-subs"].insert([ - mkQuestSub({ - achieved: true, - timeAchieved: 2000, - }), - mkQuestSub({ - achieved: false, - lastInteraction: 1000, - questID: "not_achieved_quest", - }), - ]); - - const res = await mockApi.get("/api/v1/users/1/games/iidx/SP/targets/recently-raised"); - - t.equal(res.statusCode, 200); - - t.hasStrict(res.body.body, { - quests: [{ questID: "not_achieved_quest" }], - goals: [{ goalID: "not_achieved_goal" }], - goalSubs: [{ goalID: "not_achieved_goal" }], - questSubs: [{ questID: "not_achieved_quest" }], - user: { id: 1 }, - }); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/router.ts b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/router.ts index 29de5ecbf..d2bd08699 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/router.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/router.ts @@ -1,7 +1,7 @@ import { log } from "#lib/log/log.js"; import { GetRelevantGoals } from "#lib/targets/goals"; import { GetParentQuests } from "#lib/targets/quests"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetRecentlyAchievedGoals, GetRecentlyAchievedQuests, @@ -84,7 +84,7 @@ router.get("/on-chart/:chartID", async (req, res) => { const chartID = req.params.chartID; - const chart = await db.anyCharts[game].findOne({ chartID, playtype }); + const chart = await MONGODB_KILL.anyCharts[game].findOne({ chartID, playtype }); if (!chart) { return res.status(404).json({ @@ -105,7 +105,7 @@ router.get("/on-chart/:chartID", async (req, res) => { const quests = await GetParentQuests(user.id, game, playtype, goalSubs); - const questSubs = await db["quest-subs"].find({ + const questSubs = await MONGODB_KILL["quest-subs"].find({ questID: { $in: quests.map((e) => e.questID) }, }); @@ -131,7 +131,7 @@ router.get("/on-folder/:folderID", async (req, res) => { const folderID = req.params.folderID; - const folder = await db.folders.findOne({ folderID, playtype }); + const folder = await MONGODB_KILL.folders.findOne({ folderID, playtype }); if (!folder) { return res.status(404).json({ @@ -142,7 +142,7 @@ router.get("/on-folder/:folderID", async (req, res) => { const folderChartIDs = await GetFolderChartIDs(folderID); - const allGoalSubs = await db["goal-subs"].find({ + const allGoalSubs = await MONGODB_KILL["goal-subs"].find({ userID: user.id, game, playtype, @@ -154,19 +154,19 @@ router.get("/on-folder/:folderID", async (req, res) => { // or if it's on any of the charts in the folder. // this is convenient for the UI, atleast. const goals = await Promise.all([ - db.goals.find({ + MONGODB_KILL.goals.find({ "charts.type": { $in: ["single", "multi"] }, "charts.data": { $in: folderChartIDs }, goalID: { $in: goalIDs }, }), - db.goals.find({ + MONGODB_KILL.goals.find({ "charts.type": "folder", "charts.data": folderID, goalID: { $in: goalIDs }, }), ]).then((r) => r.flat()); - const goalSubs = await db["goal-subs"].find({ + const goalSubs = await MONGODB_KILL["goal-subs"].find({ goalID: { $in: goals.map((e) => e.goalID) }, userID: user.id, game, @@ -175,7 +175,7 @@ router.get("/on-folder/:folderID", async (req, res) => { const quests = await GetParentQuests(user.id, game, playtype, goalSubs); - const questSubs = await db["quest-subs"].find({ + const questSubs = await MONGODB_KILL["quest-subs"].find({ questID: { $in: quests.map((e) => e.questID) }, }); @@ -200,8 +200,8 @@ router.get("/all-subs", async (req, res) => { const { user, game, playtype } = GetUGPT(req); const [goalSubs, questSubs] = await Promise.all([ - db["goal-subs"].find({ userID: user.id, game, playtype }), - db["quest-subs"].find({ userID: user.id, game, playtype }), + MONGODB_KILL["goal-subs"].find({ userID: user.id, game, playtype }), + MONGODB_KILL["quest-subs"].find({ userID: user.id, game, playtype }), ]); return res.status(200).json({ diff --git a/typescript/server/src/server/router/api/v1/users/_userID/imports/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/imports/router.oldtest.ts similarity index 100% rename from typescript/server/src/server/router/api/v1/users/_userID/imports/router.test.ts rename to typescript/server/src/server/router/api/v1/users/_userID/imports/router.oldtest.ts diff --git a/typescript/server/src/server/router/api/v1/users/_userID/imports/router.ts b/typescript/server/src/server/router/api/v1/users/_userID/imports/router.ts index 93ab5b681..d42703b15 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/imports/router.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/imports/router.ts @@ -3,7 +3,7 @@ import type { ImportTrackerDocument, ImportTypes } from "tachi-common"; import { TachiConfig } from "#lib/setup/config"; import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { DeleteUndefinedProps } from "#utils/misc"; import { GetTachiData } from "#utils/req-tachi-data"; import { Router } from "express"; @@ -42,7 +42,7 @@ router.get( DeleteUndefinedProps(query); - const imports = await db.imports.find(query, { + const imports = await MONGODB_KILL.imports.find(query, { sort: { timeFinished: -1 }, limit: 500, }); @@ -88,7 +88,7 @@ router.get( DeleteUndefinedProps(query); - const trackers = await db["import-trackers"].find(query, { + const trackers = await MONGODB_KILL["import-trackers"].find(query, { sort: { timeStarted: -1 }, limit: 500, }); diff --git a/typescript/server/src/server/router/api/v1/users/_userID/integrations/cg/_cgType/router.ts b/typescript/server/src/server/router/api/v1/users/_userID/integrations/cg/_cgType/router.ts index 432a3741c..c92035fc3 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/integrations/cg/_cgType/router.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/integrations/cg/_cgType/router.ts @@ -3,7 +3,7 @@ import type { RequestHandler } from "express-serve-static-core"; import type { CGCardInfo } from "tachi-common"; import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetTachiData } from "#utils/req-tachi-data"; import { Router } from "express"; import { p } from "prudence"; @@ -33,7 +33,7 @@ router.get("/", ValidateCGType, RequireSelfRequestFromUser, async (req, res) => const user = GetTachiData(req, "requestedUser"); const cgType = req.params.cgType as CGServices; - const cardInfo = await db["cg-card-info"].findOne({ + const cardInfo = await MONGODB_KILL["cg-card-info"].findOne({ userID: user.id, service: cgType, }); @@ -88,7 +88,7 @@ router.put( userID: user.id, }; - await db["cg-card-info"].update( + await MONGODB_KILL["cg-card-info"].update( { userID: user.id, service: cgType, @@ -117,7 +117,7 @@ router.delete("/", ValidateCGType, RequireSelfRequestFromUser, async (req, res) const user = GetTachiData(req, "requestedUser"); const cgType = req.params.cgType as CGServices; - await db["cg-card-info"].remove({ + await MONGODB_KILL["cg-card-info"].remove({ userID: user.id, service: cgType, }); diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/fervidex/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/integrations/fervidex/router.oldtest.ts similarity index 84% rename from typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/fervidex/router.test.ts rename to typescript/server/src/server/router/api/v1/users/_userID/integrations/fervidex/router.oldtest.ts index cf6c9729f..37b249805 100644 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/fervidex/router.test.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/integrations/fervidex/router.oldtest.ts @@ -1,6 +1,6 @@ import type { UserDocument } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; @@ -12,7 +12,7 @@ t.test("GET /api/v1/users/:userID/integrations/fervidex/settings", async (t) => const cookie = await CreateFakeAuthCookie(mockApi); t.test("Should return null if this user has no settings set.", async (t) => { - await db["fer-settings"].remove({}); + await MONGODB_KILL["fer-settings"].remove({}); const res = await mockApi .get("/api/v1/users/1/integrations/fervidex/settings") @@ -26,7 +26,10 @@ t.test("GET /api/v1/users/:userID/integrations/fervidex/settings", async (t) => }); t.test("Should return this users settings if they have them.", async (t) => { - await db["fer-settings"].update({ userID: 1 }, { $set: { cards: ["foo", "bar"] } }); + await MONGODB_KILL["fer-settings"].update( + { userID: 1 }, + { $set: { cards: ["foo", "bar"] } }, + ); const res = await mockApi .get("/api/v1/users/1/integrations/fervidex/settings") @@ -55,7 +58,7 @@ t.test("GET /api/v1/users/:userID/integrations/fervidex/settings", async (t) => t.equal(res2.statusCode, 403); // insert a fake user doc so this doesn't 404 - await db.users.insert({ + await MONGODB_KILL.users.insert({ id: 2, username: "foo", usernameLowercase: "foo", @@ -88,7 +91,7 @@ t.test("PATCH /api/v1/users/:userID/integrations/fervidex/settings", async (t) = t.strictSame(res.body.body.cards, ["foo", "bar"]); - const dbRes = await db["fer-settings"].findOne({ userID: 1 }); + const dbRes = await MONGODB_KILL["fer-settings"].findOne({ userID: 1 }); t.strictSame(dbRes?.cards, ["foo", "bar"]); @@ -96,7 +99,7 @@ t.test("PATCH /api/v1/users/:userID/integrations/fervidex/settings", async (t) = }); t.test("Should insert a setting filter document if one doesn't exist.", async (t) => { - await db["fer-settings"].remove({}); + await MONGODB_KILL["fer-settings"].remove({}); const res = await mockApi .patch("/api/v1/users/1/integrations/fervidex/settings") @@ -107,7 +110,7 @@ t.test("PATCH /api/v1/users/:userID/integrations/fervidex/settings", async (t) = t.strictSame(res.body.body.cards, ["foo", "bar"]); - const dbRes = await db["fer-settings"].findOne({ userID: 1 }); + const dbRes = await MONGODB_KILL["fer-settings"].findOne({ userID: 1 }); t.strictSame(dbRes?.cards, ["foo", "bar"]); @@ -124,7 +127,7 @@ t.test("PATCH /api/v1/users/:userID/integrations/fervidex/settings", async (t) = t.strictSame(res.body.body.cards, null); - const dbRes = await db["fer-settings"].findOne({ userID: 1 }); + const dbRes = await MONGODB_KILL["fer-settings"].findOne({ userID: 1 }); t.strictSame(dbRes?.cards, null); diff --git a/typescript/server/src/server/router/api/v1/users/_userID/integrations/fervidex/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/integrations/fervidex/router.test.ts deleted file mode 100644 index cf6c9729f..000000000 --- a/typescript/server/src/server/router/api/v1/users/_userID/integrations/fervidex/router.test.ts +++ /dev/null @@ -1,135 +0,0 @@ -import type { UserDocument } from "tachi-common"; - -import db from "#services/mongo/db"; -import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import t from "tap"; - -t.test("GET /api/v1/users/:userID/integrations/fervidex/settings", async (t) => { - t.beforeEach(ResetDBState); - - const cookie = await CreateFakeAuthCookie(mockApi); - - t.test("Should return null if this user has no settings set.", async (t) => { - await db["fer-settings"].remove({}); - - const res = await mockApi - .get("/api/v1/users/1/integrations/fervidex/settings") - .set("Cookie", cookie); - - t.equal(res.statusCode, 200); - - t.equal(res.body.body, null); - - t.end(); - }); - - t.test("Should return this users settings if they have them.", async (t) => { - await db["fer-settings"].update({ userID: 1 }, { $set: { cards: ["foo", "bar"] } }); - - const res = await mockApi - .get("/api/v1/users/1/integrations/fervidex/settings") - .set("Cookie", cookie); - - t.equal(res.statusCode, 200); - - t.strictSame(res.body.body, { - userID: 1, - forceStaticImport: false, - cards: ["foo", "bar"], - }); - - t.end(); - }); - - t.test("Must require self-key level authentication.", async (t) => { - const res = await mockApi.get("/api/v1/users/1/integrations/fervidex/settings"); - - t.equal(res.statusCode, 401); - - const res2 = await mockApi - .get("/api/v1/users/1/integrations/fervidex/settings") - .set("Authorization", "Bearer fake_api_token"); - - t.equal(res2.statusCode, 403); - - // insert a fake user doc so this doesn't 404 - await db.users.insert({ - id: 2, - username: "foo", - usernameLowercase: "foo", - } as UserDocument); - - const res3 = await mockApi - .get("/api/v1/users/2/integrations/fervidex/settings") - .set("Cookie", cookie); - - t.equal(res3.statusCode, 403); - - t.end(); - }); - - t.end(); -}); - -t.test("PATCH /api/v1/users/:userID/integrations/fervidex/settings", async (t) => { - t.beforeEach(ResetDBState); - - const cookie = await CreateFakeAuthCookie(mockApi); - - t.test("Should update a users settings.", async (t) => { - const res = await mockApi - .patch("/api/v1/users/1/integrations/fervidex/settings") - .send({ - cards: ["foo", "bar"], - }) - .set("Cookie", cookie); - - t.strictSame(res.body.body.cards, ["foo", "bar"]); - - const dbRes = await db["fer-settings"].findOne({ userID: 1 }); - - t.strictSame(dbRes?.cards, ["foo", "bar"]); - - t.end(); - }); - - t.test("Should insert a setting filter document if one doesn't exist.", async (t) => { - await db["fer-settings"].remove({}); - - const res = await mockApi - .patch("/api/v1/users/1/integrations/fervidex/settings") - .send({ - cards: ["foo", "bar"], - }) - .set("Cookie", cookie); - - t.strictSame(res.body.body.cards, ["foo", "bar"]); - - const dbRes = await db["fer-settings"].findOne({ userID: 1 }); - - t.strictSame(dbRes?.cards, ["foo", "bar"]); - - t.end(); - }); - - t.test("Should null settings if null is provided.", async (t) => { - const res = await mockApi - .patch("/api/v1/users/1/integrations/fervidex/settings") - .send({ - cards: null, - }) - .set("Cookie", cookie); - - t.strictSame(res.body.body.cards, null); - - const dbRes = await db["fer-settings"].findOne({ userID: 1 }); - - t.strictSame(dbRes?.cards, null); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1/users/_userID/integrations/fervidex/router.ts b/typescript/server/src/server/router/api/v1/users/_userID/integrations/fervidex/router.ts index fa1f54813..4c6d517b3 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/integrations/fervidex/router.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/integrations/fervidex/router.ts @@ -1,6 +1,6 @@ import prValidate from "#server/middleware/prudence-validate"; import { RequireKamaitachi } from "#server/middleware/type-require"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { DeleteUndefinedProps } from "#utils/misc"; import { optNull } from "#utils/prudence"; import { GetTachiData } from "#utils/req-tachi-data"; @@ -21,7 +21,7 @@ router.use(RequireSelfRequestFromUser); router.get("/settings", async (req, res) => { const user = GetTachiData(req, "requestedUser"); - const settingsDoc = await db["fer-settings"].findOne({ + const settingsDoc = await MONGODB_KILL["fer-settings"].findOne({ userID: user.id, }); @@ -69,7 +69,7 @@ router.patch( }); } - await db["fer-settings"].update( + await MONGODB_KILL["fer-settings"].update( { userID: user.id }, { $set: modifyDocument, @@ -79,7 +79,7 @@ router.patch( }, ); - const settings = await db["fer-settings"].findOne({ userID: user.id }); + const settings = await MONGODB_KILL["fer-settings"].findOne({ userID: user.id }); return res.status(200).json({ success: true, diff --git a/typescript/server/src/server/router/api/v1/users/_userID/integrations/kai/_kaiType/middleware.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/integrations/kai/_kaiType/middleware.oldtest.ts similarity index 100% rename from typescript/server/src/server/router/api/v1/users/_userID/integrations/kai/_kaiType/middleware.test.ts rename to typescript/server/src/server/router/api/v1/users/_userID/integrations/kai/_kaiType/middleware.oldtest.ts diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/kai/_kaiType/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/integrations/kai/_kaiType/router.oldtest.ts similarity index 91% rename from typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/kai/_kaiType/router.test.ts rename to typescript/server/src/server/router/api/v1/users/_userID/integrations/kai/_kaiType/router.oldtest.ts index bce471d4b..e2a74ef26 100644 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/kai/_kaiType/router.test.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/integrations/kai/_kaiType/router.oldtest.ts @@ -1,4 +1,4 @@ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; @@ -19,7 +19,7 @@ t.test("GET /api/v1/users/:userID/integrations/kai/:kaiType", async (t) => { }); t.test("Should return true if authed with this kaiType", async (t) => { - await db["kai-auth-tokens"].insert({ + await MONGODB_KILL["kai-auth-tokens"].insert({ refreshToken: "refresh", service: "FLO", token: "bar", @@ -35,7 +35,7 @@ t.test("GET /api/v1/users/:userID/integrations/kai/:kaiType", async (t) => { }); t.test("Should return the auth status of this user specifically.", async (t) => { - await db["kai-auth-tokens"].insert([ + await MONGODB_KILL["kai-auth-tokens"].insert([ { refreshToken: "refresh", service: "EAG", diff --git a/typescript/server/src/server/router/api/v1/users/_userID/integrations/kai/_kaiType/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/integrations/kai/_kaiType/router.test.ts deleted file mode 100644 index bce471d4b..000000000 --- a/typescript/server/src/server/router/api/v1/users/_userID/integrations/kai/_kaiType/router.test.ts +++ /dev/null @@ -1,65 +0,0 @@ -import db from "#services/mongo/db"; -import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import t from "tap"; - -t.test("GET /api/v1/users/:userID/integrations/kai/:kaiType", async (t) => { - t.beforeEach(ResetDBState); - - const cookie = await CreateFakeAuthCookie(mockApi); - - t.test("Should return false if unauthed with this kaiType", async (t) => { - const res = await mockApi.get("/api/v1/users/1/integrations/kai/flo").set("Cookie", cookie); - - t.equal(res.statusCode, 200); - t.equal(res.body.body.authStatus, false); - - t.end(); - }); - - t.test("Should return true if authed with this kaiType", async (t) => { - await db["kai-auth-tokens"].insert({ - refreshToken: "refresh", - service: "FLO", - token: "bar", - userID: 1, - }); - - const res = await mockApi.get("/api/v1/users/1/integrations/kai/flo").set("Cookie", cookie); - - t.equal(res.statusCode, 200); - t.equal(res.body.body.authStatus, true); - - t.end(); - }); - - t.test("Should return the auth status of this user specifically.", async (t) => { - await db["kai-auth-tokens"].insert([ - { - refreshToken: "refresh", - service: "EAG", - token: "bar", - userID: 1, - }, - { - refreshToken: "refresh", - service: "FLO", - token: "baz", - userID: 2, - }, - ]); - - const res = await mockApi.get("/api/v1/users/1/integrations/kai/flo").set("Cookie", cookie); - - t.equal(res.statusCode, 200); - t.equal(res.body.body.authStatus, false); - - t.end(); - }); - - t.end(); -}); - -// test is currently undoable due to issues with mocking out real Fetch calls. -t.todo("PATCH /api/v1/users/:userID/integrations/kai/:kaiType/oauth2callback"); diff --git a/typescript/server/src/server/router/api/v1/users/_userID/integrations/kai/_kaiType/router.ts b/typescript/server/src/server/router/api/v1/users/_userID/integrations/kai/_kaiType/router.ts index 310e7ca9e..adb74288b 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/integrations/kai/_kaiType/router.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/integrations/kai/_kaiType/router.ts @@ -5,7 +5,7 @@ import { } from "#lib/score-import/import-types/common/api-kai/utils"; import prValidate from "#server/middleware/prudence-validate"; import { RequireKamaitachi } from "#server/middleware/type-require"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import fetch from "#utils/fetch"; import { NotNullish } from "#utils/misc"; import { GetKaiAuth, RevokeKaiAuth } from "#utils/queries/auth"; @@ -189,7 +189,7 @@ router.post( const j = json as { access_token: string; refresh_token: string }; - await db["kai-auth-tokens"].update( + await MONGODB_KILL["kai-auth-tokens"].update( { userID: user.id, service: kaiType, diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/kshook-sv6c/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/integrations/kshook-sv6c/router.oldtest.ts similarity index 87% rename from typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/kshook-sv6c/router.test.ts rename to typescript/server/src/server/router/api/v1/users/_userID/integrations/kshook-sv6c/router.oldtest.ts index a09d5aa4c..efc6fce1f 100644 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/kshook-sv6c/router.test.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/integrations/kshook-sv6c/router.oldtest.ts @@ -1,6 +1,6 @@ import type { UserDocument } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; @@ -12,7 +12,7 @@ t.test("GET /api/v1/users/:userID/integrations/kshook-sv6c/settings", async (t) const cookie = await CreateFakeAuthCookie(mockApi); t.test("Should return null if this user has no settings set.", async (t) => { - await db["kshook-sv6c-settings"].remove({}); + await MONGODB_KILL["kshook-sv6c-settings"].remove({}); const res = await mockApi .get("/api/v1/users/1/integrations/kshook-sv6c/settings") @@ -52,7 +52,7 @@ t.test("GET /api/v1/users/:userID/integrations/kshook-sv6c/settings", async (t) t.equal(res2.statusCode, 403); // insert a fake user doc so this doesn't 404 - await db.users.insert({ + await MONGODB_KILL.users.insert({ id: 2, username: "foo", usernameLowercase: "foo", @@ -83,7 +83,7 @@ t.test("PATCH /api/v1/users/:userID/integrations/kshook-sv6c/settings", async (t t.strictSame(res.body.body.forceStaticImport, true); - const dbRes = await db["kshook-sv6c-settings"].findOne({ userID: 1 }); + const dbRes = await MONGODB_KILL["kshook-sv6c-settings"].findOne({ userID: 1 }); t.strictSame(dbRes?.forceStaticImport, true); @@ -91,7 +91,7 @@ t.test("PATCH /api/v1/users/:userID/integrations/kshook-sv6c/settings", async (t }); t.test("Should insert a setting filter document if one doesn't exist.", async (t) => { - await db["kshook-sv6c-settings"].remove({}); + await MONGODB_KILL["kshook-sv6c-settings"].remove({}); const res = await mockApi .patch("/api/v1/users/1/integrations/kshook-sv6c/settings") @@ -100,7 +100,7 @@ t.test("PATCH /api/v1/users/:userID/integrations/kshook-sv6c/settings", async (t t.strictSame(res.body.body.forceStaticImport, true); - const dbRes = await db["kshook-sv6c-settings"].findOne({ userID: 1 }); + const dbRes = await MONGODB_KILL["kshook-sv6c-settings"].findOne({ userID: 1 }); t.strictSame(dbRes?.forceStaticImport, true); diff --git a/typescript/server/src/server/router/api/v1/users/_userID/integrations/kshook-sv6c/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/integrations/kshook-sv6c/router.test.ts deleted file mode 100644 index a09d5aa4c..000000000 --- a/typescript/server/src/server/router/api/v1/users/_userID/integrations/kshook-sv6c/router.test.ts +++ /dev/null @@ -1,111 +0,0 @@ -import type { UserDocument } from "tachi-common"; - -import db from "#services/mongo/db"; -import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import t from "tap"; - -t.test("GET /api/v1/users/:userID/integrations/kshook-sv6c/settings", async (t) => { - t.beforeEach(ResetDBState); - - const cookie = await CreateFakeAuthCookie(mockApi); - - t.test("Should return null if this user has no settings set.", async (t) => { - await db["kshook-sv6c-settings"].remove({}); - - const res = await mockApi - .get("/api/v1/users/1/integrations/kshook-sv6c/settings") - .set("Cookie", cookie); - - t.equal(res.statusCode, 200); - - t.equal(res.body.body, null); - - t.end(); - }); - - t.test("Should return this users settings if they have them.", async (t) => { - const res = await mockApi - .get("/api/v1/users/1/integrations/kshook-sv6c/settings") - .set("Cookie", cookie); - - t.equal(res.statusCode, 200); - - t.strictSame(res.body.body, { - userID: 1, - forceStaticImport: true, - }); - - t.end(); - }); - - t.test("Must require self-key level authentication.", async (t) => { - const res = await mockApi.get("/api/v1/users/1/integrations/kshook-sv6c/settings"); - - t.equal(res.statusCode, 401); - - const res2 = await mockApi - .get("/api/v1/users/1/integrations/kshook-sv6c/settings") - .set("Authorization", "Bearer fake_api_token"); - - t.equal(res2.statusCode, 403); - - // insert a fake user doc so this doesn't 404 - await db.users.insert({ - id: 2, - username: "foo", - usernameLowercase: "foo", - } as UserDocument); - - const res3 = await mockApi - .get("/api/v1/users/2/integrations/kshook-sv6c/settings") - .set("Cookie", cookie); - - t.equal(res3.statusCode, 403); - - t.end(); - }); - - t.end(); -}); - -t.test("PATCH /api/v1/users/:userID/integrations/kshook-sv6c/settings", async (t) => { - t.beforeEach(ResetDBState); - - const cookie = await CreateFakeAuthCookie(mockApi); - - t.test("Should update a users settings.", async (t) => { - const res = await mockApi - .patch("/api/v1/users/1/integrations/kshook-sv6c/settings") - .send({ forceStaticImport: true }) - .set("Cookie", cookie); - - t.strictSame(res.body.body.forceStaticImport, true); - - const dbRes = await db["kshook-sv6c-settings"].findOne({ userID: 1 }); - - t.strictSame(dbRes?.forceStaticImport, true); - - t.end(); - }); - - t.test("Should insert a setting filter document if one doesn't exist.", async (t) => { - await db["kshook-sv6c-settings"].remove({}); - - const res = await mockApi - .patch("/api/v1/users/1/integrations/kshook-sv6c/settings") - .send({ forceStaticImport: true }) - .set("Cookie", cookie); - - t.strictSame(res.body.body.forceStaticImport, true); - - const dbRes = await db["kshook-sv6c-settings"].findOne({ userID: 1 }); - - t.strictSame(dbRes?.forceStaticImport, true); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1/users/_userID/integrations/kshook-sv6c/router.ts b/typescript/server/src/server/router/api/v1/users/_userID/integrations/kshook-sv6c/router.ts index a73afcb08..dbe0bdeca 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/integrations/kshook-sv6c/router.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/integrations/kshook-sv6c/router.ts @@ -1,6 +1,6 @@ import prValidate from "#server/middleware/prudence-validate"; import { RequireKamaitachi } from "#server/middleware/type-require"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetTachiData } from "#utils/req-tachi-data"; import { Router } from "express"; @@ -19,7 +19,7 @@ router.use(RequireSelfRequestFromUser); router.get("/settings", async (req, res) => { const user = GetTachiData(req, "requestedUser"); - const settingsDoc = await db["kshook-sv6c-settings"].findOne({ + const settingsDoc = await MONGODB_KILL["kshook-sv6c-settings"].findOne({ userID: user.id, }); @@ -44,7 +44,7 @@ router.patch("/settings", prValidate({ forceStaticImport: "boolean" }), async (r const user = GetTachiData(req, "requestedUser"); - await db["kshook-sv6c-settings"].update( + await MONGODB_KILL["kshook-sv6c-settings"].update( { userID: user.id }, { $set: { @@ -56,7 +56,7 @@ router.patch("/settings", prValidate({ forceStaticImport: "boolean" }), async (r }, ); - const settings = await db["kshook-sv6c-settings"].findOne({ userID: user.id }); + const settings = await MONGODB_KILL["kshook-sv6c-settings"].findOne({ userID: user.id }); return res.status(200).json({ success: true, diff --git a/typescript/server/src/server/router/api/v1/users/_userID/integrations/myt/router.ts b/typescript/server/src/server/router/api/v1/users/_userID/integrations/myt/router.ts index b4365f73a..6cfbcf578 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/integrations/myt/router.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/integrations/myt/router.ts @@ -2,7 +2,7 @@ import type { MytCardInfo } from "tachi-common"; import prValidate from "#server/middleware/prudence-validate"; import { RequireKamaitachi } from "#server/middleware/type-require"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetTachiData } from "#utils/req-tachi-data"; import { Router } from "express"; import { p } from "prudence"; @@ -22,7 +22,7 @@ router.use(RequireSelfRequestFromUser); router.get("/", async (req, res) => { const user = GetTachiData(req, "requestedUser"); - const cardInfo = await db["myt-card-info"].findOne({ + const cardInfo = await MONGODB_KILL["myt-card-info"].findOne({ userID: user.id, }); @@ -62,7 +62,7 @@ router.put( cardAccessCode, }; - await db["myt-card-info"].update( + await MONGODB_KILL["myt-card-info"].update( { userID: user.id }, { $set: newCardInfo }, { @@ -87,7 +87,7 @@ router.put( router.delete("/", async (req, res) => { const user = GetTachiData(req, "requestedUser"); - await db["cg-card-info"].remove({ userID: user.id }); + await MONGODB_KILL["cg-card-info"].remove({ userID: user.id }); return res.status(200).json({ success: true, diff --git a/typescript/server/src/server/router/api/v1/users/_userID/invites/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/invites/router.oldtest.ts similarity index 89% rename from typescript/server/src/server/router/api/v1/users/_userID/invites/router.test.ts rename to typescript/server/src/server/router/api/v1/users/_userID/invites/router.oldtest.ts index 671a27cf2..e50c55a43 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/invites/router.test.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/invites/router.oldtest.ts @@ -1,5 +1,5 @@ import { ONE_MONTH } from "#lib/constants/time"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; @@ -12,7 +12,7 @@ t.test("GET /api/v1/users/:userID/invites", async (t) => { const cookie = await CreateFakeAuthCookie(mockApi); t.test("Should return all of this users created invites and who used them.", async (t) => { - await db.users.insert({ + await MONGODB_KILL.users.insert({ id: 2, username: "other_dude", usernameLowercase: "other_dude", @@ -81,7 +81,10 @@ t.test("GET /api/v1/users/:userID/invites/limit", async (t) => { // because this thing is time-based, we need to make sure // this is always relative to right now. - await db.users.update({ id: 1 }, { $set: { joinDate: Date.now() - ONE_MONTH * 2.5 } }); + await MONGODB_KILL.users.update( + { id: 1 }, + { $set: { joinDate: Date.now() - ONE_MONTH * 2.5 } }, + ); const res = await mockApi.get("/api/v1/users/1/invites/limit").set("Cookie", cookie); t.equal(res.body.body.invites, 2); @@ -113,13 +116,16 @@ t.test("POST /api/v1/users/:userID/invites/create", async (t) => { const cookie = await CreateFakeAuthCookie(mockApi); t.test("Should create a new invite.", async (t) => { - await db.users.update({ id: 1 }, { $set: { joinDate: Date.now() - ONE_MONTH * 2.5 } }); + await MONGODB_KILL.users.update( + { id: 1 }, + { $set: { joinDate: Date.now() - ONE_MONTH * 2.5 } }, + ); const res = await mockApi.post("/api/v1/users/1/invites/create").set("Cookie", cookie); t.equal(res.statusCode, 200); - const dbRes = await db.invites.findOne({ + const dbRes = await MONGODB_KILL.invites.findOne({ code: res.body.body.code, }); @@ -129,11 +135,11 @@ t.test("POST /api/v1/users/:userID/invites/create", async (t) => { }); t.test("Should honor invite limit.", async (t) => { - await db.invites.remove({}); + await MONGODB_KILL.invites.remove({}); // users with less than a month of life in them dont get invites, // so this will force an invite limit honor. - await db.users.update( + await MONGODB_KILL.users.update( { id: 1 }, { $set: { joinDate: Date.now(), authLevel: UserAuthLevels.USER } }, ); diff --git a/typescript/server/src/server/router/api/v1/users/_userID/invites/router.ts b/typescript/server/src/server/router/api/v1/users/_userID/invites/router.ts index ee2240b86..bfba41e34 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/invites/router.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/invites/router.ts @@ -1,6 +1,6 @@ import { GetTotalAllowedInvites } from "#lib/invites/invites"; import { RequireInvitesEnabled } from "#server/middleware/type-require"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { Random20Hex } from "#utils/misc"; import { GetTachiData } from "#utils/req-tachi-data"; import { GetUsersWithIDs } from "#utils/user"; @@ -22,7 +22,7 @@ router.use(RequireSelfRequestFromUser); router.get("/", async (req, res) => { const user = GetTachiData(req, "requestedUser"); - const invites = await db.invites.find( + const invites = await MONGODB_KILL.invites.find( { createdBy: user.id, }, @@ -53,7 +53,7 @@ router.get("/", async (req, res) => { router.get("/limit", async (req, res) => { const user = GetTachiData(req, "requestedUser"); - const invites = await db.invites.count({ createdBy: user.id }); + const invites = await MONGODB_KILL.invites.count({ createdBy: user.id }); const limit = GetTotalAllowedInvites(user); return res.status(200).json({ @@ -76,18 +76,18 @@ router.post("/create", async (req, res) => { const userID = user.id; try { - const lockExists = await db["invite-locks"].findOne({ + const lockExists = await MONGODB_KILL["invite-locks"].findOne({ userID, }); if (!lockExists) { - await db["invite-locks"].insert({ + await MONGODB_KILL["invite-locks"].insert({ userID, locked: false, }); } - const isNotLocked = await db["invite-locks"].findOneAndUpdate( + const isNotLocked = await MONGODB_KILL["invite-locks"].findOneAndUpdate( { userID, locked: false, @@ -106,7 +106,7 @@ router.post("/create", async (req, res) => { }); } - const existingInvites = await db.invites.count({ createdBy: user.id }); + const existingInvites = await MONGODB_KILL.invites.count({ createdBy: user.id }); if ( existingInvites >= GetTotalAllowedInvites(user) && @@ -127,7 +127,7 @@ router.post("/create", async (req, res) => { createdBy: user.id, }; - await db.invites.insert(inviteDoc); + await MONGODB_KILL.invites.insert(inviteDoc); return res.status(200).json({ success: true, @@ -135,7 +135,7 @@ router.post("/create", async (req, res) => { body: inviteDoc, }); } finally { - await db["invite-locks"].findOneAndUpdate( + await MONGODB_KILL["invite-locks"].findOneAndUpdate( { userID, }, diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/notifications/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/notifications/router.oldtest.ts similarity index 92% rename from typescript/server/src/server/router/api/v1postgres/users/_userID/notifications/router.test.ts rename to typescript/server/src/server/router/api/v1/users/_userID/notifications/router.oldtest.ts index d7de7d3f5..eac1639f2 100644 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/notifications/router.test.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/notifications/router.oldtest.ts @@ -1,6 +1,6 @@ import type { NotificationDocument } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; import { mkFakeNotification, mkFakeUser } from "#test-utils/misc"; import mockApi from "#test-utils/mock-api"; @@ -11,12 +11,12 @@ t.test("GET /api/v1/users/:userID/notifications", async (t) => { t.beforeEach(ResetDBState); t.beforeEach(() => Promise.all([ - db.notifications.insert([ + MONGODB_KILL.notifications.insert([ mkFakeNotification({ notifID: "read", read: true, sentAt: 2 }), mkFakeNotification({ notifID: "unread", read: false, sentAt: 3 }), mkFakeNotification({ notifID: "not_ours", sentTo: 2, sentAt: 4 }), ]), - db.users.insert(mkFakeUser(2)), + MONGODB_KILL.users.insert(mkFakeUser(2)), ]), ); @@ -69,13 +69,13 @@ t.test("POST /api/v1/users/:userID/notifications/delete-all", async (t) => { t.beforeEach(ResetDBState); t.beforeEach(() => Promise.all([ - db.notifications.insert([ + MONGODB_KILL.notifications.insert([ mkFakeNotification({ notifID: "read", read: true, sentAt: 2 }), mkFakeNotification({ notifID: "unread", read: false, sentAt: 3 }), mkFakeNotification({ notifID: "unread_2", read: false, sentAt: 4 }), mkFakeNotification({ notifID: "not_ours", sentTo: 2, sentAt: 5 }), ]), - db.users.insert(mkFakeUser(2)), + MONGODB_KILL.users.insert(mkFakeUser(2)), ]), ); @@ -90,7 +90,7 @@ t.test("POST /api/v1/users/:userID/notifications/delete-all", async (t) => { t.strictSame(res.body.body, {}, "Should have no response body."); - const dbRes = await db.notifications.findOne({ + const dbRes = await MONGODB_KILL.notifications.findOne({ sentTo: 1, read: false, }); @@ -135,13 +135,13 @@ t.test("POST /api/v1/users/:userID/notifications/delete-all", async (t) => { t.beforeEach(ResetDBState); t.beforeEach(() => Promise.all([ - db.notifications.insert([ + MONGODB_KILL.notifications.insert([ mkFakeNotification({ notifID: "read", read: true, sentAt: 2 }), mkFakeNotification({ notifID: "unread", read: false, sentAt: 3 }), mkFakeNotification({ notifID: "unread_2", read: false, sentAt: 4 }), mkFakeNotification({ notifID: "not_ours", sentTo: 2, sentAt: 5 }), ]), - db.users.insert(mkFakeUser(2)), + MONGODB_KILL.users.insert(mkFakeUser(2)), ]), ); @@ -156,7 +156,7 @@ t.test("POST /api/v1/users/:userID/notifications/delete-all", async (t) => { t.strictSame(res.body.body, {}, "Should have no response body."); - const dbRes = await db.notifications.findOne({ + const dbRes = await MONGODB_KILL.notifications.findOne({ sentTo: 1, }); diff --git a/typescript/server/src/server/router/api/v1/users/_userID/notifications/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/notifications/router.test.ts deleted file mode 100644 index d7de7d3f5..000000000 --- a/typescript/server/src/server/router/api/v1/users/_userID/notifications/router.test.ts +++ /dev/null @@ -1,197 +0,0 @@ -import type { NotificationDocument } from "tachi-common"; - -import db from "#services/mongo/db"; -import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; -import { mkFakeNotification, mkFakeUser } from "#test-utils/misc"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import t from "tap"; - -t.test("GET /api/v1/users/:userID/notifications", async (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(() => - Promise.all([ - db.notifications.insert([ - mkFakeNotification({ notifID: "read", read: true, sentAt: 2 }), - mkFakeNotification({ notifID: "unread", read: false, sentAt: 3 }), - mkFakeNotification({ notifID: "not_ours", sentTo: 2, sentAt: 4 }), - ]), - db.users.insert(mkFakeUser(2)), - ]), - ); - - const cookie = await CreateFakeAuthCookie(mockApi); - - t.test("Should return all of this user's notifications, read or unread.", async (t) => { - const res = await mockApi.get("/api/v1/users/1/notifications").set("Cookie", cookie); - - t.equal(res.statusCode, 200, "Should return 200"); - - t.strictSame( - res.body.body.map((e: NotificationDocument) => e.notifID), - ["unread", "read"], - "Should return the exact notifications we expected (in most-recent order), and no more.", - ); - - t.end(); - }); - - t.test("Should return 401 if not authenticated.", async (t) => { - const res = await mockApi.get("/api/v1/users/1/notifications"); - - t.equal(res.statusCode, 401); - - t.end(); - }); - - t.test("Should return 403 if authed as someone else.", async (t) => { - const res = await mockApi.get("/api/v1/users/2/notifications").set("Cookie", cookie); - - t.equal(res.statusCode, 403); - - t.end(); - }); - - t.test("Should return 403 if authed as right user without self-key", async (t) => { - const res = await mockApi - .get("/api/v1/users/1/notifications") - .set("Authorization", "Bearer fake_api_token"); - - t.equal(res.statusCode, 403); - - t.end(); - }); - - t.end(); -}); - -t.test("POST /api/v1/users/:userID/notifications/delete-all", async (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(() => - Promise.all([ - db.notifications.insert([ - mkFakeNotification({ notifID: "read", read: true, sentAt: 2 }), - mkFakeNotification({ notifID: "unread", read: false, sentAt: 3 }), - mkFakeNotification({ notifID: "unread_2", read: false, sentAt: 4 }), - mkFakeNotification({ notifID: "not_ours", sentTo: 2, sentAt: 5 }), - ]), - db.users.insert(mkFakeUser(2)), - ]), - ); - - const cookie = await CreateFakeAuthCookie(mockApi); - - t.test("Should mark all of a user's notifications as read.", async (t) => { - const res = await mockApi - .post("/api/v1/users/1/notifications/mark-all-read") - .set("Cookie", cookie); - - t.equal(res.statusCode, 200, "Should return 200."); - - t.strictSame(res.body.body, {}, "Should have no response body."); - - const dbRes = await db.notifications.findOne({ - sentTo: 1, - read: false, - }); - - t.equal(dbRes, null, "Should not find any unread messages in the users inbox."); - - t.end(); - }); - - t.test("Should return 401 if not authenticated.", async (t) => { - const res = await mockApi.post("/api/v1/users/1/notifications/mark-all-read"); - - t.equal(res.statusCode, 401); - - t.end(); - }); - - t.test("Should return 403 if authed as someone else.", async (t) => { - const res = await mockApi - .post("/api/v1/users/2/notifications/mark-all-read") - .set("Cookie", cookie); - - t.equal(res.statusCode, 403); - - t.end(); - }); - - t.test("Should return 403 if authed as right user without self-key", async (t) => { - const res = await mockApi - .post("/api/v1/users/1/notifications/mark-all-read") - .set("Authorization", "Bearer fake_api_token"); - - t.equal(res.statusCode, 403); - - t.end(); - }); - - t.end(); -}); - -t.test("POST /api/v1/users/:userID/notifications/delete-all", async (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(() => - Promise.all([ - db.notifications.insert([ - mkFakeNotification({ notifID: "read", read: true, sentAt: 2 }), - mkFakeNotification({ notifID: "unread", read: false, sentAt: 3 }), - mkFakeNotification({ notifID: "unread_2", read: false, sentAt: 4 }), - mkFakeNotification({ notifID: "not_ours", sentTo: 2, sentAt: 5 }), - ]), - db.users.insert(mkFakeUser(2)), - ]), - ); - - const cookie = await CreateFakeAuthCookie(mockApi); - - t.test("Should empty a user's notification inbox.", async (t) => { - const res = await mockApi - .post("/api/v1/users/1/notifications/delete-all") - .set("Cookie", cookie); - - t.equal(res.statusCode, 200, "Should return 200."); - - t.strictSame(res.body.body, {}, "Should have no response body."); - - const dbRes = await db.notifications.findOne({ - sentTo: 1, - }); - - t.equal(dbRes, null, "Should not find any messages in the users inbox."); - - t.end(); - }); - - t.test("Should return 401 if not authenticated.", async (t) => { - const res = await mockApi.post("/api/v1/users/1/notifications/delete-all"); - - t.equal(res.statusCode, 401); - - t.end(); - }); - - t.test("Should return 403 if authed as someone else.", async (t) => { - const res = await mockApi - .post("/api/v1/users/2/notifications/delete-all") - .set("Cookie", cookie); - - t.equal(res.statusCode, 403); - - t.end(); - }); - - t.test("Should return 403 if authed as right user without self-key", async (t) => { - const res = await mockApi - .post("/api/v1/users/1/notifications/delete-all") - .set("Authorization", "Bearer fake_api_token"); - - t.equal(res.statusCode, 403); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1/users/_userID/notifications/router.ts b/typescript/server/src/server/router/api/v1/users/_userID/notifications/router.ts index b18a13898..fec60609d 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/notifications/router.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/notifications/router.ts @@ -1,5 +1,5 @@ import { ONE_SECOND } from "#lib/constants/time"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetTachiData } from "#utils/req-tachi-data"; import { Router } from "express"; @@ -19,7 +19,7 @@ router.use(RequireSelfRequestFromUser); router.get("/", async (req, res) => { const user = GetTachiData(req, "requestedUser"); - const notifs = await db.notifications.find( + const notifs = await MONGODB_KILL.notifications.find( { sentTo: user.id, }, @@ -45,7 +45,7 @@ router.get("/", async (req, res) => { router.post("/mark-all-read", async (req, res) => { const user = GetTachiData(req, "requestedUser"); - const updateRes = await db.notifications.update( + const updateRes = await MONGODB_KILL.notifications.update( { sentTo: user.id, @@ -80,7 +80,7 @@ router.post("/mark-all-read", async (req, res) => { router.post("/delete-all", async (req, res) => { const user = GetTachiData(req, "requestedUser"); - const deleted = await db.notifications.remove({ + const deleted = await MONGODB_KILL.notifications.remove({ sentTo: user.id, // See mark-all-read for an explanation of this behaviour. diff --git a/typescript/server/src/server/router/api/v1/users/_userID/pfp/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/pfp/router.oldtest.ts similarity index 90% rename from typescript/server/src/server/router/api/v1/users/_userID/pfp/router.test.ts rename to typescript/server/src/server/router/api/v1/users/_userID/pfp/router.oldtest.ts index dbea92d20..91eb4ef08 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/pfp/router.test.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/pfp/router.oldtest.ts @@ -1,6 +1,6 @@ import { CDNStoreOrOverwrite } from "#lib/cdn/cdn"; import { GetProfilePictureURL } from "#lib/cdn/url-format"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; import { GetKTDataBuffer } from "#test-utils/test-data"; @@ -33,7 +33,7 @@ t.test("GET /api/v1/users/:userID/pfp", (t) => { t.test("Should return a custom profile picture if one is set", async (t) => { await CDNStoreOrOverwrite(GetProfilePictureURL(1, "checksum"), "foo"); - await db.users.update({ id: 1 }, { $set: { customPfpLocation: "checksum" } }); + await MONGODB_KILL.users.update({ id: 1 }, { $set: { customPfpLocation: "checksum" } }); const res = await mockApi.get("/api/v1/users/1/pfp").redirects(1); t.equal(res.body.toString(), "foo"); @@ -65,7 +65,7 @@ t.test("PUT /api/v1/users/:userID/pfp", (t) => { }); t.test("Should set a profile picture if user has custom pfp", async (t) => { - await db.users.update({ id: 1 }, { $set: { customPfpLocation: "checksum" } }); + await MONGODB_KILL.users.update({ id: 1 }, { $set: { customPfpLocation: "checksum" } }); const img = GetKTDataBuffer("/images/acorn.png"); diff --git a/typescript/server/src/server/router/api/v1/users/_userID/pfp/router.ts b/typescript/server/src/server/router/api/v1/users/_userID/pfp/router.ts index cb891a368..f6c2e145c 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/pfp/router.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/pfp/router.ts @@ -4,7 +4,7 @@ import { ONE_MEGABYTE } from "#lib/constants/filesize"; import { log } from "#lib/log/log.js"; import { RequirePermissions } from "#server/middleware/auth"; import { CreateMulterSingleUploadMiddleware } from "#server/middleware/multer-upload"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { HashSHA256 } from "#utils/crypto"; import { GetTachiData } from "#utils/req-tachi-data"; import { FormatUserDoc } from "#utils/user"; @@ -68,7 +68,10 @@ router.put( req.session.tachi.user.customPfpLocation = contentHash; } - await db.users.update({ id: user.id }, { $set: { customPfpLocation: contentHash } }); + await MONGODB_KILL.users.update( + { id: user.id }, + { $set: { customPfpLocation: contentHash } }, + ); return res.status(200).json({ success: true, @@ -121,7 +124,7 @@ router.delete( await CDNDelete(GetProfilePictureURL(user.id, user.customPfpLocation)); - await db.users.update({ id: user.id }, { $set: { customPfpLocation: null } }); + await MONGODB_KILL.users.update({ id: user.id }, { $set: { customPfpLocation: null } }); return res.status(200).json({ success: true, diff --git a/typescript/server/src/server/router/api/v1/users/_userID/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/router.oldtest.ts similarity index 90% rename from typescript/server/src/server/router/api/v1/users/_userID/router.test.ts rename to typescript/server/src/server/router/api/v1/users/_userID/router.oldtest.ts index dbb15833e..da687af62 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/router.test.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/router.oldtest.ts @@ -1,7 +1,7 @@ import type { GameGroup, ImportDocument, ImportTypes, UserGameStats } from "tachi-common"; import { ONE_DAY, ONE_YEAR } from "#lib/constants/time"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; @@ -85,7 +85,7 @@ t.test("PATCH /api/v1/users/:userID", async (t) => { t.equal(res.statusCode, 401); // needs self key auth - await db["api-tokens"].insert({ + await MONGODB_KILL["api-tokens"].insert({ token: "noperm", permissions: {}, identifier: "No permissions token", @@ -115,7 +115,7 @@ t.test("PATCH /api/v1/users/:userID", async (t) => { t.equal(res.body.body.status, "Hello World!"); - const dbUser = await db.users.findOne({ id: 1 }); + const dbUser = await MONGODB_KILL.users.findOne({ id: 1 }); t.equal(dbUser?.status, "Hello World!"); @@ -129,7 +129,7 @@ t.test("PATCH /api/v1/users/:userID", async (t) => { t.equal(res.statusCode, 200); - const dbUser = await db.users.findOne({ id: 1 }); + const dbUser = await MONGODB_KILL.users.findOne({ id: 1 }); t.equal(dbUser?.about, ""); @@ -143,7 +143,7 @@ t.test("PATCH /api/v1/users/:userID", async (t) => { t.equal(res.statusCode, 400); - const dbUser = await db.users.findOne({ id: 1 }); + const dbUser = await MONGODB_KILL.users.findOne({ id: 1 }); t.equal(dbUser?.about, "test_user_not_real"); @@ -151,7 +151,7 @@ t.test("PATCH /api/v1/users/:userID", async (t) => { }); t.test("Shouldn't alter other properties.", async (t) => { - await db.users.update({ id: 1 }, { $set: { "socialMedia.discord": "foo#123" } }); + await MONGODB_KILL.users.update({ id: 1 }, { $set: { "socialMedia.discord": "foo#123" } }); const res = await mockApi.patch("/api/v1/users/1").set("Cookie", cookie).send({ status: "Hello World!", @@ -159,7 +159,7 @@ t.test("PATCH /api/v1/users/:userID", async (t) => { t.equal(res.body.body.status, "Hello World!"); - const dbUser = await db.users.findOne({ id: 1 }); + const dbUser = await MONGODB_KILL.users.findOne({ id: 1 }); t.equal(dbUser?.status, "Hello World!"); t.equal(dbUser?.about, "test_user_not_real"); @@ -175,7 +175,7 @@ t.test("PATCH /api/v1/users/:userID", async (t) => { t.equal(res.body.body.socialMedia.twitter, "zkldi"); - const dbUser = await db.users.findOne({ id: 1 }); + const dbUser = await MONGODB_KILL.users.findOne({ id: 1 }); t.equal(dbUser?.socialMedia.twitter, "zkldi"); @@ -189,7 +189,7 @@ t.test("PATCH /api/v1/users/:userID", async (t) => { t.equal(res.body.body.socialMedia.youtube, "zkldi"); - const dbUser = await db.users.findOne({ id: 1 }); + const dbUser = await MONGODB_KILL.users.findOne({ id: 1 }); t.equal(dbUser?.socialMedia.youtube, "zkldi"); @@ -199,7 +199,7 @@ t.test("PATCH /api/v1/users/:userID", async (t) => { t.equal(res2.body.body.socialMedia.youtube, "zkldi"); - const dbUser2 = await db.users.findOne({ id: 1 }); + const dbUser2 = await MONGODB_KILL.users.findOne({ id: 1 }); t.equal(dbUser2?.socialMedia.youtube, "zkldi"); @@ -209,7 +209,7 @@ t.test("PATCH /api/v1/users/:userID", async (t) => { t.equal(res3.body.body.socialMedia.youtube, "zkldi"); - const dbUser3 = await db.users.findOne({ id: 1 }); + const dbUser3 = await MONGODB_KILL.users.findOne({ id: 1 }); t.equal(dbUser3?.socialMedia.youtube, "zkldi"); @@ -223,7 +223,7 @@ t.test("PATCH /api/v1/users/:userID", async (t) => { t.equal(res.body.body.socialMedia.github, "zkldi"); - const dbUser = await db.users.findOne({ id: 1 }); + const dbUser = await MONGODB_KILL.users.findOne({ id: 1 }); t.equal(dbUser?.socialMedia.github, "zkldi"); @@ -237,7 +237,7 @@ t.test("PATCH /api/v1/users/:userID", async (t) => { t.equal(res.body.body.socialMedia.twitch, "zkldi"); - const dbUser = await db.users.findOne({ id: 1 }); + const dbUser = await MONGODB_KILL.users.findOne({ id: 1 }); t.equal(dbUser?.socialMedia.twitch, "zkldi"); @@ -251,7 +251,7 @@ t.test("PATCH /api/v1/users/:userID", async (t) => { t.equal(res.body.body.socialMedia.steam, "zkldi"); - const dbUser = await db.users.findOne({ id: 1 }); + const dbUser = await MONGODB_KILL.users.findOne({ id: 1 }); t.equal(dbUser?.socialMedia.steam, "zkldi"); @@ -317,7 +317,7 @@ t.test("POST /api/v1/users/:userID/change-password", async (t) => { t.equal(res.statusCode, 200); - const newPrivateInfo = await db["user-private-information"].findOne({ + const newPrivateInfo = await MONGODB_KILL["user-private-information"].findOne({ userID: 1, }); @@ -338,7 +338,7 @@ t.test("GET /api/v1/users/:userID/game-stats", (t) => { t.beforeEach(ResetDBState); t.test("Should return all of a user's game stats.", async (t) => { - await db["game-stats"].remove({}); + await MONGODB_KILL["game-stats"].remove({}); const stats: Array = [ { @@ -370,7 +370,7 @@ t.test("GET /api/v1/users/:userID/game-stats", (t) => { }, ]; - await db["game-stats"].insert(stats); + await MONGODB_KILL["game-stats"].insert(stats); const res = await mockApi.get("/api/v1/users/test_zkldi/game-stats"); @@ -422,7 +422,7 @@ t.test("GET /api/v1/users/:userID/recent-imports", (t) => { } t.test("Should work cross game and ignore imports without userIntent.", async (t) => { - await db.imports.insert([ + await MONGODB_KILL.imports.insert([ mkImport("iidx", "api/flo-iidx", Date.now() - ONE_DAY, true), mkImport("iidx", "api/flo-iidx", Date.now() - ONE_DAY, true), mkImport("sdvx", "api/flo-sdvx", Date.now() - ONE_DAY, false), @@ -449,7 +449,7 @@ t.test("GET /api/v1/users/:userID/recent-imports", (t) => { }); t.test("Should ignore imports that were too long ago.", async (t) => { - await db.imports.insert([ + await MONGODB_KILL.imports.insert([ mkImport("iidx", "api/flo-iidx", Date.now() - ONE_DAY, true), mkImport("iidx", "ir/fervidex", Date.now() - ONE_YEAR, true), mkImport("iidx", "ir/fervidex", Date.now() - ONE_YEAR, true), diff --git a/typescript/server/src/server/router/api/v1/users/_userID/router.ts b/typescript/server/src/server/router/api/v1/users/_userID/router.ts index 311bdf6d6..7c3fb69d0 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/router.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/router.ts @@ -14,7 +14,7 @@ import { log } from "#lib/log/log.js"; import { GetRivalIDs } from "#lib/rivals/rivals"; import { ServerConfig } from "#lib/setup/config"; import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { DeleteUndefinedProps, IsNonEmptyString, Random20Hex, StripUrl } from "#utils/misc"; import { optNullFluffStrField } from "#utils/prudence"; import { @@ -187,7 +187,7 @@ router.patch( DeleteUndefinedProps(modifyObject); - await db.users.update( + await MONGODB_KILL.users.update( { id: user.id, }, @@ -238,7 +238,7 @@ router.get("/game-stats", async (req, res) => { { __rankingData?: Record; } & UserGameStats - > = await db["game-stats"].find({ userID: user.id }); + > = await MONGODB_KILL["game-stats"].find({ userID: user.id }); await Promise.all( stats.map(async (s) => { @@ -301,7 +301,7 @@ router.get("/recent-summary", async (req, res) => { router.get("/is-email-verified", RequireSelfRequestFromUser, async (req, res) => { const user = GetUser(req); - const verifyInfo = await db["verify-email-codes"].findOne({ + const verifyInfo = await MONGODB_KILL["verify-email-codes"].findOne({ userID: user.id, }); @@ -328,7 +328,7 @@ router.get("/is-email-verified", RequireSelfRequestFromUser, async (req, res) => router.get("/email", RequireSelfRequestFromUser, async (req, res) => { const user = GetUser(req); - const email = await db["user-private-information"].findOne({ + const email = await MONGODB_KILL["user-private-information"].findOne({ userID: user.id, }); @@ -368,7 +368,7 @@ router.post( email: string; }; - const privateInfo = await db["user-private-information"].findOne({ + const privateInfo = await MONGODB_KILL["user-private-information"].findOne({ userID: user.id, }); @@ -401,7 +401,7 @@ router.post( log.info(`User ${user.id} changed email from ${privateInfo.email} to ${body.email}`); - await db["user-private-information"].update( + await MONGODB_KILL["user-private-information"].update( { userID: user.id, }, @@ -416,11 +416,11 @@ router.post( const resetEmailCode = Random20Hex(); // clear out the previous email code! - await db["verify-email-codes"].remove({ + await MONGODB_KILL["verify-email-codes"].remove({ userID: user.id, }); - await db["verify-email-codes"].insert({ + await MONGODB_KILL["verify-email-codes"].insert({ code: resetEmailCode, userID: user.id, email: body.email, @@ -476,7 +476,7 @@ router.post( }); } - const privateInfo = await db["user-private-information"].findOne({ + const privateInfo = await MONGODB_KILL["user-private-information"].findOne({ userID: user.id, }); @@ -503,7 +503,7 @@ router.post( const newPasswordHash = await HashPassword(body["!password"]); - await db["user-private-information"].update( + await MONGODB_KILL["user-private-information"].update( { userID: user.id, }, @@ -566,7 +566,7 @@ router.post( }); } - const privateInfo = await db["user-private-information"].findOne({ + const privateInfo = await MONGODB_KILL["user-private-information"].findOne({ userID: user.id, }); @@ -607,7 +607,7 @@ router.post( }); } - await db.users.update( + await MONGODB_KILL.users.update( { id: user.id, }, @@ -619,7 +619,7 @@ router.post( }, ); - await db["user-name-changes"].insert({ + await MONGODB_KILL["user-name-changes"].insert({ userID: user.id, username: body.newUsername, timestamp: Date.now(), @@ -682,24 +682,25 @@ router.get("/last-username-change", async (req, res) => { router.get("/recent-imports", async (req, res) => { const user = GetUser(req); - const recentImports: Array<{ _id: ImportTypes; count: integer }> = await db.imports.aggregate([ - { - $match: { - userID: user.id, - timeFinished: { $gt: Date.now() - ONE_MONTH }, - userIntent: true, - importType: { - $nin: ["file/mypagescraper-records-csv", "file/mypagescraper-player-csv"], + const recentImports: Array<{ _id: ImportTypes; count: integer }> = + await MONGODB_KILL.imports.aggregate([ + { + $match: { + userID: user.id, + timeFinished: { $gt: Date.now() - ONE_MONTH }, + userIntent: true, + importType: { + $nin: ["file/mypagescraper-records-csv", "file/mypagescraper-player-csv"], + }, }, }, - }, - { - $group: { - _id: "$importType", - count: { $sum: 1 }, + { + $group: { + _id: "$importType", + count: { $sum: 1 }, + }, }, - }, - ]); + ]); // rename _id to importType. const imports = recentImports.map((e) => ({ importType: e._id, count: e.count })); @@ -719,8 +720,8 @@ router.get("/recent-imports", async (req, res) => { router.get("/stats", async (req, res) => { const user = GetUser(req); - const scoreCount = await db.scores.count({ userID: user.id }); - const sessionCount = await db.sessions.count({ userID: user.id }); + const scoreCount = await MONGODB_KILL.scores.count({ userID: user.id }); + const sessionCount = await MONGODB_KILL.sessions.count({ userID: user.id }); return res.status(200).json({ success: true, @@ -761,13 +762,13 @@ router.get( const user = GetUser(req); - const gpts = await db["game-stats"].find({ + const gpts = await MONGODB_KILL["game-stats"].find({ userID: user.id, }); const data: Partial> = {}; - const settings = await db["user-settings"].findOne({ userID: user.id }); + const settings = await MONGODB_KILL["user-settings"].findOne({ userID: user.id }); if (!settings) { log.error(`User ${FormatUserDoc(user)} doesn't have any settings?`); diff --git a/typescript/server/src/server/router/api/v1/users/_userID/sessions/router.ts b/typescript/server/src/server/router/api/v1/users/_userID/sessions/router.ts index da499dbab..edacec57b 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/sessions/router.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/sessions/router.ts @@ -1,4 +1,4 @@ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetUser } from "#utils/req-tachi-data"; import { Router } from "express"; @@ -14,7 +14,7 @@ const router: Router = Router({ mergeParams: true }); router.get("/calendar", async (req, res) => { const user = GetUser(req); - const sessions = await db.sessions.find( + const sessions = await MONGODB_KILL.sessions.find( { userID: user.id }, { projection: { diff --git a/typescript/server/src/server/router/api/v1/users/_userID/settings/router.test.ts b/typescript/server/src/server/router/api/v1/users/_userID/settings/router.oldtest.ts similarity index 87% rename from typescript/server/src/server/router/api/v1/users/_userID/settings/router.test.ts rename to typescript/server/src/server/router/api/v1/users/_userID/settings/router.oldtest.ts index 8efd3937e..3cfd5d54a 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/settings/router.test.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/settings/router.oldtest.ts @@ -1,6 +1,6 @@ import type { UserDocument } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; @@ -52,7 +52,7 @@ t.test("PATCH /api/v1/users/:userID/settings", async (t) => { }, }); - const dbRes = await db["user-settings"].findOne({ userID: 1 }); + const dbRes = await MONGODB_KILL["user-settings"].findOne({ userID: 1 }); t.strictSame(dbRes, { userID: 1, @@ -73,7 +73,7 @@ t.test("PATCH /api/v1/users/:userID/settings", async (t) => { t.equal(res.statusCode, 400); - const dbRes = await db["user-settings"].findOne({ userID: 1 }); + const dbRes = await MONGODB_KILL["user-settings"].findOne({ userID: 1 }); t.strictSame( dbRes, @@ -119,9 +119,9 @@ t.test("PATCH /api/v1/users/:userID/settings", async (t) => { }); t.test("Must be authenticated as that user.", async (t) => { - const user = await db.users.findOne({}); + const user = await MONGODB_KILL.users.findOne({}); - await db.users.insert( + await MONGODB_KILL.users.insert( deepmerge(user!, { id: 2, username: "something_else", @@ -140,7 +140,7 @@ t.test("PATCH /api/v1/users/:userID/settings", async (t) => { t.equal(res.statusCode, 403); - const dbRes = await db["user-settings"].findOne({ userID: 1 }); + const dbRes = await MONGODB_KILL["user-settings"].findOne({ userID: 1 }); t.equal( dbRes?.preferences.developerMode, @@ -152,7 +152,7 @@ t.test("PATCH /api/v1/users/:userID/settings", async (t) => { }); t.test("Must not work with an API key.", async (t) => { - await db["api-tokens"].insert({ + await MONGODB_KILL["api-tokens"].insert({ identifier: "no perm", permissions: { // has relevant permissions @@ -172,7 +172,7 @@ t.test("PATCH /api/v1/users/:userID/settings", async (t) => { t.equal(res.statusCode, 403); - const dbRes = await db["user-settings"].findOne({ userID: 1 }); + const dbRes = await MONGODB_KILL["user-settings"].findOne({ userID: 1 }); t.equal( dbRes?.preferences.developerMode, @@ -190,7 +190,7 @@ t.test("PATCH /api/v1/users/:userID/settings", async (t) => { t.equal(res.statusCode, 401); - const dbRes = await db["user-settings"].findOne({ userID: 1 }); + const dbRes = await MONGODB_KILL["user-settings"].findOne({ userID: 1 }); t.equal( dbRes?.preferences.developerMode, diff --git a/typescript/server/src/server/router/api/v1/users/_userID/settings/router.ts b/typescript/server/src/server/router/api/v1/users/_userID/settings/router.ts index 2ccbd1dea..608a0769d 100644 --- a/typescript/server/src/server/router/api/v1/users/_userID/settings/router.ts +++ b/typescript/server/src/server/router/api/v1/users/_userID/settings/router.ts @@ -1,6 +1,6 @@ import { log } from "#lib/log/log.js"; import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetTachiData } from "#utils/req-tachi-data"; import { FormatUserDoc, GetSettingsForUser } from "#utils/user"; import { Router } from "express"; @@ -17,7 +17,7 @@ const router: Router = Router({ mergeParams: true }); router.get("/", async (req, res) => { const user = GetTachiData(req, "requestedUser"); - const settings = await db["user-settings"].findOne({ + const settings = await MONGODB_KILL["user-settings"].findOne({ userID: user.id, }); @@ -80,7 +80,7 @@ router.patch( modifyObject[`preferences.${k}`] = v; } - await db["user-settings"].update( + await MONGODB_KILL["user-settings"].update( { userID: user.id, }, diff --git a/typescript/server/src/server/router/api/v1postgres/users/router.test.ts b/typescript/server/src/server/router/api/v1/users/router.oldtest.ts similarity index 93% rename from typescript/server/src/server/router/api/v1postgres/users/router.test.ts rename to typescript/server/src/server/router/api/v1/users/router.oldtest.ts index 3bf27e133..2932e156f 100644 --- a/typescript/server/src/server/router/api/v1postgres/users/router.test.ts +++ b/typescript/server/src/server/router/api/v1/users/router.oldtest.ts @@ -1,6 +1,6 @@ import type { UserDocument } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; import t from "tap"; @@ -39,7 +39,7 @@ t.test("GET /api/v1/users", (t) => { t.equal(res.body.body.length, 0); - await db.users.insert({ + await MONGODB_KILL.users.insert({ username: "online_dude", usernameLowercase: "online_dude", lastSeen: Date.now(), @@ -55,7 +55,7 @@ t.test("GET /api/v1/users", (t) => { }); t.test("Should return users sorted by lastSeen if no arguments are passed.", async (t) => { - await db.users.insert({ + await MONGODB_KILL.users.insert({ username: "online_dude", usernameLowercase: "online_dude", lastSeen: Date.now(), diff --git a/typescript/server/src/server/router/api/v1/users/router.test.ts b/typescript/server/src/server/router/api/v1/users/router.test.ts deleted file mode 100644 index 3bf27e133..000000000 --- a/typescript/server/src/server/router/api/v1/users/router.test.ts +++ /dev/null @@ -1,77 +0,0 @@ -import type { UserDocument } from "tachi-common"; - -import db from "#services/mongo/db"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import t from "tap"; - -t.test("GET /api/v1/users", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should search users if search param is set.", async (t) => { - const res = await mockApi.get("/api/v1/users?search=zkldi"); - - t.equal(res.body.body.length, 1); - t.equal(res.body.body[0].username, "test_zkldi"); - - const res2 = await mockApi.get("/api/v1/users?search=nothing"); - - t.equal(res2.body.body.length, 0); - - const res3 = await mockApi.get("/api/v1/users?search=ZklDI"); - - t.equal(res3.body.body.length, 1); - t.equal(res3.body.body[0].username, "test_zkldi"); - - t.end(); - }); - - t.test("Should sanitise input for regex stuff.", async (t) => { - const res = await mockApi.get("/api/v1/users?search=.*"); - - t.equal(res.body.body.length, 0); - - t.end(); - }); - - t.test("Should restrict returns to only online users if online is set.", async (t) => { - const res = await mockApi.get("/api/v1/users?online=true"); - - t.equal(res.body.body.length, 0); - - await db.users.insert({ - username: "online_dude", - usernameLowercase: "online_dude", - lastSeen: Date.now(), - id: 2, - } as UserDocument); - - const res2 = await mockApi.get("/api/v1/users?online=true"); - - t.equal(res2.body.body.length, 1); - t.equal(res2.body.body[0].usernameLowercase, "online_dude"); - - t.end(); - }); - - t.test("Should return users sorted by lastSeen if no arguments are passed.", async (t) => { - await db.users.insert({ - username: "online_dude", - usernameLowercase: "online_dude", - lastSeen: Date.now(), - id: 2, - } as UserDocument); - - const res = await mockApi.get("/api/v1/users"); - - t.equal(res.body.body.length, 2); - t.strictSame( - res.body.body.map((e: UserDocument) => e.usernameLowercase), - ["online_dude", "test_zkldi"], - ); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1/users/router.ts b/typescript/server/src/server/router/api/v1/users/router.ts index baf655d8d..d86f52c25 100644 --- a/typescript/server/src/server/router/api/v1/users/router.ts +++ b/typescript/server/src/server/router/api/v1/users/router.ts @@ -1,8 +1,12 @@ +import { SELECT_USER, ToUserDocument } from "#lib/db-formats/user.js"; import { SearchUsersRegExp } from "#lib/search/search"; -import db from "#services/mongo/db"; +import DB from "#services/pg/db.js"; import { IsString } from "#utils/misc"; +import { apiSuccess } from "#utils/response.js"; +import { UnixMillisecondsToISO8601 } from "#utils/time.js"; import { GetOnlineCutoff } from "#utils/user"; import { Router } from "express"; +import { type UserDocument } from "tachi-common"; import userIDRouter from "./_userID/router"; @@ -34,19 +38,26 @@ router.get("/", async (req, res) => { users = await SearchUsersRegExp(search, onlyOnline); } else { - const query = onlyOnline ? { lastSeen: { $gt: GetOnlineCutoff() } } : {}; + let query = DB.selectFrom("account").select(SELECT_USER); - users = await db.users.find(query, { - sort: { lastSeen: -1 }, - limit: 100, - }); + if (onlyOnline) { + const onlineCutoff = GetOnlineCutoff(); + query = query + .where("last_seen", ">", UnixMillisecondsToISO8601(onlineCutoff)) + .orderBy("last_seen", "desc") + .limit(100); + } + + users = await query + .orderBy("last_seen", "desc") + .limit(100) + .execute() + .then((res) => res.map(ToUserDocument)); } - return res.status(200).json({ - success: true, - description: `Returned ${users.length} users.`, - body: users, - }); + return res + .status(200) + .json(apiSuccess>(`Returned ${users.length} users.`, users)); }); router.use("/:userID", userIDRouter); diff --git a/typescript/server/src/server/router/api/v1postgres/activity/router.ts b/typescript/server/src/server/router/api/v1postgres/activity/router.ts deleted file mode 100644 index b7270577b..000000000 --- a/typescript/server/src/server/router/api/v1postgres/activity/router.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { GetRecentActivityForMultipleGames } from "#lib/activity/activity"; -import { TachiConfig } from "#lib/setup/config"; -import { Router } from "express"; -import { GetGameGroupConfig } from "tachi-common"; - -const router: Router = Router({ mergeParams: true }); - -/** - * Retrieve *all* activity across every game on the site. - * - * @param session - See CreateActivityRouteHandler - * @param startTime - See CreateActivityRouteHandler - * - * @name GET /api/v1/activity - */ -router.get("/", async (req, res) => { - const qStartTime = req.query.startTime as string | undefined; - - const startTime = qStartTime ? Number(qStartTime) : null; - - if (Number.isNaN(startTime)) { - return res.status(400).json({ - success: false, - description: `Invalid startTime, got a non number.`, - }); - } - - const gpts = []; - - for (const game of TachiConfig.GAMES) { - const playtypes = GetGameGroupConfig(game).playtypes; - - for (const playtype of playtypes) { - gpts.push({ game, playtype, query: {} }); - } - } - - const data = await GetRecentActivityForMultipleGames(gpts, undefined, startTime); - - return res.status(200).json({ - success: true, - description: `Returned global activity.`, - body: data, - }); -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/admin/router.ts b/typescript/server/src/server/router/api/v1postgres/admin/router.ts deleted file mode 100644 index 70c32e522..000000000 --- a/typescript/server/src/server/router/api/v1postgres/admin/router.ts +++ /dev/null @@ -1,422 +0,0 @@ -import { SYMBOL_TACHI_API_AUTH } from "#lib/constants/tachi"; -import { log } from "#lib/log/log.js"; -import { SendSiteAnnouncementNotification } from "#lib/notifications/notification-wrappers"; -import { UpdateGoalsForUser } from "#lib/score-import/framework/goals/goals"; -import { UpdateQuestsForUser } from "#lib/score-import/framework/quests/quests"; -import { DeleteMultipleScores, DeleteScore } from "#lib/score-mutation/delete-scores"; -import { TachiConfig } from "#lib/setup/config"; -import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; -import { RecalcAllScores, UpdateAllPBs } from "#utils/calculations/recalc-scores"; -import { RecalcSessions } from "#utils/calculations/recalc-sessions"; -import { IsValidPlaytype } from "#utils/misc"; -import DestroyUserGamePlaytypeData from "#utils/reset-state/destroy-ugpt"; -import { GetScoresFromSession } from "#utils/session"; -import { GetUserWithID, ResolveUser } from "#utils/user"; -import { type RequestHandler, Router } from "express"; -import { p } from "prudence"; -import { - type GameGroup, - type GoalSubscriptionDocument, - type integer, - type Playtype, - UserAuthLevels, -} from "tachi-common"; - -const router: Router = Router({ mergeParams: true }); - -const RequireAdminLevel: RequestHandler = async (req, res, next) => { - if (req[SYMBOL_TACHI_API_AUTH].userID === null) { - return res.status(401).json({ - success: false, - description: `You are not authenticated.`, - }); - } - - const userDoc = await GetUserWithID(req[SYMBOL_TACHI_API_AUTH].userID); - - if (!userDoc) { - log.error( - `Api Token ${req[SYMBOL_TACHI_API_AUTH].token} is assigned to ${req[SYMBOL_TACHI_API_AUTH].userID}, who does not exist?`, - ); - - return res.status(500).json({ - success: false, - description: `An internal error has occured.`, - }); - } - - if (userDoc.authLevel !== UserAuthLevels.ADMIN) { - return res.status(403).json({ - success: false, - description: `You are not authorised to perform this.`, - }); - } - - next(); -}; - -router.use(RequireAdminLevel); - -/** - * Resynchronises all PBs that match the given query or users. - * - * @param userIDs - Optionally, An array of integers of users to resync. - * @param filter - Optionally, the set of scores to resync. - * - * @name POST /api/v1/admin/resync-pbs - */ -router.post( - "/resync-pbs", - prValidate({ - userIDs: p.optional([p.isPositiveInteger]), - filter: "*object", - }), - async (req, res) => { - const body = req.safeBody as { - filter?: object; - userIDs?: Array; - }; - - await UpdateAllPBs(body.userIDs, body.filter); - - return res.status(200).json({ - success: true, - description: `Done.`, - body: {}, - }); - }, -); - -/** - * Force Delete anyones score. - * - * @param scoreID - The scoreID to delete. - * - * @name POST /api/v1/admin/delete-score - */ -router.post("/delete-score", prValidate({ scoreID: "string" }), async (req, res) => { - const body = req.safeBody as { scoreID: string }; - - const score = await db.scores.findOne({ scoreID: body.scoreID }); - - if (!score) { - return res.status(404).json({ - success: false, - description: `This score does not exist.`, - }); - } - - await DeleteScore(score); - - return res.status(200).json({ - success: true, - description: `Removed score.`, - body: {}, - }); -}); - -/** - * Force Delete anyones session. - * - * @param sessionID - The sessionID to delete. - * - * @name POST /api/v1/admin/delete-session - */ -router.post("/delete-session", prValidate({ sessionID: "string" }), async (req, res) => { - const body = req.safeBody as { sessionID: string }; - - const session = await db.sessions.findOne({ scoreID: body.sessionID }); - - if (!session) { - return res.status(404).json({ - success: false, - description: `This session does not exist.`, - }); - } - - const scores = await GetScoresFromSession(session); - - await DeleteMultipleScores(scores); - - return res.status(200).json({ - success: true, - description: `Removed session.`, - body: {}, - }); -}); - -/** - * Destroys a users UGPT profile and forces a leaderboard recalc. - * - * @param userID - The U... - * @param game - The G... - * @param playtype - And the PT to delete. - * - * @name POST /api/v1/admin/destroy-ugpt - */ -router.post( - "/destroy-ugpt", - prValidate({ - userID: p.isInteger, - game: p.isIn(TachiConfig.GAMES), - playtype: (self, parent) => { - if (typeof self !== "string") { - return "Expected a string for a playtype."; - } - - if (!IsValidPlaytype(parent.game as GameGroup, self)) { - return `Invalid playtype of ${self} for game ${parent.game as GameGroup}.`; - } - - return true; - }, - }), - async (req, res) => { - const { userID, game, playtype } = req.safeBody as { - game: GameGroup; - playtype: Playtype; - userID: integer; - }; - - const ugpt = await db["game-stats"].findOne({ - userID, - game, - playtype, - }); - - if (!ugpt) { - return res.status(404).json({ - success: false, - description: `No stats for ${userID} (${game} ${playtype}) exist.`, - }); - } - - await DestroyUserGamePlaytypeData(userID, game, playtype); - - return res.status(200).json({ - success: true, - description: `Completely destroyed UGPT for ${userID} (${game} ${playtype}).`, - body: {}, - }); - }, -); - -/** - * Destroy a chart and all of its scores (and sessions). - * - * @param chartID - The chartID to delete. - * @param game - The game this chart is for. Necessary for doing lookups. - * - * @name POST /api/v1/admin/destroy-chart - */ -router.post( - "/destroy-chart", - prValidate({ chartID: "string", game: p.isIn(TachiConfig.GAMES) }), - async (req, res) => { - const body = req.safeBody as { - chartID: string; - game: GameGroup; - }; - - const { game, chartID } = body; - - const scores = await db.scores.find({ - chartID, - }); - - await DeleteMultipleScores(scores); - - await db.anyCharts[game].remove({ - chartID, - }); - - await db["personal-bests"].remove({ - chartID, - }); - - return res.status(200).json({ - success: true, - description: `Obliterated chart.`, - body: {}, - }); - }, -); - -/** - * Perform a site recalc on this set of scores. - * - * @name POST /api/v1/admin/recalc - */ -router.post("/recalc", async (req, res) => { - const filter = req.safeBody; - - await RecalcAllScores(filter); - - const scoreIDs = ( - await db.scores.find(filter, { - projection: { - scoreID: 1, - }, - }) - ).map((e) => e.scoreID); - - await RecalcSessions({ - scoreIDs: { $in: scoreIDs }, - }); - - return res.status(200).json({ - success: true, - description: `Recalced scores.`, - body: { - scoresRecalced: scoreIDs.length, - }, - }); -}); - -/** - * Send an announcement to the site. - * - * @name POST /api/v1/admin/announcement - */ -router.post( - "/announcement", - prValidate({ - game: p.optional(p.isIn(TachiConfig.GAMES)), - playtype: "*string", - title: "string", - }), - async (req, res) => { - const { game, playtype, title } = req.safeBody as { - game?: GameGroup; - playtype?: string; - title: string; - }; - - let maybePlaytype: Playtype | undefined; - - if (game && playtype) { - if (!IsValidPlaytype(game, playtype)) { - return res.status(400).json({ - success: false, - description: `Invalid playtype '${playtype}' for game '${game}'.`, - }); - } - - maybePlaytype = playtype; - } - - await SendSiteAnnouncementNotification(title, game, maybePlaytype); - - return res.status(200).json({ - success: true, - description: `Sent notification '${title}'.`, - body: {}, - }); - }, -); - -/** - * Make this user a Tachi supporter. - * - * @name POST /api/v1/admin/supporter/:userID - */ -router.post("/supporter/:userID", async (req, res) => { - const user = await ResolveUser(req.params.userID); - - if (!user) { - return res.status(404).json({ - success: false, - description: `This user does not exist.`, - }); - } - - await db.users.update({ id: user.id }, { $set: { isSupporter: true } }); - - return res.status(200).json({ - success: true, - description: `Done.`, - body: {}, - }); -}); - -/** - * Un-Make this user a Tachi supporter. - * - * @name POST /api/v1/admin/supporter/:userID - */ -router.delete("/supporter/:userID", async (req, res) => { - const user = await ResolveUser(req.params.userID); - - if (!user) { - return res.status(404).json({ - success: false, - description: `This user does not exist.`, - }); - } - - await db.users.update({ id: user.id }, { $set: { isSupporter: false } }); - - return res.status(200).json({ - success: true, - description: `Done.`, - body: {}, - }); -}); - -/** - * Reprocess all goals for every user. This should be used to un-screw the site - * if the server goes down or peoples goals fall out of sync. Obviously, this - * should never happen, but the error handling around this stuff is really wacky. - * - * @name POST /api/v1/admin/reprocess-all-goals - */ -router.post("/reprocess-all-goals", async (req, res) => { - const ugpts = await db["game-stats"].find({}); - - const promises = []; - - for (const ugpt of ugpts) { - promises.push(async () => { - const goalSubs = await db["goal-subs"].find({ - game: ugpt.game, - playtype: ugpt.playtype, - userID: ugpt.userID, - }); - - const goalSubsMap = new Map(); - - for (const gSub of goalSubs) { - goalSubsMap.set(gSub.goalID, gSub); - } - - const goals = await db.goals.find({ - goalID: { $in: goalSubs.map((e) => e.goalID) }, - }); - - await UpdateGoalsForUser(goals, goalSubsMap, ugpt.userID, log); - - const allQuestSubs = await db["quest-subs"].find({ - game: ugpt.game, - playtype: ugpt.playtype, - userID: ugpt.userID, - }); - - const quests = await db.quests.find({ - questID: { $in: allQuestSubs.map((e) => e.questID) }, - }); - - await UpdateQuestsForUser(quests, allQuestSubs, ugpt.game, ugpt.userID, log); - }); - } - - await Promise.all(promises); - - return res.status(200).json({ - success: true, - description: "Reprocessed all goals.", - body: {}, - }); -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/auth/auth.ts b/typescript/server/src/server/router/api/v1postgres/auth/auth.ts deleted file mode 100644 index c07dc4368..000000000 --- a/typescript/server/src/server/router/api/v1postgres/auth/auth.ts +++ /dev/null @@ -1,193 +0,0 @@ -import type { PrivateUserInfoDocument } from "#utils/types"; - -import { log } from "#lib/log/log.js"; -import { Env, ServerConfig } from "#lib/setup/config"; -import db from "#services/mongo/db"; -import nodeFetch from "#utils/fetch"; -import { Random20Hex } from "#utils/misc"; -import { CreateURLWithParams } from "#utils/url"; -import { FormatUserDoc } from "#utils/user"; -import bcrypt from "bcryptjs"; -import { p } from "prudence"; -import { - type integer, - UserAuthLevels, - type UserDocument, - type UserSettingsDocument, -} from "tachi-common"; - -const BCRYPT_SALT_ROUNDS = 12; - -export const ValidatePassword = (self: unknown) => - (typeof self === "string" && self.length >= 8) || "Passwords must be 8 characters or more."; - -const LAZY_EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/u; - -export const ValidateEmail = p.regex(LAZY_EMAIL_REGEX); - -/** - * Compares a plaintext string of a users password to a hash. - * @param plaintext The provided user input. - * @param password The hash to compare against. - */ -export function PasswordCompare(plaintext: string, password: string) { - return bcrypt.compare(plaintext, password); -} - -export function ReinstateInvite(code: string) { - log.info(`Reinstated Invite ${code}`); - return db.invites.update( - { - code, - }, - { - $set: { - consumed: false, - consumedAt: null, - consumedBy: null, - }, - }, - ); -} - -export async function AddNewInvite(user: UserDocument) { - const code = Random20Hex(); - - const result = await db.invites.insert({ - code, - consumed: false, - createdBy: user.id, - createdAt: Date.now(), - consumedAt: null, - consumedBy: null, - }); - - log.info(`User ${FormatUserDoc(user)} created an invite.`); - - return result; -} - -export const DEFAULT_USER_SETTINGS: UserSettingsDocument["preferences"] = { - developerMode: false, - advancedMode: false, - invisible: false, - contentiousContent: false, - deletableScores: false, -}; - -export function HashPassword(plaintext: string) { - return bcrypt.hash(plaintext, BCRYPT_SALT_ROUNDS); -} - -export async function AddNewUser( - username: string, - plaintext: string, - email: string, - userID: integer, -) { - const hashedPassword = await HashPassword(plaintext); - - log.debug(`Hashed password for ${username}.`); - - const userDoc: UserDocument = { - id: userID, - username, - usernameLowercase: username.toLowerCase(), - about: "I'm a fairly nondescript person.", - socialMedia: {}, - status: null, - customBannerLocation: null, - customPfpLocation: null, - joinDate: Date.now(), - lastSeen: Date.now(), - authLevel: UserAuthLevels.USER, - badges: [], - }; - - // all created users on a dev instance should be admins, for convenience. - if (Env.NODE_ENV === "dev") { - userDoc.authLevel = UserAuthLevels.ADMIN; - } - - const res = await db.users.insert(userDoc); - - const settingsRes = await InsertDefaultUserSettings(userID); - - await InsertPrivateUserInfo(userID, hashedPassword, email); - - return { newUser: res, newSettings: settingsRes }; -} - -export function InsertPrivateUserInfo(userID: integer, hashedPassword: string, email: string) { - const privateInfo: PrivateUserInfoDocument = { - userID, - email, - password: hashedPassword, - }; - - return db["user-private-information"].insert(privateInfo); -} - -export function InsertDefaultUserSettings(userID: integer) { - log.debug(`Inserting default settings for ${userID}.`); - const UserSettingsDocument: UserSettingsDocument = { - userID, - following: [], - preferences: DEFAULT_USER_SETTINGS, - }; - - return db["user-settings"].insert(UserSettingsDocument); -} - -export async function ValidateCaptcha( - recaptcha: string, - remoteAddr: string | undefined, - fetch = nodeFetch, -) { - const url = CreateURLWithParams(`https://www.google.com/recaptcha/api/siteverify`, { - secret: ServerConfig.CAPTCHA_SECRET_KEY, - response: recaptcha, - remoteip: remoteAddr ?? "", - }); - - const googleCaptchaRes: unknown = await fetch(url.href).then((r) => r.json()); - - const err = p( - googleCaptchaRes, - { - success: "boolean", - }, - {}, - { allowExcessKeys: true }, - ); - - if (err) { - log.warn( - { googleCaptchaRes, err }, - `Google ReCaptcha returned something without a success property? Assuming this captcha check failed.`, - ); - return false; - } - - // asserted above - const gcr = googleCaptchaRes as { success: boolean }; - - if (!gcr.success) { - log.debug({ gcr }, `Failed GCaptcha response`); - } - - return gcr.success; -} - -export function MountAuthCookie( - req: Express.Request, - user: UserDocument, - settings: UserSettingsDocument, -) { - req.session.tachi = { - user, - settings, - }; - - req.session.cookie.maxAge = 3.154e10; -} diff --git a/typescript/server/src/server/router/api/v1postgres/auth/router.ts b/typescript/server/src/server/router/api/v1postgres/auth/router.ts deleted file mode 100644 index 18ba98810..000000000 --- a/typescript/server/src/server/router/api/v1postgres/auth/router.ts +++ /dev/null @@ -1,575 +0,0 @@ -import type { integer } from "tachi-common"; - -import { SendEmail } from "#lib/email/client"; -import { EmailFormatResetPassword, EmailFormatVerifyEmail } from "#lib/email/formats"; -import { log } from "#lib/log/log.js"; -import { Env, ServerConfig, TachiConfig } from "#lib/setup/config"; -import prValidate from "#server/middleware/prudence-validate"; -import { - AggressiveRateLimitMiddleware, - HyperAggressiveRateLimitMiddleware, -} from "#server/middleware/rate-limiter"; -import db from "#services/mongo/db"; -import { DecrementCounterValue, GetNextCounterValue } from "#utils/db"; -import { Random20Hex } from "#utils/misc"; -import { - CheckIfEmailInUse, - FormatUserDoc, - GetSettingsForUser, - GetUserCaseInsensitive, - GetUserPrivateInfo, - GetUserWithID, - GetUserWithIDGuaranteed, -} from "#utils/user"; -import { Router } from "express"; -import { p } from "prudence"; - -import { - AddNewUser, - HashPassword, - InsertDefaultUserSettings, - MountAuthCookie, - PasswordCompare, - ReinstateInvite, - ValidateCaptcha, - ValidateEmail, - ValidatePassword, -} from "./auth"; - -const router: Router = Router({ mergeParams: true }); - -/** - * Logs in a user. - * @name POST /api/v1/auth/login - */ -router.post( - "/login", - AggressiveRateLimitMiddleware, - prValidate( - { - username: p.regex(/^[a-zA-Z_-][a-zA-Z0-9_-]{2,20}$/u), - "!password": ValidatePassword, - captcha: "string", - }, - { - username: - "Invalid username. Usernames cannot start with a number, and must be between 2 and 20 characters.", - captcha: "Please fill out the captcha.", - }, - undefined, - "debug", - ), - async (req, res) => { - if (req.session.tachi?.user.id !== undefined) { - // Dual logins should destroy the users session and recreate it. - req.session.tachi = undefined; - } - - const body = req.safeBody as { - "!password": string; - captcha: string; - username: string; - }; - - log.debug(`Received login request with username ${body.username} (${req.ip})`); - - /* istanbul ignore next */ - if (Env.NODE_ENV === "production" || Env.NODE_ENV === "staging") { - log.debug("Validating captcha..."); - const validCaptcha = await ValidateCaptcha(body.captcha, req.socket.remoteAddress); - - if (!validCaptcha) { - log.debug("Captcha failed."); - return res.status(400).json({ - success: false, - description: `Captcha failed.`, - }); - } - - log.debug("Captcha validated!"); - } else { - log.warn("Skipped captcha check because not in production."); - } - - const requestedUser = await GetUserCaseInsensitive(body.username); - - if (!requestedUser) { - log.debug(`Invalid username for login ${body.username}.`); - return res.status(404).json({ - success: false, - description: `This user does not exist.`, - }); - } - - const privateInfo = await GetUserPrivateInfo(requestedUser.id); - - if (!privateInfo) { - log.error( - { requestedUser }, - `State desync for user ${FormatUserDoc( - requestedUser, - )}. This user has no password/email information?`, - ); - - return res.status(500).json({ - success: false, - description: `An internal server error has occured.`, - }); - } - - const passwordMatch = await PasswordCompare(body["!password"], privateInfo.password); - - if (!passwordMatch) { - log.debug("Invalid password provided."); - return res.status(403).json({ - success: false, - description: `Invalid password.`, - }); - } - - const user = await GetUserWithID(requestedUser.id); - - if (!user) { - log.error({ requestedUser }, `User logged in as someone who does not exist?`); - return res.status(500).json({ - success: false, - description: `An internal server error has occured.`, - }); - } - - let settings = await GetSettingsForUser(requestedUser.id); - - if (!settings) { - log.warn(`User ${FormatUserDoc(user)} has no settings. Inserting default settings.`); - settings = await InsertDefaultUserSettings(user.id); - } - - MountAuthCookie(req, user, settings); - - log.debug(`${FormatUserDoc(requestedUser)} Logged in.`); - - return res.status(200).json({ - success: true, - description: `Successfully logged in as ${FormatUserDoc(requestedUser)}`, - body: { - userID: requestedUser.id, - }, - }); - }, -); - -/** - * Registers a new user. - * @name POST /api/v1/auth/register - */ -router.post( - "/register", - AggressiveRateLimitMiddleware, - prValidate( - { - username: p.regex(/^[a-zA-Z_-][a-zA-Z0-9_-]{2,20}$/u), - "!password": ValidatePassword, - email: ValidateEmail, - inviteCode: "*string", - captcha: "string", - }, - { - username: - "Usernames must be between 3 and 20 characters long, can only contain alphanumeric characters and cannot start with a number.", - email: "Invalid email.", - inviteCode: "Invalid invite code.", - captcha: "Please fill out the captcha.", - }, - undefined, - "debug", - ), - async (req, res) => { - if (!TachiConfig.SIGNUPS_ENABLED) { - return res.status(501).json({ - success: false, - description: `Signups are not currently enabled.`, - }); - } - - const body = req.safeBody as { - "!password": string; - captcha: string; - email: string; - inviteCode?: string; - username: string; - }; - - // force lowercase for emails to avoid case-confusion in lookups... - body.email = body.email.toLowerCase(); - - if (body.inviteCode === undefined && ServerConfig.INVITE_CODE_CONFIG) { - return res.status(400).json({ - success: false, - description: `No invite code given, yet the server uses invites.`, - }); - } - - log.debug(`received register request with username ${body.username} (${req.ip})`); - - /* istanbul ignore next */ - if (Env.NODE_ENV === "production" || Env.NODE_ENV === "staging") { - log.debug("Validating captcha..."); - const validCaptcha = await ValidateCaptcha(body.captcha, req.socket.remoteAddress); - - if (!validCaptcha) { - log.debug("Captcha failed."); - return res.status(400).json({ - success: false, - description: `Captcha failed.`, - }); - } - - log.debug("Captcha validated."); - } else { - log.warn("Skipped captcha check because not in production."); - } - - const existingUser = await GetUserCaseInsensitive(body.username); - - if (existingUser) { - log.debug(`Invalid username ${body.username}, already in use.`); - return res.status(409).json({ - success: false, - description: "This username is already in use.", - }); - } - - const existingEmail = await CheckIfEmailInUse(body.email); - - if (existingEmail) { - log.info(`User attempted to sign up with email that was already in use.`); - return res.status(409).json({ - success: false, - description: `This email is already in use.`, - }); - } - - let hasInsertedUserID: integer | null = null; - - try { - const userID = await GetNextCounterValue("users"); - - if (ServerConfig.INVITE_CODE_CONFIG) { - const inviteCodeDoc = await db.invites.findOneAndUpdate( - { - code: body.inviteCode, - consumed: false, - }, - { - $set: { - consumed: true, - consumedAt: Date.now(), - consumedBy: userID, - }, - }, - ); - - if (!inviteCodeDoc) { - log.info(`Invalid invite code given: ${body.inviteCode}.`); - return res.status(401).json({ - success: false, - description: `This invite code is not valid.`, - }); - } - - log.info(`Consumed invite ${inviteCodeDoc.code}.`); - } - - // if we get to this point, We're good to create the user. - - const { newUser, newSettings } = await AddNewUser( - body.username, - body["!password"], - body.email, - userID, - ); - - hasInsertedUserID = newUser.id; - - // re-fetch the user like this so we guaranteeably omit the private fields. - const user = await GetUserWithIDGuaranteed(newUser.id); - - MountAuthCookie(req, user, newSettings); - - // If we have an EMAIL_CONFIG set, send out - // authentication emails. - // Otherwise, don't bother; this is equivalent to - // automatically verifying all users' emails. - if (ServerConfig.EMAIL_CONFIG) { - const resetEmailCode = Random20Hex(); - - await db["verify-email-codes"].insert({ - code: resetEmailCode, - userID, - email: body.email, - }); - - const { text, html } = EmailFormatVerifyEmail(user.username, resetEmailCode); - - void SendEmail(body.email, "Email Verification", html, text); - } - - return res.status(200).json({ - success: true, - description: `Successfully created account ${body.username}!`, - body: user, - }); - } catch (err) { - log.error({ err }, `Bailed on user creation ${body.username}.`); - - if (ServerConfig.INVITE_CODE_CONFIG && body.inviteCode !== undefined) { - await ReinstateInvite(body.inviteCode); - } - - if (hasInsertedUserID !== null) { - log.warn( - `Removing user ${body.username} (#${hasInsertedUserID}), as their document was created, but creation still failed.`, - ); - await db.users.remove({ username: body.username }); - await db["user-settings"].remove({ userID: hasInsertedUserID }); - await db["user-private-information"].remove({ userID: hasInsertedUserID }); - } - - await DecrementCounterValue("users"); - - return res.status(500).json({ - success: false, - description: "An internal server error has occured.", - }); - } - }, -); - -/** - * Verifies the provided email according to the code provided. - * - * @param code - The emailCode set in the /register function. - * - * @name POST /api/v1/auth/verify-email - */ -router.post( - "/verify-email", - AggressiveRateLimitMiddleware, - prValidate({ - code: "string", - }), - async (req, res) => { - const body = req.safeBody as { - code: string; - }; - - const code = await db["verify-email-codes"].findOne({ - code: body.code, - }); - - if (!code) { - return res.status(400).json({ - success: false, - description: `This email code is invalid.`, - }); - } - - await db["verify-email-codes"].remove({ - code: body.code, - }); - - return res.status(200).json({ - success: true, - description: `Verified email!`, - body: {}, - }); - }, -); - -/** - * Resend a verification email, for when they fall through the - * cracks. - * - * @param email - The email to send a verification email to. - * - * @name POST /api/v1/auth/resend-verify-email - */ -router.post("/resend-verify-email", HyperAggressiveRateLimitMiddleware, async (req, res) => { - // Immediately send a response so the existence of emails - // cannot be timing attacked out. - res.status(200).json({ - success: true, - description: `Sent an email if the email address has not been verified.`, - body: {}, - }); - - const user = req.session.tachi?.user; - - if (!user) { - return; - } - - const userID = user.id; - - const verifyInfo = await db["verify-email-codes"].findOne({ userID }); - - if (!verifyInfo) { - log.warn(`Attempted to send reset email to ${userID}, but no verifyInfo was set for them.`); - return; - } - - // Send the email again. - - const { text, html } = EmailFormatVerifyEmail(user.username, verifyInfo.code); - - void SendEmail(verifyInfo.email, "Email Verification", html, text); -}); - -/** - * Logs out the requesting user. - * @name POST /api/v1/auth/logout - */ -router.post("/logout", (req, res) => { - if (req.session.tachi?.user.id === undefined) { - return res.status(409).json({ - success: false, - description: `You are not logged in.`, - }); - } - - req.session.destroy(() => 0); - - return res.status(200).json({ - success: true, - description: `Logged Out.`, - body: {}, - }); -}); - -/** - * Creates a password reset code for a user. The user will then - * be able to trigger POST /reset-password with that code. - * - * @param email - The email associated with the account you want to reset. - * - * @name POST /api/v1/auth/forgot-password - */ -router.post( - "/forgot-password", - HyperAggressiveRateLimitMiddleware, - prValidate({ email: "string" }), - async (req, res) => { - if (!ServerConfig.EMAIL_CONFIG && Env.NODE_ENV !== "test") { - return res.status(501).json({ - success: false, - description: `This server does not support password resets.`, - }); - } - - const body = req.safeBody as { - email: string; - }; - - body.email = body.email.toLowerCase(); - - log.debug(`received password reset request for ${body.email}.`); - - // For timing attack and infosec reasons, we can't do anything but **immediately** return here. - res.status(202).json({ - success: true, - description: "A code has been sent to your email.", - body: {}, - }); - - const userPrivateInfo = await db["user-private-information"].findOne({ - email: body.email, - }); - - if (userPrivateInfo) { - const user = await db.users.findOne({ id: userPrivateInfo.userID }); - - if (!user) { - log.error( - `User ${userPrivateInfo.userID} has private information but no real account.`, - ); - return; - } - - const code = `M${Random20Hex()}`; - - log.debug(`Created password reset code for ${FormatUserDoc(user)}.`); - - await db["password-reset-codes"].insert({ - code, - userID: user.id, - createdOn: Date.now(), - }); - - const { html, text } = EmailFormatResetPassword(user.username, code, req.ip); - - void SendEmail(userPrivateInfo.email, "Reset Password", html, text); - } else { - log.info( - `Silently rejected password reset request for ${body.email}, as no user has this email.`, - ); - } - }, -); - -/** - * Takes a code generated from /forgot-password, a new password, - * and performs the reset for the user. - * - * @param password - The users new password. - * @param code - The code to use to reset this password. - * - * @name POST /api/v1/auth/reset-password - */ -router.post( - "/reset-password", - AggressiveRateLimitMiddleware, - prValidate({ - code: "string", - "!password": ValidatePassword, - }), - async (req, res) => { - const body = req.safeBody as { - "!password": string; - code: string; - }; - - const code = await db["password-reset-codes"].findOneAndDelete({ - code: body.code, - }); - - if (!code) { - return res.status(404).json({ - success: false, - description: `Invalid Reset Code.`, - }); - } - - const encryptedPassword = await HashPassword(body["!password"]); - - await db["user-private-information"].update( - { - userID: code.userID, - }, - { - $set: { - password: encryptedPassword, - }, - }, - ); - - log.info(`User ${code.userID} reset their password.`); - - return res.status(200).json({ - success: true, - description: `Reset your password.`, - body: {}, - }); - }, -); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/clients/middleware.test.ts b/typescript/server/src/server/router/api/v1postgres/clients/middleware.test.ts deleted file mode 100644 index fa4f24029..000000000 --- a/typescript/server/src/server/router/api/v1postgres/clients/middleware.test.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { SYMBOL_TACHI_DATA } from "#lib/constants/tachi"; -import { expressRequestMock } from "#test-utils/mock-request"; -import ResetDBState from "#test-utils/resets"; -import t from "tap"; - -import { GetClientFromID, RequireOwnershipOfClient } from "./middleware"; - -t.test("#GetClientFromID", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should assign the client to req[@@TachiData] if exists.", async (t) => { - const { req } = await expressRequestMock(GetClientFromID, { - params: { - clientID: "OAUTH2_CLIENT_ID", - }, - [SYMBOL_TACHI_DATA]: {}, - }); - - t.strictSame( - req[SYMBOL_TACHI_DATA]?.apiClientDoc, - { - clientID: "OAUTH2_CLIENT_ID", - - // clientSecret: "OAUTH2_CLIENT_SECRET", - name: "Test_Service", - author: 1, - requestedPermissions: ["customise_profile"], - redirectUri: "https://example.com/callback", - webhookUri: null, - apiKeyTemplate: null, - apiKeyFilename: null, - }, - "Should assign clientDoc with secret omitted.", - ); - - t.end(); - }); - - t.test("Should return 404 if client does not exist.", async (t) => { - const { res } = await expressRequestMock(GetClientFromID, { - params: { - clientID: "NONSENSE", - }, - [SYMBOL_TACHI_DATA]: {}, - }); - - t.equal(res.statusCode, 404); - - t.hasStrict(res._getJSONData(), { - success: false, - description: "This client does not exist.", - }); - - t.end(); - }); - - t.end(); -}); - -t.test("#RequireOwnershipOfClient", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should return 401 if the user has no authentication.", async (t) => { - const { res } = await expressRequestMock(RequireOwnershipOfClient, { - safeBody: { - __terribleHackOauth2ClientDoc: { - author: 1, - }, - }, - [SYMBOL_TACHI_DATA]: {}, - session: {}, - }); - - t.equal(res.statusCode, 401); - - t.end(); - }); - - t.test("Should return 403 if the user does not own this client.", async (t) => { - const { res } = await expressRequestMock(RequireOwnershipOfClient, { - safeBody: { - __terribleHackOauth2ClientDoc: { - author: 1, - }, - }, - session: { - tachi: { - user: { - id: 2, - }, - }, - }, - [SYMBOL_TACHI_DATA]: {}, - }); - - t.equal(res.statusCode, 403); - - t.end(); - }); - - t.test("Should continue if this is their session.", async (t) => { - const { res } = await expressRequestMock(RequireOwnershipOfClient, { - safeBody: { - __terribleHackOauth2ClientDoc: { - author: 1, - }, - }, - session: { - tachi: { - user: { - id: 1, - }, - }, - }, - [SYMBOL_TACHI_DATA]: {}, - }); - - t.equal(res.statusCode, 200); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1postgres/clients/middleware.ts b/typescript/server/src/server/router/api/v1postgres/clients/middleware.ts deleted file mode 100644 index 93de2eb4e..000000000 --- a/typescript/server/src/server/router/api/v1postgres/clients/middleware.ts +++ /dev/null @@ -1,68 +0,0 @@ -import type { RequestHandler } from "express"; -import type { TachiAPIClientDocument } from "tachi-common"; - -import { Env } from "#lib/setup/config"; -import db from "#services/mongo/db"; -import { AssignToReqTachiData, GetTachiData } from "#utils/req-tachi-data"; - -export const GetClientFromID: RequestHandler = async (req, res, next) => { - const client = await db["api-clients"].findOne( - { - clientID: req.params.clientID, - }, - { - projection: { - clientSecret: 0, - }, - }, - ); - - if (!client) { - return res.status(404).json({ - success: false, - description: `This client does not exist.`, - }); - } - - AssignToReqTachiData(req, { apiClientDoc: client }); - - next(); -}; - -export const RequireOwnershipOfClient: RequestHandler = (req, res, next) => { - let client: Omit; - - // @hack - // Sadly, expMiddlewareMock doesn't support mounting symbol props on - // request. To hack around this for testing, we perform this hack. - // There's an open issue for this here: https://github.com/i-like-robots/express-request-mock/issues/19 - /* istanbul ignore next */ - if ( - Env.NODE_ENV === "test" && - (req.safeBody.__terribleHackOauth2ClientDoc as TachiAPIClientDocument | undefined) - ) { - // obviously a glaring hack and security flaw - this only applies - // in testing. - client = req.safeBody.__terribleHackOauth2ClientDoc as TachiAPIClientDocument; - } else { - client = GetTachiData(req, "apiClientDoc"); - } - - const user = req.session.tachi?.user; - - if (!user) { - return res.status(401).json({ - success: false, - description: `You are not authenticated (for a session-level request, atleast).`, - }); - } - - if (user.id !== client.author) { - return res.status(403).json({ - success: false, - description: `You are not authorized to perform this action.`, - }); - } - - next(); -}; diff --git a/typescript/server/src/server/router/api/v1postgres/clients/router.ts b/typescript/server/src/server/router/api/v1postgres/clients/router.ts deleted file mode 100644 index 6baea9023..000000000 --- a/typescript/server/src/server/router/api/v1postgres/clients/router.ts +++ /dev/null @@ -1,356 +0,0 @@ -import { log } from "#lib/log/log.js"; -import { ServerConfig } from "#lib/setup/config"; -import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; -import { DedupeArr, DeleteUndefinedProps, IsValidURL, Random20Hex } from "#utils/misc"; -import { optNull } from "#utils/prudence"; -import { GetTachiData } from "#utils/req-tachi-data"; -import { FormatUserDoc } from "#utils/user"; -import { Router } from "express"; -import { p } from "prudence"; -import { - ALL_PERMISSIONS, - type APIPermissions, - type TachiAPIClientDocument, - UserAuthLevels, -} from "tachi-common"; - -import { GetClientFromID, RequireOwnershipOfClient } from "./middleware"; - -const router: Router = Router({ mergeParams: true }); - -/** - * Retrieve the clients you created. Must be performed with a session-level request. - * - * @warn This also returns the client_secrets! Those *have* to be kept secret. - * - * @name GET /api/v1/clients - */ -router.get("/", async (req, res) => { - const user = req.session.tachi?.user; - - if (!user) { - return res.status(401).json({ - success: false, - description: `You are not authenticated (for a session-level request, atleast).`, - }); - } - - const clients = await db["api-clients"].find({ - author: user.id, - }); - - return res.status(200).json({ - success: true, - description: `Returned ${clients.length} clients.`, - body: clients, - }); -}); - -/** - * Create a new API Client. Requires session-level auth. - * - * @param name - A string that identifies this client. - * @param redirectUri - The redirectUri this client uses. - * @param webhookUri - Optionally, a webhookUri to call with webhook events. - * @param apiKeyTemplate - Optionally, a static format to apply when doing static auth. - * @param apiKeyFilename - Optionally, a filename to automatically download the template to, when doing - * static flow. - * @param permissions - An array of APIPermissions this client is expected to use. - * - * @name POST /api/v1/clients/create - */ -router.post( - "/create", - prValidate({ - name: p.isBoundedString(3, 80), - redirectUri: "?string", - webhookUri: "?string", - apiKeyTemplate: (self) => { - if (self === null) { - return true; - } - - if (typeof self !== "string") { - return "Expected a string."; - } - - if (!self.includes("%%TACHI_KEY%%")) { - return "Must contain %%TACHI_KEY%% as part of the template."; - } - - return true; - }, - apiKeyFilename: "?string", - permissions: [p.isIn(Object.keys(ALL_PERMISSIONS))], - }), - async (req, res) => { - if (!req.session.tachi?.user) { - return res.status(401).json({ - success: false, - description: `You are not authenticated.`, - }); - } - - const body = req.safeBody as { - apiKeyFilename: string | null; - apiKeyTemplate: string | null; - name: string; - permissions: Array; - redirectUri: string | null; - webhookUri: string | null; - }; - - const existingClients = await db["api-clients"].find({ - author: req.session.tachi.user.id, - }); - - // Note: Admins are excluded from the API client cap. - if ( - req.session.tachi.user.authLevel !== UserAuthLevels.ADMIN && - existingClients.length >= ServerConfig.OAUTH_CLIENT_CAP - ) { - return res.status(400).json({ - success: false, - description: `You have created too many API clients. The current cap is ${ServerConfig.OAUTH_CLIENT_CAP}.`, - }); - } - - const permissions = DedupeArr(body.permissions); - - if (permissions.length === 0) { - return res.status(400).json({ - success: false, - description: `Invalid permissions -- Need to require atleast one.`, - }); - } - - if (body.redirectUri !== null && !IsValidURL(body.redirectUri)) { - return res.status(400).json({ - success: false, - description: `Invalid Redirect URL.`, - }); - } - - if (body.webhookUri !== null && !IsValidURL(body.webhookUri)) { - return res.status(400).json({ - success: false, - description: `Invalid Webhook URL.`, - }); - } - - const clientID = `CI${Random20Hex()}`; - const clientSecret = `CS${Random20Hex()}`; - - const clientDoc: TachiAPIClientDocument = { - clientID, - clientSecret, - requestedPermissions: permissions, - name: body.name, - author: req.session.tachi.user.id, - redirectUri: body.redirectUri, - webhookUri: body.webhookUri ?? null, - apiKeyFilename: body.apiKeyFilename ?? null, - apiKeyTemplate: body.apiKeyTemplate ?? null, - }; - - await db["api-clients"].insert(clientDoc); - - log.info( - `User ${FormatUserDoc(req.session.tachi.user)} created a new API Client ${ - body.name - } (${clientID}).`, - ); - - return res.status(200).json({ - success: true, - description: `Created a new API client.`, - body: clientDoc, - }); - }, -); - -/** - * Retrieves information about the client at this ID. - * - * @name GET /api/v1/clients/:clientID - */ -router.get("/:clientID", GetClientFromID, (req, res) => { - const client = GetTachiData(req, "apiClientDoc"); - - return res.status(200).json({ - success: true, - description: `Retrieved client ${client.name}.`, - body: client, - }); -}); - -/** - * Update an existing client. The requester must be the owner of this - * client, and must also be making a session-level request. - * - * @param name - Change the name of this client. - * @param webhookUri - Change a bound webhookUri for this client. - * @param redirectUri - Change a bound redirectUri for this client. - * @param apiKeyFormat - Change the APIKeyFormat for this client. - * @param apiKeyFilename - Change the APIKeyFilename for this client. - * - * @name PATCH /api/v1/clients/:clientID - */ -router.patch( - "/:clientID", - GetClientFromID, - RequireOwnershipOfClient, - prValidate({ - name: p.optional(p.isBoundedString(3, 80)), - apiKeyTemplate: optNull((self) => { - if (typeof self !== "string") { - return "Expected a string."; - } - - if (!self.includes("%%TACHI_KEY%%")) { - return "Must contain a %%TACHI_KEY%% placeholder."; - } - - return true; - }), - apiKeyFilename: optNull(p.isBoundedString(3, 80)), - webhookUri: optNull((self) => { - if (typeof self !== "string") { - return "Expected a string."; - } - - const res = IsValidURL(self); - - if (!res) { - return "Invalid URL."; - } - - return true; - }), - redirectUri: optNull((self) => { - if (typeof self !== "string") { - return "Expected a string."; - } - - const res = IsValidURL(self); - - if (!res) { - return "Invalid URL."; - } - - return true; - }), - }), - async (req, res) => { - const body = req.safeBody as { - apiKeyFilename?: string | null; - apiKeyTemplate?: string | null; - name?: string; - permissions?: Array; - redirectUri?: string | null; - webhookUri?: string | null; - }; - - const client = GetTachiData(req, "apiClientDoc"); - - DeleteUndefinedProps(req.safeBody); - - if (Object.keys(req.safeBody).length === 0) { - return res.status(400).json({ - success: false, - description: `No changes to make.`, - }); - } - - const newClient = await db["api-clients"].findOneAndUpdate( - { - clientID: client.clientID, - }, - { - $set: req.safeBody, - }, - ); - - log.info( - `API Client ${client.name} (${client.clientID}) has been renamed to ${body.name}.`, - ); - - return res.status(200).json({ - success: true, - description: `Updated client.`, - body: newClient, - }); - }, -); - -/** - * Resets the clientSecret for this client. - * This will NOT invalidate any existing tokens, as per oauth2 spec. - * - * @name POST /api/v1/clients/:clientID/reset-secret - */ -router.post( - "/:clientID/reset-secret", - GetClientFromID, - RequireOwnershipOfClient, - async (req, res) => { - const client = GetTachiData(req, "apiClientDoc"); - const clientName = `${client.name} (${client.clientID})`; - - log.info(`received request to reset client secret for ${clientName}`); - - const newSecret = Random20Hex(); - - const newClient = await db["api-clients"].findOneAndUpdate( - { - clientID: client.clientID, - }, - { - $set: { clientSecret: newSecret }, - }, - ); - - log.info(`Reset secret for ${clientName}.`); - - return res.status(200).json({ - success: true, - description: `Reset secret.`, - body: newClient, - }); - }, -); - -/** - * Delete this client. Must be authorized at a session-request level. - * - * @name DELETE /api/v1/clients/:clientID - */ -router.delete("/:clientID", GetClientFromID, RequireOwnershipOfClient, async (req, res) => { - const client = GetTachiData(req, "apiClientDoc"); - - const clientName = `${client.name} (${client.clientID})`; - - log.info(`received request to destroy API Client ${client.name} (${client.clientID})`); - - log.debug(`Removing API Client ${clientName}.`); - await db["api-clients"].remove({ - clientID: client.clientID, - }); - log.info(`Removed API Client ${clientName}.`); - - log.debug(`Removing all associated api tokens.`); - const result = await db["api-tokens"].remove({ - fromOAuth2Client: client.clientID, - }); - - log.info(`Removed ${result.deletedCount} api tokens from ${clientName}.`); - - return res.status(200).json({ - success: true, - description: `Deleted ${clientName}.`, - body: {}, - }); -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/config/router.test.ts b/typescript/server/src/server/router/api/v1postgres/config/router.test.ts deleted file mode 100644 index 69978af64..000000000 --- a/typescript/server/src/server/router/api/v1postgres/config/router.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { ServerConfig, TachiConfig } from "#lib/setup/config"; -import mockApi from "#test-utils/mock-api"; -import t from "tap"; - -t.test("GET /api/v1/config", async (t) => { - const res = await mockApi.get("/api/v1/config"); - - t.strictSame(res.body.body, TachiConfig, "Should return TachiConfig info"); - - t.end(); -}); - -t.test("GET /api/v1/config/beatoraja-queue-size", async (t) => { - const res = await mockApi.get("/api/v1/config/beatoraja-queue-size"); - - t.equal( - res.body.body, - ServerConfig.BEATORAJA_QUEUE_SIZE, - "Should return integer equal to BEATORAJA_QUEUE_SIZE.", - ); - - t.end(); -}); - -t.test("GET /api/v1/config/max-rivals", async (t) => { - const res = await mockApi.get("/api/v1/config/max-rivals"); - - t.equal(res.body.body, ServerConfig.MAX_RIVALS, "Should return integer equal to MAX_RIVALS."); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1postgres/config/router.ts b/typescript/server/src/server/router/api/v1postgres/config/router.ts deleted file mode 100644 index 5d4aa9d8e..000000000 --- a/typescript/server/src/server/router/api/v1postgres/config/router.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { ServerConfig, TachiConfig } from "#lib/setup/config"; -import { RequireBokutachi } from "#server/middleware/type-require"; -import { Router } from "express"; - -const router: Router = Router({ mergeParams: true }); - -/** - * Returns Tachi Configuration info, such as server name, type, supported games - * and more. - * - * @name GET /api/v1/config - */ -router.get("/", (req, res) => - res.status(200).json({ - success: true, - description: `Returned configuration info.`, - body: TachiConfig, - }), -); - -/** - * Returns the value of the BEATORAJA_QUEUE_SIZE. - * - * @name GET /api/v1/config/beatoraja-queue-size - */ -router.get("/beatoraja-queue-size", RequireBokutachi, (req, res) => - res.status(200).json({ - success: true, - description: `Returned BEATORAJA_QUEUE_SIZE.`, - body: ServerConfig.BEATORAJA_QUEUE_SIZE, - }), -); - -/** - * Returns the maximum amount of rivals a user can have on this instance. - * - * @name GET /api/v1/config/max-rivals - */ -router.get("/max-rivals", (req, res) => - res.status(200).json({ - success: true, - description: `Returned MAX_RIVALS.`, - body: ServerConfig.MAX_RIVALS, - }), -); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/games/@gameSpecificRoutes/bms/router.ts b/typescript/server/src/server/router/api/v1postgres/games/@gameSpecificRoutes/bms/router.ts deleted file mode 100644 index 450a30175..000000000 --- a/typescript/server/src/server/router/api/v1postgres/games/@gameSpecificRoutes/bms/router.ts +++ /dev/null @@ -1,158 +0,0 @@ -import type { Playtypes } from "tachi-common"; - -import { - CUSTOM_TACHI_BMS_TABLES, - HandleBMSTableBodyRequest, - HandleBMSTableHeaderRequest, - HandleBMSTableHTMLRequest, - type TachiBMSTable, -} from "#lib/game-specific/custom-bms-tables"; -import db from "#services/mongo/db"; -import { AssignToReqTachiData, GetTachiData } from "#utils/req-tachi-data"; -import { type RequestHandler, Router } from "express"; - -import { ValidatePlaytypeFromParamFor } from "../../_game/_playtype/middleware"; - -const router: Router = Router({ mergeParams: true }); - -const FindCustomBMSTable: RequestHandler = (req, res, next) => { - const { playtype, tableUrlName } = req.params; - - // find the table - const customTable = CUSTOM_TACHI_BMS_TABLES.find((t) => t.urlName === tableUrlName); - - if (!customTable) { - return res.status(404).json({ - success: false, - description: `No such table with the ID '${tableUrlName}' exists.`, - }); - } - - if (customTable.playtype && customTable.playtype !== playtype) { - return res.status(404).json({ - success: false, - description: `The table '${tableUrlName}' exists, but is for ${customTable.playtype}, not ${playtype}.`, - }); - } - - if (customTable.forSpecificUser === true) { - return res.status(404).json({ - success: false, - description: `The table '${tableUrlName}' exists, but is user-specific. You should be fetching this table from /api/v1/users/:userID instead of /api/v1/games.`, - }); - } - - AssignToReqTachiData(req, { customBMSTable: customTable }); - - next(); -}; - -/** - * List all custom BMS tables this instance of Tachi is emitting. - * - * @name GET /api/v1/games/bms/:playtype/custom-tables - */ -router.get("/:playtype/custom-tables", ValidatePlaytypeFromParamFor("bms"), (req, res) => { - const tables: Array< - Pick - > = []; - - for (const table of CUSTOM_TACHI_BMS_TABLES.filter( - (e) => e.playtype === req.params.playtype || e.playtype === null, - )) { - tables.push({ - forSpecificUser: table.forSpecificUser, - urlName: table.urlName, - tableName: table.tableName, - symbol: table.symbol, - description: table.description, - }); - } - - return res.status(200).json({ - success: true, - description: `Found ${tables.length} custom table(s).`, - body: tables, - }); -}); - -/** - * Return some HTML for this custom table. - * - * @note Since this is the GPT route, trying to fetch user specific custom tables - * will result in a 404. This applies for all subsequent :tableUrlName routes. - * - * @name GET /api/v1/games/bms/:playtype/custom-tables/:tableUrlName - */ -router.get( - "/:playtype/custom-tables/:tableUrlName", - ValidatePlaytypeFromParamFor("bms"), - FindCustomBMSTable, - (req, res) => { - const customTable = GetTachiData(req, "customBMSTable"); - - // This handles returning a response for us. - return HandleBMSTableHTMLRequest(customTable, req, res); - }, -); - -/** - * Return the header.json for this custom table. - * - * @name GET /api/v1/games/bms/:playtype/custom-tables/:tableUrlName/header.json - */ -router.get( - "/:playtype/custom-tables/:tableUrlName/header.json", - ValidatePlaytypeFromParamFor("bms"), - FindCustomBMSTable, - (req, res) => { - const customTable = GetTachiData(req, "customBMSTable"); - - // This handles returning a response for us. - return HandleBMSTableHeaderRequest(customTable, req, res); - }, -); - -/** - * Return the body.json for this custom table. - * - * @name GET /api/v1/games/bms/:playtype/custom-tables/:tableUrlName/body.json - */ -router.get( - "/:playtype/custom-tables/:tableUrlName/body.json", - ValidatePlaytypeFromParamFor("bms"), - FindCustomBMSTable, - (req, res) => { - const customTable = GetTachiData(req, "customBMSTable"); - - // This handles returning a response for us. - return HandleBMSTableBodyRequest(customTable, req, res); - }, -); - -/** - * Return *all* the charts that have defined sieglinde values for this game. - * - * @name GET /api/v1/games/bms/:playtype/sieglinde-charts - */ -router.get("/:playtype/sieglinde-charts", ValidatePlaytypeFromParamFor("bms"), async (req, res) => { - const playtype = req.params.playtype as Playtypes["bms"]; - - const charts = await db.charts.bms.find({ - playtype, - $or: [{ "data.sglEC": { $gt: 0 } }, { "data.sglHC": { $gt: 0 } }], - }); - - const songs = await db.songs.bms.find({ id: { $in: charts.map((e) => e.songID) } }); - - return res.status(200).json({ - success: true, - description: `Found ${charts.length} chart(s).`, - body: { - songs, - charts, - }, - }); -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/games/@gameSpecificRoutes/iidx/router.ts b/typescript/server/src/server/router/api/v1postgres/games/@gameSpecificRoutes/iidx/router.ts deleted file mode 100644 index 655b92a25..000000000 --- a/typescript/server/src/server/router/api/v1postgres/games/@gameSpecificRoutes/iidx/router.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { - CUSTOM_TACHI_IIDX_PLAYLISTS, - type TachiIIDXPlaylist, -} from "#lib/game-specific/iidx-playlists"; -import { Router } from "express"; - -import { ValidatePlaytypeFromParamFor } from "../../_game/_playtype/middleware"; - -const router: Router = Router({ mergeParams: true }); - -/** - * List all the playlists we have available. - * - * @name GET /api/v1/games/iidx/:playtype/playlists - */ -router.get("/:playtype/playlists", ValidatePlaytypeFromParamFor("iidx"), (req, res) => { - const playlists = CUSTOM_TACHI_IIDX_PLAYLISTS.filter( - (e) => e.playtype === null || e.playtype === req.params.playtype, - ); - - const body = []; - - for (const playlist of playlists) { - body.push({ - forSpecificUser: playlist.forSpecificUser, - urlName: playlist.urlName, - playlistName: playlist.playlistName, - description: playlist.description, - }); - } - - return res.status(200).json({ - success: true, - description: `Found ${playlists.length} playlist(s)`, - body: playlists, - }); -}); - -/** - * Retrieve this playlist. - * - * @name GET /api/v1/games/iidx/:playtype/playlists/:playlistID - */ -router.get( - "/:playtype/playlists/:playlistID", - ValidatePlaytypeFromParamFor("iidx"), - async (req, res) => { - const playlist: TachiIIDXPlaylist | undefined = CUSTOM_TACHI_IIDX_PLAYLISTS.find( - (e) => - (e.playtype === null || e.playtype === req.params.playtype) && - e.urlName === req.params.playlistID, - ); - - if (!playlist) { - return res.status(404).json({ - success: false, - description: `No such playlist '${req.params.playlistID}' exists for '${req.params.playtype}'.`, - }); - } - - if (playlist.forSpecificUser === true) { - return res.status(404).json({ - success: false, - description: `This playlist is for a specific user. Use the /users/:userID endpoint instead.`, - }); - } - - const body = await playlist.getPlaylists(req.params.playtype as "DP" | "SP"); - - return res.status(200).json(body); - }, -); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/games/@gameSpecificRoutes/router.ts b/typescript/server/src/server/router/api/v1postgres/games/@gameSpecificRoutes/router.ts deleted file mode 100644 index 04783afdf..000000000 --- a/typescript/server/src/server/router/api/v1postgres/games/@gameSpecificRoutes/router.ts +++ /dev/null @@ -1,14 +0,0 @@ -// This file is special. These routes are "gptSpecific". They only apply to certain games -// and playtypes. This is for things like - say - custom BMS tables. - -import { Router } from "express"; - -import bmsRouter from "./bms/router"; -import iidxRouter from "./iidx/router"; - -const router: Router = Router({ mergeParams: true }); - -router.use("/bms", bmsRouter); -router.use("/iidx", iidxRouter); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/charts/_chartID/middleware.ts b/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/charts/_chartID/middleware.ts deleted file mode 100644 index f51db76e0..000000000 --- a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/charts/_chartID/middleware.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { RequestHandler } from "express"; - -import db from "#services/mongo/db"; -import { AssignToReqTachiData, GetGPT } from "#utils/req-tachi-data"; - -export const ValidateAndGetChart: RequestHandler = async (req, res, next) => { - const { game, playtype } = GetGPT(req); - - const chart = await db.anyCharts[game].findOne({ - chartID: req.params.chartID, - - // technically redundant, but we're under playtypes here URL wise. - // this means we cant match an SP chart when we're under IIDX SP, for - // example. - playtype, - }); - - if (!chart) { - return res.status(404).json({ - success: false, - description: `The chart ${req.params.chartID} does not exist.`, - }); - } - - AssignToReqTachiData(req, { chartDoc: chart }); - - next(); -}; diff --git a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/charts/_chartID/router.ts b/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/charts/_chartID/router.ts deleted file mode 100644 index 1ca909242..000000000 --- a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/charts/_chartID/router.ts +++ /dev/null @@ -1,182 +0,0 @@ -import type { FilterQuery } from "mongodb"; - -import { log } from "#lib/log/log.js"; -import { SearchUsersRegExp } from "#lib/search/search"; -import db from "#services/mongo/db"; -import { IsString } from "#utils/misc"; -import { GetTachiData } from "#utils/req-tachi-data"; -import { ParseStrPositiveNonZeroInt } from "#utils/string-checks"; -import { GetUsersWithIDs } from "#utils/user"; -import { Router } from "express"; -import { type FolderDocument, FormatChart } from "tachi-common"; - -import { ValidateAndGetChart } from "./middleware"; - -const router: Router = Router({ mergeParams: true }); - -router.use(ValidateAndGetChart); - -/** - * Returns the chart (and the parent song) at this chart ID. - * - * @name GET /api/v1/games/:game/:playtype/charts/:chartID - */ -router.get("/", async (req, res) => { - const chart = GetTachiData(req, "chartDoc"); - const game = GetTachiData(req, "game"); - - const song = await db.anySongs[game].findOne({ - id: chart.songID, - }); - - if (!song) { - log.error( - `Song ${chart.songID} does not exist, yet chart ${chart.chartID} has it as a parent?`, - ); - - return res.status(500).json({ - success: false, - description: `An internal server error has occured.`, - }); - } - - return res.status(200).json({ - success: true, - description: `Returned ${FormatChart(game, song, chart)}.`, - body: { - song, - chart, - }, - }); -}); - -/** - * Returns any folders that contain this chart. - * - * @param inactive - Also include inactive folders. - * - * @name GET /api/v1/games/:game/:playtype/charts/:chartID/folders - */ -router.get("/folders", async (req, res) => { - const chart = GetTachiData(req, "chartDoc"); - - const folderIDs = await db["folder-chart-lookup"].find( - { - chartID: chart.chartID, - }, - { - projection: { - folderID: 1, - }, - }, - ); - - const query: FilterQuery = { - folderID: { $in: folderIDs.map((e) => e.folderID) }, - }; - - if (req.query.inactive === undefined) { - query.inactive = false; - } - - const folders = await db.folders.find(query); - - return res.status(200).json({ - success: true, - description: `Found ${folders.length} folders that contain this chart.`, - body: folders, - }); -}); - -/** - * Returns the total amount of unique players that have played this chart. - * - * @name GET /api/v1/games/:game/:playtype/charts/:chartID/playcount - */ -router.get("/playcount", async (req, res) => { - const chart = GetTachiData(req, "chartDoc"); - - const count = await db["personal-bests"].count({ chartID: chart.chartID }); - - return res.status(200).json({ - success: true, - description: `Counted scores for chart.`, - body: { - count, - }, - }); -}); - -/** - * Returns the personal bests for this chart in batches of 100. - * These are returned sorted by their ranking. - * - * @param startRanking - The ranking to start iterating from - defaults to 1. - * - * @name GET /api/v1/games/:game/:playtype/charts/:chartID/pbs - */ -router.get("/pbs", async (req, res) => { - const chart = GetTachiData(req, "chartDoc"); - - const startRanking = ParseStrPositiveNonZeroInt(req.query.startRanking) ?? 1; - - const pbs = await db["personal-bests"].find( - { - chartID: chart.chartID, - "rankingData.rank": { $gte: startRanking }, - }, - { - limit: 100, - sort: { - "rankingData.rank": 1, - }, - }, - ); - - const users = await GetUsersWithIDs(pbs.map((e) => e.userID)); - - return res.status(200).json({ - success: true, - description: `Returned ${pbs.length} scores.`, - body: { - pbs, - users, - }, - }); -}); - -/** - * Searches the PBs on this chart for the given user(s). - * - * @param search - The user to search for - * - * @name GET /api/v1/games/:game/:playtype/charts/:chartID/pbs/search - */ -router.get("/pbs/search", async (req, res) => { - const chart = GetTachiData(req, "chartDoc"); - - if (!IsString(req.query.search)) { - return res.status(400).json({ - success: false, - description: `Invalid parameter for search.`, - }); - } - - const users = await SearchUsersRegExp(req.query.search); - - const pbs = await db["personal-bests"].find({ - chartID: chart.chartID, - userID: { $in: users.map((e) => e.id) }, - }); - - return res.status(200).json({ - success: true, - description: `Returned ${pbs.length} scores.`, - body: { - pbs, - users, - }, - }); -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/charts/router.test.ts b/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/charts/router.test.ts deleted file mode 100644 index 291187b2d..000000000 --- a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/charts/router.test.ts +++ /dev/null @@ -1,220 +0,0 @@ -import db from "#services/mongo/db"; -import { mkFakePBIIDXSP } from "#test-utils/misc"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import { LoadTachiIIDXData, Testing511SPA } from "#test-utils/test-data"; -import t from "tap"; - -t.test("GET /api/v1/games/:game/:playtype/charts", (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(LoadTachiIIDXData); - - t.test("Should return the most popular charts if no param is set.", async (t) => { - await db["personal-bests"].insert([ - mkFakePBIIDXSP({ - chartID: Testing511SPA.chartID, - userID: 1, - }), - mkFakePBIIDXSP({ - chartID: Testing511SPA.chartID, - userID: 2, - }), - mkFakePBIIDXSP({ - chartID: Testing511SPA.chartID, - userID: 3, - }), - mkFakePBIIDXSP({ - // gambol hyper - songID: 7, - chartID: "fc7edc6bcfa701a261c89c999ddbba3e2195597b", - userID: 1, - }), - ]); - - const res = await mockApi.get("/api/v1/games/iidx/SP/charts"); - - t.hasStrict(res.body.body.charts[0], { - __playcount: 3, - chartID: Testing511SPA.chartID, - }); - - t.hasStrict(res.body.body.charts[1], { - __playcount: 1, - chartID: "fc7edc6bcfa701a261c89c999ddbba3e2195597b", - }); - - t.equal(res.body.body.charts.length, 100); - - t.end(); - }); - - t.test("Should search charts if a search param is set.", async (t) => { - await db["personal-bests"].insert([ - mkFakePBIIDXSP({ - // gambol hyper - songID: 7, - chartID: "fc7edc6bcfa701a261c89c999ddbba3e2195597b", - userID: 1, - }), - ]); - - const res = await mockApi.get("/api/v1/games/iidx/SP/charts?search=gambol"); - - t.hasStrict(res.body.body.charts[0], { - __playcount: 1, - chartID: "fc7edc6bcfa701a261c89c999ddbba3e2195597b", - }); - - // gambol has SPB, SPN and SPH - t.equal(res.body.body.charts.length, 3); - - t.end(); - }); - - t.test( - "Should only return charts the requester has played if requesterHasPlayed is set.", - async (t) => { - await db["personal-bests"].insert([ - mkFakePBIIDXSP({ - // gambol hyper - songID: 7, - chartID: "fc7edc6bcfa701a261c89c999ddbba3e2195597b", - userID: 2, - }), - mkFakePBIIDXSP({ - songID: 1, - chartID: Testing511SPA.chartID, - userID: 1, - }), - ]); - - const res = await mockApi - .get("/api/v1/games/iidx/SP/charts?requesterHasPlayed=true") - .set("Authorization", "Bearer fake_api_token"); - - t.hasStrict(res.body.body.charts[0], { - __playcount: 1, - chartID: Testing511SPA.chartID, - }); - - // The user has played 5.1.1., but not anything else loaded in the db. - // note that this endpoint works on played songs, rather than played charts. - t.equal(res.body.body.charts.length, 4); - - t.end(); - }, - ); - - t.test( - "Should only return charts the requester has played if requesterHasPlayed is set, and work with searches at the same time.", - async (t) => { - await db["personal-bests"].insert([ - mkFakePBIIDXSP({ - // gambol hyper - songID: 7, - chartID: "fc7edc6bcfa701a261c89c999ddbba3e2195597b", - userID: 1, - }), - mkFakePBIIDXSP({ - songID: 1, - chartID: Testing511SPA.chartID, - userID: 1, - }), - ]); - - const res = await mockApi - .get("/api/v1/games/iidx/SP/charts?requesterHasPlayed=true&search=gambol") - .set("Authorization", "Bearer fake_api_token"); - - t.hasStrict(res.body.body.charts[0], { - __playcount: 1, - chartID: "fc7edc6bcfa701a261c89c999ddbba3e2195597b", - }); - - // gambol has SPB, SPN and SPH, but only SPH has been played by the requester - // although 5.1.1 has been played by the requester, it should not match the - // search. - t.equal(res.body.body.charts.length, 3); - - t.end(); - }, - ); - - t.end(); -}); - -t.test("POST /api/v1/games/:game/:playtype/charts/resolve", (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(LoadTachiIIDXData); - - t.test("Should resolve a chart using tachiSongID matchType.", async (t) => { - const res = await mockApi.post("/api/v1/games/iidx/SP/charts/resolve").send({ - matchType: "tachiSongID", - identifier: "1", - difficulty: "ANOTHER", - }); - - t.equal(res.statusCode, 200); - t.equal(res.body.success, true); - t.equal(res.body.body.chart.chartID, Testing511SPA.chartID); - t.equal(res.body.body.song.id, 1); - t.equal(res.body.body.song.title, "5.1.1."); - - t.end(); - }); - - t.test("Should resolve a chart using songTitle matchType.", async (t) => { - const res = await mockApi.post("/api/v1/games/iidx/SP/charts/resolve").send({ - matchType: "songTitle", - identifier: "5.1.1.", - difficulty: "ANOTHER", - }); - - t.equal(res.statusCode, 200); - t.equal(res.body.success, true); - t.equal(res.body.body.chart.chartID, Testing511SPA.chartID); - t.equal(res.body.body.song.id, 1); - - t.end(); - }); - - t.test("Should return 404 when chart cannot be resolved.", async (t) => { - const res = await mockApi.post("/api/v1/games/iidx/SP/charts/resolve").send({ - matchType: "tachiSongID", - identifier: "99999", - difficulty: "ANOTHER", - }); - - t.equal(res.statusCode, 404); - t.equal(res.body.success, false); - t.match(res.body.description, /Could not resolve this chart/u); - - t.end(); - }); - - t.test("Should return 400 for invalid request body.", async (t) => { - const res = await mockApi.post("/api/v1/games/iidx/SP/charts/resolve").send({ - matchType: "invalidMatchType", - identifier: "1", - }); - - t.equal(res.statusCode, 400); - t.equal(res.body.success, false); - - t.end(); - }); - - t.test("Should return 400 when required fields are missing.", async (t) => { - const res = await mockApi.post("/api/v1/games/iidx/SP/charts/resolve").send({ - matchType: "tachiSongID", - // missing identifier - }); - - t.equal(res.statusCode, 400); - t.equal(res.body.success, false); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/charts/router.ts b/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/charts/router.ts deleted file mode 100644 index 547cde4b9..000000000 --- a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/charts/router.ts +++ /dev/null @@ -1,164 +0,0 @@ -import { SYMBOL_TACHI_API_AUTH } from "#lib/constants/tachi"; -import { log } from "#lib/log/log.js"; -import { ResolveSongAndChart } from "#lib/score-import/import-types/common/batch-manual/converter"; -import { SearchSpecificGameSongs } from "#lib/search/search"; -import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; -import { IsString } from "#utils/misc"; -import { FindChartsOnPopularity } from "#utils/queries/charts"; -import { GetGPT } from "#utils/req-tachi-data"; -import { Router } from "express"; -import { - type ChartDocument, - type integer, - type MatchTypeResolver, - type UGPTSettingsDocument, -} from "tachi-common"; -import { PR_RESOLVER } from "tachi-common/lib/schemas"; - -import chartIDRouter from "./_chartID/router"; - -const router: Router = Router({ mergeParams: true }); - -/** - * Searches for charts on this game - if no search parameter is given, - * returns the 100 most popular charts for this game. - * - * @param search - The song title to match on. - * @param noIntelligentOmit - If present, will not perform intelligent - * chart omissions from results. - * @param requesterHasPlayed - If present, will only return charts the - * requesting user has a PB on. If this request doesn't belong to a user, - * this returns 401. - * - * @name GET /api/v1/games/:game/:playtype/charts - */ -router.get("/", async (req, res) => { - const { game, playtype } = GetGPT(req); - - let songIDs: Array | undefined; - - if (IsString(req.query.search)) { - const songs = await SearchSpecificGameSongs(game, req.query.search, 100); - - songIDs = songs.map((e) => e.id); - } - - if (IsString(req.query.requesterHasPlayed)) { - const userID = req[SYMBOL_TACHI_API_AUTH].userID; - - if (userID === null) { - return res.status(401).json({ - success: false, - description: `You must be authorised as a user to use the requesterHasPlayed option.`, - }); - } - - const playedSongs = ( - await db["personal-bests"].find( - { userID, game, playtype }, - { projection: { songID: 1 } }, - ) - ).map((e) => e.songID); - - if (songIDs) { - songIDs = songIDs.filter((e) => playedSongs.includes(e)); - } else { - songIDs = playedSongs; - } - } - - const skip = 0; - const limit = 100; - - let charts = (await FindChartsOnPopularity( - game, - playtype, - - // if empty, we want the set of all songs. Otherwise, constrict input. - songIDs, - skip, - limit, - "personal-bests", - )) as Array; - - // @optimisable - // could use songIDs from above instead of refetching - // but this is not very expensive. - const songs = await db.anySongs[game].find({ - id: { $in: charts.map((e) => e.songID) }, - }); - - // Edge case. - // If the game is IIDX and the player does not want - // to see 2dxtra charts, we need to remove them from the - // result of a search. - // - // Since most players will have this off, this is not a significant - // performance hit. - if (game === "iidx" && req.query.noIntelligentOmit === undefined) { - if (req[SYMBOL_TACHI_API_AUTH].userID === null) { - charts = charts.filter( - (e) => (e as ChartDocument<"iidx:DP" | "iidx:SP">).data["2dxtraSet"] === null, - ); - } else { - const iidxSettings = (await db["game-settings"].findOne({ - userID: req[SYMBOL_TACHI_API_AUTH].userID, - game, - playtype, - })) as UGPTSettingsDocument<"iidx:DP" | "iidx:SP"> | null; - - if (!iidxSettings?.preferences.gameSpecific.display2DXTra) { - charts = charts.filter( - (e) => (e as ChartDocument<"iidx:DP" | "iidx:SP">).data["2dxtraSet"] === null, - ); - } - } - } - - return res.status(200).json({ - success: true, - description: `Returned ${charts.length} charts.`, - body: { - charts, - songs, - }, - }); -}); - -/** - * Use the tachi "resolve" engine to identify a chart instead of - * using the Tachi IDs. Used to get a chart. - * - * @name POST /api/v1/users/:userID/games/:game/:playtype/pbs/resolve - */ -router.post("/resolve", prValidate(PR_RESOLVER), async (req, res) => { - const { game, playtype } = GetGPT(req); - - const safeBody = { - ...req.safeBody, - game, - playtype, - } as unknown as MatchTypeResolver; - const got = await ResolveSongAndChart(safeBody, log); - - if (!got) { - return res.status(404).json({ - success: false, - description: `Could not resolve this chart with details: ${safeBody.matchType}:${safeBody.identifier} (Extra specifiers: version=${safeBody.version}, artist=${safeBody.artist})`, - }); - } - - return res.status(200).json({ - success: true, - description: "Successfully retrieved chart info.", - body: { - chart: got.chart, - song: got.song, - }, - }); -}); - -router.use("/:chartID", chartIDRouter); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/folders/middleware.ts b/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/folders/middleware.ts deleted file mode 100644 index 8d0c5a2b6..000000000 --- a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/folders/middleware.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { RequestHandler } from "express"; - -import db from "#services/mongo/db"; -import { AssignToReqTachiData, GetGPT } from "#utils/req-tachi-data"; - -export const GetFolderFromParam: RequestHandler = async (req, res, next) => { - const { game, playtype } = GetGPT(req); - - const folder = await db.folders.findOne({ folderID: req.params.folderID, game, playtype }); - - if (!folder) { - return res.status(404).json({ - success: false, - description: `This folder does not exist.`, - }); - } - - AssignToReqTachiData(req, { folderDoc: folder }); - - next(); -}; diff --git a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/folders/router.ts b/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/folders/router.ts deleted file mode 100644 index 84f50e565..000000000 --- a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/folders/router.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { SearchCollection } from "#lib/search/search"; -import db from "#services/mongo/db"; -import { GetFolderCharts } from "#utils/folder"; -import { IsString } from "#utils/misc"; -import { GetGPT, GetTachiData } from "#utils/req-tachi-data"; -import { Router } from "express"; - -import { GetFolderFromParam } from "./middleware"; - -const router: Router = Router({ mergeParams: true }); - -/** - * Search the folders for this GPT. - * - * @param search - The query to search for. - * @param inactive - Also show inactive folders, such as those on old versions. - * - * @name GET /api/v1/games/:game/:playtype/folders - */ -router.get("/", async (req, res) => { - const { game, playtype } = GetGPT(req); - - if (!IsString(req.query.search)) { - return res.status(400).json({ - success: false, - description: `Invalid value for search.`, - }); - } - - // if inactive is passed, we need this to be undefined so that - // mongodb returns both inactive and active folders. - // Otherwise, only return active folders. - const inactive = req.query.inactive === undefined ? false : undefined; - - const folders = await SearchCollection( - db.folders, - req.query.search, - "folders", - { game, playtype, inactive }, - 100, - ); - - return res.status(200).json({ - success: true, - description: `Returned ${folders.length} folders.`, - body: folders, - }); -}); - -/** - * Get the folder at this ID, alongside its charts and songs. - * - * @name GET /api/v1/games/:game/:playtype/folders/:folderID - */ -router.get("/:folderID", GetFolderFromParam, async (req, res) => { - const folder = GetTachiData(req, "folderDoc"); - - const { songs, charts } = await GetFolderCharts(folder, {}, true); - - return res.status(200).json({ - success: true, - description: `Returned data for folder ${folder.title}`, - body: { - songs, - charts, - folder, - }, - }); -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/middleware.ts b/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/middleware.ts deleted file mode 100644 index e69cfd65b..000000000 --- a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/middleware.ts +++ /dev/null @@ -1,38 +0,0 @@ -import type { RequestHandler } from "express"; - -import { AssignToReqTachiData, GetTachiData } from "#utils/req-tachi-data"; -import { type GameGroup, GetGameGroupConfig, type Playtype } from "tachi-common"; - -export const ValidatePlaytypeFromParam: RequestHandler = (req, res, next) => { - const game = GetTachiData(req, "game"); - - const gameConfig = GetGameGroupConfig(game); - - if (!gameConfig.playtypes.includes(req.params.playtype as Playtype)) { - return res.status(400).json({ - success: false, - description: `The playtype ${req.params.playtype} is not supported.`, - }); - } - - AssignToReqTachiData(req, { playtype: req.params.playtype as Playtype }); - - next(); -}; - -export const ValidatePlaytypeFromParamFor = - (game: GameGroup): RequestHandler => - (req, res, next) => { - const gameConfig = GetGameGroupConfig(game); - - if (!gameConfig.playtypes.includes(req.params.playtype as Playtype)) { - return res.status(400).json({ - success: false, - description: `The playtype ${req.params.playtype} is not supported.`, - }); - } - - AssignToReqTachiData(req, { playtype: req.params.playtype as Playtype }); - - next(); - }; diff --git a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/router.test.ts b/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/router.test.ts deleted file mode 100644 index d8ba31ba6..000000000 --- a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/router.test.ts +++ /dev/null @@ -1,165 +0,0 @@ -import db from "#services/mongo/db"; -import { mkFakeGameStats, mkFakeUser } from "#test-utils/misc"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import { FakeOtherUser } from "#test-utils/test-data"; -import dm from "deepmerge"; -import { GetGamePTConfig, type UserDocument, type UserGameStats } from "tachi-common"; -import t from "tap"; - -t.test("GET /api/v1/games/:game/:playtype", (t) => { - t.test("Should return information about the game:playtype.", async (t) => { - const res = await mockApi.get("/api/v1/games/iidx/SP"); - - t.hasStrict(GetGamePTConfig("iidx", "SP"), res.body.body.config); - - t.equal(res.body.body.chartCount, 1); - t.equal(res.body.body.playerCount, 1); - t.equal(res.body.body.scoreCount, 1); - - t.end(); - }); - - t.test("Should reject invalid playtypes for this game.", async (t) => { - const res = await mockApi.get("/api/v1/games/iidx/Single"); - - t.equal(res.statusCode, 400); - - t.end(); - }); - - t.end(); -}); - -t.test("GET /api/v1/games/:game/:playtype/leaderboard", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should return the leaderboards for this game", async (t) => { - const res = await mockApi.get("/api/v1/games/iidx/SP/leaderboard"); - - t.equal(res.statusCode, 200); - t.equal(res.body.body.gameStats.length, 1); - t.equal(res.body.body.users.length, 1); - - t.hasStrict(res.body.body, { - gameStats: [ - { - userID: 1, - game: "iidx", - playtype: "SP", - }, - ], - users: [ - { - id: 1, - }, - ], - }); - - t.end(); - }); - - t.test("Should reject unknown alg", async (t) => { - const res = await mockApi.get("/api/v1/games/iidx/SP/leaderboard?alg=naiveRating"); - - t.equal(res.statusCode, 400); - - t.end(); - }); - - t.test("Should use provided algorithm to resort results.", async (t) => { - await db["game-stats"].insert([ - { - userID: 2, - game: "iidx", - playtype: "SP", - ratings: { - BPI: 100, - }, - }, - { - userID: 3, - game: "iidx", - playtype: "SP", - ratings: { - BPI: 50, - }, - }, - ] as Array); - - await db.users.insert([ - FakeOtherUser, - dm(FakeOtherUser, { - username: "foo", - usernameLowercase: "foo", - id: 3, - }) as UserDocument, - ]); - - const res = await mockApi.get("/api/v1/games/iidx/SP/leaderboard?alg=BPI"); - - t.strictSame( - res.body.body.gameStats.map((e: UserGameStats) => e.userID), - [2, 3, 1], - ); - - t.end(); - }); - - t.end(); -}); - -t.test("GET /api/v1/games/:game/:playtype/players", (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(async () => { - await db.users.insert([ - mkFakeUser(2, { usernameLowercase: "scrimblo" }), - mkFakeUser(3, { usernameLowercase: "scrimblo_2" }), - mkFakeUser(4, { usernameLowercase: "scrimblo_3" }), - mkFakeUser(5, { usernameLowercase: "cloudy" }), - ]); - - await db["game-stats"].insert([ - mkFakeGameStats(2), - mkFakeGameStats(3, { game: "iidx", playtype: "DP" }), - mkFakeGameStats(4, { game: "bms", playtype: "7K" }), - mkFakeGameStats(5), - ]); - }); - - t.test("Should find the users where this game has been played.", async (t) => { - const res = await mockApi.get("/api/v1/games/iidx/SP/players?search=scrimblo"); - - t.hasStrict(res.body.body, [{ id: 2 }]); - t.equal(res.body.body.length, 1); - - t.end(); - }); - - t.test("Should definitely honour the search parameter.", async (t) => { - const res = await mockApi.get("/api/v1/games/iidx/SP/players?search=nobody"); - - t.strictSame(res.body.body, []); - - t.end(); - }); - - t.test("Should definitely honour the GPT.", async (t) => { - const res = await mockApi.get("/api/v1/games/iidx/DP/players?search=scrimblo"); - - t.hasStrict(res.body.body, [{ id: 3 }]); - t.equal(res.body.body.length, 1); - - t.end(); - }); - - t.test("Should require the search parameter.", async (t) => { - const res = await mockApi.get("/api/v1/games/iidx/DP/players"); - - t.equal(res.statusCode, 400); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/router.ts b/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/router.ts deleted file mode 100644 index a1488b1fb..000000000 --- a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/router.ts +++ /dev/null @@ -1,285 +0,0 @@ -import type { FindOptions } from "monk"; - -import { CreateActivityRouteHandler } from "#lib/activity/activity"; -import { ONE_HOUR } from "#lib/constants/time"; -import { SearchUsersRegExp } from "#lib/search/search"; -import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; -import { GetRelevantSongsAndCharts } from "#utils/db"; -import { IsString } from "#utils/misc"; -import { GetGPT } from "#utils/req-tachi-data"; -import { - CheckStrProfileAlg, - CheckStrScoreAlg, - ParseStrPositiveNonZeroInt, -} from "#utils/string-checks"; -import { GetUsersWithIDs } from "#utils/user"; -import { Router } from "express"; -import NodeCache from "node-cache"; -import { - FormatGameGroup, - type GameGroup, - GetGamePTConfig, - type integer, - type Playtype, - type UserGameStats, -} from "tachi-common"; - -import chartsRouter from "./charts/router"; -import foldersRouter from "./folders/router"; -import { ValidatePlaytypeFromParam } from "./middleware"; -import songIDRouter from "./songs/_songID/router"; -import tablesRouter from "./tables/router"; -import targetsRouter from "./targets/router"; - -const router: Router = Router({ mergeParams: true }); - -router.use(ValidatePlaytypeFromParam); - -const gptStatCache = new NodeCache(); - -async function GetGameStats( - game: GameGroup, - playtype: Playtype, -): Promise<{ chartCount: integer; playerCount: integer; scoreCount: integer }> { - const cacheRes = gptStatCache.get(`${game}:${playtype}`); - - if (cacheRes === undefined) { - const [scoreCount, playerCount, chartCount] = await Promise.all([ - db.scores.count({ - game, - playtype, - }), - db["game-stats"].count({ - game, - playtype, - }), - db.anyCharts[game].count({ playtype }), - ]); - - gptStatCache.set(`${game}:${playtype}`, { scoreCount, playerCount, chartCount }, ONE_HOUR); - - return { scoreCount, playerCount, chartCount }; - } - - return cacheRes as { chartCount: integer; playerCount: integer; scoreCount: integer }; -} - -/** - * Returns the configuration for this game along with some statistics. - * - * @name GET /api/v1/games/:game/:playtype - */ -router.get("/", async (req, res) => { - const { game, playtype } = GetGPT(req); - - const { scoreCount, playerCount, chartCount } = await GetGameStats(game, playtype); - - return res.status(200).json({ - success: true, - description: `Retrieved information about ${FormatGameGroup(game, playtype)}`, - body: { - config: GetGamePTConfig(game, playtype), - scoreCount, - playerCount, - chartCount, - }, - }); -}); - -/** - * Returns user-game-stats for this game in batches of 500. - * This is sorted by the games default-sorting-statistic. - * - * @param alg - An alternative algorithm to use instead of the gpts default. - * @param limit - How many users to return at most. Defaults (and is limited to) 500. - * - * @name GET /api/v1/games/:game/:playtype/leaderboard - */ -router.get("/leaderboard", async (req, res) => { - const { game, playtype } = GetGPT(req); - const gptConfig = GetGamePTConfig(game, playtype); - - const limit = ParseStrPositiveNonZeroInt(req.query.limit) ?? 100; - - if (limit > 500) { - return res.status(400).json({ - success: false, - description: `Invalid limit. Limit is capped at 500.`, - }); - } - - let alg = gptConfig.defaultProfileRatingAlg; - - if (IsString(req.query.alg)) { - const temp = CheckStrProfileAlg(game, playtype, req.query.alg); - - if (temp === null) { - return res.status(400).json({ - success: false, - description: `Invalid value of ${ - req.query.alg - } for alg. Expected one of ${Object.keys(gptConfig.profileRatingAlgs).join(", ")}`, - }); - } - - alg = temp; - } - - const options: FindOptions = { - sort: { - [`ratings.${alg}`]: -1, - }, - limit, - }; - - const gameStats = await db["game-stats"].find( - { - game, - playtype, - }, - options, - ); - - const users = await GetUsersWithIDs(gameStats.map((e) => e.userID)); - - return res.status(200).json({ - success: true, - description: `Returned ${gameStats.length} user's game stats.`, - body: { - gameStats, - users, - }, - }); -}); - -/** - * Returns the best scores for this game. - * - * @param alg - An alternative algorithm to use instead of the gpts default. - * @param limit - How many scores to return. - * - * @name GET /api/v1/games/:game/:playtype/pb-leaderboard - */ -router.get("/pb-leaderboard", async (req, res) => { - const { game, playtype } = GetGPT(req); - const gptConfig = GetGamePTConfig(game, playtype); - - const limit = ParseStrPositiveNonZeroInt(req.query.limit) ?? 50; - - if (limit > 50) { - return res.status(400).json({ - success: false, - description: `Cannot specify a limit higher than 50.`, - }); - } - - let alg = gptConfig.defaultScoreRatingAlg; - - if (IsString(req.query.alg)) { - const temp = CheckStrScoreAlg(game, playtype, req.query.alg); - - if (temp === null) { - return res.status(400).json({ - success: false, - description: `Invalid value of ${ - req.query.alg - } for alg. Expected one of ${Object.keys(gptConfig.profileRatingAlgs).join(", ")}`, - }); - } - - alg = temp; - } - - const pbs = await db["personal-bests"].find( - { - game, - playtype, - }, - { - sort: { - [`calculatedData.${alg}`]: -1, - }, - limit, - }, - ); - - const users = await GetUsersWithIDs(pbs.map((e) => e.userID)); - - const { songs, charts } = await GetRelevantSongsAndCharts(pbs, game); - - return res.status(200).send({ - success: true, - description: `Successfully returned ${pbs.length} pbs.`, - body: { - pbs, - songs, - charts, - users, - }, - }); -}); - -/** - * Search users that have played this game. - * - * @param search - The username to search for. - * - * @name GET /api/v1/games/:game/:playtype/players - */ -router.get( - "/players", - prValidate({ - search: "string", - }), - async (req, res) => { - const { game, playtype } = GetGPT(req); - - const { search } = req.query as { - search: string; - }; - - const users = await SearchUsersRegExp(search); - - const gameStats = await db["game-stats"].find({ - userID: { $in: users.map((e) => e.id) }, - game, - playtype, - }); - - const thoseWithStats = gameStats.map((e) => e.userID); - - const gptPlayers = users.filter((e) => thoseWithStats.includes(e.id)); - - return res.status(200).json({ - success: true, - description: `Found ${gptPlayers.length} user(s)`, - body: gptPlayers, - }); - }, -); - -/** - * Retrieve activity for this GPT. - * - * @name GET /api/v1/games/:game/:playtype/activity - */ -router.get("/activity", (req, res) => { - const { game, playtype } = GetGPT(req); - - const route = CreateActivityRouteHandler({ - game, - playtype, - }); - - // this handles responding - void route(req, res); -}); - -router.use("/charts", chartsRouter); -router.use("/songs/:songID", songIDRouter); -router.use("/folders", foldersRouter); -router.use("/tables", tablesRouter); -router.use("/targets", targetsRouter); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/songs/_songID/middleware.ts b/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/songs/_songID/middleware.ts deleted file mode 100644 index 304c51276..000000000 --- a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/songs/_songID/middleware.ts +++ /dev/null @@ -1,31 +0,0 @@ -import type { RequestHandler } from "express"; - -import db from "#services/mongo/db"; -import { AssignToReqTachiData, GetTachiData } from "#utils/req-tachi-data"; -import { ParseStrPositiveInt } from "#utils/string-checks"; - -export const ValidateAndGetSong: RequestHandler = async (req, res, next) => { - const songID = ParseStrPositiveInt(req.params.songID); - - if (songID === null) { - return res.status(400).json({ - success: false, - description: `Invalid songID - could not be converted into integer?`, - }); - } - - const game = GetTachiData(req, "game"); - - const song = await db.anySongs[game].findOne({ id: songID }); - - if (!song) { - return res.status(404).json({ - success: false, - description: `No song exists with the songID ${songID}.`, - }); - } - - AssignToReqTachiData(req, { songDoc: song }); - - next(); -}; diff --git a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/songs/_songID/router.test.ts b/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/songs/_songID/router.test.ts deleted file mode 100644 index 8b1f49abe..000000000 --- a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/songs/_songID/router.test.ts +++ /dev/null @@ -1,41 +0,0 @@ -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import { LoadTachiIIDXData } from "#test-utils/test-data"; -import t from "tap"; - -t.test("GET /api/v1/games/:game/:playtype/songs/:songID", (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(LoadTachiIIDXData); - - t.test("Should return the song at this ID and all of its charts.", async (t) => { - const res = await mockApi.get("/api/v1/games/iidx/SP/songs/1"); - - t.equal(res.body.body.charts.length, 4); - - t.equal(res.body.body.song.id, 1); - - t.end(); - }); - - t.test("Should return 404 if this songID does not exist.", async (t) => { - const res = await mockApi.get("/api/v1/games/iidx/SP/songs/0"); - - t.equal(res.statusCode, 404); - - t.end(); - }); - - t.test("Should return 400 if songID is not coercible into an integer.", async (t) => { - const res = await mockApi.get("/api/v1/games/iidx/SP/songs/1.5"); - - t.equal(res.statusCode, 400); - - const res2 = await mockApi.get("/api/v1/games/iidx/SP/songs/FOO"); - - t.equal(res2.statusCode, 400); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/songs/_songID/router.ts b/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/songs/_songID/router.ts deleted file mode 100644 index 3470a592c..000000000 --- a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/songs/_songID/router.ts +++ /dev/null @@ -1,35 +0,0 @@ -import db from "#services/mongo/db"; -import { GetGPT, GetTachiData } from "#utils/req-tachi-data"; -import { Router } from "express"; - -import { ValidateAndGetSong } from "./middleware"; - -const router: Router = Router({ mergeParams: true }); - -router.use(ValidateAndGetSong); - -/** - * Returns the song at this ID and its child chart documents. - * - * @name GET /api/v1/games/:game/:playtype/songs/:songID - */ -router.get("/", async (req, res) => { - const song = GetTachiData(req, "songDoc"); - const { game, playtype } = GetGPT(req); - - const charts = await db.anyCharts[game].find({ - songID: song.id, - playtype, - }); - - return res.status(200).json({ - success: true, - description: `Returned ${charts.length} charts for song ${song.title}.`, - body: { - song, - charts, - }, - }); -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/tables/middleware.ts b/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/tables/middleware.ts deleted file mode 100644 index a3574f1d9..000000000 --- a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/tables/middleware.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { RequestHandler } from "express"; - -import db from "#services/mongo/db"; -import { AssignToReqTachiData, GetTachiData } from "#utils/req-tachi-data"; - -export const GetTableFromParam: RequestHandler = async (req, res, next) => { - const game = GetTachiData(req, "game"); - const playtype = GetTachiData(req, "playtype"); - - const table = await db.tables.findOne({ tableID: req.params.tableID, game, playtype }); - - if (!table) { - return res.status(404).json({ - success: false, - description: `This table does not exist.`, - }); - } - - AssignToReqTachiData(req, { tableDoc: table }); - - next(); -}; diff --git a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/tables/router.ts b/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/tables/router.ts deleted file mode 100644 index 2e8217fd0..000000000 --- a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/tables/router.ts +++ /dev/null @@ -1,72 +0,0 @@ -import type { FilterQuery } from "mongodb"; - -import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; -import { GetFoldersFromTable } from "#utils/folder"; -import { GetGPT, GetTachiData } from "#utils/req-tachi-data"; -import { Router } from "express"; -import { FormatGameGroup, type TableDocument } from "tachi-common"; - -import { GetTableFromParam } from "./middleware"; - -const router: Router = Router({ mergeParams: true }); - -/** - * Return all the tables for this game. - * - * @param showInactive - If present, also show "inactive" tables. - * - * @name GET /api/v1/games/:game/:playtype/tables - */ -router.get("/", async (req, res) => { - const { game, playtype } = GetGPT(req); - - const query: FilterQuery = { game, playtype }; - - if (req.query.showInactive === undefined) { - query.inactive = false; - } - - const tables = await db.tables.find(query); - - if (tables.length === 0) { - log.error( - `The game ${FormatGameGroup( - game, - playtype, - )} has no tables. This renders table support for the game broken!`, - ); - return res.status(500).json({ - success: false, - description: "This game has no tables.", - }); - } - - return res.status(200).json({ - success: true, - description: `Returned ${tables.length} tables.`, - body: tables, - }); -}); - -/** - * Return the folder documents that make up this table. - * - * @name GET /api/v1/games/:game/:playtype/tables/:tableID - */ -router.get("/:tableID", GetTableFromParam, async (req, res) => { - const table = GetTachiData(req, "tableDoc"); - - const folders = await GetFoldersFromTable(table); - - return res.status(200).json({ - success: true, - description: `Returned ${folders.length} for table ${table.title}.`, - body: { - folders, - table, - }, - }); -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/targets/goals/router.ts b/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/targets/goals/router.ts deleted file mode 100644 index f46d0200b..000000000 --- a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/targets/goals/router.ts +++ /dev/null @@ -1,180 +0,0 @@ -import type { GoalDocument } from "tachi-common"; - -import { log } from "#lib/log/log.js"; -import { CreateGoalTitle, ValidateGoalChartsAndCriteria } from "#lib/targets/goal-utils"; -import { GetQuestsThatContainGoal } from "#lib/targets/goals"; -import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; -import { GetMostSubscribedGoals } from "#utils/db"; -import { AssignToReqTachiData, GetGPT, GetTachiData } from "#utils/req-tachi-data"; -import { GetUsersWithIDs } from "#utils/user"; -import { type RequestHandler, Router } from "express"; -import { p } from "prudence"; - -const router: Router = Router({ mergeParams: true }); - -/** - * Get the most popular goals for this GPT. - * - * @name GET /api/v1/games/:game/:playtype/targets/goals/popular - */ -router.get("/popular", async (req, res) => { - const { game, playtype } = GetGPT(req); - - const goals = await GetMostSubscribedGoals({ game, playtype }); - - return res.status(200).json({ - success: true, - description: `Returned ${goals.length} goals.`, - body: goals, - }); -}); - -/** - * Given a partial goal, return a name for it. This formats the goal into something like - * "AAA 100 charts in the Level 12 folder". - * - * This is used by the quest editor, as the controls for formatting charts are done - * on the backend. - * - * This is a post request because it expects nested data, and get requests suck - * for that. - * - * @name POST /api/v1/games/:game/:playtype/targets/goals/format - */ -router.post( - "/format", - prValidate( - { - criteria: { - // we do proper validation on this later. - key: "string", - value: p.gte(0), - - mode: p.isIn("single", "absolute", "proportion"), - countNum: (self, parent) => { - if (parent.mode === "single") { - return ( - self === undefined || - "Invalid countNum for mode 'single'. Must not have one!" - ); - } - - // proper validation later. - return p.gte(0)(self); - }, - }, - charts: { - type: p.isIn("single", "multi", "folder"), - data: (self, parent) => { - if (parent.type === "single") { - return ( - typeof self === "string" || - "Expected a string in charts.data due to charts.type being 'single'." - ); - } else if (parent.type === "multi") { - return ( - (Array.isArray(self) && - self.every((k) => typeof k === "string") && - self.length <= 10 && - self.length > 1) || - "Expected an array of 2 to 10 strings in charts.data due to charts.type being 'multi'." - ); - /* istanbul ignore next */ - } else if (parent.type === "folder") { - return ( - typeof self === "string" || - "Expected a string in charts.data due to charts.type being 'folder'." - ); - } - - // impossible to reach, so doesn't count for coverage. - /* istanbul ignore next */ - return "Unknown charts.type."; - }, - }, - }, - undefined, - undefined, - "debug", - ), - async (req, res) => { - const { game, playtype } = GetGPT(req); - - const { charts, criteria } = req.safeBody as { - charts: GoalDocument["charts"]; - criteria: GoalDocument["criteria"]; - }; - - try { - await ValidateGoalChartsAndCriteria(charts, criteria, game, playtype); - } catch (e) { - const err = e as Error; - - return res.status(400).json({ - success: false, - description: `Invalid goal: ${err.message}.`, - }); - } - - const title = await CreateGoalTitle(charts, criteria, game, playtype); - - return res.status(200).json({ - success: true, - description: `Formatted goal.`, - body: title, - }); - }, -); - -const ResolveGoalID: RequestHandler = async (req, res, next) => { - const { game, playtype } = GetGPT(req); - const goalID = req.params.goalID; - - const goal = await db.goals.findOne({ - goalID, - game, - playtype, - }); - - if (!goal) { - return res.status(404).json({ - success: false, - description: `A goal with ID ${goalID} doesn't exist.`, - }); - } - - AssignToReqTachiData(req, { goalDoc: goal }); - - next(); -}; - -/** - * Retrieve information about this goal and who is subscribed to it. - * - * @name GET /api/v1/games/:game/:playtype/targets/goals/:goalID - */ -router.get("/:goalID", ResolveGoalID, async (req, res) => { - const goal = GetTachiData(req, "goalDoc"); - - const goalSubs = await db["goal-subs"].find({ - goalID: goal.goalID, - }); - - const users = await GetUsersWithIDs(goalSubs.map((e) => e.userID)); - - const parentQuests = await GetQuestsThatContainGoal(goal.goalID); - - return res.status(200).json({ - success: true, - description: `Retrieved information about ${goal.name}.`, - body: { - goal, - goalSubs, - users, - parentQuests, - }, - }); -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/targets/questlines/router.test.ts b/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/targets/questlines/router.test.ts deleted file mode 100644 index ee1ec2d14..000000000 --- a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/targets/questlines/router.test.ts +++ /dev/null @@ -1,96 +0,0 @@ -import type { QuestDocument, QuestlineDocument } from "tachi-common"; - -import db from "#services/mongo/db"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import { TestingIIDXSPQuest } from "#test-utils/test-data"; -import dm from "deepmerge"; -import t from "tap"; - -const TestingIIDXSPQuestline: QuestlineDocument = { - name: "Testing Questline", - desc: "foo", - game: "iidx", - quests: [TestingIIDXSPQuest.questID, "other_quest"], - playtype: "SP", - questlineID: "quest_set", -}; - -t.test("GET /api/v1/games/:game/:playtype/targets/questlines", (t) => { - t.beforeEach(ResetDBState); - - function mkSet(merge: any) { - return dm(TestingIIDXSPQuestline, merge) as QuestlineDocument; - } - - t.test("Should return all questlines for this game.", async (t) => { - await db.questlines.insert([ - mkSet({ name: "Testing Set", questlineID: "name" }), - mkSet({ name: "Testing Other Set", questlineID: "similar_name" }), - mkSet({ name: "Different Name", questlineID: "radically_different_name" }), - mkSet({ - game: "chunithm", - playtype: "Single", - questlineID: "matching name but different gpt", - }), - mkSet({ playtype: "DP", questlineID: "matching name but different playtype" }), - ]); - - const res = await mockApi.get("/api/v1/games/iidx/SP/targets/questlines"); - - t.hasStrict( - (res.body.body.questlines as Array).sort((a, b) => - a.name.localeCompare(b.name), - ), - [ - { questlineID: "radically_different_name" }, - { questlineID: "similar_name" }, - { questlineID: "name" }, - ], - ); - - t.end(); - }); - - t.end(); -}); - -t.test("GET /api/v1/games/:game/:playtype/targets/questlines/:questlineID", (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(async () => { - await db.questlines.insert(TestingIIDXSPQuestline); - await db.quests.insert([ - TestingIIDXSPQuest, - dm(TestingIIDXSPQuest, { questID: "other_quest" }) as QuestDocument, - ]); - }); - - t.test("Should return the questline and its quests.", async (t) => { - const res = await mockApi.get("/api/v1/games/iidx/SP/targets/questlines/quest_set"); - - t.equal(res.statusCode, 200, "Should return 200."); - - t.hasStrict(res.body.body.questline, { - questlineID: TestingIIDXSPQuestline.questlineID, - }); - - t.hasStrict( - (res.body.body.quests as Array).sort((a, b) => - a.questID.localeCompare(b.questID), - ), - [{ questID: TestingIIDXSPQuest.questID }, { questID: "other_quest" }], - ); - - t.end(); - }); - - t.test("Should return 404 if the questline doesn't exist.", async (t) => { - const res = await mockApi.get("/api/v1/games/iidx/SP/targets/questlines/foobar"); - - t.equal(res.statusCode, 404); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/targets/questlines/router.ts b/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/targets/questlines/router.ts deleted file mode 100644 index cff4d51b9..000000000 --- a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/targets/questlines/router.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { FindStandaloneQuests, GetGoalsInQuests } from "#lib/targets/quests"; -import db from "#services/mongo/db"; -import { GetChildQuests } from "#utils/db"; -import { IsString } from "#utils/misc"; -import { AssignToReqTachiData, GetGPT, GetTachiData } from "#utils/req-tachi-data"; -import { type RequestHandler, Router } from "express"; - -const router: Router = Router({ mergeParams: true }); - -const ResolveQuestlineID: RequestHandler = async (req, res, next) => { - const { game, playtype } = GetGPT(req); - const questlineID = req.params.questlineID; - - const questline = await db.questlines.findOne({ - questlineID, - game, - playtype, - }); - - if (!questline) { - return res.status(404).json({ - success: false, - description: `A questline with ID ${questlineID} doesn't exist.`, - }); - } - - AssignToReqTachiData(req, { questlineDoc: questline }); - - next(); -}; - -/** - * Retrieve all questlines for this GPT. Also, return any standalone quests. - * - * @name GET /api/v1/games/:game/:playtype/targets/questlines - */ -router.get("/", async (req, res) => { - const { game, playtype } = GetGPT(req); - - const questlines = await db.questlines.find({ game, playtype }); - - const standalone = await FindStandaloneQuests(game, playtype); - const standaloneGoals = await GetGoalsInQuests(standalone); - - return res.status(200).json({ - success: true, - description: `Returned ${questlines.length} questlines.`, - body: { questlines, standalone, standaloneGoals }, - }); -}); - -/** - * Retrieve a specific questline. - * - * @name GET /api/v1/games/:game/:playtype/targets/questlines/:questlineID - */ -router.get("/:questlineID", ResolveQuestlineID, async (req, res) => { - const questline = GetTachiData(req, "questlineDoc"); - - const quests = await GetChildQuests(questline); - - const goals = await GetGoalsInQuests(quests); - - return res.status(200).json({ - success: true, - description: `Retrieved questline '${questline.name}'.`, - body: { - quests, - questline, - goals, - }, - }); -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/targets/quests/router.ts b/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/targets/quests/router.ts deleted file mode 100644 index 3421a2f75..000000000 --- a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/targets/quests/router.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { SearchCollection } from "#lib/search/search"; -import { GetGoalsInQuest, GetGoalsInQuests } from "#lib/targets/quests"; -import db from "#services/mongo/db"; -import { IsString } from "#utils/misc"; -import { AssignToReqTachiData, GetGPT, GetTachiData } from "#utils/req-tachi-data"; -import { GetUsersWithIDs } from "#utils/user"; -import { type RequestHandler, Router } from "express"; - -const router: Router = Router({ mergeParams: true }); - -const ResolveQuestID: RequestHandler = async (req, res, next) => { - const { game, playtype } = GetGPT(req); - const questID = req.params.questID; - - const quest = await db.quests.findOne({ - questID, - game, - playtype, - }); - - if (!quest) { - return res.status(404).json({ - success: false, - description: `A quest with ID ${questID} doesn't exist.`, - }); - } - - AssignToReqTachiData(req, { questDoc: quest }); - - next(); -}; - -/** - * Search quests for this GPT. - * - * @param search - The query to search for. - * - * @name GET /api/v1/games/:game/:playtype/targets/quests - */ -router.get("/", async (req, res) => { - const { game, playtype } = GetGPT(req); - - if (!IsString(req.query.search)) { - return res.status(400).json({ - success: false, - description: `Invalid value for search.`, - }); - } - - const quests = await SearchCollection( - db.quests, - req.query.search, - "quests", - { game, playtype }, - 50, - ); - const goals = await GetGoalsInQuests(quests); - - return res.status(200).json({ - success: true, - description: `Returned ${quests.length} quests.`, - body: { quests, goals }, - }); -}); - -/** - * Retrieve information about this quest and who is subscribed to it. - * - * @name GET /api/v1/games/:game/:playtype/targets/quests/:questID - */ -router.get("/:questID", ResolveQuestID, async (req, res) => { - const quest = GetTachiData(req, "questDoc"); - - const questSubs = await db["quest-subs"].find({ - questID: quest.questID, - }); - - const users = await GetUsersWithIDs(questSubs.map((e) => e.userID)); - - const goals = await GetGoalsInQuest(quest); - - const parentQuestlines = await db.questlines.find({ - quests: quest.questID, - }); - - return res.status(200).json({ - success: true, - description: `Retrieved information about ${quest.name}.`, - body: { - quest, - questSubs, - users, - goals, - parentQuestlines, - }, - }); -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/targets/router.ts b/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/targets/router.ts deleted file mode 100644 index 3fd4f95cf..000000000 --- a/typescript/server/src/server/router/api/v1postgres/games/_game/_playtype/targets/router.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { - GetRecentlyAchievedGoals, - GetRecentlyAchievedQuests, - GetRecentlyInteractedGoals, - GetRecentlyInteractedQuests, -} from "#utils/db"; -import { GetGPT } from "#utils/req-tachi-data"; -import { Router } from "express"; -import { FormatGameGroup } from "tachi-common"; - -import goalsRouter from "./goals/router"; -import questlineRouter from "./questlines/router"; -import questsRouter from "./quests/router"; - -const router: Router = Router({ mergeParams: true }); - -/** - * Retrieve all of this game's recently achieved goals and quests. - * - * @name GET /api/v1/games/:game/:playtype/targets/recently-achieved - */ -router.get("/recently-achieved", async (req, res) => { - const { game, playtype } = GetGPT(req); - - const [{ goals, goalSubs }, { quests, questSubs }] = await Promise.all([ - GetRecentlyAchievedGoals({ game, playtype }), - GetRecentlyAchievedQuests({ game, playtype }), - ]); - - return res.status(200).json({ - success: true, - description: `Retrieved some recently achieved targets for ${FormatGameGroup(game, playtype)}`, - body: { - goals, - goalSubs, - quests, - questSubs, - }, - }); -}); - -/** - * Retrieve all of this game's recently interacted-with goals and quests. - * - * @name GET /api/v1/games/:game/:playtype/targets/recently-raised - */ -router.get("/recently-raised", async (req, res) => { - const { game, playtype } = GetGPT(req); - - const [{ goals, goalSubs }, { quests, questSubs }] = await Promise.all([ - GetRecentlyInteractedGoals({ game, playtype }), - GetRecentlyInteractedQuests({ game, playtype }), - ]); - - return res.status(200).json({ - success: true, - description: `Retrieved some recently interacted-with targets for ${FormatGameGroup( - game, - playtype, - )}`, - body: { - goals, - goalSubs, - quests, - questSubs, - }, - }); -}); - -router.use("/goals", goalsRouter); -router.use("/quests", questsRouter); -router.use("/questlines", questlineRouter); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/games/_game/middleware.ts b/typescript/server/src/server/router/api/v1postgres/games/_game/middleware.ts deleted file mode 100644 index bbb3745b6..000000000 --- a/typescript/server/src/server/router/api/v1postgres/games/_game/middleware.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { RequestHandler } from "express"; - -import { TachiConfig } from "#lib/setup/config"; -import { IsValidGame } from "#utils/misc"; -import { AssignToReqTachiData } from "#utils/req-tachi-data"; - -export const ValidateGameFromParam: RequestHandler = (req, res, next) => { - const game = req.params.game; - - if (game === undefined) { - throw new Error( - `Expected parameter of game when ValidateGameFromParam was called on ${req.originalUrl}.`, - ); - } - - if (!IsValidGame(game)) { - return res.status(400).json({ - success: false, - description: `Invalid/unsupported game ${ - req.params.game - } - Expected any of ${TachiConfig.GAMES.join(", ")}`, - }); - } - - AssignToReqTachiData(req, { game }); - - next(); -}; diff --git a/typescript/server/src/server/router/api/v1postgres/games/_game/router.test.ts b/typescript/server/src/server/router/api/v1postgres/games/_game/router.test.ts deleted file mode 100644 index e7fce157f..000000000 --- a/typescript/server/src/server/router/api/v1postgres/games/_game/router.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -import mockApi from "#test-utils/mock-api"; -import { GetGameGroupConfig } from "tachi-common"; -import t from "tap"; - -t.test("GET /api/v1/games/:game", (t) => { - t.test("Should parse the game from the header", async (t) => { - const res = await mockApi.get("/api/v1/games/iidx"); - - t.hasStrict(GetGameGroupConfig("iidx"), res.body.body); - - t.end(); - }); - - t.test("Should reject an unsupported game.", async (t) => { - const res = await mockApi.get("/api/v1/games/invalid_game"); - - t.equal(res.statusCode, 400); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1postgres/games/_game/router.ts b/typescript/server/src/server/router/api/v1postgres/games/_game/router.ts deleted file mode 100644 index aa9f10ca8..000000000 --- a/typescript/server/src/server/router/api/v1postgres/games/_game/router.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { GetTachiData } from "#utils/req-tachi-data"; -import { Router } from "express"; -import { GetGameGroupConfig } from "tachi-common"; - -import playtypeRouter from "./_playtype/router"; -import { ValidateGameFromParam } from "./middleware"; - -const router: Router = Router({ mergeParams: true }); - -router.use(ValidateGameFromParam); - -/** - * Returns the configuration for this game. - * - * @name GET /api/v1/games/:game - */ -router.get("/", (req, res) => { - const game = GetTachiData(req, "game"); - - return res.status(200).json({ - success: true, - description: `Returned information for ${game}`, - body: GetGameGroupConfig(game), - }); -}); - -router.use("/:playtype", playtypeRouter); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/games/router.test.ts b/typescript/server/src/server/router/api/v1postgres/games/router.test.ts deleted file mode 100644 index 88a2e6cf9..000000000 --- a/typescript/server/src/server/router/api/v1postgres/games/router.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { TachiConfig } from "#lib/setup/config"; -import mockApi from "#test-utils/mock-api"; -import { GetGameGroupConfig } from "tachi-common"; -import t from "tap"; - -t.test("GET /api/v1/games", async (t) => { - // lets just run some basic tests that this contains all of our supported games - // and also returns configs properly. - const res = await mockApi.get("/api/v1/games"); - - t.strictSame(res.body.body.supportedGames, TachiConfig.GAMES); - - t.hasStrict( - { - ...res.body.body.configs.iidx, - // songData doesn't serialise nicely as it has functions on it. - songData: null, - }, - { - ...GetGameGroupConfig("iidx"), - songData: null, - }, - ); - t.equal(Object.keys(res.body.body.configs).length, TachiConfig.GAMES.length); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1postgres/games/router.ts b/typescript/server/src/server/router/api/v1postgres/games/router.ts deleted file mode 100644 index b1b234901..000000000 --- a/typescript/server/src/server/router/api/v1postgres/games/router.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { TachiConfig } from "#lib/setup/config"; -import { Router } from "express"; -import { GetGameGroupConfig } from "tachi-common"; - -import gameSpecificRoutes from "./@gameSpecificRoutes/router"; -import gameRouter from "./_game/router"; - -const router: Router = Router({ mergeParams: true }); - -/** - * Declares the supported games for this version of tachi. - * Not sure if this endpoint has any purpose, to be honest. - * - * @name GET /api/v1/games - */ -router.get("/", (req, res) => { - // this line is a bit too 'smart' for its own good, but whatever. - const configs = Object.fromEntries(TachiConfig.GAMES.map((e) => [e, GetGameGroupConfig(e)])); - - return res.status(200).json({ - success: true, - description: `Returned support information for ${TachiConfig.GAMES.length} game(s).`, - body: { - supportedGames: TachiConfig.GAMES, - configs, - }, - }); -}); - -router.use("/:game", gameRouter); - -// These routes are mounted at /api/v1/games and add things that are game specific, -// such as /bms/7K/tables/sieglindeEC. Simple enough. -router.use("/", gameSpecificRoutes); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/import/router.ts b/typescript/server/src/server/router/api/v1postgres/import/router.ts deleted file mode 100644 index 2cc2974bb..000000000 --- a/typescript/server/src/server/router/api/v1postgres/import/router.ts +++ /dev/null @@ -1,186 +0,0 @@ -import type { ScoreImportJobData } from "#lib/score-import/worker/types"; -import type { APIImportTypes, FileUploadImportTypes } from "tachi-common"; - -import { SIXTEEN_MEGABTYES } from "#lib/constants/filesize"; -import { SYMBOL_TACHI_API_AUTH } from "#lib/constants/tachi"; -import { log } from "#lib/log/log.js"; -import { ExpressWrappedScoreImportMain } from "#lib/score-import/framework/express-wrapper"; -import { DeorphanScores } from "#lib/score-import/framework/orphans/orphans"; -import { MakeScoreImport } from "#lib/score-import/framework/score-import"; -import { ServerConfig, TachiConfig } from "#lib/setup/config"; -import { RequirePermissions } from "#server/middleware/auth"; -import { CreateMulterSingleUploadMiddleware } from "#server/middleware/multer-upload"; -import prValidate from "#server/middleware/prudence-validate"; -import { ScoreImportRateLimiter } from "#server/middleware/rate-limiter"; -import { Random20Hex } from "#utils/misc"; -import { FormatUserDoc, GetUserWithIDGuaranteed } from "#utils/user"; -import { Router } from "express"; -import { p } from "prudence"; - -const router: Router = Router({ mergeParams: true }); - -const ParseMultipartScoredata = CreateMulterSingleUploadMiddleware("scoreData", SIXTEEN_MEGABTYES); - -const fileImportTypes = TachiConfig.IMPORT_TYPES.filter((e) => e.startsWith("file/")); -const apiImportTypes = TachiConfig.IMPORT_TYPES.filter((e) => e.startsWith("api/")); - -/** - * Import scores from a file. Expects the post request to be multipart, and to provide a scoreData file. - * - * @param importType - The import type for this file. - * @param file - The actual file. Should be passed as multipart. - * - * @name POST /api/v1/import/file - */ -router.post( - "/file", - RequirePermissions("submit_score"), - ScoreImportRateLimiter, - ParseMultipartScoredata, - prValidate( - { - importType: p.isIn(fileImportTypes), - }, - {}, - { allowExcessKeys: true }, - ), - async (req, res) => { - if (!req.file) { - return res.status(400).json({ - success: false, - description: `No file provided.`, - }); - } - - const importType = req.safeBody.importType as FileUploadImportTypes; - - const userIntent = req.header("X-User-Intent")?.toLowerCase() === "true"; - - if (ServerConfig.USE_EXTERNAL_SCORE_IMPORT_WORKER) { - const importID = Random20Hex(); - - const job: ScoreImportJobData = { - importID, - userID: req[SYMBOL_TACHI_API_AUTH].userID!, - userIntent, - importType, - parserArguments: [req.file, req.safeBody], - }; - - // Fire the score import, but make no guarantees about its state. - void MakeScoreImport(job); - - return res.status(202).json({ - success: true, - description: - "Import loaded into queue. You can poll the provided URL for information on when its complete.", - body: { - url: `${ServerConfig.OUR_URL}/api/v1/imports/${importID}/poll-status`, - importID, - }, - }); - } - - // Fire the score import and wait for it to finish! - const importResponse = await ExpressWrappedScoreImportMain( - req[SYMBOL_TACHI_API_AUTH].userID!, - userIntent, - importType, - [req.file, req.safeBody], - ); - - return res.status(importResponse.statusCode).json(importResponse.body); - }, -); - -/** - * Import scores from another API. This typically will perform a full sync. - * @name POST /api/v1/import/from-api - */ -router.post( - "/from-api", - RequirePermissions("submit_score"), - prValidate( - { - importType: p.isIn(apiImportTypes), - }, - {}, - { allowExcessKeys: true }, - ), - async (req, res) => { - const importType = req.safeBody.importType as APIImportTypes; - - const importID = Random20Hex(); - - const userID = req[SYMBOL_TACHI_API_AUTH].userID!; - - const userIntent = req.header("X-User-Intent")?.toLowerCase() === "true"; - - if (ServerConfig.USE_EXTERNAL_SCORE_IMPORT_WORKER) { - const job: ScoreImportJobData = { - importID, - userID, - userIntent, - importType, - parserArguments: [userID], - }; - - // Fire the score import, but make no guarantees about its state. - void MakeScoreImport(job); - - return res.status(202).json({ - success: true, - description: - "Import loaded into queue. You can poll the provided URL for information on when its complete.", - body: { - url: `${ServerConfig.OUR_URL}/api/v1/imports/${importID}/poll-status`, - importID, - }, - }); - } - - // Fire the score import and wait for it to finish! - const importResponse = await ExpressWrappedScoreImportMain( - userID, - userIntent, - importType, - [userID], - ); - - return res.status(importResponse.statusCode).json(importResponse.body); - }, -); - -/** - * Force Tachi to reprocess your orphaned scores. This is automatically done - * daily, but this endpoint allows users to speed that up. - * - * @name POST /api/v1/import/orphans - */ -router.post("/orphans", RequirePermissions("submit_score"), async (req, res) => { - const userDoc = await GetUserWithIDGuaranteed(req[SYMBOL_TACHI_API_AUTH].userID!); - - log.info(`User ${FormatUserDoc(userDoc)} forced an orphan sync.`); - - const { processed, removed, failed, success } = await DeorphanScores( - { userID: userDoc.id }, - log, - ); - - log.info(`Finished attempting deorphaning.`); - - log.info(`Success: ${success} | Failed ${failed} | Removed ${removed}.`); - - return res.status(200).json({ - success: true, - description: `Reprocessed ${processed} orphan scores.`, - body: { - processed, - failed, - success, - removed, - }, - }); -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/imports/middleware.ts b/typescript/server/src/server/router/api/v1postgres/imports/middleware.ts deleted file mode 100644 index e4a1b2670..000000000 --- a/typescript/server/src/server/router/api/v1postgres/imports/middleware.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { RequestHandler } from "express"; - -import { SYMBOL_TACHI_API_AUTH } from "#lib/constants/tachi"; -import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; -import { AssignToReqTachiData, GetTachiData } from "#utils/req-tachi-data"; -import { IsRequesterAdmin } from "#utils/user"; - -export const GetImportFromParam: RequestHandler = async (req, res, next) => { - const importDoc = await db.imports.findOne({ importID: req.params.importID }); - - if (!importDoc) { - return res.status(404).json({ - success: false, - description: `This import does not exist.`, - }); - } - - AssignToReqTachiData(req, { importDoc }); - - next(); -}; - -export const RequireOwnershipOfImportOrAdmin: RequestHandler = async (req, res, next) => { - const importDoc = GetTachiData(req, "importDoc"); - const userID = req[SYMBOL_TACHI_API_AUTH].userID; - - if (userID === null) { - return res.status(401).json({ - success: false, - description: `You are not authorised as anyone, and this endpoint requires us to know who you are.`, - }); - } - - if (importDoc.userID !== userID) { - if (await IsRequesterAdmin(req[SYMBOL_TACHI_API_AUTH])) { - log.info(`Admin ${userID} interacted with someone elses import.`); - next(); - return; - } - - return res.status(403).json({ - success: false, - description: `You are not authorised to perform this action.`, - }); - } - - next(); -}; diff --git a/typescript/server/src/server/router/api/v1postgres/imports/router.test.ts b/typescript/server/src/server/router/api/v1postgres/imports/router.test.ts deleted file mode 100644 index f953744e4..000000000 --- a/typescript/server/src/server/router/api/v1postgres/imports/router.test.ts +++ /dev/null @@ -1,166 +0,0 @@ -import db from "#services/mongo/db"; -import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; -import { mkFakeImport } from "#test-utils/misc"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import { FakeImport } from "#test-utils/test-data"; -import { UserAuthLevels } from "tachi-common"; -import t from "tap"; - -t.test("GET /api/v1/imports/:importID", (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(() => db.imports.insert(FakeImport)); - - t.test("Should return the import at this ID.", async (t) => { - const res = await mockApi.get(`/api/v1/imports/${FakeImport.importID}`); - - t.equal(res.statusCode, 200, "Should return 200."); - - t.hasStrict( - res.body.body, - { - user: { - id: 1, - }, - scores: [ - { - scoreID: FakeImport.scoreIDs[0], - }, - ], - charts: [ - { - chartID: res.body.body.scores[0].chartID, - }, - ], - songs: [ - { - id: res.body.body.scores[0].songID, - }, - ], - import: { - importID: FakeImport.importID, - }, - }, - "Should return the import and some info about it.", - ); - - t.end(); - }); - - t.test("Should return 404 if the import doesn't exist.", async (t) => { - const res = await mockApi.get("/api/v1/imports/bad-import"); - - t.equal(res.statusCode, 404, "Should return 404."); - - t.end(); - }); - - t.end(); -}); - -t.test("POST /api/v1/imports/:importID/revert", async (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(() => db.imports.insert(FakeImport)); - - const cookie = await CreateFakeAuthCookie(mockApi); - - t.test("Should revert the import at this ID.", async (t) => { - const res = await mockApi - .post(`/api/v1/imports/${FakeImport.importID}/revert`) - .set("Cookie", cookie); - - t.equal(res.statusCode, 200, "Should return 200."); - - t.strictSame(res.body.body, {}, "The response body should be empty."); - - t.resolveMatch( - db.scores.findOne({ scoreID: FakeImport.scoreIDs[0] }), - - // @ts-expect-error https://github.com/DefinitelyTyped/DefinitelyTyped/pull/60020 - null, - "The scores that were part of this import should be deleted.", - ); - - t.end(); - }); - - t.test("Should return 404 if the import doesn't exist.", async (t) => { - const res = await mockApi.post(`/api/v1/imports/doesnt-exist/revert`).set("Cookie", cookie); - - t.equal(res.statusCode, 404, "Should return 404."); - - t.end(); - }); - - t.test("Should return 401 if the user isn't authed.", async (t) => { - const res = await mockApi.post(`/api/v1/imports/${FakeImport.importID}/revert`); - - t.equal(res.statusCode, 401, "Should return 401."); - - t.end(); - }); - - t.test("Should return 403 if the user is authed as someone else.", async (t) => { - await db.users.update( - { - id: 1, - }, - { - $set: { authLevel: UserAuthLevels.USER }, - }, - ); - - const someoneElsesImport = mkFakeImport({ userID: 2, importID: "someone_elses" }); - - await db.imports.insert(someoneElsesImport); - - const res = await mockApi - .post(`/api/v1/imports/${someoneElsesImport.importID}/revert`) - .set("Cookie", cookie); - - t.equal(res.statusCode, 403, "Should return 403."); - - t.end(); - }); - - t.test( - "Should allow reverting another users import if the requester is an admin.", - async (t) => { - await db.users.update( - { - id: 1, - }, - { - $set: { authLevel: UserAuthLevels.ADMIN }, - }, - ); - - const someoneElsesImport = mkFakeImport({ userID: 2, importID: "someone_elses" }); - - await db.imports.insert(someoneElsesImport); - - const res = await mockApi - .post(`/api/v1/imports/${someoneElsesImport.importID}/revert`) - .set("Cookie", cookie); - - t.equal(res.statusCode, 200, "Should return 200."); - - t.strictSame(res.body.body, {}, "The response body should be empty."); - - t.resolveMatch( - db.scores.findOne({ scoreID: FakeImport.scoreIDs[0] }), - - // @ts-expect-error https://github.com/DefinitelyTyped/DefinitelyTyped/pull/60020 - null, - "The scores that were part of this import should be deleted.", - ); - - t.end(); - }, - ); - - t.end(); -}); - -t.todo("GET /api/v1/imports"); -t.todo("GET /api/v1/imports/failed"); diff --git a/typescript/server/src/server/router/api/v1postgres/imports/router.ts b/typescript/server/src/server/router/api/v1postgres/imports/router.ts deleted file mode 100644 index fdc08a5ee..000000000 --- a/typescript/server/src/server/router/api/v1postgres/imports/router.ts +++ /dev/null @@ -1,368 +0,0 @@ -import type { ScoreImportWorkerReturns } from "#lib/score-import/worker/types"; -import type { FilterQuery } from "mongodb"; -import type { ImportTrackerDocument, ImportTypes } from "tachi-common"; - -import { JOB_RETRY_COUNT } from "#lib/constants/tachi"; -import { RevertImport } from "#lib/imports/imports"; -import { log } from "#lib/log/log.js"; -import ScoreImportQueue, { ScoreImportQueueEvents } from "#lib/score-import/worker/queue"; -import { ServerConfig, TachiConfig } from "#lib/setup/config"; -import { RequirePermissions } from "#server/middleware/auth"; -import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; -import { GetRelevantSongsAndCharts } from "#utils/db"; -import { DeleteUndefinedProps } from "#utils/misc"; -import { GetTachiData } from "#utils/req-tachi-data"; -import { GetUsersWithIDs, GetUserWithID } from "#utils/user"; -import { Router } from "express"; -import { p } from "prudence"; - -import { GetImportFromParam, RequireOwnershipOfImportOrAdmin } from "./middleware"; - -const router: Router = Router({ mergeParams: true }); - -/** - * Query imports. Returns the 500 most recently-finished imports. - * - * @param importType - Optionally, limit the returns to only this import type. - * @param userIntent - Optionally, limit returns to only those with or without userIntent. - * - * @name GET /api/v1/imports - */ -router.get( - "/", - prValidate({ - importType: p.optional(p.isIn(TachiConfig.IMPORT_TYPES)), - userIntent: p.optional(p.isIn("true", "false")), - }), - async (req, res) => { - const importType = req.query.importType as ImportTypes | undefined; - - // all query input ends up as strings, so we need convert it into an optional - // boolean - const userIntent = - req.query.userIntent === undefined ? undefined : req.query.userIntent === "true"; - - const query = { - userIntent, - importType, - }; - - DeleteUndefinedProps(query); - - const imports = await db.imports.find(query, { - sort: { timeFinished: -1 }, - limit: 500, - }); - - // mayaswell attach the users for better UI. - const users = await GetUsersWithIDs(imports.map((e) => e.userID)); - - return res.status(200).json({ - success: true, - description: `Found ${imports.length} imports.`, - body: { - imports, - users, - }, - }); - }, -); - -/** - * Query *failed* imports. Returns the 500 most recently-finished imports. - * - * This is done by checking import-trackers for imports that ended with a thrown - * error. An import is considered 'failed' if ScoreImportFatalError is thrown at any - * point during the process, or if any unknown error is thrown. - * - * @param importType - Optionally, limit the returns to only this import type. - * @param userIntent - Optionally, limit returns to only those with or without userIntent. - * - * @name GET /api/v1/imports/failed - */ -router.get( - "/failed", - prValidate({ - importType: p.optional(p.isIn(TachiConfig.IMPORT_TYPES)), - userIntent: p.optional(p.isIn("true", "false")), - }), - async (req, res) => { - const importType = req.query.importType as ImportTypes | undefined; - - // all query input ends up as strings, so we need convert it into an optional - // boolean - const userIntent = - req.query.userIntent === undefined ? undefined : req.query.userIntent === "true"; - - const query: FilterQuery = { - userIntent, - importType, - type: "FAILED", - }; - - DeleteUndefinedProps(query); - - const trackers = await db["import-trackers"].find(query, { - sort: { timeStarted: -1 }, - limit: 500, - }); - - // mayaswell attach the users for better UI. - const users = await GetUsersWithIDs(trackers.map((e) => e.userID)); - - return res.status(200).json({ - success: true, - description: `Found ${trackers.length} failed imports.`, - body: { - failedImports: trackers, - users, - }, - }); - }, -); - -/** - * Retrieve an import with this ID. - * - * @name GET /api/v1/imports/:importID - */ -router.get("/:importID", GetImportFromParam, async (req, res) => { - const importDoc = GetTachiData(req, "importDoc"); - - const scores = await db.scores.find({ - scoreID: { $in: importDoc.scoreIDs }, - }); - - const { songs, charts } = await GetRelevantSongsAndCharts(scores, importDoc.game); - - const sessions = await db.sessions.find({ - sessionID: { $in: importDoc.createdSessions.map((e) => e.sessionID) }, - }); - - const user = await GetUserWithID(importDoc.userID); - - if (!user) { - log.error(`User ${importDoc.userID} doesn't exist, yet has a session?`); - return res.status(500).json({ - success: false, - description: `An internal server error has occured.`, - }); - } - - return res.status(200).json({ - success: true, - description: `Returned info about this session.`, - body: { - scores, - songs, - charts, - sessions, - import: importDoc, - user, - }, - }); -}); - -/** - * Delete this import and revert it from having ever happened. This un-imports all - * of the scores that were imported. - * - * Must be a request from the owner of this import. - * - * Counterintuitively, this endpoint requires the "delete_score" permission. This is - * because reverting an import is actually just deleting all of its scores. - * - * @name POST /api/v1/imports/:importID/revert - */ -router.post( - "/:importID/revert", - GetImportFromParam, - RequireOwnershipOfImportOrAdmin, - RequirePermissions("delete_score"), - async (req, res) => { - const importDoc = GetTachiData(req, "importDoc"); - - const k = await RevertImport(importDoc); - - if (k !== null) { - return res.status(409).json({ - success: false, - description: `You already have an import or a revert ongoing.`, - }); - } - - return res.status(200).json({ - success: true, - description: `Reverted import.`, - body: {}, - }); - }, -); - -// Finding jobs is slightly harder than just doing a key lookup, because of retrying. -async function FindImportJob(importID: string) { - const possibleImportIDs = []; - - for (let i = 1; i <= JOB_RETRY_COUNT; i++) { - possibleImportIDs.push(`${importID}:TRY${i}`); - } - - try { - // Note that instead of the cleaner await-inside-for here, we parallelise this - // for performance. - // Just for scalings sake. - const maybeJob = ( - await Promise.all(possibleImportIDs.map((i) => ScoreImportQueue.getJob(i))) - ).find((k) => k); - - return maybeJob; - } catch (err) { - return undefined; - } -} - -/** - * Retrieve the status of an ongoing import. - * If the import has been finalised and was successful, return 200. - * - * If the import is ongoing, return its progress. - * - * If the import was never ongoing, return 404. - * - * If the import was finalised and was unsuccessful (i.e. threw a fatal error) - * return its error information in expressified form. - * - * @name GET /api/v1/import/:importID/poll-status - */ -router.get("/:importID/poll-status", async (req, res) => { - if (!ServerConfig.USE_EXTERNAL_SCORE_IMPORT_WORKER) { - return res.status(501).json({ - success: false, - description: `${TachiConfig.NAME} does not use an external score import worker. Polling imports is not possible. This import may be ongoing, or it may have never occured.`, - }); - } - - const importDoc = await db.imports.findOne({ importID: req.params.importID }); - - if (importDoc) { - return res.status(200).json({ - success: true, - description: `Import was completed!`, - body: { - importStatus: "completed", - import: importDoc, - }, - }); - } - - const job = await FindImportJob(req.params.importID); - - if (!job) { - const tracker = await db["import-trackers"].findOne({ - importID: req.params.importID, - }); - - if (!tracker) { - return res.status(404).json({ - success: false, - description: `There is no ongoing import here.`, - }); - } - - // the user has requested the status of the import before the job has even - // been sent to redis. This is rare, but prevents a race condition of saying - // that an import is not ongoing when it is. - - switch (tracker.type) { - case "ONGOING": - return res.status(200).json({ - success: true, - description: `Import is ongoing.`, - body: { - importStatus: "ongoing", - progress: 0, - }, - }); - case "FAILED": - return res.status(tracker.error.statusCode ?? 500).json({ - success: false, - description: tracker.error.message, - }); - default: - throw new Error( - // @ts-expect-error shouldn't happen - `Unknown tracker type ${tracker.type}, expected ONGOING or FAILED.`, - ); - } - } - - let isFailed; - - try { - isFailed = await job.isFailed(); - } catch (err) { - log.info(`Failed to read job: ${err}`); - isFailed = true; - } - - let isCompleted; - - try { - isCompleted = await job.isCompleted(); - } catch (err) { - log.info(`Failed to read job: ${err}`); - isCompleted = false; - } - - // job.isFailed() actually means a critical error has occured. - // As in, an unhandled exception was thrown. - if (isFailed) { - log.error({ job }, "Internal Server Error with job?"); - - return res.status(500).json({ - success: false, - description: `An internal service error has occured with this import. This has been reported!`, - }); - } else if (isCompleted) { - const content = (await job.waitUntilFinished( - ScoreImportQueueEvents, - )) as ScoreImportWorkerReturns; - - // Since job.isFailed() is for whether a job had a fatal exception - // or not. We still want to check whether a job failed from say, - // nonsense user input. - // As such, if content.success == true, then the import was - // successful. - // Else, it was a "score import fatal error", which means the user - // screwed something up and we had to bail on the import. - if (content.success) { - return res.status(200).json({ - success: true, - description: `Import was completed!`, - body: { - importStatus: "completed", - import: content.importDocument, - }, - }); - } - - return res.status(content.statusCode).json({ - success: false, - description: content.description, - }); - } - - const progress = job.progress; - - return res.status(200).json({ - success: true, - description: `Import is ongoing.`, - body: { - importStatus: "ongoing", - progress: progress === 0 ? { description: "Starting up import." } : progress, - }, - }); -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/oauth/router.ts b/typescript/server/src/server/router/api/v1postgres/oauth/router.ts deleted file mode 100644 index e5a595d46..000000000 --- a/typescript/server/src/server/router/api/v1postgres/oauth/router.ts +++ /dev/null @@ -1,128 +0,0 @@ -import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; -import { Random20Hex } from "#utils/misc"; -import { Router } from "express"; -import { p } from "prudence"; - -const router: Router = Router({ mergeParams: true }); - -/** - * Converts an auth code into a valid API key that is returned. - * - * @note The params here are deliberately snake cased as that's what - * the digitalocean examples for oauth2 do. I have no idea whether that's - * part of the spec or not, but it probably is. - * - * @param client_id - The id for the client requesting a token. - * @param client_secret - The secret for the client. - * @param grant_type - Only exactly "authorization_code" is supported at the moment. - * @param redirect_uri - Must be the exact redirectUri registered with this client. - * @param code - The code to convert into an API token. - * - * @name POST /api/v1/oauth/token - */ -router.post( - "/token", - prValidate({ - client_id: "string", - client_secret: "string", - grant_type: p.is("authorization_code"), - redirect_uri: "string", - code: "string", - }), - async (req, res) => { - const body = req.safeBody as { - client_id: string; - client_secret: string; - code: string; - grant_type: "authorization_code"; - redirect_uri: string; - }; - - const client = await db["api-clients"].findOne({ - clientID: body.client_id, - }); - - if (!client) { - return res.status(404).json({ - success: false, - description: `This client does not exist.`, - }); - } - - if (client.clientSecret !== body.client_secret) { - return res.status(403).json({ - success: false, - description: `Invalid secret.`, - }); - } - - // I honest to god have no idea what the point of this check is - // but it's part of the oauth spec. - if (client.redirectUri !== body.redirect_uri) { - return res.status(400).json({ - success: false, - description: `This redirect_uri does not match with your registered client redirect_uri ${client.redirectUri}.`, - }); - } - - const codeDoc = await db["oauth2-auth-codes"].findOne({ code: body.code }); - - if (!codeDoc) { - return res.status(404).json({ - success: false, - description: `This code does not exist.`, - }); - } - - // don't let people auth with the same code multiple times. - await db["oauth2-auth-codes"].remove({ code: body.code }); - - const apiDoc = { - userID: codeDoc.userID, - token: Random20Hex(), - identifier: `${client.name} Token`, - - // converts ["a","b"] to {a: true, b: true}. - permissions: Object.fromEntries(client.requestedPermissions.map((e) => [e, true])), - fromAPIClient: client.clientID, - }; - - // Now we can actually register the api key (lol) - await db["api-tokens"].insert(apiDoc); - - return res.status(200).json({ - success: true, - description: `Successfully authenticated.`, - body: apiDoc, - }); - }, -); - -/** - * Creates an authorization code for this user (inferred from session). - * - * @name POST /api/v1/oauth/create-code - */ -router.post("/create-code", async (req, res) => { - if (!req.session.tachi?.user) { - return res.status(401).json({ - success: false, - description: `You are not authenticated.`, - }); - } - - const code = Random20Hex(); - - const doc = { code, userID: req.session.tachi.user.id, createdOn: Date.now() }; - - await db["oauth2-auth-codes"].insert(doc); - - return res.status(200).json({ - success: true, - description: `Successfully created code.`, - body: doc, - }); -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/router.test.ts b/typescript/server/src/server/router/api/v1postgres/router.test.ts deleted file mode 100644 index f75b352a5..000000000 --- a/typescript/server/src/server/router/api/v1postgres/router.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { ClearTestingRateLimitCache } from "#server/middleware/rate-limiter"; -import mockApi from "#test-utils/mock-api"; -import t from "tap"; - -// just a rudimentary test for rate-limiting. We fire 150 requests at GET /api/v1 -// (which does a server status check) -// and then check any of them return 429. -t.test("Rate Limiting Test", async (t) => { - ClearTestingRateLimitCache(); - - const promises = []; - - // default rate limit is 500, so lets go a bit over - for (let i = 0; i < 520; i++) { - promises.push(mockApi.get("/api/v1/status")); - } - - const res = await Promise.all(promises); - - const rateLimited = res.filter((e) => e.statusCode === 429); - - t.ok(rateLimited.length > 0, "Some requests should be rate limited."); - - t.end(); -}); - -t.test("404 Handler", async (t) => { - ClearTestingRateLimitCache(); - - const res = await mockApi.get("/api/v1/invalid_route_that_will_never_exist"); - - t.equal(res.statusCode, 404); - t.strictSame(res.body, { - success: false, - description: "Endpoint Not Found.", - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1postgres/router.ts b/typescript/server/src/server/router/api/v1postgres/router.ts deleted file mode 100644 index c65eba03f..000000000 --- a/typescript/server/src/server/router/api/v1postgres/router.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { NormalRateLimitMiddleware } from "#server/middleware/rate-limiter"; -import { Router } from "express"; - -import activityRouter from "./activity/router"; -import adminRouter from "./admin/router"; -import authRouter from "./auth/router"; -import clientsRouter from "./clients/router"; -import configRouter from "./config/router"; -import gamesRouter from "./games/router"; -import importRouter from "./import/router"; -import importsRouter from "./imports/router"; -import oauthRouter from "./oauth/router"; -import scoresRouter from "./scores/router"; -import searchRouter from "./search/router"; -import seedsRouter from "./seeds/router"; -import sessionsRouter from "./sessions/router"; -import statusRouter from "./status/router"; -import usersRouter from "./users/router"; - -const router: Router = Router({ mergeParams: true }); - -// Auth is up here so it can have special rate limiting rules, -// since it needs slightly harsher ones! -router.use("/auth", authRouter); - -// Everything else can use the normal rate limiter! -router.use(NormalRateLimitMiddleware); - -router.use("/admin", adminRouter); -router.use("/activity", activityRouter); -router.use("/status", statusRouter); -router.use("/import", importRouter); -router.use("/imports", importsRouter); -router.use("/users", usersRouter); -router.use("/games", gamesRouter); -router.use("/search", searchRouter); -router.use("/scores", scoresRouter); -router.use("/sessions", sessionsRouter); -router.use("/oauth", oauthRouter); -router.use("/clients", clientsRouter); -router.use("/config", configRouter); -router.use("/seeds", seedsRouter); - -/** - * Return a JSON 404 response if an endpoint is hit that does not exist. - * - * @name ALL /api/v1/* - */ -router.all("*", (req, res) => - res.status(404).json({ - success: false, - description: "Endpoint Not Found.", - }), -); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/scores/_scoreID/middleware.ts b/typescript/server/src/server/router/api/v1postgres/scores/_scoreID/middleware.ts deleted file mode 100644 index 6a2e8e72f..000000000 --- a/typescript/server/src/server/router/api/v1postgres/scores/_scoreID/middleware.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { RequestHandler } from "express"; - -import { SYMBOL_TACHI_API_AUTH } from "#lib/constants/tachi"; -import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; -import { AssignToReqTachiData, GetTachiData } from "#utils/req-tachi-data"; -import { IsRequesterAdmin } from "#utils/user"; - -export const GetScoreFromParam: RequestHandler = async (req, res, next) => { - const score = await db.scores.findOne({ scoreID: req.params.scoreID }); - - if (!score) { - return res.status(404).json({ - success: false, - description: `This score does not exist.`, - }); - } - - AssignToReqTachiData(req, { scoreDoc: score }); - - next(); -}; - -export const RequireOwnershipOfScoreOrAdmin: RequestHandler = async (req, res, next) => { - const score = GetTachiData(req, "scoreDoc"); - const userID = req[SYMBOL_TACHI_API_AUTH].userID; - - if (userID === null) { - return res.status(401).json({ - success: false, - description: `You are not authorised as anyone, and this endpoint requires us to know who you are.`, - }); - } - - if (score.userID !== userID) { - if (await IsRequesterAdmin(req[SYMBOL_TACHI_API_AUTH])) { - log.info(`Admin ${userID} interacted with someone elses .`); - next(); - return; - } - - return res.status(403).json({ - success: false, - description: `You are not authorised to perform this action.`, - }); - } - - next(); -}; diff --git a/typescript/server/src/server/router/api/v1postgres/scores/_scoreID/router.ts b/typescript/server/src/server/router/api/v1postgres/scores/_scoreID/router.ts deleted file mode 100644 index 77a950c91..000000000 --- a/typescript/server/src/server/router/api/v1postgres/scores/_scoreID/router.ts +++ /dev/null @@ -1,170 +0,0 @@ -import { log } from "#lib/log/log.js"; -import { DeleteScore } from "#lib/score-mutation/delete-scores"; -import { RequirePermissions } from "#server/middleware/auth"; -import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; -import { GetTachiData } from "#utils/req-tachi-data"; -import { GetUserWithID } from "#utils/user"; -import { Router } from "express"; -import { p } from "prudence"; - -import { GetScoreFromParam, RequireOwnershipOfScoreOrAdmin } from "./middleware"; - -const router: Router = Router({ mergeParams: true }); - -router.use(GetScoreFromParam); - -/** - * Retrieve the score document at this ID. - * - * @param getRelated - Gets the related song and chart document for this score, aswell. - * - * @name GET /api/v1/scores/:scoreID - */ -router.get("/", async (req, res) => { - const score = GetTachiData(req, "scoreDoc"); - - if (req.query.getRelated !== undefined) { - const [user, chart, song] = await Promise.all([ - GetUserWithID(score.userID), - db.anyCharts[score.game].findOne({ chartID: score.chartID }), - db.anySongs[score.game].findOne({ id: score.songID }), - ]); - - if (!user || !chart || !song) { - log.error( - `Score ${ - score.scoreID - } refers to non-existent data: [user,chart,song] [${!!user} ${!!chart} ${!!song}]`, - ); - - return res.status(500).json({ - success: false, - description: `An internal server error has occured.`, - }); - } - - return res.status(200).json({ - success: true, - description: `Returned score.`, - body: { - score, - user, - song, - chart, - }, - }); - } - - return res.status(200).json({ - success: true, - description: `Returned score.`, - body: { - score, - }, - }); -}); - -interface ModifiableScoreProps { - comment?: string | null; - highlight?: boolean; -} - -/** - * Modifies a score. - * - * Requires you to be the owner of this score, and have the modify_scores permission. - * - * @name PATCH /api/v1/scores/:scoreID - */ -router.patch( - "/", - RequireOwnershipOfScoreOrAdmin, - RequirePermissions("customise_score"), - prValidate({ - comment: p.optional(p.nullable(p.isBoundedString(1, 120))), - highlight: "*boolean", - }), - async (req, res) => { - const body = req.safeBody as { - comment?: string | null; - highlight?: boolean; - }; - - const score = GetTachiData(req, "scoreDoc"); - - const modifyOption: ModifiableScoreProps = {}; - - if (body.comment !== undefined) { - modifyOption.comment = body.comment; - } - - if (body.highlight !== undefined) { - modifyOption.highlight = body.highlight; - } - - if (Object.keys(modifyOption).length === 0) { - return res.status(400).json({ - success: false, - description: `This request modifies nothing about the score.`, - }); - } - - const newScore = await db.scores.findOneAndUpdate( - { scoreID: score.scoreID }, - { $set: modifyOption }, - ); - - if (modifyOption.highlight === true || modifyOption.highlight === false) { - await db["personal-bests"].findOneAndUpdate( - { - chartID: score.chartID, - userID: score.userID, - }, - { - $set: { - highlight: modifyOption.highlight, - }, - }, - ); - } - - return res.status(200).json({ - success: true, - description: `Updated score.`, - body: newScore, - }); - }, -); - -/** - * Deletes the score. - * - * @param blacklist - Whether to blacklist this scoreID or not. - * A blacklisted score will never be reimported. - * - * @name DELETE /api/v1/scores/:scoreID - */ -router.delete( - "/", - RequireOwnershipOfScoreOrAdmin, - prValidate({ blacklist: "*boolean" }), - RequirePermissions("delete_score"), - async (req, res) => { - const body = req.safeBody as { - blacklist?: boolean; - }; - - const score = GetTachiData(req, "scoreDoc"); - - await DeleteScore(score, body.blacklist); - - return res.status(200).json({ - success: true, - description: `Successfully deleted score.`, - body: {}, - }); - }, -); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/scores/router.ts b/typescript/server/src/server/router/api/v1postgres/scores/router.ts deleted file mode 100644 index 374ff8ce9..000000000 --- a/typescript/server/src/server/router/api/v1postgres/scores/router.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Router } from "express"; - -import scoreIDRouter from "./_scoreID/router"; - -const router: Router = Router({ mergeParams: true }); - -/** - * Nothing? Maybe we can think of a good use for this endpoint at some point. - * - * @name GET /api/v1/scores/:scoreID - */ -// router.get("/", async (req, res) => {}); - -router.use("/:scoreID", scoreIDRouter); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/search/router.test.ts b/typescript/server/src/server/router/api/v1postgres/search/router.test.ts deleted file mode 100644 index 81e659d01..000000000 --- a/typescript/server/src/server/router/api/v1postgres/search/router.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -import type { ChartDocument, SongDocument } from "tachi-common"; - -import db from "#services/mongo/db"; -import { mkFakeGameSettings, mkFakeUser } from "#test-utils/misc"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import { LoadTachiIIDXData } from "#test-utils/test-data"; -import t from "tap"; - -t.test("GET /api/v1/search", (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(LoadTachiIIDXData); - - t.test("Should search users and songs.", async (t) => { - const res = await mockApi.get("/api/v1/search?search=zkldi"); - - t.equal(res.body.body.users.length, 1); - - t.equal(res.body.body.users[0].username, "test_zkldi"); - t.equal(res.body.body.users[0].__isRival, false); - - t.end(); - }); - - t.test("Should highlight users as rivals if they're rivals of the requester", async (t) => { - await db.users.insert( - mkFakeUser(2, { username: "scoobydoo", usernameLowercase: "scoobydoo" }), - ); - - await db["game-settings"].remove({}); - await db["game-settings"].insert(mkFakeGameSettings(1, "iidx", "SP", { rivals: [2] })); - - const res = await mockApi - .get("/api/v1/search?search=scoobydoo") - .set("Authorization", "Bearer fake_api_token"); - - t.equal(res.body.body.users.length, 1); - - t.equal(res.body.body.users[0].__isRival, true); - - t.end(); - }); - - t.test("Should reject requests without a query.", async (t) => { - const res = await mockApi.get("/api/v1/search"); - - t.equal(res.statusCode, 400); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1postgres/search/router.ts b/typescript/server/src/server/router/api/v1postgres/search/router.ts deleted file mode 100644 index c76f41f8d..000000000 --- a/typescript/server/src/server/router/api/v1postgres/search/router.ts +++ /dev/null @@ -1,123 +0,0 @@ -import type { FilterQuery } from "mongodb"; - -import { SYMBOL_TACHI_API_AUTH } from "#lib/constants/tachi"; -import { - SearchFolders, - SearchForChartHash, - SearchGamesSongsCharts, - SearchUsersRegExp, -} from "#lib/search/search"; -import { TachiConfig } from "#lib/setup/config"; -import { IsString } from "#utils/misc"; -import { GetAllUserRivals, GetUserPlayedGPTs } from "#utils/user"; -import { Router } from "express"; -import { - type FolderDocument, - GameGroup, - GetGameGroupConfig, - GetGPTString, - type GPTString, - type integer, - type SongDocument, - type UserDocument, -} from "tachi-common"; - -const router: Router = Router({ mergeParams: true }); - -/** - * Performs a generic "search" across Tachi. - * - * @param search - The criteria to search on. - * - * @name GET /api/v1/search - */ -router.get("/", async (req, res) => { - if (!IsString(req.query.search)) { - return res.status(400).json({ - success: false, - description: "No search parameter given.", - }); - } - - const userID = req[SYMBOL_TACHI_API_AUTH].userID; - - let filter: FilterQuery = {}; - - let relevantGPTs: Array = TachiConfig.GAMES.flatMap((g) => - GetGameGroupConfig(g).playtypes.map((pt) => GetGPTString(g, pt)), - ); - - if (userID !== null) { - // if the requesting user exists, and they've set this param, - // only return info related to games they've played. - if (IsString(req.query.hasPlayedGame)) { - const gpts = await GetUserPlayedGPTs(userID); - - // @hack This is a bit lazy. We should really be filtering the user stuff - // on GPTs with an $or query. - filter = { - game: { $in: gpts.map((e) => e.game) }, - playtype: { $in: gpts.map((e) => e.playtype) }, - }; - - relevantGPTs = gpts.map((e) => GetGPTString(e.game, e.playtype)); - } - } - - const [users, charts, folders] = await Promise.all([ - SearchUsersRegExp(req.query.search), - SearchGamesSongsCharts(req.query.search, relevantGPTs), - SearchFolders(req.query.search, filter), - ]); - - // @ts-expect-error Handled below -- the field is added by the below for loop. - const usersWithRivalTag: Array<{ __isRival: boolean } & UserDocument> = users; - - let rivals: Array = []; - - if (userID !== null) { - rivals = await GetAllUserRivals(userID); - } - - for (const user of usersWithRivalTag) { - user.__isRival = rivals.includes(user.id); - } - - return res.status(200).json({ - success: true, - description: `Searched everything.`, - body: { - users, - charts, - folders, - }, - }); -}); - -/** - * Search checksums for charts, instead of matching on song title. - * - * @param search - The hash to search on. - * - * @note This matches MD5 and SHA256 for BMS/PMS, GSv3 for ITG and SHA1 for USC. - */ -router.get("/chart-hash", async (req, res) => { - if (!IsString(req.query.search)) { - return res.status(400).json({ - success: false, - description: "No search parameter given.", - }); - } - - const charts = await SearchForChartHash(req.query.search); - - return res.status(200).json({ - success: true, - description: `Searched for chart hash ${req.query.search}.`, - body: { - charts, - }, - }); -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/seeds/router.ts b/typescript/server/src/server/router/api/v1postgres/seeds/router.ts deleted file mode 100644 index 11d590f7c..000000000 --- a/typescript/server/src/server/router/api/v1postgres/seeds/router.ts +++ /dev/null @@ -1,317 +0,0 @@ -import { log } from "#lib/log/log.js"; -import { PullDatabaseSeeds } from "#lib/seeds/repo"; -import { Env } from "#lib/setup/config"; -import prValidate from "#server/middleware/prudence-validate"; -import { RequireLocalDevelopment } from "#server/middleware/type-require"; -import { GetCommit, ListGitCommitsInPath } from "#utils/git"; -import { asyncExec, IsString } from "#utils/misc"; -import { Router } from "express"; -import fsSync from "fs"; -import fs from "fs/promises"; -import path from "path"; - -// Routes for interacting with the `seeds` folder in this instance of Tachi. - -// Why do we have this, and why is it limited to only local development? -// The answer is that we have a "Seeds UI" that runs in the client. For local development -// it's useful to be able to see the current state of the seeds on-disk, and diff that -// against various local commits. As such, we need an api such that the client can -// interface with our local seeds. - -// In production/staging, we use GitHub as a source of truth for our git repository. -// In local dev, we have this option available too, but we also enable this API. - -const router: Router = Router({ mergeParams: true }); - -router.use(RequireLocalDevelopment); - -// there's a lady who's sure -// all that glitters is gold -const LOCAL_DEV_SEEDS_PATH = path.join( - __dirname, - - // and she's buying a... - "../../../../../../../seeds/collections", -); -const TEST_SEEDS_PATH = path.join(__dirname, "../../../../../test-utils/mock-db"); - -const LOCAL_SEEDS_PATH = Env.NODE_ENV === "test" ? TEST_SEEDS_PATH : LOCAL_DEV_SEEDS_PATH; - -if (Env.NODE_ENV === "dev" || Env.NODE_ENV === "test") { - if (!fsSync.existsSync(LOCAL_SEEDS_PATH)) { - log.error(`Failed to load seeds routes, could not find any seeds/collections checked out at ${LOCAL_SEEDS_PATH}. -These were expected to be present as this is local-development! -All seeds routes will return 500.`); - } -} - -/** - * No-Op route for checking whether this feature is supported by this instance of Tachi. - * - * @name GET /api/v1/seeds - */ -router.get("/", (req, res) => - res.status(200).json({ - success: true, - description: `Local seeds are available on this instance of Tachi.`, - body: {}, - }), -); - -/** - * Check whether there are changes to the seeds in this local development - * instance that have not been committed yet. - * - * @name GET /api/v1/seeds/has-uncommitted-changes - */ -router.get("/has-uncommitted-changes", async (req, res) => { - const { stdout, stderr } = await asyncExec(`git status --porcelain`); - - if (stderr) { - log.error({ stderr }, `Failed to read git status --porcelain.`); - return res.status(500).json({ - success: false, - description: `Failed to check current git status.`, - }); - } - - // if any change contains seeds/collections, it's probably uncommitted - // local changes. - const hasUncommittedChanges = stdout - .split("\n") - - // note that doing this properly is frustrating. This has false positives for - // routes that partially contain this route. I've ameliorated this slightly with - // a leading space, but that is not a proper solution. - .some((row) => / seeds\/collections/u.exec(row)); - - return res.status(200).json({ - success: true, - description: hasUncommittedChanges - ? "This local instance has uncommitted changes." - : "This local instance does not have uncommitted changes.", - body: hasUncommittedChanges, - }); -}); - -/** - * List commits that have affected seeds. - * - * This format is a partial implementation of what GitHub's REST API returns. As such, - * an implementing client has far less work to do with respect to handling local + remote - * servers. - * - * @param file - If provided, only returns commits that have touched this specific file. - * - * @name GET /api/v1/seeds/commits - */ -router.get( - "/commits", - prValidate({ - branch: "string", - file: "*string", - }), - async (req, res) => { - // validated by prudence. - const file = req.query.file as string | undefined; - const branch = req.query.branch as string; - - const seeds = await PullDatabaseSeeds(); - const collections = (await seeds.ListCollections()).map((e) => `${e}.json`); - - if (IsString(file) && !collections.includes(file)) { - return res.status(400).json({ - success: false, - description: `Invalid file of '${file}' requested. Expected any of ${collections.join( - ", ", - )}`, - }); - } - - // if we don't have a file, use the do-nothing path. - const realFile = file ?? "."; - - // only check commits in seeds/collections - const commits = await ListGitCommitsInPath( - branch, - path.join("seeds", "collections", realFile), - ); - - return res.status(200).json({ - success: true, - description: `Found ${commits.length} commits.`, - body: commits, - }); - }, -); - -/** - * List branches available on this local repository. - * - * This returns all branches under `branches`, and the currently selected branch - * as `checkedout`, which might be null if the HEAD is currently detached. - * - * @name GET /api/v1/seeds/branches - */ -router.get("/branches", async (req, res) => { - const { stdout: branches } = await asyncExec(`PAGER=cat git branch --no-color -v`); - - const allBranches = []; - let currentBranch: { name: string; sha: string } | null = null; - - for (const branchStr of branches.split("\n")) { - const match = /^ *(\*?) +(.*?) +([a-f0-9]*)/u.exec(branchStr) as - | [string, string, string, string] - | null; - - if (match === null) { - continue; - } - - const [_, isCurrent, branchName, sha] = match; - - if (branchName.startsWith("(HEAD detatched at")) { - continue; - } - - if (branchName === "") { - continue; - } - - const branch = { name: branchName, sha }; - - if (isCurrent === "*") { - currentBranch = branch; - } - - allBranches.push(branch); - } - - return res.status(200).json({ - success: true, - description: `Found ${allBranches.length} branches.`, - body: { - branches: allBranches, - current: currentBranch, - }, - }); -}); - -/** - * Retrieve the current state of the collection as of this revision. - * - * This returns a record of "songs-iidx.json" -> PARSED_SONGS_IIDX_JSON_CONTENT - * for all collections as of that current revision. As such, you should treat all - * returned records as if they might not be present (as they might not be). - * - * @param revision - The revision fetched. This is resolved using standard git rules, - * and can therefore be a branch name, a commit name, or anything else git will resolve - * like HEAD@{2020-01-01}. - * - * If no revision is provided, the current uncommitted state on disk is returned instead. - * - * @name GET /api/v1/seeds/collections - */ -router.get( - "/collections", - prValidate({ - revision: "*string", - }), - async (req, res) => { - // asserted by prudence - const rev = req.query.revision as string | undefined; - - const data: Record = {}; - - // use local disk - if (rev === undefined) { - const files = await fs.readdir(LOCAL_SEEDS_PATH); - - await Promise.all( - files.map(async (file) => { - const content = await fs.readFile(path.join(LOCAL_SEEDS_PATH, file), "utf-8"); - - data[file] = JSON.parse(content); - }), - ); - } else { - // we have a revision. - - if (rev.includes(":")) { - return res.status(400).json({ - success: false, - description: `Git Revisions cannot contain ':' characters.`, - }); - } - - // @warn we don't actually bother doing any real shell - // escaping here, since these routes are only enabled in local development. - const { stdout: fileStdout } = await asyncExec( - `PAGER=cat git show '${rev}:seeds/collections' | tail -n +3`, - ); - - // @warn this breaks for files that have newlines in - // I don't care. - // also, this ends with a trailing newline which means we get a trailing - // empty filename, gotta strip that out. - const files = fileStdout.split("\n").filter((e) => e !== ""); - - await Promise.all( - files.map(async (file) => { - // git show fails with 128 *if* this file doesn't exist at the time - // of this commit. however, the files we're iterating over are the - // files in the collection as of this commit, so, this should never - // crash in that way, right? - const { stdout: content } = await asyncExec( - `PAGER=cat git show '${rev}:seeds/collections/${file}'`, - ); - - data[file] = JSON.parse(content); - }), - ); - } - - return res.status(200).json({ - success: true, - description: `Retrieved data ${rev ? `as of ${rev}` : "off of the current disk"}.`, - body: data, - }); - }, -); - -/** - * Retrieve information about the provided commit. - * - * @param sha - The commit to fetch information about. Technically, this can be the name - * of any git object, but you probably shouldn't. - * - * @name GET /api/v1/seeds/commit - */ -router.get( - "/commit", - prValidate({ - sha: "string", - }), - async (req, res) => { - // asserted by prudence - const sha = req.query.sha as string; - - try { - const commit = await GetCommit(sha); - - return res.status(200).json({ - success: true, - description: `Found commit '${sha}'.`, - body: commit, - }); - } catch (err) { - log.info({ err }, `Failed to fetch commit.`); - return res.status(404).json({ - success: false, - description: `Failed to fetch commit. It may not exist.`, - }); - } - }, -); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/sessions/_sessionID/middleware.ts b/typescript/server/src/server/router/api/v1postgres/sessions/_sessionID/middleware.ts deleted file mode 100644 index 581cab34b..000000000 --- a/typescript/server/src/server/router/api/v1postgres/sessions/_sessionID/middleware.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { RequestHandler } from "express"; - -import { SYMBOL_TACHI_API_AUTH } from "#lib/constants/tachi"; -import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; -import { AssignToReqTachiData, GetTachiData } from "#utils/req-tachi-data"; - -export const GetSessionFromParam: RequestHandler = async (req, res, next) => { - const session = await db.sessions.findOne({ - sessionID: req.params.sessionID, - }); - - if (!session) { - return res.status(404).json({ - success: false, - description: `This session does not exist.`, - }); - } - - AssignToReqTachiData(req, { sessionDoc: session }); - - next(); -}; - -export const RequireOwnershipOfSession: RequestHandler = (req, res, next) => { - const userID = req[SYMBOL_TACHI_API_AUTH].userID; - const session = GetTachiData(req, "sessionDoc"); - - if (userID !== session.userID) { - return res.status(403).json({ - success: false, - description: `You are not authorised to modify this session.`, - }); - } - - next(); -}; diff --git a/typescript/server/src/server/router/api/v1postgres/sessions/_sessionID/router.ts b/typescript/server/src/server/router/api/v1postgres/sessions/_sessionID/router.ts deleted file mode 100644 index eb2e762cb..000000000 --- a/typescript/server/src/server/router/api/v1postgres/sessions/_sessionID/router.ts +++ /dev/null @@ -1,365 +0,0 @@ -import { GetSessionScoreInfo } from "#lib/score-import/framework/sessions/sessions"; -import { RequirePermissions } from "#server/middleware/auth"; -import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; -import { GetEnumDistForFolderAsOf } from "#utils/folder"; -import { AddToSetInRecord } from "#utils/misc"; -import { GetTachiData } from "#utils/req-tachi-data"; -import { GetUserWithID } from "#utils/user"; -import { Router } from "express"; -import { p } from "prudence"; -import { - type FolderDocument, - GetGamePTConfig, - GetScoreEnumConfs, - GetScoreMetrics, - type integer, - type ScoreDocument, -} from "tachi-common"; -import { optNull } from "tachi-common/lib/schemas"; - -import { GetSessionFromParam, RequireOwnershipOfSession } from "./middleware"; - -const router: Router = Router({ mergeParams: true }); - -router.use(GetSessionFromParam); - -/** - * Retrieves the session, its scores and the related songs and charts. - * - * @name GET /api/v1/sessions/:sessionID - */ -router.get("/", async (req, res) => { - const session = GetTachiData(req, "sessionDoc"); - - const scores = await db.scores.find({ - scoreID: { $in: session.scoreIDs }, - }); - - const [songs, charts, user, scoreInfo] = await Promise.all([ - db.anySongs[session.game].find({ - id: { $in: scores.map((e) => e.songID) }, - }), - db.anyCharts[session.game].find({ - chartID: { $in: scores.map((e) => e.chartID) }, - }), - GetUserWithID(session.userID), - GetSessionScoreInfo(session), - ]); - - return res.status(200).json({ - success: true, - description: `Successfully returned session ${session.name}.`, - body: { - session, - songs, - charts, - scores, - user, - scoreInfo, - }, - }); -}); - -/** - * Retrieves additional statistics about folder raises as a result of this session. - * - * More obviously, this endpoint returns stuff like "This session resulted in 4 more - * hard clears on the Level 12 folder." - * - * This allows us to render pretty things in the UI, showing the user what their - * best stats were. - * - * @warn This is probably the most complicated route in all of Tachi. Sorry about that. - * - * @name GET /api/v1/sessions/:sessionID/folder-raises - */ -router.get("/folder-raises", async (req, res) => { - const session = GetTachiData(req, "sessionDoc"); - - const gptConfig = GetGamePTConfig(session.game, session.playtype); - - const scoreInfo = await GetSessionScoreInfo(session); - - const enumRaises = []; - - for (const metric of GetScoreMetrics(gptConfig, "ENUM")) { - enumRaises.push( - ...scoreInfo - .filter((e) => !e.isNewScore && (e.deltas[metric] ?? -1) > 0) - .map((e) => e.scoreID), - ); - } - - // create lookup tables for a scoreID to its delta. We use this later to find out - // what the "original" score's grade or lamp was prior to this raise. - const enumDeltas: Record> = {}; - - for (const sci of scoreInfo) { - if (sci.isNewScore) { - continue; - } - - enumDeltas[sci.scoreID] = sci.deltas; - } - - const enumScoreMetrics = GetScoreEnumConfs(gptConfig); - - const relevantScores = await db.scores.find({ - scoreID: { $in: session.scoreIDs }, - }); - - const chartIDs = relevantScores.map((e) => e.chartID); - - // what folderIDs were involved in this session? - const affectedFolderIDs = ( - await db["folder-chart-lookup"].find( - { - chartID: { $in: chartIDs }, - }, - { - projection: { folderID: 1 }, - }, - ) - ).map((e) => e.folderID); - - // find all the active folder documents raised in this session. - const folders = await db.folders.find({ - folderID: { $in: affectedFolderIDs }, - inactive: false, - }); - - const bestEnumMap = new Map(); - - for (const score of relevantScores) { - for (const [metric, conf] of Object.entries(enumScoreMetrics)) { - if ( - // @ts-expect-error lazy index cheating - score.scoreData.enumIndexes[metric]! < - conf.values.indexOf(conf.minimumRelevantValue) - ) { - // isn't relevant - continue; - } - - const mapKey = `${score.chartID}-${metric}`; - - const existing = bestEnumMap.get(mapKey); - - if (!existing) { - bestEnumMap.set(mapKey, score); - } else if ( - // @ts-expect-error lazy index cheating - score.scoreData.enumIndexes[metric] > existing.scoreData.enumIndexes[metric] - ) { - bestEnumMap.set(mapKey, score); - } - } - } - - const raiseInfo: Array<{ - folder: FolderDocument; - previousCount: integer; // how many AAAs/HARD CLEARs/whatevers was on this - raisedCharts: Array; // Array; - totalCharts: integer; - // folder before this session? - type: string; - value: string; - }> = []; - - await Promise.all( - folders.map(async (folder) => { - // what was the grade and lamp distribution on this folder before the session? - const { chartIDs, cumulativeEnumDist } = await GetEnumDistForFolderAsOf( - session.userID, - folder.folderID, - session.timeStarted, - ); - - // what is the distribution of raises on this folder? - // NOTE: instead of storing an integer here - // i.e. For the Level 12 folder: - // AAA: 5 <- 5 new AAAs, - // AA: 2 <- 2 new AAs, etc. - // we store a Set of chartIDs instead, so - // AAA: ["chart1","chart2", ...] with size 5. - // This is so we can display *what* charts were raised in the UI. - // This type results in looking like: - // - // { - // grade: { - // AAA: [chartID, chartID2], - // AA: [chartID3] - // }, - // lamp: { - // "HARD CLEAR": [chartID2] - // } - // } - - for (const [metric, conf] of Object.entries(enumScoreMetrics)) { - const metricDist: Record> = {}; - const previousDist = cumulativeEnumDist[metric]!; - - for (const chartID of chartIDs) { - const bestEnumOnThisChart = bestEnumMap.get(`${chartID}-${metric}`); - - if (!bestEnumOnThisChart) { - continue; - } - - const gradeDeltaSc = scoreInfo.find( - (s) => s.scoreID === bestEnumOnThisChart.scoreID, - ); - - // if no grade delta exists then they raised from 0 - // @ts-expect-error silly cheaty enum access - let gradeDelta = bestEnumOnThisChart.scoreData.enumIndexes[metric]!; - - if ( - gradeDeltaSc && - !gradeDeltaSc.isNewScore && - gradeDeltaSc.deltas[metric] !== undefined - ) { - gradeDelta = gradeDeltaSc.deltas[metric]!; - } - - // get all the enums this counts as a raise for. - // that is to say: if you get an AAA, that also counts as a raise - // for an AA, etc. - - // however, this should only extend down to whatever the previous - // best enum on this chart was. - // luckily, we can calculate this by checking what the grade is now - // and taking away the delta. That gets us the original. - // If this is less than the clearGradeIndex, use that instead. - - // note: we add one to this because .slice is inclusive, - // so if we have a EX HARD CLEAR (i=7) with a raise of two, - // minusing two will take us to CLEAR (i=5), and the - // inclusivity will result in us - // slicing ["CLEAR", "HARD CLEAR", "EX HARD CLEAR"] - // (i=5), (i=6) (i=7) - // but this wasn't a new clear! this was only a new HARD CLEAR - // and EX HARD CLEAR, so - // we want ["HARD CLEAR", "EX HARD CLEAR"]. - const originalIndex = - // @ts-expect-error silly cheaty enum access - bestEnumOnThisChart.scoreData.enumIndexes[metric]! - gradeDelta + 1; - - // lowerbound the original grade at the minimum-relevant enum. - const minimumGrade = Math.max( - conf.values.indexOf(conf.minimumRelevantValue), - originalIndex, - ); - - for (const grade of conf.values.slice( - minimumGrade, - // @ts-expect-error silly cheaty enum access (2) - - bestEnumOnThisChart.scoreData.enumIndexes[metric]! + 1, - )) { - AddToSetInRecord(grade, metricDist, chartID); - } - } - - for (const [enumVal, raisedCharts] of Object.entries(metricDist)) { - raiseInfo.push({ - folder, - - previousCount: previousDist[enumVal] ?? 0, - - raisedCharts: Array.from(raisedCharts), - type: metric, - value: enumVal, - totalCharts: chartIDs.length, - }); - } - } - - // now that we know what we've raised, and what was there at the start - // we can push that. - }), - ); - - return res.status(200).json({ - success: true, - description: `Retrieved folder raises.`, - body: raiseInfo, - }); -}); - -interface ModifiableSessionProps { - name?: string; - desc?: string | null; - highlight?: boolean; -} - -/** - * Modifies a session. - * - * Requires the requester to be the owner of the session, alongside having the - * customise_session permission. - * - * @param name - A new name for the session. - * @param desc - A new desc for the session. - * @param highlight - Update the highlighted state of the session with this. - * - * @name PATCH /api/v1/sessions/:sessionID - */ -router.patch( - "/", - RequireOwnershipOfSession, - RequirePermissions("customise_session"), - prValidate( - { - name: p.optional(p.isBoundedString(3, 80)), - desc: optNull(p.isBoundedString(3, 120)), - highlight: "*boolean", - }, - {}, - { allowExcessKeys: true }, - ), - async (req, res) => { - const session = GetTachiData(req, "sessionDoc"); - - const updateExp: ModifiableSessionProps = {}; - - const body = req.safeBody as { - desc?: string | null; - highlight?: boolean; - name?: string; - }; - - if (body.name) { - updateExp.name = body.name; - } - - if (body.desc !== undefined) { - updateExp.desc = body.desc; - } - - if (typeof body.highlight === "boolean") { - updateExp.highlight = body.highlight; - } - - if (Object.keys(updateExp).length === 0) { - return res.status(400).json({ - success: false, - description: `This request modifies nothing about this session.`, - }); - } - - const newSession = await db.sessions.findOneAndUpdate( - { sessionID: session.sessionID }, - { $set: updateExp }, - ); - - return res.status(200).json({ - success: true, - description: `Updated Session.`, - body: newSession, - }); - }, -); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/sessions/router.ts b/typescript/server/src/server/router/api/v1postgres/sessions/router.ts deleted file mode 100644 index 674cb8723..000000000 --- a/typescript/server/src/server/router/api/v1postgres/sessions/router.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Router } from "express"; - -import sessionIDRouter from "./_sessionID/router"; - -const router: Router = Router({ mergeParams: true }); - -/** - * nothing, yet. - * - * @name GET /api/v1/sessions - */ -// router.get("/", async (req, res) => {}); - -router.use("/:sessionID", sessionIDRouter); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/status/router.test.ts b/typescript/server/src/server/router/api/v1postgres/status/router.test.ts deleted file mode 100644 index 8ba315728..000000000 --- a/typescript/server/src/server/router/api/v1postgres/status/router.test.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { VERSION_PRETTY } from "#lib/constants/version"; -import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; -import mockApi from "#test-utils/mock-api"; -import t from "tap"; - -t.test("GET /api/v1/status", async (t) => { - const cookie = await CreateFakeAuthCookie(mockApi); - - t.test("Should return the current time and the server version.", async (t) => { - const res = await mockApi.get("/api/v1/status").set("Cookie", cookie); - - t.equal(res.statusCode, 200); - t.equal(res.body.success, true); - t.ok( - Math.abs(Date.now() - res.body.body.serverTime) < 5_000, - "Should be roughly the current time (5 seconds lenience)", - ); - t.type(res.body.body.startTime, "number", "Should return a number for startTime."); - t.equal(res.body.body.version, VERSION_PRETTY); - t.equal(res.body.body.whoami, 1); - - t.end(); - }); - - t.test("Should echo the provided echo param.", async (t) => { - const res = await mockApi.get("/api/v1/status?echo=foobar").set("Cookie", cookie); - - t.equal(res.statusCode, 200); - t.equal(res.body.success, true); - t.equal(res.body.body.echo, "foobar"); - t.ok( - Math.abs(Date.now() - res.body.body.serverTime) < 5_000, - "Should be roughly the current time (5 seconds lenience)", - ); - t.type(res.body.body.startTime, "number", "Should return a number for startTime."); - t.equal(res.body.body.version, VERSION_PRETTY); - t.equal(res.body.body.whoami, 1); - - t.end(); - }); - - t.end(); -}); - -t.test("POST /api/v1/status", async (t) => { - const cookie = await CreateFakeAuthCookie(mockApi); - - t.test("Should return the current time and the server version.", async (t) => { - const res = await mockApi.post("/api/v1/status").set("Cookie", cookie); - - t.equal(res.statusCode, 200); - t.equal(res.body.success, true); - t.ok( - Math.abs(Date.now() - res.body.body.serverTime) < 5_000, - "Should be roughly the current time (5 seconds lenience)", - ); - t.equal(res.body.body.version, VERSION_PRETTY); - t.equal(res.body.body.whoami, 1); - t.type(res.body.body.startTime, "number", "Should return a number for startTime."); - - t.end(); - }); - - t.test("Should echo the provided echo param.", async (t) => { - const res = await mockApi.post("/api/v1/status").set("Cookie", cookie).send({ - echo: "foobar", - }); - - t.equal(res.statusCode, 200); - t.equal(res.body.success, true); - t.equal(res.body.body.echo, "foobar"); - t.ok( - Math.abs(Date.now() - res.body.body.serverTime) < 5_000, - "Should be roughly the current time (5 seconds lenience)", - ); - t.equal(res.body.body.version, VERSION_PRETTY); - t.equal(res.body.body.whoami, 1); - t.type(res.body.body.startTime, "number", "Should return a number for startTime."); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1postgres/status/router.ts b/typescript/server/src/server/router/api/v1postgres/status/router.ts deleted file mode 100644 index 3190b37e2..000000000 --- a/typescript/server/src/server/router/api/v1postgres/status/router.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { SYMBOL_TACHI_API_AUTH } from "#lib/constants/tachi"; -import { VERSION_PRETTY } from "#lib/constants/version"; -import { Router } from "express"; - -const router: Router = Router({ mergeParams: true }); - -const startTime = Date.now(); - -/** - * Returns the current status of the Tachi Server. - * - * @name GET /api/v1/status - */ -router.get("/", (req, res) => { - let echo; - - if (typeof req.query.echo === "string") { - echo = req.query.echo; - } - - return res.status(200).json({ - success: true, - description: "Status check successful.", - body: { - serverTime: Date.now(), - startTime, - version: VERSION_PRETTY, - whoami: req[SYMBOL_TACHI_API_AUTH].userID, - - // converts {foo: true, bar: false, baz: true} into [foo, baz] - permissions: Object.entries(req[SYMBOL_TACHI_API_AUTH].permissions) - .filter((e) => e[1]) - .map((e) => e[0]), - echo, - }, - }); -}); - -/** - * Returns the current status of the Tachi Server, but as a POST - * request, for that kind of testing. - * - * @name POST /api/v1/status - */ -router.post("/", (req, res) => { - let echo; - - if (typeof req.safeBody.echo === "string") { - echo = req.safeBody.echo; - } - - return res.status(200).json({ - success: true, - description: "Status check successful.", - body: { - serverTime: Date.now(), - startTime, - version: VERSION_PRETTY, - whoami: req[SYMBOL_TACHI_API_AUTH].userID, - - // converts {foo: true, bar: false, baz: true} into [foo, baz] - permissions: Object.entries(req[SYMBOL_TACHI_API_AUTH].permissions) - .filter((e) => e[1]) - .map((e) => e[0]), - echo, - }, - }); -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/api-tokens/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/api-tokens/router.ts deleted file mode 100644 index 6cf7e692b..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/api-tokens/router.ts +++ /dev/null @@ -1,181 +0,0 @@ -import { log } from "#lib/log/log.js"; -import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; -import { Random20Hex } from "#utils/misc"; -import { GetTachiData } from "#utils/req-tachi-data"; -import { FormatUserDoc } from "#utils/user"; -import { Router } from "express"; -import { p } from "prudence"; -import { ALL_PERMISSIONS, type APIPermissions, type APITokenDocument } from "tachi-common"; - -import { RequireSelfRequestFromUser } from "../middleware"; - -const router: Router = Router({ mergeParams: true }); - -router.use(RequireSelfRequestFromUser); - -/** - * Retrieve this users API tokens. - * This request MUST be performed with session-level auth. - * - * @name GET /api/v1/users/:userID/api-tokens - */ -router.get("/", async (req, res) => { - const user = GetTachiData(req, "requestedUser"); - - const keys = await db["api-tokens"].find({ - userID: user.id, - }); - - return res.status(200).json({ - success: true, - description: `Returned ${keys.length} keys.`, - body: keys, - }); -}); - -/** - * Create a new API token. - * - * @param clientID - Create a token that has the permissions implied from this client. - * @param identifier - A user provided string to identify this API Key. - * @param permissions - An array of strings dictating what permissions to create with. - * This is incompatible with the first option. - * - * @name POST /api/v1/users/:userID/api-tokens/create - */ -router.post( - "/create", - prValidate({ - permissions: p.optional([p.isIn(Object.keys(ALL_PERMISSIONS))]), - identifier: "*string", - clientID: "*string", - }), - async (req, res) => { - const body = req.safeBody as { - clientID?: string; - identifier?: string; - permissions?: Array; - }; - - if (body.clientID !== undefined && body.permissions) { - return res.status(400).json({ - success: false, - description: `Cannot use ClientID creation and permissions creation at the same time!`, - }); - } - - let permissions: Array; - - const user = GetTachiData(req, "requestedUser"); - - let identifier: string; - let fromAPIClient = null; - - if (body.clientID !== undefined) { - const client = await db["api-clients"].findOne( - { - clientID: body.clientID, - }, - { - projection: { - clientSecret: 0, - }, - }, - ); - - if (!client) { - return res.status(404).json({ - success: false, - description: `This client does not exist.`, - }); - } - - const exists = await db["api-tokens"].findOne({ - userID: user.id, - fromAPIClient: client.clientID, - }); - - if (exists) { - return res.status(200).json({ - success: true, - description: `Returned existing key`, - body: exists, - }); - } - - permissions = client.requestedPermissions; - identifier = client.name; - fromAPIClient = client.clientID; - - log.info( - `Creating API Key for ${FormatUserDoc(user)} from ${client.name} specification.`, - ); - } else if (body.permissions) { - permissions = body.permissions; - identifier = body.identifier ?? "Custom Token"; - - log.info(`Creating API Key for ${FormatUserDoc(user)} with ${permissions.join(", ")}.`); - } else { - return res.status(400).json({ - success: false, - description: `Invalid request, must specify either clientID or permissions.`, - }); - } - - const permissionsObject = Object.fromEntries(permissions.map((e) => [e, true])); - - const apiTokenDocument: APITokenDocument = { - identifier, - permissions: permissionsObject, - token: Random20Hex(), - userID: user.id, - fromAPIClient, - }; - - await db["api-tokens"].insert(apiTokenDocument); - - log.info(`Inserted new API Key for ${FormatUserDoc(user)}.`); - - return res.status(200).json({ - success: true, - description: `Successfully created new API Token.`, - body: apiTokenDocument, - }); - }, -); - -/** - * Delete this token. - * - * @name DELETE /api/v1/users/:userID/api-token/:token - */ -router.delete("/:token", async (req, res) => { - const user = GetTachiData(req, "requestedUser"); - - log.info(`received request from ${FormatUserDoc(user)} to delete token ${req.params.token}.`); - - const token = await db["api-tokens"].findOne({ - token: req.params.token, - userID: user.id, - }); - - if (!token) { - return res.status(404).json({ - success: false, - description: `This key does not exist.`, - }); - } - - await db["api-tokens"].remove({ token: req.params.token }); - - log.info(`Deleted ${req.params.token}, which belonged to ${FormatUserDoc(user)}.`); - - return res.status(200).json({ - success: true, - description: `Removed Token.`, - body: {}, - }); -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/banner/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/banner/router.ts deleted file mode 100644 index 634139227..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/banner/router.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { CDNDelete, CDNRedirect, CDNStoreOrOverwrite } from "#lib/cdn/cdn"; -import { GetProfileBannerURL } from "#lib/cdn/url-format"; -import { ONE_MEGABYTE } from "#lib/constants/filesize"; -import { log } from "#lib/log/log.js"; -import { RequirePermissions } from "#server/middleware/auth"; -import { CreateMulterSingleUploadMiddleware } from "#server/middleware/multer-upload"; -import db from "#services/mongo/db"; -import { HashSHA256 } from "#utils/crypto"; -import { GetTachiData } from "#utils/req-tachi-data"; -import { FormatUserDoc } from "#utils/user"; -import { Router } from "express"; - -import { RequireAuthedAsUser } from "../middleware"; - -// note: this is just the ../pfp/router.ts code copied and altered. - -const router: Router = Router({ mergeParams: true }); - -/** - * Sets a profile banner. - * - * @param banner - A JPG, PNG or GIF file less than 1mb. - * @note although GIFs are supported, this functionality isn't documented on the site. - * this is kind of an easter egg. - * - * @name PUT /api/v1/users/:userID/banner - */ -router.put( - "/", - RequireAuthedAsUser, - RequirePermissions("customise_profile"), - CreateMulterSingleUploadMiddleware("banner", ONE_MEGABYTE), - async (req, res) => { - const user = GetTachiData(req, "requestedUser"); - - if (!user.customBannerLocation) { - log.debug(`User ${FormatUserDoc(user)} set a custom profile banner.`); - } else { - log.debug(`User ${FormatUserDoc(user)} updated their profile banner.`); - } - - if (!req.file) { - log.error( - `Conflicting state - no req.file has been populated but passed middleware? (${FormatUserDoc( - user, - )})`, - ); - return res.status(500).json({ - success: false, - description: `An internal error has occured.`, - }); - } - - const contentHash = HashSHA256(req.file.buffer); - - if ( - req.file.mimetype === "image/jpeg" || - req.file.mimetype === "image/png" || - req.file.mimetype === "image/gif" - ) { - await CDNStoreOrOverwrite(GetProfileBannerURL(user.id, contentHash), req.file.buffer); - } else { - return res.status(400).json({ - success: false, - description: `Invalid file - only JPG and PNG files are supported.`, - }); - } - - if (req.session.tachi?.user) { - req.session.tachi.user.customBannerLocation = contentHash; - } - - await db.users.update({ id: user.id }, { $set: { customBannerLocation: contentHash } }); - - return res.status(200).json({ - success: true, - description: `Stored profile banner.`, - body: { - get: req.originalUrl, - }, - }); - }, -); - -/** - * Returns this user's profile banner. If the user does not have a custom profile banner, - * return the default profile banner. - * - * @name GET /api/v1/users/:userID/banner - */ -router.get("/", (req, res) => { - const user = GetTachiData(req, "requestedUser"); - - if (!user.customBannerLocation) { - res.setHeader("Content-Type", "image/png"); - CDNRedirect(res, "/users/default/banner"); - return; - } - - // express sniffs whether this is a png or jpg **and** browsers dont care either. - CDNRedirect(res, GetProfileBannerURL(user.id, user.customBannerLocation)); -}); - -/** - * Deletes this user's profile banner, and go back to the default profile banner. - * - * @name DELETE /api/v1/users/:userID/banner - */ -router.delete( - "/", - RequireAuthedAsUser, - RequirePermissions("customise_profile"), - async (req, res) => { - const user = GetTachiData(req, "requestedUser"); - - if (!user.customBannerLocation) { - return res.status(404).json({ - success: false, - description: `You do not have a custom profile banner to delete.`, - }); - } - - await CDNDelete(GetProfileBannerURL(user.id, user.customBannerLocation)); - await db.users.update({ id: user.id }, { $set: { customBannerLocation: null } }); - - return res.status(200).json({ - success: true, - description: `Removed custom profile banner.`, - body: {}, - }); - }, -); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/following/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/following/router.ts deleted file mode 100644 index 4c6dc8651..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/following/router.ts +++ /dev/null @@ -1,196 +0,0 @@ -import type { integer } from "tachi-common"; - -import { log } from "#lib/log/log.js"; -import { ServerConfig } from "#lib/setup/config"; -import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; -import { GetUser } from "#utils/req-tachi-data"; -import { FormatUserDoc, GetUsersWithIDs, GetUserWithID } from "#utils/user"; -import { Router } from "express"; -import { p } from "prudence"; - -import { RequireSelfRequestFromUser } from "../middleware"; - -const router: Router = Router({ mergeParams: true }); - -/** - * Retrieve who this user is following. - * - * @note Following a user means you get updates from them in your global activity feed. - * - * @name GET /api/v1/users/:userID/following - */ -router.get("/", async (req, res) => { - const user = GetUser(req); - - const settings = await db["user-settings"].findOne({ userID: user.id }); - - if (!settings) { - log.error({ user }, `User ${FormatUserDoc(user)} has no settings?`); - - return res.status(500).json({ - success: false, - description: `This user has no settings.`, - }); - } - - const friends = await GetUsersWithIDs(settings.following); - - return res.status(200).json({ - success: true, - description: `Found ${friends.length} friend${friends.length !== 1 ? "s" : ""}.`, - body: { - friends, - }, - }); -}); - -/** - * Follow a new user. - * - * @param userID - The user to follow. - * - * @name POST /api/v1/users/:userID/following/add - */ -router.post( - "/add", - RequireSelfRequestFromUser, - prValidate({ userID: p.isPositiveInteger }), - async (req, res) => { - const user = GetUser(req); - - const { userID: toFollow } = req.safeBody as { userID: integer }; - - if (user.id === toFollow) { - return res.status(400).json({ - success: false, - description: `Can't follow yourself. Bit self-indulgent!`, - }); - } - - const settings = await db["user-settings"].findOne({ userID: user.id }); - - if (!settings) { - log.error({ user }, `User ${FormatUserDoc(user)} has no settings?`); - - return res.status(500).json({ - success: false, - description: `This user has no settings.`, - }); - } - - if (settings.following.includes(toFollow)) { - return res.status(409).json({ - success: false, - description: `You are already following this user.`, - }); - } - - if (settings.following.length >= ServerConfig.MAX_FOLLOWING_AMOUNT) { - return res.status(400).json({ - success: false, - description: `You are following too many people. The max is ${ServerConfig.MAX_FOLLOWING_AMOUNT}.`, - }); - } - - const userToFollow = await GetUserWithID(toFollow); - - if (!userToFollow) { - return res.status(400).json({ - success: false, - description: `No user with the id '${toFollow}' exists.`, - }); - } - - // Instead of using $push in mongodb, we create a new array and set that. - // due to the above guard, it's not possible for this to ever result - // in a race condition. - const following = [...settings.following, toFollow]; - - await db["user-settings"].update( - { - userID: user.id, - }, - { - $set: { - following, - }, - }, - ); - - return res.status(200).json({ - success: true, - description: `Added ${userToFollow.username}.`, - body: {}, - }); - }, -); - -/** - * Unfollow a user. - * - * @param userID - The user to unfollow. - * - * @name POST /api/v1/users/:userID/following/remove - */ -router.post( - "/remove", - RequireSelfRequestFromUser, - prValidate({ userID: p.isPositiveInteger }), - async (req, res) => { - const user = GetUser(req); - - const { userID: toFollow } = req.safeBody as { userID: integer }; - - const settings = await db["user-settings"].findOne({ userID: user.id }); - - if (!settings) { - log.error({ user }, `User ${FormatUserDoc(user)} has no settings?`); - - return res.status(500).json({ - success: false, - description: `This user has no settings.`, - }); - } - - if (!settings.following.includes(toFollow)) { - return res.status(409).json({ - success: false, - description: `You are not following this user.`, - }); - } - - const userToFollow = await GetUserWithID(toFollow); - - if (!userToFollow) { - return res.status(400).json({ - success: false, - description: `No user with the id '${toFollow}' exists.`, - }); - } - - // Instead of using $pull in mongodb, we create a new array and set that. - // due to the above guard, it's not possible for this to ever result - // in a race condition. - const following = settings.following.filter((e) => e !== toFollow); - - await db["user-settings"].update( - { - userID: user.id, - }, - { - $set: { - following, - }, - }, - ); - - return res.status(200).json({ - success: true, - description: `Unfollowed ${userToFollow.username}.`, - body: {}, - }); - }, -); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/@gameSpecificRoutes/bms/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/games/@gameSpecificRoutes/bms/router.ts deleted file mode 100644 index 334400566..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/@gameSpecificRoutes/bms/router.ts +++ /dev/null @@ -1,168 +0,0 @@ -import { - CUSTOM_TACHI_BMS_TABLES, - HandleBMSTableBodyRequest, - HandleBMSTableHeaderRequest, - HandleBMSTableHTMLRequest, -} from "#lib/game-specific/custom-bms-tables"; -import { ValidatePlaytypeFromParamFor } from "#server/router/api/v1/games/_game/_playtype/middleware.js"; -import db from "#services/mongo/db"; -import { AssignToReqTachiData, GetTachiData, GetUGPT, GetUser } from "#utils/req-tachi-data"; -import { type RequestHandler, Router } from "express"; - -const router: Router = Router({ mergeParams: true }); - -const FindCustomBMSTable: RequestHandler = (req, res, next) => { - const { playtype, tableUrlName } = req.params; - - // find the table - const customTable = CUSTOM_TACHI_BMS_TABLES.find((t) => t.urlName === tableUrlName); - - if (!customTable) { - return res.status(404).json({ - success: false, - description: `No such table with the ID '${tableUrlName}' exists.`, - }); - } - - if (customTable.playtype && customTable.playtype !== playtype) { - return res.status(404).json({ - success: false, - description: `The table '${tableUrlName}' exists, but is for ${customTable.playtype}, not ${playtype}.`, - }); - } - - if (customTable.forSpecificUser !== true) { - return res.status(404).json({ - success: false, - description: `The table '${tableUrlName}' exists, but isn't user specific. You should be fetching this table from /api/v1/games instead of /api/v1/users/:userID.`, - }); - } - - AssignToReqTachiData(req, { customBMSTable: customTable }); - - next(); -}; - -/** - * Return some HTML for this custom table. - * - * @note Since this is the UGPT route, trying to fetch GPT custom tables - * will result in a 404. This applies for all subsequent :tableUrlName routes. - * - * @name GET /api/v1/users/:userID/games/bms/:playtype/custom-tables/:tableUrlName - */ -router.get( - "/:playtype/custom-tables/:tableUrlName", - ValidatePlaytypeFromParamFor("bms"), - FindCustomBMSTable, - (req, res) => { - const customTable = GetTachiData(req, "customBMSTable"); - - // This handles returning a response for us. - return HandleBMSTableHTMLRequest(customTable, req, res); - }, -); - -/** - * Return the header.json for this custom table. - * - * @name GET /api/v1/users/:userID/games/bms/:playtype/custom-tables/:tableUrlName/header.json - */ -router.get( - "/:playtype/custom-tables/:tableUrlName/header.json", - ValidatePlaytypeFromParamFor("bms"), - FindCustomBMSTable, - (req, res) => { - const customTable = GetTachiData(req, "customBMSTable"); - - // This handles returning a response for us. - return HandleBMSTableHeaderRequest(customTable, req, res); - }, -); - -/** - * Return the body.json for this custom table. - * - * @name GET /api/v1/users/:userID/games/bms/:playtype/custom-tables/:tableUrlName/body.json - */ -router.get( - "/:playtype/custom-tables/:tableUrlName/body.json", - ValidatePlaytypeFromParamFor("bms"), - FindCustomBMSTable, - (req, res) => { - const customTable = GetTachiData(req, "customBMSTable"); - - // This handles returning a response for us. - return HandleBMSTableBodyRequest(customTable, req, res); - }, -); - -const MD5_CHECKSUM_LENGTH = "60b725f10c9c85c70d97880dfe8191b3".length; -const SHA256_CHECKSUM_LENGTH = "87428fc522803d31065e7bce3cf03fe475096631e5e07bbd7a0fde60c4cf25c7" - .length; - -/** - * Get this user's best chart on the given chart MD5 or SHA256. - * - * @name GET /api/v1/users/:userID/games/bms/:playtype/best-score/:checksum - */ -router.get( - "/:playtype/best-score/:checksum", - ValidatePlaytypeFromParamFor("bms"), - async (req, res) => { - const user = GetUser(req); - - if (!req.params.checksum) { - return res.status(400).json({ success: false, description: "No checksum provided." }); - } - - const checksum = req.params.checksum.toLowerCase(); - - let query = {}; - - if (!/^[0-9a-f]+$/u.exec(checksum)) { - return res.status(400).json({ - success: false, - description: "Invalid checksum (Was not a MD5 or SHA256 checksum).", - }); - } - - if (checksum.length === MD5_CHECKSUM_LENGTH) { - query = { - "data.hashMD5": checksum, - }; - } else if (checksum.length === SHA256_CHECKSUM_LENGTH) { - query = { - "data.hashSHA256": checksum, - }; - } else { - return res.status(400).json({ - success: false, - description: "Invalid checksum length (Was not a MD5 or SHA256 checksum).", - }); - } - - const chart = await db.charts.bms.findOne({ - ...query, - }); - - if (!chart) { - return res - .status(404) - .json({ success: false, description: "No chart found with the given checksum." }); - } - - const pb = await db["personal-bests"].findOne({ - game: "bms", - playtype: chart.playtype, - userID: user.id, - chartID: chart.chartID, - }); - - const description = pb ? "Best score found." : "Player has not played this chart."; - - return res.status(200).json({ success: true, description, body: pb }); - }, -); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/@gameSpecificRoutes/iidx/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/games/@gameSpecificRoutes/iidx/router.ts deleted file mode 100644 index 7574c74af..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/@gameSpecificRoutes/iidx/router.ts +++ /dev/null @@ -1,223 +0,0 @@ -import type { - ChartDocument, - integer, - PBScoreDocument, - Playtypes, - SongDocument, -} from "tachi-common"; -import type { GetEnumValue } from "tachi-common/types/metrics"; - -import { - CUSTOM_TACHI_IIDX_PLAYLISTS, - type TachiIIDXPlaylist, -} from "#lib/game-specific/iidx-playlists"; -import { ResolveSongAndChart } from "#lib/score-import/import-types/common/batch-manual/converter"; -import { EAM_VERSION_NAMES } from "#lib/score-import/import-types/common/eamusement-iidx-csv/parser"; -import { AggressiveRateLimitMiddleware } from "#server/middleware/rate-limiter"; -import { ValidatePlaytypeFromParamFor } from "#server/router/api/v1/games/_game/_playtype/middleware.js"; -import db from "#services/mongo/db"; -import { GetUser } from "#utils/req-tachi-data"; -import { Router } from "express"; - -const router: Router = Router({ mergeParams: true }); - -const EAMUSEMENT_CSV_HEADER = `バージョン,タイトル,ジャンル,アーティスト,プレー回数,BEGINNER 難易度,BEGINNER スコア,BEGINNER PGreat,BEGINNER Great,BEGINNER ミスカウント,BEGINNER クリアタイプ,BEGINNER DJ LEVEL,NORMAL 難易度,NORMAL スコア,NORMAL PGreat,NORMAL Great,NORMAL ミスカウント,NORMAL クリアタイプ,NORMAL DJ LEVEL,HYPER 難易度,HYPER スコア,HYPER PGreat,HYPER Great,HYPER ミスカウント,HYPER クリアタイプ,HYPER DJ LEVEL,ANOTHER 難易度,ANOTHER スコア,ANOTHER PGreat,ANOTHER Great,ANOTHER ミスカウント,ANOTHER クリアタイプ,ANOTHER DJ LEVEL,LEGGENDARIA 難易度,LEGGENDARIA スコア,LEGGENDARIA PGreat,LEGGENDARIA Great,LEGGENDARIA ミスカウント,LEGGENDARIA クリアタイプ,LEGGENDARIA DJ LEVEL,最終プレー日時`; - -function ConvertEamGrade(grade: GetEnumValue<"iidx:DP" | "iidx:SP", "grade">) { - // eamusement has no concept of max or max-. - if (grade === "MAX" || grade === "MAX-") { - return "AAA"; - } - - return grade; -} - -function ConvertEamLamp(lamp: GetEnumValue<"iidx:DP" | "iidx:SP", "lamp">) { - if (lamp === "FULL COMBO") { - return "FULLCOMBO CLEAR"; // weird, but ok - } - - return lamp; -} - -/** - * Retrieve this users PBs in eamusement CSV format. - * - * @name GET /api/v1/users/:userID/games/iidx/:playtype/eamusement-csv - */ -router.get( - "/:playtype/eamusement-csv", - ValidatePlaytypeFromParamFor("iidx"), - AggressiveRateLimitMiddleware, - async (req, res) => { - const game = "iidx"; - - const playtype = req.params.playtype as Playtypes["iidx"]; - const user = GetUser(req); - - const pbData: Array<{ - _id: integer; - pbs: Array>; - song: SongDocument<"iidx">; - }> = await db["personal-bests"].aggregate([ - { - $match: { - userID: user.id, - game, - playtype, - isPrimary: true, - }, - }, - { - $group: { - // group our scores on the song - _id: "$songID", - pbs: { $push: "$$ROOT" }, - }, - }, - { - $lookup: { - from: "songs-iidx", - foreignField: "id", - localField: "_id", - as: "song", - }, - }, - { - $unwind: "$song", - }, - ]); - - const rows = [EAMUSEMENT_CSV_HEADER]; - - // get all relevant charts - const charts = await db.charts.iidx.find({ - songID: { $in: pbData.map((e) => e.song.id) }, - difficulty: { $in: ["NORMAL", "HYPER", "ANOTHER", "LEGGENDARIA"] }, - }); - - // get a lookup table for songID + difficulty -> chart. - const chartMap = new Map(); - - for (const chart of charts) { - chartMap.set(`${chart.songID}-${chart.difficulty}`, chart); - } - - for (const { pbs, song } of pbData) { - let version = "UNKNOWN"; - const tachiVer = song.data.displayVersion; - - if (tachiVer !== null) { - // @ts-expect-error We're abusing enums which already aren't meant - // for this kind of lookup task. Ah well! - - version = EAM_VERSION_NAMES[tachiVer] ?? tachiVer; - } - - const row = [ - version, - song.title, - song.data.genre, - song.artist, - "0", // always 0, who cares? - ]; - - let lastPlayed = 0; - - for (const difficulty of [ - "BEGINNER", - "NORMAL", - "HYPER", - "ANOTHER", - "LEGGENDARIA", - ] as const) { - const chart = chartMap.get(`${song.id}-${difficulty}`); - let pb; - - // this song might not have a beginner/normal/hyper/another/legg - if (chart) { - // try and find the user's PB - pb = pbs.find((e) => e.chartID === chart.chartID); - } - - if (pb) { - row.push( - chart ? chart.level : "0", - pb.scoreData.score.toString(), // ex - pb.scoreData.judgements.pgreat?.toString() ?? "0", // pgreat - pb.scoreData.judgements.great?.toString() ?? "0", // great - pb.scoreData.optional.bp?.toString() ?? "0", // BP - ConvertEamLamp(pb.scoreData.lamp), // lamp - ConvertEamGrade(pb.scoreData.grade), // grade - ); - - if (pb.timeAchieved !== null && lastPlayed < pb.timeAchieved) { - lastPlayed = pb.timeAchieved; - } - } else { - row.push( - chart ? chart.level : "0", - "0", // ex - "0", // pgreat - "0", // great - "---", // BP - "NO PLAY", // lamp - "---", // grade - ); - } - } - - // last played. This will be 1970-01-01 if this user has never played this chart - // with a timestamp. - row.push(new Date(lastPlayed).toISOString()); - - // IIDX uses a "naive" CSV format. that is to say -- there's no escaping. - // God forbid a song title like "19, november" get output here, because it will - // just break the format. That's what the official site does though. - // bug-for-bug compatibility! - // at the very least, we'll replace , with \,. That should be fine. - rows.push(row.map((e) => e.replace(/,/gu, "\\,")).join(",")); - } - - return res.status(200).json({ - success: true, - description: `Created e-amusement CSV.`, - body: rows.join("\n"), - }); - }, -); - -/** - * Retrieve this playlist. - * - * @name GET /api/v1/users/:userID/games/iidx/:playtype/playlists/:playlistID - */ -router.get("/:playtype/playlists/:playlistID", async (req, res) => { - const user = GetUser(req); - - const playlist: TachiIIDXPlaylist | undefined = CUSTOM_TACHI_IIDX_PLAYLISTS.find( - (e) => - (e.playtype === null || e.playtype === req.params.playtype) && - e.urlName === req.params.playlistID, - ); - - if (!playlist) { - return res.status(404).json({ - success: false, - description: `No such playlist '${req.params.playlistID}' exists for '${req.params.playtype}'.`, - }); - } - - if (playlist.forSpecificUser !== true) { - return res.status(404).json({ - success: false, - description: `This playlist is not for a specific user. Use the /games/:game endpoint instead.`, - }); - } - - const body = await playlist.getPlaylists(user.id, req.params.playtype as "DP" | "SP"); - - return res.status(200).json(body); -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/@gameSpecificRoutes/jubeat/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/games/@gameSpecificRoutes/jubeat/router.ts deleted file mode 100644 index 6cb4223fe..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/@gameSpecificRoutes/jubeat/router.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { GetPBsForJubility } from "#game-implementations/games/jubeat"; -import { GetRelevantSongsAndCharts } from "#utils/db"; -import { GetUser } from "#utils/req-tachi-data"; -import { Router } from "express"; - -const router: Router = Router({ mergeParams: true }); - -/** - * Retrieve the PBs that went into this users jubility ranking. - * - * @name GET /api/v1/users/:userID/games/jubeat/Single/jubility - */ -router.get("/Single/jubility", async (req, res) => { - const user = GetUser(req); - - const { bestHotScores, bestScores } = await GetPBsForJubility(user.id); - - const { songs, charts } = await GetRelevantSongsAndCharts( - [...bestHotScores, ...bestScores], - "jubeat", - ); - - return res.status(200).json({ - success: true, - description: `Retrieved scores that went into this users jubility.`, - body: { - songs, - charts, - pickUp: bestHotScores, - other: bestScores, - }, - }); -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/@gameSpecificRoutes/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/games/@gameSpecificRoutes/router.ts deleted file mode 100644 index 51e91822a..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/@gameSpecificRoutes/router.ts +++ /dev/null @@ -1,17 +0,0 @@ -// This file is special. These routes are "gptSpecific". They only apply to certain games -// and playtypes. This is for things like - say - eamusement exports, which only make sense -// for certain games. - -import { Router } from "express"; - -import bmsRouter from "./bms/router"; -import iidxRouter from "./iidx/router"; -import jubeatRouter from "./jubeat/router"; - -const router: Router = Router({ mergeParams: true }); - -router.use("/bms", bmsRouter); -router.use("/iidx", iidxRouter); -router.use("/jubeat", jubeatRouter); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/folders/_folderID/router.test.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/folders/_folderID/router.test.ts deleted file mode 100644 index 79bf24df6..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/folders/_folderID/router.test.ts +++ /dev/null @@ -1,204 +0,0 @@ -import type { ChartDocument, FolderDocument, ScoreDocument, SongDocument } from "tachi-common"; - -import db from "#services/mongo/db"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import { - GetKTDataJSON, - Testing511SPA, - TestingIIDXFolderSP10, - TestingIIDXSPScore, - TestingIIDXSPScorePB, -} from "#test-utils/test-data"; -import { CreateFolderChartLookup } from "#utils/folder"; -import deepmerge from "deepmerge"; -import t from "tap"; - -t.test("GET /api/v1/users/:userID/games/:game/:playtype/folders/:folderID", (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(async () => { - const folder = deepmerge(TestingIIDXFolderSP10, { - folderID: "testing_folder", - }) as FolderDocument; - - await db.folders.insert(folder); - await CreateFolderChartLookup(folder, true); - await db["personal-bests"].insert(deepmerge(TestingIIDXSPScorePB, {})); - }); - - t.test("Should return a user's PBs on a given folder.", async (t) => { - const res = await mockApi.get("/api/v1/users/1/games/iidx/SP/folders/testing_folder"); - - t.equal(res.body.body.songs.length, 1); - t.equal(res.body.body.charts.length, 1); - t.equal(res.body.body.charts[0].chartID, Testing511SPA.chartID); - t.equal(res.body.body.pbs.length, 1); - t.equal(res.body.body.pbs[0].chartID, Testing511SPA.chartID); - t.equal(res.body.body.folder.folderID, "testing_folder"); - - t.end(); - }); - - t.test("Should return 404 if this folder does not exist.", async (t) => { - const res = await mockApi.get("/api/v1/users/1/games/iidx/SP/folders/fake_folder"); - - t.equal(res.statusCode, 404); - - t.end(); - }); - - t.end(); -}); - -t.test("GET /api/v1/users/:userID/games/:game/:playtype/folders/:folderID/timeline", (t) => { - const folder = deepmerge(TestingIIDXFolderSP10, { - folderID: "testing_folder", - }) as FolderDocument; - - t.beforeEach(ResetDBState); - t.beforeEach(async () => { - await db.folders.insert(folder); - await CreateFolderChartLookup(folder, true); - await db["personal-bests"].insert(deepmerge(TestingIIDXSPScorePB, {})); - }); - - t.test("Should return the users scores on this folder in timeline order", async (t) => { - // set up a more realistic scenario so we can fire more data at it. - await db.songs.iidx.remove({}); - await db.charts.iidx.remove({}); - await db["folder-chart-lookup"].remove({}); - await db.songs.iidx.insert( - GetKTDataJSON("./tachi/tachi-songs-iidx.json") as Array>, - ); - await db.charts.iidx.insert( - GetKTDataJSON("./tachi/tachi-charts-iidx.json") as Array< - ChartDocument<"iidx:DP" | "iidx:SP"> - >, - ); - - await CreateFolderChartLookup(folder, true); - - await db.scores.insert([ - deepmerge(TestingIIDXSPScore, { - scoreID: "OTHER_SCORE_ID", - timeAchieved: 500, - songID: 5, - chartID: "f3e7f84103d68f9f27193f037f35d0bca8c6d607", - }), - deepmerge(TestingIIDXSPScore, { - scoreID: "OTHER_SCORE_ID_2", - timeAchieved: 100, - songID: 5, - chartID: "f3e7f84103d68f9f27193f037f35d0bca8c6d607", - }), - deepmerge(TestingIIDXSPScore, { - scoreID: "OTHER_SCORE_ID_NULL", - timeAchieved: null, - songID: 5, - chartID: "f3e7f84103d68f9f27193f037f35d0bca8c6d607", - }), - deepmerge(TestingIIDXSPScore, { - scoreData: { - lamp: "EASY CLEAR", - enumIndexes: { - lamp: 3, - }, - }, - scoreID: "OTHER_SCORE_ID_3", - timeAchieved: 50, - songID: 5, - chartID: "f3e7f84103d68f9f27193f037f35d0bca8c6d607", - }), - deepmerge(TestingIIDXSPScore, { - scoreID: "OTHER_SCORE_ID_4", - timeAchieved: 200, - songID: 6, - chartID: "3b5fa295d243c131752e2f1ad835998f6dad2e97", - }), - ]); - - const res = await mockApi.get( - "/api/v1/users/1/games/iidx/SP/folders/testing_folder/timeline?criteriaType=lamp&criteriaValue=CLEAR", - ); - - t.equal(res.body.body.scores.length, 3); - - t.equal( - res.body.body.scores[0].scoreID, - "TESTING_SCORE_ID", - "First score should be TESTING_SCORE_ID, as it has a timeAchieved of null.", - ); - t.equal( - res.body.body.scores[1].scoreID, - "OTHER_SCORE_ID_2", - "Second score returned should be OTHER_SCORE_ID_2", - ); - t.equal( - res.body.body.scores[2].scoreID, - "OTHER_SCORE_ID_4", - "Third score should be OTHER_SCORE_ID_4, as it was achieved later.", - ); - t.notOk( - res.body.body.scores.some((k: ScoreDocument) => k.scoreID === "OTHER_SCORE_ID_NULL"), - "OTHER_SCORE_ID_NULL should never appear, as it is a null timestamp for a score that has a real timestamp.", - ); - - t.end(); - }); - - t.test("Should reject invalid criteriaTypes", async (t) => { - const res = await mockApi.get( - "/api/v1/users/1/games/iidx/SP/folders/testing_folder/timeline?criteriaType=invalid", - ); - - t.equal(res.statusCode, 400); - - t.end(); - }); - - t.test("Should reject invalid indexes for lamps", async (t) => { - const res = await mockApi.get( - "/api/v1/users/1/games/iidx/SP/folders/testing_folder/timeline?criteriaType=lamp&criteriaValue=-1", - ); - - t.equal(res.statusCode, 400); - - const res2 = await mockApi.get( - "/api/v1/users/1/games/iidx/SP/folders/testing_folder/timeline?criteriaType=lamp&criteriaValue=0.5", - ); - - t.equal(res2.statusCode, 400); - - const res3 = await mockApi.get( - "/api/v1/users/1/games/iidx/SP/folders/testing_folder/timeline?criteriaType=lamp&criteriaValue=100", - ); - - t.equal(res3.statusCode, 400); - - t.end(); - }); - - t.test("Should reject invalid indexes for grades", async (t) => { - const res = await mockApi.get( - "/api/v1/users/1/games/iidx/SP/folders/testing_folder/timeline?criteriaType=grade&criteriaValue=-1", - ); - - t.equal(res.statusCode, 400); - - const res2 = await mockApi.get( - "/api/v1/users/1/games/iidx/SP/folders/testing_folder/timeline?criteriaType=grade&criteriaValue=0.5", - ); - - t.equal(res2.statusCode, 400); - - const res3 = await mockApi.get( - "/api/v1/users/1/games/iidx/SP/folders/testing_folder/timeline?criteriaType=grade&criteriaValue=100", - ); - - t.equal(res3.statusCode, 400); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/folders/_folderID/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/folders/_folderID/router.ts deleted file mode 100644 index de24abad5..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/folders/_folderID/router.ts +++ /dev/null @@ -1,211 +0,0 @@ -import type { FilterQuery } from "mongodb"; - -import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; -import { GetEnumDistForFolder, GetFolderCharts, GetPBsOnFolder } from "#utils/folder"; -import { GetTachiData, GetUGPT } from "#utils/req-tachi-data"; -import { ParseStrPositiveInt } from "#utils/string-checks"; -import { Router } from "express"; -import { - GetGamePTConfig, - GetScoreMetricConf, - type ScoreDocument, - ValidateMetric, -} from "tachi-common"; - -import { GetFolderFromParam } from "../../../../../../../games/_game/_playtype/folders/middleware"; -import { RequireSelfRequestFromUser } from "../../../../../middleware"; - -const router: Router = Router({ mergeParams: true }); - -router.use(GetFolderFromParam); - -/** - * Returns a users pbs on this folder. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/folders/:folderID - */ -router.get("/", async (req, res) => { - const { user } = GetUGPT(req); - - const folder = GetTachiData(req, "folderDoc"); - - const { songs, charts, pbs } = await GetPBsOnFolder(user.id, folder); - - return res.status(200).json({ - success: true, - description: `Returned ${pbs.length} pbs.`, - body: { - songs, - charts, - pbs, - folder, - }, - }); -}); - -/** - * Returns a users stats on this folder. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/folders/:folderID/stats - */ -router.get("/stats", async (req, res) => { - const { user } = GetUGPT(req); - - const folder = GetTachiData(req, "folderDoc"); - - const stats = await GetEnumDistForFolder(user.id, folder); - - return res.status(200).json({ - success: true, - description: `Returned statistics for ${folder.title}.`, - body: { - folder, - stats, - }, - }); -}); - -/** - * Add a folder to the list of recently-viewed folders. This can only - * be performed by a session-level token, to stop rogue API keys from causing - * trouble. Also, it's a post request, to avoid funny SSRF stuff. - * - * @name POST /api/v1/users/:userID/games/:game/:playtype/folders/:folderID/viewed - */ -router.post("/viewed", RequireSelfRequestFromUser, async (req, res) => { - const { user } = GetUGPT(req); - - const folder = GetTachiData(req, "folderDoc"); - - await db["recent-folder-views"].update( - { - userID: user.id, - game: folder.game, - playtype: folder.playtype, - folderID: folder.folderID, - }, - { - $set: { - lastViewed: Date.now(), - }, - }, - { - upsert: true, - }, - ); - - return res.status(200).json({ - success: true, - description: `Recorded a view on ${folder.title}.`, - body: {}, - }); -}); - -// note: this path is disgustingly long. :( -/** - * Returns the users scores in order of when they met this criteria. - * - * @param criteriaType - Any metric for this GPT. - * @param criteriaValue - Any valid value for that metric. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/folders/:folderID/timeline - */ -router.get( - "/timeline", - prValidate({ - criteriaType: "string", - criteriaValue: "string", - }), - async (req, res) => { - const { user, game, playtype } = GetUGPT(req); - - const folder = GetTachiData(req, "folderDoc"); - const gptConfig = GetGamePTConfig(game, playtype); - - // as asserted by prudence. - const metric = req.query.criteriaType as string; - - const conf = GetScoreMetricConf(gptConfig, metric); - - if (!conf || conf.type !== "ENUM") { - return res.status(400).json({ - success: false, - description: `Invalid metric '${metric}' passed. Expected an ENUM for this game.`, - }); - } - - const criteriaValue = conf.values.indexOf(req.query.criteriaValue as string); - - if (criteriaValue === -1) { - return res.status(400).json({ - success: false, - description: `Invalid criteriaValue of ${req.query.criteriaValue} for ${metric}.`, - }); - } - - const { songs, charts } = await GetFolderCharts(folder, {}, true); - - const err = ValidateMetric(gptConfig, metric, criteriaValue); - - if (typeof err === "string") { - return res.status(400).json({ - success: false, - description: err, - }); - } - - const matchCriteria: FilterQuery = { - userID: user.id, - game, - playtype, - chartID: { $in: charts.map((e) => e.chartID) }, - }; - - matchCriteria[`scoreData.enumIndexes.${metric}`] = { $gte: criteriaValue }; - - // Returns a unique score per-chart that was the first score to achieve - // this criteria on that chart. - const scoresAgg: Array<{ doc: ScoreDocument }> = await db.scores.aggregate([ - { - $match: matchCriteria, - }, - { - $addFields: { - __sortTime: { $ifNull: ["$timeAchieved", Infinity, "$timeAchieved"] }, - }, - }, - { - $sort: { - __sortTime: 1, - }, - }, - { - $group: { - _id: "$chartID", - doc: { $first: "$$ROOT" }, - }, - }, - { - $unset: ["doc.__sortTime"], - }, - ]); - - const scores = scoresAgg - .map((e) => e.doc) - .sort((a, b) => (a.timeAchieved ?? 0) - (b.timeAchieved ?? 0)); - - return res.status(200).json({ - success: true, - description: `Returned ${scores.length} scores for ${charts.length} charts.`, - body: { - songs, - charts, - scores, - folder, - }, - }); - }, -); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/folders/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/folders/router.ts deleted file mode 100644 index 19ba009ac..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/folders/router.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { SearchCollection } from "#lib/search/search"; -import db from "#services/mongo/db"; -import { GetEnumDistForFolders, GetRecentlyViewedFolders } from "#utils/folder"; -import { IsString } from "#utils/misc"; -import { GetUGPT } from "#utils/req-tachi-data"; -import { Router } from "express"; - -import folderIDRouter from "./_folderID/router"; - -const router: Router = Router({ mergeParams: true }); - -/** - * Search folders, and supplant a users grade+lamp distribution on that - * folder as part of the returns. - * - * This is a "beefed-up" version of GPT /folders, but with this users - * stats returned at the same time. - * - * @param search - What to search for. - * @param inactive - Also show inactive folders, such as those on old versions. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/folders - */ -router.get("/", async (req, res) => { - const { game, playtype, user } = GetUGPT(req); - - if (!IsString(req.query.search)) { - return res.status(400).json({ - success: false, - description: `Invalid value for search.`, - }); - } - - // if inactive is passed, we need this to be undefined so that - // mongodb returns both inactive and active folders. - // Otherwise, only return active folders. - const inactive = req.query.inactive === undefined ? false : undefined; - - const folders = await SearchCollection( - db.folders, - req.query.search, - "folders", - { game, playtype, inactive }, - 20, - ); - - const stats = await GetEnumDistForFolders(user.id, folders); - - return res.status(200).json({ - success: true, - description: `Returned ${stats.length} folders.`, - body: { - folders, - stats, - }, - }); -}); - -/** - * Get a users most recently interacted with folders. - * - * A folder is interacted with if it is directly fetched using a session key. - * This - in UI terms - is when the user clicks on that folder. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/folders/recent - */ -router.get("/recent", async (req, res) => { - const { game, playtype, user } = GetUGPT(req); - - const { views, folders } = await GetRecentlyViewedFolders(user.id, game, playtype); - - const stats = await GetEnumDistForFolders(user.id, folders); - - return res.status(200).json({ - success: true, - description: `Returned ${views.length} recently interacted with folders.`, - body: { - views, - folders, - stats, - }, - }); -}); - -router.use("/:folderID", folderIDRouter); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/middleware.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/middleware.ts deleted file mode 100644 index 863ff2c69..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/middleware.ts +++ /dev/null @@ -1,59 +0,0 @@ -import type { RequestHandler } from "express"; - -import db from "#services/mongo/db"; -import { IsValidGame, IsValidPlaytype } from "#utils/misc"; -import { AssignToReqTachiData, GetTachiData } from "#utils/req-tachi-data"; - -export const CheckUserPlayedGamePlaytype: RequestHandler = async (req, res, next) => { - const user = GetTachiData(req, "requestedUser"); - - const game = req.params.game; - const playtype = req.params.playtype; - - if (game === undefined) { - throw new Error( - `Unexpected lack of game parameter when CheckUserPlayedGamePlaytype called on ${req.originalUrl}.`, - ); - } - - if (playtype === undefined) { - throw new Error( - `Unexpected lack of playtype parameter when CheckUserPlayedGamePlaytype called on ${req.originalUrl}.`, - ); - } - - if (!IsValidGame(game)) { - return res.status(400).json({ - success: false, - description: `The game ${req.params.game} is not supported.`, - }); - } - - if (!IsValidPlaytype(game, playtype)) { - return res.status(400).json({ - success: false, - description: `The game ${game} does not have a playtype called ${playtype}.`, - }); - } - - const stats = await db["game-stats"].findOne({ - userID: user.id, - game, - playtype, - }); - - if (!stats) { - return res.status(404).json({ - success: false, - description: `The user ${user.username} has not played ${req.params.game} (${req.params.playtype})`, - }); - } - - AssignToReqTachiData(req, { - requestedUserGameStats: stats, - game, - playtype, - }); - - next(); -}; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/pbs/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/pbs/router.ts deleted file mode 100644 index c55642dd3..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/pbs/router.ts +++ /dev/null @@ -1,338 +0,0 @@ -import { log } from "#lib/log/log.js"; -import { GetRivalUsers } from "#lib/rivals/rivals"; -import { ResolveSongAndChart } from "#lib/score-import/import-types/common/batch-manual/converter"; -import { SearchSpecificGameSongsAndCharts } from "#lib/search/search"; -import prValidate from "#server/middleware/prudence-validate"; -import { AggressiveRateLimitMiddleware } from "#server/middleware/rate-limiter"; -import db from "#services/mongo/db"; -import { GetRelevantSongsAndCharts } from "#utils/db"; -import { IsValidScoreAlg } from "#utils/misc"; -import { GetAdjacentAbove, GetAdjacentBelow } from "#utils/queries/pbs"; -import { GetUGPT } from "#utils/req-tachi-data"; -import { FilterChartsAndSongs, GetPBOnChart, GetScoreIDsFromComposed } from "#utils/scores"; -import { GetUsersWithIDs } from "#utils/user"; -import { Router } from "express"; -import { GetGamePTConfig, type MatchTypeResolver } from "tachi-common"; -import { PR_RESOLVER } from "tachi-common/lib/schemas"; - -const router: Router = Router({ mergeParams: true }); - -/** - * Searches a user's personal bests. - * - * @param search - The search criteria. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/pbs - */ -router.get("/", async (req, res) => { - const { user, game, playtype } = GetUGPT(req); - - if (typeof req.query.search !== "string") { - return res.status(400).json({ - success: false, - description: `Invalid value of for search parameter.`, - }); - } - - const { songs: allSongs, charts: allCharts } = await SearchSpecificGameSongsAndCharts( - game, - req.query.search, - playtype, - ); - - const pbs = await db["personal-bests"].find( - { - chartID: { $in: allCharts.map((e) => e.chartID) }, - userID: user.id, - }, - { - sort: { - timeAchieved: -1, - }, - limit: 30, - }, - ); - - const { songs, charts } = FilterChartsAndSongs(pbs, allCharts, allSongs); - - return res.status(200).json({ - success: true, - description: `Retrieved ${pbs.length} personal bests.`, - body: { - pbs, - songs, - charts, - }, - }); -}); - -/** - * Returns all of a users personal bests. - * - * @warn This endpoint is probably quite expensive. We'll need to do - * some performance tests. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/pbs/all - */ -router.get("/all", AggressiveRateLimitMiddleware, async (req, res) => { - const { user, game, playtype } = GetUGPT(req); - - const pbs = await db["personal-bests"].find({ - userID: user.id, - game, - playtype, - isPrimary: true, - }); - - const { songs, charts } = await GetRelevantSongsAndCharts(pbs, game); - - return res.status(200).json({ - success: true, - description: `Returned ${pbs.length} PBs.`, - body: { pbs, songs, charts }, - }); -}); - -/** - * Returns a users best 100 personal-bests for this game. - * - * @param alg - Specifies an override for the default algorithm - * to sort on. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/pbs/best - */ -router.get("/best", prValidate({ alg: "*string" }), async (req, res) => { - const { user, game, playtype } = GetUGPT(req); - - const gptConfig = GetGamePTConfig(game, playtype); - - if (req.query.alg !== undefined && !IsValidScoreAlg(gptConfig, req.query.alg)) { - return res.status(400).json({ - success: false, - description: `Invalid score algorithm. Expected any of ${Object.keys( - gptConfig.scoreRatingAlgs, - ).join(", ")}`, - }); - } - - const alg = (req.query.alg as string | undefined) ?? gptConfig.defaultScoreRatingAlg; - - const pbs = await db["personal-bests"].find( - { - userID: user.id, - game, - playtype, - isPrimary: true, - }, - { - limit: 100, - sort: { - [`calculatedData.${alg}`]: -1, - }, - }, - ); - - const { songs, charts } = await GetRelevantSongsAndCharts(pbs, game); - - return res.status(200).json({ - success: true, - description: `Retrieved ${pbs.length} personal bests.`, - body: { - pbs, - songs, - charts, - }, - }); -}); - -/** - * Returns a user's PB on the given chart. If the user has not played this chart, 404 is - * returned. - * - * @param getComposition - Also retrieves the score documents that composed this PB. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/pbs/:chartID - */ -router.get("/:chartID", async (req, res) => { - const { user, game, playtype } = GetUGPT(req); - - const chart = await db.anyCharts[game].findOne({ - chartID: req.params.chartID, - playtype, - }); - - if (!chart) { - return res.status(404).json({ - success: false, - description: `This chart does not exist.`, - }); - } - - const pb = await db["personal-bests"].findOne({ - chartID: req.params.chartID, - userID: user.id, - }); - - if (!pb) { - return res.status(404).json({ - success: false, - description: `This user has not played this chart.`, - }); - } - - if (req.query.getComposition !== undefined) { - const scoreIDs = GetScoreIDsFromComposed(pb); - - const scores = await db.scores.find({ - scoreID: { $in: scoreIDs }, - }); - - return res.status(200).json({ - success: true, - description: `Successfully retrieved PB for user.`, - body: { - scores, - chart, - pb, - }, - }); - } - - return res.status(200).json({ - success: true, - description: `Successfully retrieved PB for user.`, - body: { - pb, - chart, - }, - }); -}); - -/** - * Returns a user's PB on the given chart, and all of their rivals performances aswell. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/pbs/:chartID/rivals - */ -router.get("/:chartID/rivals", async (req, res) => { - const { user, game, playtype } = GetUGPT(req); - - const rivals = await GetRivalUsers(user.id, game, playtype); - - const pbs = await db["personal-bests"].find({ - userID: { $in: rivals.map((e) => e.id) }, - chartID: req.params.chartID, - }); - - const usersPB = await GetPBOnChart(user.id, req.params.chartID); - - if (usersPB) { - pbs.push(usersPB); - } - - return res.status(200).json({ - success: true, - description: `Retrieved PBs and Rival PBs.`, - body: { - pbs, - rivals, - }, - }); -}); - -/** - * Return this users PB on this chart, and 5 nearby players on the - * leaderboard. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/pbs/:chartID/leaderboard-adjacent - */ -router.get("/:chartID/leaderboard-adjacent", async (req, res) => { - const { user, game, playtype } = GetUGPT(req); - - const chart = await db.anyCharts[game].findOne({ - chartID: req.params.chartID, - playtype, - }); - - if (!chart) { - return res.status(404).json({ - success: false, - description: `This chart does not exist.`, - }); - } - - const pb = await db["personal-bests"].findOne({ - chartID: req.params.chartID, - userID: user.id, - }); - - if (!pb) { - return res.status(404).json({ - success: false, - description: `This user has not played this chart.`, - }); - } - - const [adjacentAbove, adjacentBelow] = await Promise.all([ - GetAdjacentAbove(pb), - GetAdjacentBelow(pb), - ]); - - const users = await GetUsersWithIDs([...adjacentAbove, ...adjacentBelow].map((e) => e.userID)); - - return res.status(200).json({ - success: true, - description: `Successfully retrieved PB for user.`, - body: { - pb, - chart, - adjacentAbove, - adjacentBelow, - users, - }, - }); -}); - -/** - * Use the tachi "resolve" engine to identify a chart instead of - * using the Tachi IDs. Used to get a PB. - * - * @name POST /api/v1/users/:userID/games/:game/:playtype/pbs/resolve - */ -router.post("/resolve", prValidate(PR_RESOLVER), async (req, res) => { - const { user, game, playtype } = GetUGPT(req); - - const safeBody = { - ...req.safeBody, - game, - playtype, - } as unknown as MatchTypeResolver; - const got = await ResolveSongAndChart(safeBody, log); - - if (!got) { - return res.status(404).json({ - success: false, - description: `Could not resolve this chart with details: ${safeBody.matchType}:${safeBody.identifier} (Extra specifiers: version=${safeBody.version}, artist=${safeBody.artist})`, - }); - } - - const pb = await GetPBOnChart(user.id, got.chart.chartID); - - if (!pb) { - return res.status(404).json({ - success: false, - description: `This user has not played this chart.`, - }); - } - - return res.status(200).json({ - success: true, - description: "Successfully retrieved PB for user.", - body: { - pb, - chart: got.chart, - song: got.song, - }, - }); -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/rivals/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/rivals/router.ts deleted file mode 100644 index c97f16c09..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/rivals/router.ts +++ /dev/null @@ -1,191 +0,0 @@ -import { CreateActivityRouteHandler } from "#lib/activity/activity"; -import { SetRivalsFailReasons } from "#lib/constants/err-codes"; -import { GetChallengerUsers, GetRivalIDs, GetRivalUsers, SetRivals } from "#lib/rivals/rivals"; -import { RequirePermissions } from "#server/middleware/auth"; -import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; -import { GetRelevantSongsAndCharts } from "#utils/db"; -import { IsString } from "#utils/misc"; -import { GetUGPT } from "#utils/req-tachi-data"; -import { CheckStrScoreAlg } from "#utils/string-checks"; -import { GetUsersWithIDs } from "#utils/user"; -import { Router } from "express"; -import { p } from "prudence"; -import { FormatGameGroup, GetGamePTConfig, type integer } from "tachi-common"; - -import { RequireAuthedAsUser } from "../../../../middleware"; - -const router: Router = Router({ mergeParams: true }); - -/** - * Returns all of this user's set rivals. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/rivals - */ -router.get("/", async (req, res) => { - const { user, game, playtype } = GetUGPT(req); - - const rivals = await GetRivalUsers(user.id, game, playtype); - - return res.status(200).json({ - success: true, - description: `Returned ${rivals.length} rivals.`, - body: rivals, - }); -}); - -/** - * Sets the user's rivals for this GPT. - * - * @param rivalIDs - An array of rivalIDs to set as their rivals. - * - * @name PUT /api/v1/users/:userID/games/:game/:playtype/rivals - */ -router.put( - "/", - RequireAuthedAsUser, - RequirePermissions("manage_rivals"), - prValidate({ - rivalIDs: [p.isPositiveNonZeroInteger], - }), - async (req, res) => { - const body = req.safeBody as { - rivalIDs: Array; - }; - - const rivalIDs = body.rivalIDs; - const { user, game, playtype } = GetUGPT(req); - - const result = await SetRivals(user.id, game, playtype, rivalIDs); - - if (result === SetRivalsFailReasons.RIVALED_SELF) { - return res.status(400).json({ - success: false, - description: `You cannot rival yourself.`, - }); - } else if (result === SetRivalsFailReasons.RIVALS_HAVENT_PLAYED_GPT) { - return res.status(400).json({ - success: false, - description: `Not all of the rivals you specified have played ${FormatGameGroup( - game, - playtype, - )}.`, - }); - } else if (result === SetRivalsFailReasons.TOO_MANY) { - return res.status(400).json({ - success: false, - description: `You can't set more than 5 rivals.`, - }); - } - - return res.status(200).json({ - success: true, - description: `Set ${rivalIDs.length} rivals.`, - body: {}, - }); - }, -); - -/** - * Return all of the users that are rivalling this user for this GPT. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/rivals/challengers - */ -router.get("/challengers", async (req, res) => { - const { user, game, playtype } = GetUGPT(req); - - const challengers = await GetChallengerUsers(user.id, game, playtype); - - return res.status(200).json({ - success: true, - description: `Returned ${challengers.length} challengers.`, - body: challengers, - }); -}); - -/** - * Retrieve a "PB leaderboard" for this user's set of rivals. - * - * This is - effectively - the best 100 scores from this set of users on the given - * rating algorithm. - * - * @param alg - The score rating algorithm to sort on. Defaults to whatever the GPTConfig - * default is. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/rivals/pb-leaderboard - */ -router.get("/pb-leaderboard", async (req, res) => { - const { user, game, playtype } = GetUGPT(req); - const gptConfig = GetGamePTConfig(game, playtype); - - let alg = gptConfig.defaultScoreRatingAlg; - - if (IsString(req.query.alg)) { - const temp = CheckStrScoreAlg(game, playtype, req.query.alg); - - if (temp === null) { - return res.status(400).json({ - success: false, - description: `Invalid value of ${ - req.query.alg - } for alg. Expected one of ${Object.keys(gptConfig.scoreRatingAlgs).join(", ")}`, - }); - } - - alg = temp; - } - - const rivalIDs = await GetRivalIDs(user.id, game, playtype); - const userSet = [...rivalIDs, user.id]; - - const pbs = await db["personal-bests"].find( - { - game, - playtype, - userID: { $in: userSet }, - }, - { - sort: { - [`calculatedData.${alg}`]: -1, - }, - limit: 100, - }, - ); - - const users = await GetUsersWithIDs(pbs.map((e) => e.userID)); - - const { songs, charts } = await GetRelevantSongsAndCharts(pbs, game); - - return res.status(200).send({ - success: true, - description: `Successfully returned ${pbs.length} pbs.`, - body: { - pbs, - songs, - charts, - users, - }, - }); -}); - -/** - * Retrieve activity for this user's set of rivals. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/rivals/activity - */ -router.get("/activity", async (req, res) => { - const { game, playtype, user } = GetUGPT(req); - - const rivalIDs = await GetRivalIDs(user.id, game, playtype); - - const route = CreateActivityRouteHandler({ - userID: { $in: rivalIDs }, - game, - playtype, - }); - - // this handles responding - void route(req, res); -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/router.test.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/router.test.ts deleted file mode 100644 index c2ecf9068..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/router.test.ts +++ /dev/null @@ -1,284 +0,0 @@ -import type { ScoreDocument } from "tachi-common"; - -import { ONE_DAY } from "#lib/constants/time"; -import db from "#services/mongo/db"; -import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import { Testing511SPA, TestingIIDXSPScore, TestingIIDXSPScorePB } from "#test-utils/test-data"; -import deepmerge from "deepmerge"; -import t from "tap"; - -t.test("GET /api/v1/users/:userID/games/:game/:playtype", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should return a users statistics for that game.", async (t) => { - const res = await mockApi.get("/api/v1/users/test_zkldi/games/iidx/SP"); - - t.hasStrict(res.body, { - success: true, - description: "Retrieved user statistics for test_zkldi (iidx SP)", - body: { - gameStats: { - userID: 1, - game: "iidx", - playtype: "SP", - classes: {}, - ratings: {}, - }, - firstScore: null, - mostRecentScore: null, - totalScores: 1, - rankingData: { - ktLampRating: { - ranking: 1, - outOf: 1, - }, - BPI: { - ranking: 1, - outOf: 1, - }, - }, - }, - }); - - t.end(); - }); - - t.test("Should return a users first score if one exists.", async (t) => { - await db.scores.insert([ - { - userID: 1, - timeAchieved: 100, - scoreID: "foo", - game: "iidx", - playtype: "SP", - }, - { - userID: 1, - timeAchieved: 200, - scoreID: "bar", - game: "iidx", - playtype: "SP", - }, - { - userID: 1, - timeAchieved: 300, - scoreID: "baz", - game: "iidx", - playtype: "SP", - }, - ] as Array); - - const res = await mockApi.get("/api/v1/users/test_zkldi/games/iidx/SP"); - - t.hasStrict(res.body, { - success: true, - description: "Retrieved user statistics for test_zkldi (iidx SP)", - body: { - gameStats: { - userID: 1, - game: "iidx", - playtype: "SP", - classes: {}, - ratings: {}, - }, - firstScore: { - timeAchieved: 100, - scoreID: "foo", - }, - mostRecentScore: { - timeAchieved: 300, - scoreID: "baz", - }, - totalScores: 4, - rankingData: { - ktLampRating: { - ranking: 1, - outOf: 1, - }, - BPI: { - ranking: 1, - outOf: 1, - }, - }, - }, - }); - - t.end(); - }); - - t.end(); -}); - -t.test("GET /api/v1/users/:userID/games/:game/:playtype/history", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should return a users history snapshots for that gpt.", async (t) => { - await db["game-stats-snapshots"].insert([ - { - userID: 1, - game: "iidx", - playtype: "SP", - - // @ts-expect-error Too lazy to sort this one out... - rankings: { - BPI: { - ranking: 2, - outOf: 4, - }, - }, - playcount: 100, - classes: {}, - ratings: {}, - timestamp: Date.now() - ONE_DAY, - }, - ]); - - const res = await mockApi.get("/api/v1/users/test_zkldi/games/iidx/SP/history"); - - // by default, it's set to the current time. we can't - // test that nicely, so lets round it to the nearest 100 seconds. - res.body.body[0].timestamp = Math.floor(res.body.body[0].timestamp / 100_000); - res.body.body[1].timestamp = Math.floor(res.body.body[1].timestamp / 100_000); - - t.strictSame(res.body.body, [ - { - rankings: { - ktLampRating: { - ranking: 1, - outOf: 1, - }, - BPI: { - ranking: 1, - outOf: 1, - }, - }, - playcount: 1, - classes: {}, - ratings: {}, - - // close enough, right? - timestamp: Math.floor(Date.now() / 100_000), - }, - { - rankings: { - BPI: { - ranking: 2, - outOf: 4, - }, - }, - playcount: 100, - classes: {}, - ratings: {}, - timestamp: Math.floor((Date.now() - ONE_DAY) / 100_000), - }, - ]); - - t.end(); - }); - - t.end(); -}); - -t.test("GET /api/v1/users/:userID/games/:game/:playtype/most-played", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should return the users most played scores.", async (t) => { - await db.scores.insert([ - // @ts-expect-error nonsense - deepmerge(TestingIIDXSPScore, { scoreID: "something_else" }), - - // @ts-expect-error nonsense - deepmerge(TestingIIDXSPScore, { scoreID: "something_else2" }), - ]); - - await db["personal-bests"].insert(TestingIIDXSPScorePB); - - const res = await mockApi.get("/api/v1/users/1/games/iidx/SP/most-played"); - - t.equal(res.body.body.pbs.length, 1); - t.equal(res.body.body.pbs[0].__playcount, 3); - - t.equal(res.body.body.songs.length, 1); - t.equal(res.body.body.songs[0].id, 1); - - t.equal(res.body.body.charts.length, 1); - t.equal(res.body.body.charts[0].chartID, Testing511SPA.chartID); - - t.end(); - }); - - t.end(); -}); - -t.test("DELETE /api/v1/users/:userID/games/:game/:playtype", async (t) => { - t.beforeEach(ResetDBState); - - const cookie = await CreateFakeAuthCookie(mockApi); - - t.test("Should delete this users profile and check passwords.", async (t) => { - const res = await mockApi - .delete(`/api/v1/users/1/games/iidx/SP`) - .set("Cookie", cookie) - .send({ - "!password": "password", - }); - - t.equal(res.status, 200, "Should return 200."); - - const dbRes = await db["game-stats"].findOne({ userID: 1, game: "iidx", playtype: "SP" }); - - t.equal(dbRes, null, "Should remove the UGPT profile from the database."); - - t.end(); - }); - - t.test("Should require a correct password.", async (t) => { - const res = await mockApi - .delete(`/api/v1/users/1/games/iidx/SP`) - .set("Cookie", cookie) - .send({ - "!password": "invalid_password", - }); - - t.equal(res.status, 403, "Should return 403."); - - const dbRes = await db["game-stats"].findOne({ userID: 1, game: "iidx", playtype: "SP" }); - - t.not(dbRes, null, "Should NOT remove the UGPT profile from the database."); - - t.end(); - }); - - t.test("Should require a password.", async (t) => { - const res = await mockApi - .delete(`/api/v1/users/1/games/iidx/SP`) - .set("Cookie", cookie) - .send({}); - - t.equal(res.status, 400, "Should return 400."); - - const dbRes = await db["game-stats"].findOne({ userID: 1, game: "iidx", playtype: "SP" }); - - t.not(dbRes, null, "Should NOT remove the UGPT profile from the database."); - - t.end(); - }); - - t.test("Should require self-key auth.", async (t) => { - const res = await mockApi.delete(`/api/v1/users/1/games/iidx/SP`).send({ - "!password": "password", - }); - - t.equal(res.status, 401, "Should return 401."); - - const dbRes = await db["game-stats"].findOne({ userID: 1, game: "iidx", playtype: "SP" }); - - t.not(dbRes, null, "Should NOT remove the UGPT profile from the database."); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/router.ts deleted file mode 100644 index 89f23f3f2..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/router.ts +++ /dev/null @@ -1,460 +0,0 @@ -import { CreateActivityRouteHandler } from "#lib/activity/activity"; -import { ONE_MONTH, ONE_WEEK, ONE_YEAR } from "#lib/constants/time"; -import { log } from "#lib/log/log.js"; -import prValidate from "#server/middleware/prudence-validate"; -import { PasswordCompare, ValidatePassword } from "#server/router/api/v1/auth/auth.js"; -import db from "#services/mongo/db"; -import { IsString } from "#utils/misc"; -import { GetTachiData, GetUGPT } from "#utils/req-tachi-data"; -import DestroyUserGamePlaytypeData from "#utils/reset-state/destroy-ugpt"; -import { CheckStrProfileAlg } from "#utils/string-checks"; -import { - FormatUserDoc, - GetAllRankings, - GetUGPTPlaycount, - GetUserPrivateInfo, - GetUsersRankingAndOutOf, - GetUsersWithIDs, -} from "#utils/user"; -import { Router } from "express"; -import { p } from "prudence"; -import { - FormatGameGroup, - GetGamePTConfig, - type GPTString, - type integer, - type PBScoreDocument, - type ProfileRatingAlgorithms, - type UserGameStatsSnapshotDocument, -} from "tachi-common"; - -import { RequireAuthedAsUser, RequireSelfRequestFromUser } from "../../../middleware"; -import foldersRouter from "./folders/router"; -import { CheckUserPlayedGamePlaytype } from "./middleware"; -import pbsRouter from "./pbs/router"; -import rivalsRouter from "./rivals/router"; -import scoresRouter from "./scores/router"; -import sessionsRouter from "./sessions/router"; -import settingsRouter from "./settings/router"; -import showcaseRouter from "./showcase/router"; -import tablesRouter from "./tables/router"; -import targetsRouter from "./targets/router"; - -const router: Router = Router({ mergeParams: true }); - -router.use(CheckUserPlayedGamePlaytype); - -/** - * Returns information about a user for this game + playtype. - * @name GET /api/v1/users/:userID/games/:game/:playtype - */ -router.get("/", async (req, res) => { - const { game, playtype, user } = GetUGPT(req); - - const stats = GetTachiData(req, "requestedUserGameStats"); - - const [totalScores, firstScore, mostRecentScore, rankingData, playtime] = await Promise.all([ - db.scores.count({ - userID: user.id, - game, - playtype, - }), - db.scores.findOne( - { - userID: user.id, - game, - playtype, - timeAchieved: { $ne: null }, - }, - { - sort: { - timeAchieved: 1, - }, - }, - ), - db.scores.findOne( - { - userID: user.id, - game, - playtype, - timeAchieved: { $ne: null }, - }, - { - sort: { - timeAchieved: -1, - }, - }, - ), - GetAllRankings(stats), - db.sessions.aggregate<[] | [{ playtime: number }]>([ - { - $match: { - userID: user.id, - game, - playtype, - }, - }, - { - $project: { - duration: { $subtract: ["$timeEnded", "$timeStarted"] }, - }, - }, - { - $group: { - _id: null, - playtime: { $sum: "$duration" }, - }, - }, - ]), - ]); - - return res.status(200).json({ - success: true, - description: `Retrieved user statistics for ${user.username} (${game} ${playtype})`, - body: { - gameStats: stats, - firstScore, - mostRecentScore, - totalScores, - rankingData, - playtime: playtime[0]?.playtime ?? 0, - }, - }); -}); - -/** - * Returns a users game-stats for the past 90 days. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/history - */ -router.get( - "/history", - prValidate({ - duration: p.optional(p.isIn("week", "month", "3mo", "year")), - }), - async (req, res) => { - const duration = req.query.duration as "3mo" | "month" | "week" | "year" | undefined; - - let time = Date.now(); - - switch (duration) { - case "year": { - time = time - ONE_YEAR; - break; - } - - // if the user doesn't define anything, default to 3month - case "3mo": - case undefined: { - time = time - ONE_MONTH * 3; - break; - } - - case "month": { - time = time - ONE_MONTH; - break; - } - - case "week": - time = time - ONE_WEEK; - } - - const { game, playtype, user } = GetUGPT(req); - - const stats = GetTachiData(req, "requestedUserGameStats"); - - const snapshots = (await db["game-stats-snapshots"].find( - { - userID: user.id, - game, - playtype, - timestamp: { $gte: time }, - }, - { - sort: { - timestamp: -1, - }, - - // avoid sending so much garbage. - projection: { - userID: 0, - game: 0, - playtype: 0, - }, - }, - )) as Array>; - - const currentSnapshot: Omit = - { - classes: stats.classes, - ratings: stats.ratings, - - // lazy, should probably be this midnight - timestamp: Date.now(), - playcount: await GetUGPTPlaycount(user.id, game, playtype), - rankings: await GetAllRankings(stats), - }; - - return res.status(200).json({ - success: true, - description: `Successfully returned history for the past ${snapshots.length} days.`, - body: [currentSnapshot, ...snapshots], - }); - }, -); - -/** - * Returns the users most played charts by playcount. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/most-played - */ -router.get("/most-played", async (req, res) => { - const { game, playtype, user } = GetUGPT(req); - - const mostPlayed: Array<{ _id: string; playcount: integer; songID: integer }> = - await db.scores.aggregate([ - { - $match: { - userID: user.id, - game, - playtype, - }, - }, - { - $group: { - _id: "$chartID", - - // micro opt - songID: { $first: "$songID" }, - playcount: { $sum: 1 }, - }, - }, - { - $sort: { - playcount: -1, - }, - }, - { - $limit: 100, - }, - ]); - - const chartIDs = mostPlayed.map((e) => e._id); - const songIDs = mostPlayed.map((e) => e.songID); - - const [songs, charts, pbs] = await Promise.all([ - await db.anySongs[game].find({ id: { $in: songIDs } }), - await db.anyCharts[game].find({ chartID: { $in: chartIDs } }), - await db["personal-bests"].find({ chartID: { $in: chartIDs }, userID: user.id }), - ]); - - const playcountMap = new Map(); - - for (const doc of mostPlayed) { - playcountMap.set(doc._id, doc.playcount); - } - - // @ts-expect-error monkeypatching - const playcountPBs = pbs as Array<{ __playcount: integer } & PBScoreDocument>; - - // monkey patch __playcount on - for (const pb of playcountPBs) { - pb.__playcount = playcountMap.get(pb.chartID) ?? 0; - } - - playcountPBs.sort((a, b) => b.__playcount - a.__playcount); - - return res.status(200).json({ - success: true, - description: `Returned ${playcountPBs.length} scores.`, - body: { - songs, - charts, - pbs: playcountPBs, - }, - }); -}); - -/** - * Returns the users around the given user on the leaderboard. - * - * @param alg - Optional, the algorithm to use. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/leaderboard-adjacent - */ -router.get("/leaderboard-adjacent", async (req, res) => { - const { game, playtype, user } = GetUGPT(req); - - const gptConfig = GetGamePTConfig(game, playtype); - - let alg = gptConfig.defaultProfileRatingAlg; - - if (IsString(req.query.alg)) { - const temp = CheckStrProfileAlg(game, playtype, req.query.alg); - - if (temp === null) { - return res.status(400).json({ - success: false, - description: `Invalid value of ${ - req.query.alg - } for alg. Expected one of ${Object.keys(gptConfig.profileRatingAlgs).join(", ")}`, - }); - } - - alg = temp; - } - - const thisUsersStats = await db["game-stats"].findOne({ game, playtype, userID: user.id }); - - if (!thisUsersStats) { - return res.status(400).json({ - success: false, - description: `This user has not played this game.`, - }); - } - - const [above, below] = await Promise.all([ - db["game-stats"].find( - { - game, - playtype, - userID: { $ne: user.id }, - [`ratings.${alg}`]: { $gt: thisUsersStats.ratings[alg] }, - }, - { - limit: 5, - sort: { - [`ratings.${alg}`]: 1, - }, - }, - ), - db["game-stats"].find( - { - game, - playtype, - userID: { $ne: user.id }, - [`ratings.${alg}`]: { $lte: thisUsersStats.ratings[alg] }, - }, - { - limit: 5, - sort: { - [`ratings.${alg}`]: -1, - }, - }, - ), - ]); - - const users = await GetUsersWithIDs([ - ...above.map((e) => e.userID), - ...below.map((e) => e.userID), - ]); - - const thisUsersRanking = await GetUsersRankingAndOutOf(thisUsersStats, alg); - - return res.status(200).json({ - success: true, - description: `Returned ${above.length + below.length} nearby stats.`, - body: { - above: above.reverse(), - below, - users, - thisUsersStats, - thisUsersRanking, - }, - }); -}); - -/** - * Retrieve activity for this user. - * - * @param session - See CreateActivityRouteHandler - * @param startTime - See CreateActivityRouteHandler - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/activity - */ -router.get("/activity", (req, res) => { - const { game, playtype, user } = GetUGPT(req); - - const route = CreateActivityRouteHandler({ - userID: user.id, - game, - playtype, - }); - - // this handles responding - void route(req, res); -}); - -/** - * Completely wipe this profile. - * Requires a self-request from this user, and also checks that the user knows their - * password. This is one of the most bolted down endpoints in the site, for obvious - * reasons. - * - * @param !password - This user's password. - * - * @name DELETE /api/v1/users/:userID/games/:game/:playtype - */ -router.delete( - "/", - RequireSelfRequestFromUser, - RequireAuthedAsUser, - prValidate({ - "!password": ValidatePassword, - }), - async (req, res) => { - const { user, game, playtype } = GetUGPT(req); - const body = req.safeBody as { - "!password": string; - }; - - log.info( - `Recieved request to delete UGPT ${FormatUserDoc(user)} ${FormatGameGroup(game, playtype)}`, - ); - - const privateInfo = await GetUserPrivateInfo(user.id); - - if (!privateInfo) { - log.error( - { user }, - `State desync for user ${FormatUserDoc( - user, - )}. This user has no password/email information?`, - ); - - return res.status(500).json({ - success: false, - description: `An internal server error has occured.`, - }); - } - - const passwordMatch = await PasswordCompare(body["!password"], privateInfo.password); - - if (!passwordMatch) { - return res.status(403).json({ - success: false, - description: `Invalid password.`, - }); - } - - await DestroyUserGamePlaytypeData(user.id, game, playtype); - - return res.status(200).json({ - success: true, - description: `Destroyed profile.`, - body: {}, - }); - }, -); - -router.use("/pbs", pbsRouter); -router.use("/scores", scoresRouter); -router.use("/sessions", sessionsRouter); -router.use("/tables", tablesRouter); -router.use("/showcase", showcaseRouter); -router.use("/settings", settingsRouter); -router.use("/folders", foldersRouter); -router.use("/targets", targetsRouter); -router.use("/rivals", rivalsRouter); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/scores/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/scores/router.ts deleted file mode 100644 index 7778462ca..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/scores/router.ts +++ /dev/null @@ -1,151 +0,0 @@ -import { SearchSpecificGameSongsAndCharts } from "#lib/search/search"; -import { HyperAggressiveRateLimitMiddleware } from "#server/middleware/rate-limiter"; -import db from "#services/mongo/db"; -import { GetRelevantSongsAndCharts } from "#utils/db"; -import { GetUGPT } from "#utils/req-tachi-data"; -import { FilterChartsAndSongs } from "#utils/scores"; -import { Router } from "express"; - -const router: Router = Router({ mergeParams: true }); - -/** - * Searches a user's individual scores. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/scores - */ -router.get("/", async (req, res) => { - const { user, game, playtype } = GetUGPT(req); - - if (typeof req.query.search !== "string") { - return res.status(400).json({ - success: false, - description: `Invalid value for search parameter.`, - }); - } - - const { songs: allSongs, charts: allCharts } = await SearchSpecificGameSongsAndCharts( - game, - req.query.search, - playtype, - ); - - const scores = await db.scores.find( - { - chartID: { $in: allCharts.map((e) => e.chartID) }, - userID: user.id, - }, - { - sort: { - timeAchieved: -1, - }, - limit: 30, - }, - ); - - const { songs, charts } = FilterChartsAndSongs(scores, allCharts, allSongs); - - return res.status(200).json({ - success: true, - description: `Retrieved ${scores.length} scores.`, - body: { - scores, - songs, - charts, - }, - }); -}); - -/** - * Retrieve all scores from this user. - * - * @warn This endpoint is expensive, and is rate-limited as such. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/scores/all - */ -router.get("/all", HyperAggressiveRateLimitMiddleware, async (req, res) => { - const { user, game, playtype } = GetUGPT(req); - - const scores = await db.scores.find({ - userID: user.id, - game, - playtype, - isPrimary: true, - }); - - const { songs, charts } = await GetRelevantSongsAndCharts(scores, game); - - return res.status(200).json({ - success: true, - description: `Returned ${scores.length} PBs.`, - body: { scores, songs, charts }, - }); -}); - -/** - * Returns a users recent 100 scores for this game. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/scores/recent - */ -router.get("/recent", async (req, res) => { - const { user, game, playtype } = GetUGPT(req); - - const recentScores = await db.scores.find( - { - userID: user.id, - game, - playtype, - }, - { - limit: 100, - sort: { - timeAchieved: -1, - }, - }, - ); - - const { songs, charts } = await GetRelevantSongsAndCharts(recentScores, game); - - return res.status(200).json({ - success: true, - description: `Retrieved ${recentScores.length} scores.`, - body: { - scores: recentScores, - songs, - charts, - }, - }); -}); - -/** - * Retrieve all the scores a user has on the given chartID. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/scores/:chartID - */ -router.get("/:chartID", async (req, res) => { - const { user, game, playtype } = GetUGPT(req); - - const chart = await db.anyCharts[game].findOne({ - chartID: req.params.chartID, - playtype, - }); - - if (!chart) { - return res.status(404).json({ - success: false, - description: `This chart does not exist.`, - }); - } - - const scores = await db.scores.find({ - userID: user.id, - chartID: chart.chartID, - }); - - return res.status(200).json({ - success: true, - description: `Returned ${scores.length} scores.`, - body: scores, - }); -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/sessions/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/sessions/router.ts deleted file mode 100644 index 0e5ad7903..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/sessions/router.ts +++ /dev/null @@ -1,248 +0,0 @@ -import { GetSessionScoreInfo } from "#lib/score-import/framework/sessions/sessions"; -import { SearchSessions } from "#lib/search/search"; -import db from "#services/mongo/db"; -import { GetUGPT } from "#utils/req-tachi-data"; -import { CheckStrSessionAlg } from "#utils/string-checks"; -import { Router } from "express"; -import { - type AnySessionRatingAlg, - GetGamePTConfig, - type SessionDocument, - type SessionScoreInfo, -} from "tachi-common"; - -const router: Router = Router({ mergeParams: true }); - -/** - * Search a users sessions. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/sessions - */ -router.get("/", async (req, res) => { - const { user, game, playtype } = GetUGPT(req); - - if (typeof req.query.search !== "string") { - return res.status(400).json({ - success: false, - description: `Invalid value for search parameter.`, - }); - } - - const sessions = await SearchSessions(req.query.search, game, playtype, user.id, 100); - - return res.status(200).json({ - success: true, - description: `Retrieved ${sessions.length} sessions.`, - body: sessions, - }); -}); - -/** - * Returns a user's best 100 sessions according to the default statistic - * for that game. - * - * @param alg - An override to specify a different algorithm for that game. - * @name GET /api/v1/users/:userID/games/:game/:playtype/sessions/best - */ -router.get("/best", async (req, res) => { - const { user, game, playtype } = GetUGPT(req); - - const gptConfig = GetGamePTConfig(game, playtype); - - let alg = gptConfig.defaultSessionRatingAlg as AnySessionRatingAlg; - - if (typeof req.query.alg === "string") { - const userAlg = CheckStrSessionAlg(game, playtype, req.query.alg); - - if (userAlg === null) { - return res.status(400).json({ - success: false, - description: `Invalid algorithm '${ - req.query.alg - }' provided. Expected any of ${Object.keys(gptConfig.sessionRatingAlgs).join( - ", ", - )}.`, - }); - } - - alg = userAlg; - } - - const sessions = await db.sessions.find( - { - userID: user.id, - game, - playtype, - }, - { - limit: 100, - sort: { - [`calculatedData.${alg}`]: -1, - }, - }, - ); - - const sessionsWithScoreInfo: Array<{ __scoreInfo: Array } & SessionDocument> = - []; - - await Promise.all( - sessions.map((session) => - GetSessionScoreInfo(session).then((r) => { - sessionsWithScoreInfo.push({ - ...session, - __scoreInfo: r, - }); - }), - ), - ); - - sessionsWithScoreInfo.sort( - (a, b) => (b.calculatedData[alg] ?? -Infinity) - (a.calculatedData[alg] ?? -Infinity), - ); - - return res.status(200).json({ - success: true, - description: `Retrieved ${sessions.length} sessions.`, - body: sessionsWithScoreInfo, - }); -}); - -/** - * Returns a users 100 most recent highlighted sessions. Returned in timeEnded order. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/sessions/highlighted - */ -router.get("/highlighted", async (req, res) => { - const { user, game, playtype } = GetUGPT(req); - - const sessions = await db.sessions.find( - { userID: user.id, game, playtype, highlight: true }, - { sort: { timeEnded: -1 }, limit: 100 }, - ); - - const sessionsWithScoreInfo: Array<{ __scoreInfo: Array } & SessionDocument> = - []; - - await Promise.all( - sessions.map((session) => - GetSessionScoreInfo(session).then((r) => { - sessionsWithScoreInfo.push({ - ...session, - __scoreInfo: r, - }); - }), - ), - ); - - return res.status(200).json({ - success: true, - description: `Returned ${sessions.length} sessions.`, - body: sessionsWithScoreInfo, - }); -}); - -/** - * Returns a users 100 most recent sessions. Returned in timeEnded order. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/sessions/recent - */ -router.get("/recent", async (req, res) => { - const { user, game, playtype } = GetUGPT(req); - - const sessions = await db.sessions.find( - { userID: user.id, game, playtype }, - { sort: { timeEnded: -1 }, limit: 100 }, - ); - - const sessionsWithScoreInfo: Array<{ __scoreInfo: Array } & SessionDocument> = - []; - - await Promise.all( - sessions.map((session) => - GetSessionScoreInfo(session).then((r) => { - sessionsWithScoreInfo.push({ - ...session, - __scoreInfo: r, - }); - }), - ), - ); - - sessionsWithScoreInfo.sort((a, b) => b.timeEnded - a.timeEnded); - - return res.status(200).json({ - success: true, - description: `Returned ${sessions.length} sessions.`, - body: sessionsWithScoreInfo, - }); -}); - -/** - * Returns a user's most recent session. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/sessions/last - */ -router.get("/last", async (req, res) => { - const { user, game, playtype } = GetUGPT(req); - - const session = await db.sessions.findOne( - { userID: user.id, game, playtype }, - { sort: { timeEnded: -1 } }, - ); - - if (!session) { - return res.status(404).json({ - success: false, - description: `This user has not got any sessions!`, - }); - } - - const scoreInfo = await GetSessionScoreInfo(session); - - return res.status(200).json({ - success: true, - description: `Returned a session.`, - body: { - session, - scoreInfo, - }, - }); -}); - -/** - * Returns all sessions, but with unecessary properties removed so as to reduce - * bandwidth. This is used for the calendar view in tachi-client, hence the name. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/sessions/calendar - */ -router.get("/calendar", async (req, res) => { - const { user, game, playtype } = GetUGPT(req); - - const sessions = await db.sessions.find( - { - userID: user.id, - game, - playtype, - }, - { - projection: { - sessionID: 1, - name: 1, - desc: 1, - highlight: 1, - timeStarted: 1, - timeEnded: 1, - game: 1, - playtype: 1, - }, - }, - ); - - return res.status(200).json({ - success: true, - description: `Found ${sessions.length} events.`, - body: sessions, - }); -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/settings/router.test.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/settings/router.test.ts deleted file mode 100644 index 257bcbc07..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/settings/router.test.ts +++ /dev/null @@ -1,440 +0,0 @@ -import type { UGPTSettingsDocument } from "tachi-common"; - -/* eslint-disable no-await-in-loop */ -import db from "#services/mongo/db"; -import { mkFakeUser } from "#test-utils/misc"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import t from "tap"; - -t.test("GET /api/v1/users/:userID/games/:game/:playtype/settings", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should return a user's settings.", async (t) => { - const res = await mockApi.get("/api/v1/users/1/games/iidx/SP/settings"); - - t.strictSame(res.body.body, { - userID: 1, - game: "iidx", - playtype: "SP", - preferences: { - preferredScoreAlg: null, - preferredSessionAlg: null, - preferredProfileAlg: null, - preferredRanking: null, - preferredDefaultEnum: null, - defaultTable: null, - stats: [], - gameSpecific: { - display2DXTra: false, - bpiTarget: 0, - }, - }, - rivals: [], - }); - - t.end(); - }); - - t.end(); -}); - -t.test("PATCH /api/v1/users/:userID/games/:game/:playtype/settings", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should update a user's settings.", async (t) => { - await db["api-tokens"].insert({ - userID: 1, - identifier: "api_token", - permissions: { - customise_profile: true, - }, - token: "api_token", - fromAPIClient: null, - }); - - const res = await mockApi - .patch("/api/v1/users/1/games/iidx/SP/settings") - .set("Authorization", "Bearer api_token") - .send({ - preferredScoreAlg: "ktLampRating", - }); - - t.strictSame( - res.body.body, - { - userID: 1, - game: "iidx", - playtype: "SP", - preferences: { - preferredScoreAlg: "ktLampRating", - preferredSessionAlg: null, - preferredProfileAlg: null, - preferredDefaultEnum: null, - preferredRanking: null, - defaultTable: null, - stats: [], - gameSpecific: { - display2DXTra: false, - bpiTarget: 0, - }, - }, - rivals: [], - }, - "Should only update the mutated properties.", - ); - - const data = await db["game-settings"].findOne({ - userID: 1, - game: "iidx", - playtype: "SP", - }); - - t.equal(data?.preferences.preferredScoreAlg, "ktLampRating"); - - t.end(); - }); - - t.test("Should update a user's game specific settings.", async (t) => { - await db["api-tokens"].insert({ - userID: 1, - identifier: "api_token", - permissions: { - customise_profile: true, - }, - token: "api_token", - fromAPIClient: null, - }); - - const res = await mockApi - .patch("/api/v1/users/1/games/iidx/SP/settings") - .set("Authorization", "Bearer api_token") - .send({ - preferredScoreAlg: "ktLampRating", - gameSpecific: { - display2DXTra: true, - bpiTarget: 50, - }, - }); - - t.strictSame( - res.body.body, - { - userID: 1, - game: "iidx", - playtype: "SP", - preferences: { - preferredScoreAlg: "ktLampRating", - preferredSessionAlg: null, - preferredProfileAlg: null, - preferredRanking: null, - preferredDefaultEnum: null, - defaultTable: null, - stats: [], - gameSpecific: { - display2DXTra: true, - bpiTarget: 50, - }, - }, - rivals: [], - }, - "Should only update the mutated properties.", - ); - - const data = await db["game-settings"].findOne({ - userID: 1, - game: "iidx", - playtype: "SP", - }); - - t.equal(data?.preferences.preferredScoreAlg, "ktLampRating"); - - t.end(); - }); - - t.test("Should update an IIDX player's bpiTarget settings.", (t) => { - t.beforeEach(async () => { - await db["api-tokens"].insert({ - userID: 1, - identifier: "api_token", - permissions: { - customise_profile: true, - }, - token: "api_token", - fromAPIClient: null, - }); - }); - - for (const target of [0, 10, 15, 17.5, 100]) { - t.test(`Should be able to update the BPI target to ${target}`, async (t) => { - const res = await mockApi - .patch("/api/v1/users/1/games/iidx/SP/settings") - .set("Authorization", "Bearer api_token") - .send({ - gameSpecific: { - bpiTarget: target, - }, - }); - - t.equal(res.statusCode, 200); - - t.strictSame( - res.body.body, - { - userID: 1, - game: "iidx", - playtype: "SP", - preferences: { - preferredScoreAlg: null, - preferredSessionAlg: null, - preferredProfileAlg: null, - preferredRanking: null, - preferredDefaultEnum: null, - defaultTable: null, - stats: [], - gameSpecific: { - display2DXTra: false, - bpiTarget: target, - }, - }, - rivals: [], - }, - "Should only update the mutated properties.", - ); - - const data = (await db["game-settings"].findOne({ - userID: 1, - game: "iidx", - playtype: "SP", - })) as UGPTSettingsDocument<"iidx:SP"> | undefined; - - t.equal(data?.preferences.gameSpecific.bpiTarget, target); - - t.end(); - }); - } - - t.test("Should reject BPI targets < -15", async (t) => { - const res = await mockApi - .patch("/api/v1/users/1/games/iidx/SP/settings") - .set("Authorization", "Bearer api_token") - .send({ - gameSpecific: { - bpiTarget: -20, - }, - }); - - t.equal(res.statusCode, 400); - - t.end(); - }); - - t.test("Should reject >= 100 BPI targets", async (t) => { - const res = await mockApi - .patch("/api/v1/users/1/games/iidx/SP/settings") - .set("Authorization", "Bearer api_token") - .send({ - gameSpecific: { - bpiTarget: 101, - }, - }); - - t.equal(res.statusCode, 400); - - t.end(); - }); - - t.end(); - }); - - t.test("Should reject arbitrary things on game specific settings.", async (t) => { - await db["api-tokens"].insert({ - userID: 1, - identifier: "api_token", - permissions: { - customise_profile: true, - }, - token: "api_token", - fromAPIClient: null, - }); - - const res = await mockApi - .patch("/api/v1/users/1/games/iidx/SP/settings") - .set("Authorization", "Bearer api_token") - .send({ - preferredScoreAlg: "BPI", - gameSpecific: { - display2DXTra: true, - arbitraryValue: {}, - }, - }); - - t.equal(res.statusCode, 400); - - t.end(); - }); - - t.test("Requires the user to be authed as the requested user.", async (t) => { - await db.users.insert(mkFakeUser(2)); - await db["api-tokens"].insert({ - userID: 2, - identifier: "api_token", - permissions: { - customise_profile: true, - }, - token: "api_token", - fromAPIClient: null, - }); - - const res = await mockApi - .patch("/api/v1/users/1/games/iidx/SP/settings") - .set("Authorization", `Bearer api_token`); - - t.equal(res.statusCode, 403); - - t.end(); - }); - - t.test("Requires the permission customise_profile", async (t) => { - await db["api-tokens"].insert({ - userID: 1, - identifier: "api_token", - permissions: { - customise_profile: false, - }, - token: "api_token", - fromAPIClient: null, - }); - - const res = await mockApi - .patch("/api/v1/users/1/games/iidx/SP/settings") - .set("Authorization", `Bearer api_token`); - - t.equal(res.statusCode, 403); - - t.end(); - }); - - t.test("Should reject invalid values.", async (t) => { - await db["api-tokens"].insert({ - userID: 1, - identifier: "foo", - permissions: { - customise_profile: true, - }, - token: "api_token", - fromAPIClient: null, - }); - - for (const key of [ - "preferredScoreAlg", - "preferredSessionAlg", - "preferredProfileAlg", - ] as const) { - const res = await mockApi - .patch("/api/v1/users/1/games/iidx/SP/settings") - .set("Authorization", "Bearer api_token") - .send({ - [key]: "nonsense", - }); - - t.equal(res.statusCode, 400); - - const data = await db["game-settings"].findOne({ - userID: 1, - game: "iidx", - playtype: "SP", - }); - - t.equal(data?.preferences[key], null, "Should be unmodified."); - } - - t.end(); - }); - - t.test("Should update a user's default table.", async (t) => { - await db["api-tokens"].insert({ - userID: 1, - identifier: "api_token", - permissions: { - customise_profile: true, - }, - token: "api_token", - fromAPIClient: null, - }); - - const res = await mockApi - .patch("/api/v1/users/1/games/iidx/SP/settings") - .set("Authorization", "Bearer api_token") - .send({ - defaultTable: "mock_table", - }); - - t.strictSame( - res.body.body, - { - userID: 1, - game: "iidx", - playtype: "SP", - preferences: { - preferredScoreAlg: null, - preferredSessionAlg: null, - preferredProfileAlg: null, - preferredRanking: null, - preferredDefaultEnum: null, - defaultTable: "mock_table", - stats: [], - gameSpecific: { - display2DXTra: false, - bpiTarget: 0, - }, - }, - rivals: [], - }, - "Should only update the mutated properties.", - ); - - const data = await db["game-settings"].findOne({ - userID: 1, - game: "iidx", - playtype: "SP", - }); - - t.equal(data?.preferences.defaultTable, "mock_table"); - - t.end(); - }); - - t.test("Should not allow updating to a table that doesn't exist.", async (t) => { - await db["api-tokens"].insert({ - userID: 1, - identifier: "api_token", - permissions: { - customise_profile: true, - }, - token: "api_token", - fromAPIClient: null, - }); - - const res = await mockApi - .patch("/api/v1/users/1/games/iidx/SP/settings") - .set("Authorization", "Bearer api_token") - .send({ - defaultTable: "fake_table", - }); - - t.equal(res.statusCode, 400); - - const data = await db["game-settings"].findOne({ - userID: 1, - game: "iidx", - playtype: "SP", - }); - - t.equal(data?.preferences.defaultTable, null); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/settings/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/settings/router.ts deleted file mode 100644 index 12470c396..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/settings/router.ts +++ /dev/null @@ -1,183 +0,0 @@ -import { log } from "#lib/log/log.js"; -import { RequirePermissions } from "#server/middleware/auth"; -import db from "#services/mongo/db"; -import { FormatPrError, optNull } from "#utils/prudence"; -import { GetUGPT } from "#utils/req-tachi-data"; -import { FormatUserDoc } from "#utils/user"; -import { Router } from "express"; -import { p } from "prudence"; -import { - GetGamePTConfig, - GetScoreMetrics, - PrudenceZodShim, - type UGPTSettingsDocument, -} from "tachi-common"; - -import { RequireAuthedAsUser } from "../../../../middleware"; - -const router: Router = Router({ mergeParams: true }); - -/** - * Update your settings. - * - * @param - See the prudence validation. - * - * @name PATCH /api/v1/users/:userID/games/:game/:playtype/settings - */ -router.patch( - "/", - RequireAuthedAsUser, - RequirePermissions("customise_profile"), - async (req, res) => { - const { user, game, playtype } = GetUGPT(req); - - const gptConfig = GetGamePTConfig(game, playtype); - - const gameSpecificSchema = PrudenceZodShim(gptConfig.preferences); - - const err = p(req.safeBody, { - preferredScoreAlg: p.optional( - p.nullable(p.isIn(Object.keys(gptConfig.scoreRatingAlgs))), - ), - preferredSessionAlg: p.optional( - p.nullable(p.isIn(Object.keys(gptConfig.sessionRatingAlgs))), - ), - preferredProfileAlg: p.optional( - p.nullable(p.isIn(Object.keys(gptConfig.profileRatingAlgs))), - ), - defaultTable: "*?string", - preferredRanking: optNull(p.isIn("global", "rival")), - - gameSpecific: optNull(gameSpecificSchema), - preferredDefaultEnum: optNull(p.isIn(...GetScoreMetrics(gptConfig, "ENUM"))), - }); - - if (err) { - return res.status(400).json({ - success: false, - description: FormatPrError(err, "Invalid game-settings."), - }); - } - - const body = req.safeBody as Partial; - - if (typeof body.defaultTable === "string") { - const table = await db.tables.findOne({ - game, - playtype, - tableID: body.defaultTable, - }); - - if (!table) { - return res.status(400).json({ - success: false, - description: `The table (${body.defaultTable}) does not exist (and therefore cannot be set as a default).`, - }); - } - } - - const updateQuery: Record = {}; - - // @warning Slightly icky dynamic prop assignment instead of copypasta. - for (const key of ["Score", "Session", "Profile"] as const) { - const k = `preferred${key}Alg` as const; - - const value = body[k]; - - if (value !== undefined) { - updateQuery[`preferences.${k}`] = value; - } - } - - if (body.preferredDefaultEnum !== undefined) { - updateQuery[`preferences.preferredDefaultEnum`] = body.preferredDefaultEnum; - } - - if (body.defaultTable !== undefined) { - updateQuery[`preferences.defaultTable`] = body.defaultTable; - } - - if (body.preferredRanking !== undefined) { - updateQuery[`preferences.preferredRanking`] = body.preferredRanking; - } - - if (body.gameSpecific) { - for (const [key, value] of Object.entries(body.gameSpecific)) { - updateQuery[`preferences.gameSpecific.${key}`] = value; - } - } - - if (Object.keys(updateQuery).length === 0) { - const settings = await db["game-settings"].findOne({ - userID: user.id, - game, - playtype, - }); - - return res.status(200).json({ - success: true, - description: `Nothing has been modified, successfully.`, - body: settings, - }); - } - - await db["game-settings"].update( - { - userID: user.id, - game, - playtype, - }, - { - $set: updateQuery, - }, - ); - - const settings = await db["game-settings"].findOne({ - userID: user.id, - game, - playtype, - }); - - if (!settings) { - log.error( - `User ${FormatUserDoc( - user, - )} has no game-settings, but has played ${game} ${playtype}?`, - ); - - return res.status(500).json({ - success: false, - description: `An internal error has occured. Do not repeat this request.`, - }); - } - - return res.status(200).json({ - success: true, - description: `Updated settings.`, - body: settings, - }); - }, -); - -/** - * Returns this user's settings. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/settings - */ -router.get("/", async (req, res) => { - const { user, game, playtype } = GetUGPT(req); - - const settings = await db["game-settings"].findOne({ - userID: user.id, - game, - playtype, - }); - - return res.status(200).json({ - success: true, - description: `Returned ${FormatUserDoc(user)}'s settings.`, - body: settings, - }); -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/showcase/router.test.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/showcase/router.test.ts deleted file mode 100644 index 5e9968063..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/showcase/router.test.ts +++ /dev/null @@ -1,430 +0,0 @@ -import db from "#services/mongo/db"; -import { mkFakeUser } from "#test-utils/misc"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import { Testing511SPA, TestingIIDXFolderSP10, TestingIIDXSPScorePB } from "#test-utils/test-data"; -import { CreateFolderChartLookup } from "#utils/folder"; -import deepmerge from "deepmerge"; -import { type ChartDocument, IIDX_GRADES, IIDX_LAMPS, type UserDocument } from "tachi-common"; -import t from "tap"; - -const SetFolders = async () => { - await db.folders.insert(TestingIIDXFolderSP10); - await CreateFolderChartLookup(TestingIIDXFolderSP10, true); - - await db["game-settings"].remove({}); - - await db["game-settings"].insert({ - userID: 1, - game: "iidx", - playtype: "SP", - preferences: { - preferredProfileAlg: null, - preferredScoreAlg: null, - preferredSessionAlg: null, - preferredRanking: null, - preferredDefaultEnum: null, - defaultTable: null, - stats: [ - { - folderID: TestingIIDXFolderSP10.folderID, - mode: "folder", - metric: "lamp", - gte: IIDX_LAMPS.HARD_CLEAR, - }, - { - chartID: Testing511SPA.chartID, - mode: "chart", - metric: "score", - }, - ], - gameSpecific: { - display2DXTra: false, - bpiTarget: 0, - }, - }, - rivals: [], - }); - - await db["personal-bests"].insert(deepmerge(TestingIIDXSPScorePB, {})); -}; - -t.test("GET /api/v1/users/:userID/games/:game/:playtype/showcase", (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(SetFolders); - - t.test("Should return the evaluated stats for this user.", async (t) => { - const res = await mockApi.get("/api/v1/users/1/games/iidx/SP/showcase"); - - t.hasStrict(res.body.body, [ - { - stat: { - mode: "folder", - }, - result: { - value: 1, - outOf: 1, - }, - }, - { - stat: { - mode: "chart", - }, - result: { - value: 1479, - }, - }, - ]); - - t.end(); - }); - - t.test("Should project another users stats over this one if projectUser is set.", async (t) => { - await db["game-settings"].insert({ - userID: 2, - game: "iidx", - playtype: "SP", - preferences: { - preferredProfileAlg: null, - preferredScoreAlg: null, - preferredSessionAlg: null, - preferredRanking: null, - preferredDefaultEnum: null, - defaultTable: null, - stats: [ - { - mode: "folder", - folderID: TestingIIDXFolderSP10.folderID, - metric: "score", - gte: 1480, - }, - ], - gameSpecific: { - display2DXTra: false, - bpiTarget: 0, - }, - }, - rivals: [], - }); - - // hack - await db.users.insert({ - username: "test_acc", - usernameLowercase: "test_acc", - id: 2, - } as UserDocument); - - const res = await mockApi.get("/api/v1/users/1/games/iidx/SP/showcase?projectUser=2"); - - t.hasStrict(res.body.body, [ - { - stat: { - mode: "folder", - }, - result: { - value: 0, - outOf: 1, - }, - }, - ]); - - t.end(); - }); - - t.test("Should return 404 if the user has not played this game.", async (t) => { - const res = await mockApi.get("/api/v1/users/1/games/bms/7K/showcase"); - - t.equal(res.statusCode, 404); - - t.match(res.body.description, /not played/iu); - - t.end(); - }); - - t.end(); -}); - -t.test("GET /api/v1/users/:userID/games/:game/:playtype/showcase/custom", (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(SetFolders); - - t.test("Should return a custom folder evaluated stat on a user.", async (t) => { - const res = await mockApi.get( - `/api/v1/users/1/games/iidx/SP/showcase/custom?mode=folder&metric=grade>e=3&folderID=${TestingIIDXFolderSP10.folderID}`, - ); - - t.hasStrict(res.body.body, { - result: { - value: 1, - outOf: 1, - }, - }); - - t.end(); - }); - - t.test("Should return a custom chart evaluated stat on a user.", async (t) => { - const res = await mockApi.get( - `/api/v1/users/1/games/iidx/SP/showcase/custom?mode=chart&metric=grade&chartID=${Testing511SPA.chartID}`, - ); - - t.hasStrict(res.body.body, { - result: { value: IIDX_GRADES.AAA }, - }); - - t.end(); - }); - - t.test("Should reject for invalid folderID.", async (t) => { - const res = await mockApi.get( - `/api/v1/users/1/games/iidx/SP/showcase/custom?mode=folder&metric=grade>e=4`, - ); - - t.equal(res.statusCode, 400, "Should reject for no folderID"); - - const res2 = await mockApi.get( - `/api/v1/users/1/games/iidx/SP/showcase/custom?mode=folder&metric=grade>e=4&folderID=foo&folderID=bar`, - ); - - t.equal(res2.statusCode, 400, "Should reject for non-string folderID"); - - t.end(); - }); - - t.test("Should reject for invalid chartID.", async (t) => { - const res = await mockApi.get( - `/api/v1/users/1/games/iidx/SP/showcase/custom?mode=chart&metric=grade>e=4`, - ); - - t.equal(res.statusCode, 400, "Should reject for no chartID"); - - const res2 = await mockApi.get( - `/api/v1/users/1/games/iidx/SP/showcase/custom?mode=chart&metric=grade&chartID=foo&chartID=bar`, - ); - - t.equal(res2.statusCode, 400, "Should reject for non-string chartID"); - - t.end(); - }); - - t.test("Should reject for chartID that doesn't exist.", async (t) => { - const res = await mockApi.get( - `/api/v1/users/1/games/iidx/SP/showcase/custom?mode=chart&metric=grade&chartID=chart_does_not_exist`, - ); - - t.equal(res.statusCode, 400); - - await db.charts.iidx.insert({ - chartID: "testing_dp_chart", - playtype: "DP", - } as ChartDocument<"iidx:DP">); - - const res2 = await mockApi.get( - `/api/v1/users/1/games/iidx/SP/showcase/custom?mode=chart&metric=grade&chartID=testing_dp_chart`, - ); - - t.equal(res2.statusCode, 400); - - t.end(); - }); - - t.test("Should reject for folderID that doesn't exist.", async (t) => { - const res = await mockApi.get( - `/api/v1/users/1/games/iidx/SP/showcase/custom?mode=folder&metric=grade>e=4&folderID=invalid`, - ); - - t.equal(res.statusCode, 400); - - const res2 = await mockApi.get( - `/api/v1/users/1/games/iidx/SP/showcase/custom?mode=folder&metric=grade>e=4&folderID=${TestingIIDXFolderSP10.folderID},invalid`, - ); - - t.equal(res2.statusCode, 400); - - t.end(); - }); - - t.test("Should reject for invalid mode", async (t) => { - const res = await mockApi.get( - `/api/v1/users/1/games/iidx/SP/showcase/custom?mode=nonsense&metric=grade>e=4&chartID=foo`, - ); - - t.equal(res.statusCode, 400); - - t.end(); - }); - - t.end(); -}); - -t.test("PUT /api/v1/users/:userID/games/:game/:playtype/showcase", (t) => { - t.beforeEach(ResetDBState); - t.beforeEach(SetFolders); - - t.beforeEach( - async () => - // eslint-disable-next-line no-return-await - await db["api-tokens"].insert({ - userID: 1, - identifier: "alt_token", - permissions: { - customise_profile: true, - }, - token: "alt_token", - fromAPIClient: null, - }), - ); - - t.test("Requires the user to be authed as the requested user.", async (t) => { - await db.users.insert(mkFakeUser(2)); - await db["api-tokens"].insert({ - userID: 2, - identifier: "alt_token", - permissions: { - customise_profile: true, - }, - token: "altz_token", - fromAPIClient: null, - }); - - const res = await mockApi - .put("/api/v1/users/1/games/iidx/SP/showcase") - .set("Authorization", `Bearer altz_token`); - - t.equal(res.statusCode, 403); - - t.end(); - }); - - t.test("Requires the permission customise_profile", async (t) => { - await db["api-tokens"].insert({ - userID: 1, - identifier: "alt_token", - permissions: { - customise_profile: false, - }, - token: "unauth_token", - fromAPIClient: null, - }); - - const res = await mockApi - .put("/api/v1/users/1/games/iidx/SP/showcase") - .set("Authorization", `Bearer unauth_token`); - - t.equal(res.statusCode, 403); - - t.end(); - }); - - t.test("Should replace a user's preferences.stats with the contained stats.", async (t) => { - const res = await mockApi - .put("/api/v1/users/1/games/iidx/SP/showcase") - .set("Authorization", `Bearer alt_token`) - .send([ - { - mode: "chart", - chartID: Testing511SPA.chartID, - metric: "lamp", - }, - ]); - - t.equal(res.statusCode, 200); - - const data = await db["game-settings"].findOne({ userID: 1, game: "iidx", playtype: "SP" }); - - t.hasStrict( - data?.preferences.stats, - [ - { - mode: "chart", - chartID: Testing511SPA.chartID, - metric: "lamp", - }, - ], - "Should update preferences.stats in the database.", - ); - - t.hasStrict( - res.body.body.preferences.stats, - [ - { - mode: "chart", - chartID: Testing511SPA.chartID, - metric: "lamp", - }, - ], - "Should return the updated preferences", - ); - - t.end(); - }); - - t.test("Should reject for chartID that doesn't exist.", async (t) => { - const res = await mockApi - .put("/api/v1/users/1/games/iidx/SP/showcase") - .set("Authorization", `Bearer alt_token`) - .send([ - { - mode: "chart", - chartID: "chart_id_does_not_exist", - metric: "lamp", - }, - ]); - - t.equal(res.statusCode, 400); - - await db.charts.iidx.insert({ - chartID: "testing_dp_chart", - playtype: "DP", - } as ChartDocument<"iidx:DP">); - - const res2 = await mockApi - .put("/api/v1/users/1/games/iidx/SP/showcase") - .set("Authorization", `Bearer alt_token`) - .send([ - { - mode: "chart", - chartID: "testing_dp_chart", - metric: "lamp", - }, - ]); - - t.equal(res2.statusCode, 400); - - t.end(); - }); - - t.test("Should reject for folderID that doesn't exist.", async (t) => { - const res = await mockApi - .put("/api/v1/users/1/games/iidx/SP/showcase") - .set("Authorization", `Bearer alt_token`) - .send([ - { - mode: "folder", - metric: "grade", - gte: 4, - folderID: "folder_does_not_exist", - }, - ]); - - t.equal(res.statusCode, 400); - - const res2 = await mockApi - .put("/api/v1/users/1/games/iidx/SP/showcase") - .set("Authorization", `Bearer alt_token`) - .send([ - { - mode: "folder", - metric: "grade", - gte: 4, - folderID: [TestingIIDXFolderSP10.folderID, "folder_does_not_exist"], - }, - ]); - - t.equal(res2.statusCode, 400); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/showcase/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/showcase/router.ts deleted file mode 100644 index 4caee3adb..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/showcase/router.ts +++ /dev/null @@ -1,339 +0,0 @@ -import { EvaluateShowcaseStat } from "#lib/showcase/evaluator"; -import { GetRelatedStatDocuments } from "#lib/showcase/get-related"; -import { EvaluateUsersStatsShowcase } from "#lib/showcase/get-stats"; -import { RequirePermissions } from "#server/middleware/auth"; -import { RequireAuthedAsUser } from "#server/router/api/v1/users/_userID/middleware.js"; -import db from "#services/mongo/db"; -import { IsRecord } from "#utils/misc"; -import { FormatPrError } from "#utils/prudence"; -import { GetUGPT } from "#utils/req-tachi-data"; -import { ResolveUser } from "#utils/user"; -import { Router } from "express"; -import { p } from "prudence"; -import { - FormatGameGroup, - GetGamePTConfig, - GetGPTString, - GetScoreMetrics, - type ShowcaseStatDetails, -} from "tachi-common"; - -const router: Router = Router({ mergeParams: true }); - -/** - * Evaluate this users set stats. - * - * @param projectUser - Project another user's stats instead of their set stats. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/showcase - */ -router.get("/", async (req, res) => { - const { user, game, playtype } = GetUGPT(req); - - let projectUser; - - if (typeof req.query.projectUser === "string") { - const user = await ResolveUser(req.query.projectUser); - - if (!user) { - return res.status(404).json({ - success: false, - description: `The projected user ${req.query.projectUser} does not exist.`, - }); - } - - projectUser = user.id; - } - - const results = await EvaluateUsersStatsShowcase(user.id, game, playtype, projectUser); - - return res.status(200).json({ - success: true, - description: `Evaluated ${results.length} stats.`, - body: results, - }); -}); - -/** - * Evalulate a custom stat on this user. - * - * @param mode - "folder" or "chart" - * @param metric - "any score metric for this game (i.e. non-optional). - * Also, "playcount" if mode is chart. - * @param chartID - If mode is "chart" this must contain the chartID the stat is referencing. - * @param folderID - If mode is "folder" this must contain the folderID the stat is referencing. - * @param gte - If mode is "folder" this must contain the value the metric must be greater than. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/showcase/custom - */ -router.get("/custom", async (req, res) => { - const { user, game, playtype } = GetUGPT(req); - - const gptConfig = GetGamePTConfig(game, playtype); - - let stat: ShowcaseStatDetails; - - const availableMetrics = GetScoreMetrics(gptConfig, ["DECIMAL", "ENUM", "INTEGER"]); - - if (req.query.mode === "folder") { - const err = p( - req.query, - { - mode: p.is("folder"), - metric: p.isIn(availableMetrics), - folderID: "string", - - // lazy regex for matching strings that look like numbers - gte: p.regex(/^[0-9]*(.[0-9])?$/u), - }, - {}, - { allowExcessKeys: true }, - ); - - if (err) { - return res.status(400).json({ - success: false, - description: FormatPrError(err, "Invalid folder stat"), - }); - } - - const folderID = req.query.folderID as string; - - const folder = await db.folders.findOne({ folderID }); - - if (!folder || folder.game !== game || folder.playtype !== playtype) { - return res.status(400).json({ - success: false, - description: `Invalid folderID - all folders must be for ${FormatGameGroup( - game, - playtype, - )}, and exist.`, - }); - } - - stat = { - mode: "folder", - metric: req.query.metric as string, - folderID, - gte: Number(req.query.gte), - }; - } else if (req.query.mode === "chart") { - const err = p( - req.query, - { - mode: p.is("chart"), - metric: p.isIn(...availableMetrics, "playcount"), - chartID: "string", - }, - {}, - { allowExcessKeys: true }, - ); - - if (err) { - return res.status(400).json({ - success: false, - description: FormatPrError(err, "Invalid chart stat"), - }); - } - - const chart = await db.anyCharts[game].findOne({ chartID: req.query.chartID as string }); - - if (!chart || chart.playtype !== playtype) { - return res.status(400).json({ - success: false, - description: `Chart does not exist, or is not for this game and playtype.`, - }); - } - - stat = { - mode: "chart", - metric: req.query.metric as string, - chartID: req.query.chartID as string, - }; - } else { - return res.status(400).json({ - success: false, - description: `Invalid stat mode - expected either 'chart' or 'folder'.`, - }); - } - - const gpt = GetGPTString(game, playtype); - - const result = await EvaluateShowcaseStat(gpt, stat, user.id); - - const related = await GetRelatedStatDocuments(stat, game); - - return res.status(200).json({ - success: true, - description: `Evaluated Stat for ${user.username}`, - body: { stat, result, related }, - }); -}); - -/** - * Replaces a user's stat showcase. - * - * @name PUT /api/v1/users/:userID/games/:game/:playtype/showcase - */ -router.put("/", RequireAuthedAsUser, RequirePermissions("customise_profile"), async (req, res) => { - const { user, game, playtype } = GetUGPT(req); - - const gptConfig = GetGamePTConfig(game, playtype); - - if (!Array.isArray(req.safeBody)) { - return res.status(400).json({ - success: false, - description: `No stats provided, or was not an array.`, - }); - } - - if (req.safeBody.length > 6) { - return res.status(400).json({ - success: false, - description: `You are only allowed 6 stats at once.`, - }); - } - - const stats = req.safeBody as Array; - - const availableMetrics = GetScoreMetrics(gptConfig, ["DECIMAL", "ENUM", "INTEGER"]); - - for (const unvalidatedStat of stats) { - let err; - - if (!IsRecord(unvalidatedStat)) { - return res.status(400).json({ - success: false, - description: `Invalid stat -- got null or a non-object.`, - }); - } - - if (unvalidatedStat.mode === "chart") { - err = p(unvalidatedStat, { - chartID: "string", - mode: p.is("chart"), - metric: p.isIn(...availableMetrics, "playcount"), - }); - } else if (unvalidatedStat.mode === "folder") { - err = p(unvalidatedStat, { - folderID: (self) => { - if (typeof self === "string") { - return true; - } else if (Array.isArray(self)) { - return self.length <= 6 && self.every((r) => typeof r === "string"); - } - - return false; - }, - mode: p.is("folder"), - metric: p.isIn(availableMetrics), - - gte: (self, parent) => { - if (typeof self !== "number") { - return "Expected a number."; - } - - if (typeof parent.metric !== "string") { - return `Expected parent.metric to be a string.`; - } - - const conf = - gptConfig.providedMetrics[parent.metric] ?? - gptConfig.derivedMetrics[parent.metric]; - - if (!conf) { - return `Invalid metric ${ - parent.metric - }, Expected any of ${availableMetrics.join(", ")}.`; - } - - if (conf.type === "ENUM") { - return p.isBoundedInteger(0, conf.values.length - 1)(self); - } - - if (conf.type === "GRAPH" || conf.type === "NULLABLE_GRAPH") { - return "Cannot set a showcase stat for this metric."; - } - - if (conf.chartDependentMax) { - return `Cannot set a folder showcase goal for this metric as it is chart dependent.`; - } - - return conf.validate(self); - }, - }); - } else { - return res.status(400).json({ - success: false, - description: `Invalid stat - Expected mode to be 'chart' or 'folder').`, - }); - } - - if (err) { - return res.status(400).json({ - success: false, - description: FormatPrError(err, "Invalid stat."), - }); - } - - const stat = unvalidatedStat as unknown as ShowcaseStatDetails; - - if (stat.mode === "chart") { - // eslint-disable-next-line no-await-in-loop - const chart = await db.anyCharts[game].findOne({ chartID: stat.chartID }); - - if (!chart || chart.playtype !== playtype) { - return res.status(400).json({ - success: false, - description: `Invalid chartID - must be a chart for this game and playtype.`, - }); - } - } else if (unvalidatedStat.mode === "folder") { - const folderIDs = Array.isArray(unvalidatedStat.folderID) - ? unvalidatedStat.folderID - : [unvalidatedStat.folderID]; - - // eslint-disable-next-line no-await-in-loop - const folders = await db.folders.find({ folderID: { $in: folderIDs } }); - - if ( - folders.length !== folderIDs.length || - !folders.every((r) => r.game === game && r.playtype === playtype) - ) { - return res.status(400).json({ - success: false, - - // this error message is kinda lazy. - description: `Invalid folderID - must be a folder for this game and playtype.`, - }); - } - } - } - - await db["game-settings"].update( - { - userID: user.id, - game, - playtype, - }, - { - $set: { - "preferences.stats": req.safeBody, - }, - }, - ); - - const newSettings = await db["game-settings"].findOne({ - userID: user.id, - game, - playtype, - }); - - return res.status(200).json({ - success: true, - description: `Updated stat showcase.`, - body: newSettings, - }); -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/tables/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/tables/router.ts deleted file mode 100644 index c1b605c5d..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/tables/router.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { GetEnumDistForFolders, GetFoldersFromTable } from "#utils/folder"; -import { GetTachiData, GetUGPT } from "#utils/req-tachi-data"; -import { Router } from "express"; - -import { GetTableFromParam } from "../../../../../../games/_game/_playtype/tables/middleware"; - -const router: Router = Router({ mergeParams: true }); - -/** - * Retrieves a users statistics on this table. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/tables/:tableID - */ -router.get("/:tableID", GetTableFromParam, async (req, res) => { - const { user } = GetUGPT(req); - - const table = GetTachiData(req, "tableDoc"); - - const folders = await GetFoldersFromTable(table); - - // @optimisable - Requests a lot of charts we don't care about - // could be cached too, i guess. - const stats = await GetEnumDistForFolders(user.id, folders); - - return res.status(200).json({ - success: true, - description: `Returned stats for ${folders.length} folders.`, - body: { - folders, - stats, - table, - }, - }); -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/targets/goals/router.test.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/targets/goals/router.test.ts deleted file mode 100644 index aebf89ebb..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/targets/goals/router.test.ts +++ /dev/null @@ -1,708 +0,0 @@ -import db from "#services/mongo/db"; -import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import { TestSnapshot } from "#test-utils/single-process-snapshot"; -import { - HC511Goal, - HC511UserGoal, - IIDXSPQuestGoals, - IIDXSPQuestGoalSubs, - Testing511SPA, - TestingIIDXFolderSP10, - TestingIIDXSPQuest, - TestingIIDXSPQuestSub, - TestingIIDXSPScore, - TestingIIDXSPScorePB, -} from "#test-utils/test-data"; -import { CreateFolderChartLookup } from "#utils/folder"; -import { - type ChartDocument, - type GoalSubscriptionDocument, - IIDX_GRADES, - IIDX_LAMPS, - type PBScoreDocument, -} from "tachi-common"; -/* eslint-disable no-await-in-loop */ -import dm from "deepmerge"; -import t from "tap"; - -// @ts-expect-error Not sure why the types break here, but they do. -const dupedGoal = dm({}, HC511Goal); - -// @ts-expect-error Not sure why the types break here, but they do. -const dupedGoalSub = dm({}, HC511UserGoal); - -// having two charts available is useful here for testing multi-chart goals. -const anotherFakeChart = dm(Testing511SPA, { - chartID: "another_chart", - difficulty: "HYPER", - data: {}, -}) as ChartDocument<"iidx:SP">; - -t.test("GET /api/v1/users/:userID/games/:game/:playtype/targets/goals", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should retrieve this user's goal subscriptions.", async (t) => { - await db.goals.insert(dupedGoal); - await db["goal-subs"].insert(dupedGoalSub); - - const res = await mockApi.get("/api/v1/users/1/games/iidx/SP/targets/goals"); - - t.equal(res.statusCode, 200); - - t.strictSame(res.body.body, { - goals: [HC511Goal], - goalSubs: [HC511UserGoal], - quests: [], - questSubs: [], - }); - - t.end(); - }); - - t.end(); -}); - -t.test("POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal", async (t) => { - t.beforeEach(ResetDBState); - - const customScorePB = dm(TestingIIDXSPScorePB, { - scoreData: TestingIIDXSPScore.scoreData, - }) as PBScoreDocument; - - t.beforeEach(async () => { - await db["personal-bests"].remove({}); - await db["personal-bests"].insert(customScorePB); - }); - - const cookie = await CreateFakeAuthCookie(mockApi); - - const baseInput = { - criteria: { - key: "percent", - value: 80, - mode: "single", - }, - charts: { - type: "single", - data: Testing511SPA.chartID, - }, - }; - - function mkInput(caseName: string, merge: any) { - const input = { - caseName, - content: dm(baseInput, merge) as any, - }; - - // if we've set this prop to undefined, we want to delete it, actually. - if (input.content.charts.data === undefined) { - delete input.content.charts.data; - } - - return input; - } - - t.beforeEach(() => db.charts.iidx.insert(anotherFakeChart)); - - // @ts-expect-error weird type issues - t.beforeEach(() => db.folders.insert(dm(TestingIIDXFolderSP10, {}))); - - t.beforeEach(() => CreateFolderChartLookup(TestingIIDXFolderSP10, true)); - - const multiCharts = { - type: "multi", - data: [Testing511SPA.chartID, "another_chart"], - }; - - const folderCharts = { - type: "folder", - data: TestingIIDXFolderSP10.folderID, - }; - - const absModeCriteria = { - mode: "absolute", - countNum: 2, - }; - - const proportionModeCriteria = { - mode: "proportion", - countNum: 1, - }; - - t.test("Should set valid goals.", async (t) => { - const validInput = [ - mkInput("Percent:Single Case", {}), - mkInput("Score:Single Case", { - criteria: { - key: "score", - value: 1000, - }, - }), - mkInput("LampIndex:Single Case", { - criteria: { - key: "lamp", - value: IIDX_LAMPS.EX_HARD_CLEAR, - }, - }), - mkInput("GradeIndex:Single Case", { - criteria: { - key: "grade", - value: IIDX_GRADES.AAA, - }, - }), - mkInput("Percent:Multi Case", { - charts: multiCharts, - }), - mkInput("Percent:Multi:Abs Case", { - criteria: absModeCriteria, - charts: multiCharts, - }), - - // NOT LEGAL FOR IIDX, due to being nonsense! NEEDS SPECIFIC TESTING. - // mkInput("Score:Multi Case", { - // criteria: { - // key: "score", - // value: 1, - // }, - // charts: multiCharts, - // }), - mkInput("LampIndex:Multi Case", { - criteria: { - key: "lamp", - value: IIDX_LAMPS.HARD_CLEAR, - }, - charts: multiCharts, - }), - mkInput("LampIndex:Multi:Abs Case", { - criteria: { - key: "lamp", - value: IIDX_LAMPS.HARD_CLEAR, - ...absModeCriteria, - }, - charts: multiCharts, - }), - - mkInput("GradeIndex:Multi Case", { - criteria: { - key: "grade", - value: IIDX_GRADES.AAA, - }, - charts: multiCharts, - }), - mkInput("GradeIndex:Multi:Abs Case", { - criteria: { - key: "grade", - value: IIDX_GRADES.AAA, - ...absModeCriteria, - }, - charts: multiCharts, - }), - mkInput("Percent:Folder Case", { - charts: folderCharts, - }), - mkInput("Percent:Folder:Abs Case", { - criteria: absModeCriteria, - charts: folderCharts, - }), - mkInput("Percent:Folder:Proportion Case", { - criteria: proportionModeCriteria, - charts: folderCharts, - }), - - // also illegal in iidx, bms and pms. - // mkInput("Score:Folder Case", { - // criteria: { - // key: "score", - // value: 1000, - // }, - // charts: folderCharts, - // }), - mkInput("LampIndex:Folder Case", { - criteria: { - key: "lamp", - value: IIDX_LAMPS.HARD_CLEAR, - }, - charts: folderCharts, - }), - mkInput("LampIndex:Folder:Abs Case", { - criteria: { - key: "lamp", - value: IIDX_LAMPS.HARD_CLEAR, - ...absModeCriteria, - }, - charts: folderCharts, - }), - mkInput("LampIndex:Folder:Proportion Case", { - criteria: { - key: "lamp", - value: IIDX_LAMPS.HARD_CLEAR, - ...proportionModeCriteria, - }, - charts: folderCharts, - }), - mkInput("GradeIndex:Folder Case", { - criteria: { - key: "grade", - value: IIDX_GRADES.AAA, - }, - charts: folderCharts, - }), - mkInput("GradeIndex:Folder:Abs Case", { - criteria: { - key: "grade", - value: IIDX_GRADES.AAA, - ...absModeCriteria, - }, - charts: folderCharts, - }), - mkInput("GradeIndex:Folder:Proportion Case", { - criteria: { - key: "grade", - value: IIDX_GRADES.AAA, - ...proportionModeCriteria, - }, - charts: folderCharts, - }), - ]; - - for (const input of validInput) { - await t.test(`Valid Goal: ${input.caseName}`, async (t) => { - const res = await mockApi - .post("/api/v1/users/1/games/iidx/SP/targets/goals/add-goal") - .set("Cookie", cookie) - .send(input.content); - - t.equal( - // this looks stupid, but results in better errmsgs. - res.statusCode === 200 ? 200 : res.body.description, - 200, - "Should return 200", - ); - - // don't bother doing db checks if this is good. - if (res.statusCode !== 200) { - return; - } - - const existsInDb = await db.goals.findOne({ - charts: input.content.charts, - criteria: input.content.criteria, - }); - - t.not(existsInDb, null, "A new goal should be added to the database."); - - const subscribed = await db["goal-subs"].findOne({ - userID: 1, - goalID: existsInDb?.goalID ?? "edge_case", - }); - - t.not( - subscribed, - null, - `Requesting user 1 should be subscribed to their new goal.`, - ); - - t.end(); - }); - } - - t.end(); - }); - - t.test("Should reject invalid goals.", async (t) => { - const invalidInput = [ - mkInput("negative percent", { - criteria: { - value: -1, - }, - }), - // as stupid as it is, it technically "is" a goal, as it's a valid - // value for percent. - // mkInput("percent of 0 is a non-goal", { - // criteria: { - // value: 0, - // }, - // }), - mkInput("percent greater than 100", { - criteria: { - value: 100.1, - }, - }), - mkInput("too big grade", { - criteria: { - key: "grade", - value: IIDX_GRADES.MAX + 1, - }, - }), - mkInput("string enum", { - criteria: { - key: "grade", - value: IIDX_GRADES.MAX.toString(), - }, - }), - mkInput("invalid grade", { - criteria: { - key: "grade", - value: 0.5, - }, - }), - mkInput("too big lamp", { - criteria: { - key: "lamp", - value: IIDX_LAMPS.FULL_COMBO + 1, - }, - }), - mkInput("invalid lamp", { - criteria: { - key: "lamp", - value: 0.5, - }, - }), - mkInput("too big score", { - criteria: { - key: "score", - value: 9_000, - }, - }), - mkInput("abs without countNum", { - criteria: { - mode: "absolute", - }, - charts: multiCharts, - }), - mkInput("proportion without countNum", { - criteria: { - mode: "proportion", - }, - charts: multiCharts, - }), - mkInput("single with countNum", { - criteria: { - mode: "single", - countNum: 123, - }, - }), - mkInput("proportion with negative countNum", { - criteria: { - mode: "proportion", - countNum: -0.5, - }, - charts: multiCharts, - }), - mkInput("proportion with greater than 1 countNum", { - criteria: { - mode: "proportion", - countNum: 1.1, - }, - charts: multiCharts, - }), - mkInput("proportion which results in countNum of 0", { - criteria: { - mode: "proportion", - countNum: 0.05, - }, - charts: multiCharts, - }), - mkInput("nonsense mode", { - criteria: { - mode: "nonsense", - }, - }), - mkInput("abs with countNum but charts.type == single", { - criteria: { - mode: "absolute", - countNum: 1, - }, - charts: { - type: "single", - }, - }), - mkInput("nonsense charts.type", { - charts: { - type: "nonsense", - }, - }), - mkInput("charts.data array when type == single", { - charts: { - type: "single", - data: multiCharts.data, - }, - }), - mkInput("charts.data array of identical chartIDs", { - charts: { - type: "multi", - data: [Testing511SPA.chartID, Testing511SPA.chartID], - }, - }), - mkInput("charts.data array of single chartID", { - charts: { - type: "multi", - data: [Testing511SPA.chartID], - }, - }), - mkInput("charts.data array of chartIDs where some don't exist", { - charts: { - type: "multi", - data: [Testing511SPA.chartID, "not_exist"], - }, - }), - mkInput("charts.data folder refers to folder that doesn't exist", { - charts: { - type: "folder", - data: "fake-folder", - }, - }), - mkInput("nonsense charts.data", { - charts: { - data: "nonsense", - }, - }), - mkInput("multi-score for iidx is illegal", { - criteria: { - key: "score", - value: 1000, - }, - charts: multiCharts, - }), - mkInput("folder-score for iidx is illegal", { - criteria: { - key: "score", - value: 1000, - }, - charts: folderCharts, - }), - mkInput("charts single but criteria not.", { - criteria: { - mode: "absolute", - countNum: 2, - }, - }), - mkInput("LampIndex:Multi:Proportion Case", { - criteria: { - key: "lamp", - value: IIDX_LAMPS.HARD_CLEAR, - ...proportionModeCriteria, - }, - charts: multiCharts, - }), - mkInput("GradeIndex:Multi:Proportion Case", { - criteria: { - key: "grade", - value: IIDX_GRADES.AAA, - ...proportionModeCriteria, - }, - charts: multiCharts, - }), - mkInput("Percent:Multi:Proportion Case", { - criteria: proportionModeCriteria, - charts: multiCharts, - }), - ]; - - for (const input of invalidInput) { - await t.test(`Invalid Goal: ${input.caseName}`, async (t) => { - const res = await mockApi - .post("/api/v1/users/1/games/iidx/SP/targets/goals/add-goal") - .set("Cookie", cookie) - .send(input.content); - - t.equal(res.statusCode, 400); - - TestSnapshot(t, res.body.description, `Invalid Goal: ${input.caseName}`); - - t.end(); - }); - } - - t.end(); - }); - - t.test("Should reject if user tries to subscribe to already subscribed goal.", async (t) => { - const res = await mockApi - .post("/api/v1/users/1/games/iidx/SP/targets/goals/add-goal") - .set("Cookie", cookie) - .send(baseInput); - - t.equal(res.statusCode, 200, "Should allow a subscription the first time."); - - const res2 = await mockApi - .post("/api/v1/users/1/games/iidx/SP/targets/goals/add-goal") - .set("Cookie", cookie) - .send(baseInput); - - t.equal(res2.statusCode, 409, "Should fail the second time."); - - t.end(); - }); - - t.test("Should reject if user tries to subscribe to immediately achieved goal.", async (t) => { - const res = await mockApi - .post("/api/v1/users/1/games/iidx/SP/targets/goals/add-goal") - .set("Cookie", cookie) - .send( - dm(baseInput, { - criteria: { - key: "score", - value: 1, - }, - }), - ); - - t.equal( - res.statusCode, - 400, - "Should disallow goal subscriptions that would be instantly achieved.", - ); - - t.end(); - }); - - t.end(); -}); - -t.test("GET /api/v1/users/:userID/games/:game/:playtype/targets/goals/:goalID", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should retrieve this user's goal subscription.", async (t) => { - await db.goals.insert(dupedGoal); - await db["goal-subs"].insert(dupedGoalSub); - - const res = await mockApi.get( - `/api/v1/users/1/games/iidx/SP/targets/goals/${HC511Goal.goalID}`, - ); - - t.equal(res.statusCode, 200); - - t.hasStrict(res.body.body, { - goal: HC511Goal, - goalSub: HC511UserGoal, - quests: [], - user: { - id: 1, - }, - }); - - t.end(); - }); - - t.test("Should return parent quests if goal has any.", async (t) => { - await db.goals.insert(IIDXSPQuestGoals); - await db["goal-subs"].insert(IIDXSPQuestGoalSubs); - await db.quests.insert(TestingIIDXSPQuest); - - if (!IIDXSPQuestGoalSubs[0] || !IIDXSPQuestGoals[0]) { - throw new Error(`Expected atleast one quest goal or sub to work with?`); - } - - const res = await mockApi.get( - `/api/v1/users/1/games/iidx/SP/targets/goals/${IIDXSPQuestGoalSubs[0].goalID}`, - ); - - t.equal(res.statusCode, 200); - - t.hasStrict(res.body.body, { - goal: IIDXSPQuestGoals[0], - goalSub: IIDXSPQuestGoalSubs[0], - quests: [TestingIIDXSPQuest], - user: { - id: 1, - }, - }); - - t.end(); - }); - - t.test("Should return 404 if the user is not subscribed to this goal ID.", async (t) => { - const res = await mockApi.get(`/api/v1/users/1/games/iidx/SP/targets/goals/INVALID_GOAL`); - - t.equal(res.statusCode, 404); - - t.end(); - }); - - t.end(); -}); - -t.test("DELETE /api/v1/users/:userID/games/:game/:playtype/targets/goals/:goalID", async (t) => { - t.beforeEach(ResetDBState); - - const cookie = await CreateFakeAuthCookie(mockApi); - - t.test("Should delete a goal subscription if subscribed.", async (t) => { - await db.goals.insert(dupedGoal); - await db["goal-subs"].insert(dupedGoalSub); - - const res = await mockApi - .delete(`/api/v1/users/1/games/iidx/SP/targets/goals/${dupedGoalSub.goalID}`) - .set("Cookie", cookie); - - t.equal(res.statusCode, 200); - - const dbRes = await db["goal-subs"].findOne({ - userID: 1, - goalID: dupedGoal.goalID, - }); - - t.equal(dbRes, null, "Should delete the goal sub from the database."); - - t.end(); - }); - - t.test("Should reject a goal deletion if goal has parent quests.", async (t) => { - await db.quests.insert(TestingIIDXSPQuest); - await db["quest-subs"].insert(TestingIIDXSPQuestSub); - await db.goals.insert(IIDXSPQuestGoals); - await db["goal-subs"].insert( - dm(dupedGoalSub, { goalID: "eg_goal_1" }) as GoalSubscriptionDocument, - ); - - const res = await mockApi - .delete(`/api/v1/users/1/games/iidx/SP/targets/goals/eg_goal_1`) - .set("Cookie", cookie); - - t.equal(res.statusCode, 400); - t.equal( - res.body.description, - `This goal is part of a quest you are subscribed to. It can only be removed by unsubscribing from the relevant quests: '${TestingIIDXSPQuest.name}'.`, - ); - - const dbRes = await db["goal-subs"].findOne({ - userID: 1, - goalID: "eg_goal_1", - }); - - t.not(dbRes, null, "Should NOT delete the goal sub from the database."); - - t.end(); - }); - - t.test("Should reject a goal deletion if not subscribed.", async (t) => { - await db.goals.insert(dupedGoal); - await db["goal-subs"].insert(dupedGoalSub); - - const res = await mockApi - .delete(`/api/v1/users/1/games/iidx/SP/targets/goals/INVALID_GOAL`) - .set("Cookie", cookie); - - t.equal(res.statusCode, 404); - - t.end(); - }); - - t.test("Should reject a goal deletion if not authed.", async (t) => { - await db.goals.insert(dupedGoal); - await db["goal-subs"].insert(dupedGoalSub); - - const res = await mockApi.delete( - `/api/v1/users/1/games/iidx/SP/targets/goals/${dupedGoalSub.goalID}`, - ); - - t.equal(res.statusCode, 401); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/targets/goals/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/targets/goals/router.ts deleted file mode 100644 index 9ad6ff471..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/targets/goals/router.ts +++ /dev/null @@ -1,302 +0,0 @@ -import { SubscribeFailReasons } from "#lib/constants/err-codes"; -import { log } from "#lib/log/log.js"; -import { ServerConfig } from "#lib/setup/config"; -import { - ConstructGoal, - GetQuestsThatContainGoal, - SubscribeToGoal, - UnsubscribeFromGoal, -} from "#lib/targets/goals"; -import { GetParentQuests } from "#lib/targets/quests"; -import { RequirePermissions } from "#server/middleware/auth"; -import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; -import { GetGoalForIDGuaranteed } from "#utils/db"; -import { AssignToReqTachiData, GetTachiData, GetUGPT } from "#utils/req-tachi-data"; -import { type RequestHandler, Router } from "express"; -import { p } from "prudence"; -import { GetGamePTConfig, type GoalDocument, type QuestDocument } from "tachi-common"; - -import { RequireAuthedAsUser } from "../../../../../middleware"; - -const router: Router = Router({ mergeParams: true }); - -/** - * Retrieves this user's set goals for this GPT. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/targets/goals - */ -router.get("/", async (req, res) => { - const { user, game, playtype } = GetUGPT(req); - - const goalSubs = await db["goal-subs"].find({ - userID: user.id, - game, - playtype, - }); - - const goals = await db.goals.find({ - goalID: { $in: goalSubs.map((e) => e.goalID) }, - }); - - const allQuests = await GetParentQuests(user.id, game, playtype, goalSubs); - - const questSubs = await db["quest-subs"].find({ userID: user.id, game, playtype }); - - const questSubIDs = questSubs.map((e) => e.questID); - - // filter parent quests to only those that this user is subscribed to - const quests = allQuests.filter((quest) => questSubIDs.includes(quest.questID)); - - return res.status(200).json({ - success: true, - description: `Retrieved ${goalSubs.length} goal(s).`, - body: { - goals, - goalSubs, - quests, - questSubs, - }, - }); -}); - -type GoalCreationBody = Pick; - -/** - * Add a goal to your account. If the goal document already exists, it is subscribed to. - * Otherwise, that goal document is created, and then subscribed to. - * - * @param criteria.key - The key for the goal to be on. This is stuff like scoreData.percent. - * @param criteria.value - The value the key must be greater than for it to count as achieved. - * @param criteria.mode - "single", "absolute" or "proportion". If abs or proportion, countNum - * must be supplied. - * @param criteria.countNum - For abs/proportion mode. Atleast N scores must achieve the - * key:value condition. - * - * @param charts.type - "single", "multi" or "folder". - * @param charts.data - an identifier for the set of charts must be - * supplied here. For single, this is a chartID. For multi, this is an array of chartIDs. - * For folder, this is a folderID. - * - * @name POST /api/v1/users/:userID/games/:game/:playtype/targets/goals/add-goal - */ -router.post( - "/add-goal", - RequireAuthedAsUser, - RequirePermissions("manage_targets"), - prValidate({ - criteria: { - // we do proper validation on this later. - key: "string", - value: p.gte(0), - - mode: p.isIn("single", "absolute", "proportion"), - countNum: (self, parent) => { - if (parent.mode === "single") { - return ( - self === undefined || - "Invalid countNum for mode 'single'. Must not have one!" - ); - } - - // proper validation later. - return p.gte(0)(self); - }, - }, - charts: { - type: p.isIn("single", "multi", "folder"), - data: (self, parent) => { - if (parent.type === "single") { - return ( - typeof self === "string" || - "Expected a string in charts.data due to charts.type being 'single'." - ); - } else if (parent.type === "multi") { - return ( - (Array.isArray(self) && - self.every((k) => typeof k === "string") && - self.length <= 10 && - self.length > 1) || - "Expected an array of 2 to 10 strings in charts.data due to charts.type being 'multi'." - ); - /* istanbul ignore next */ - } else if (parent.type === "folder") { - return ( - typeof self === "string" || - "Expected a string in charts.data due to charts.type being 'folder'." - ); - } - - // impossible to reach, so doesn't count for coverage. - /* istanbul ignore next */ - return "Unknown charts.type."; - }, - }, - }), - async (req, res) => { - const { user, game, playtype } = GetUGPT(req); - - const existingGoalsCount = await db["goal-subs"].count({ - userID: user.id, - game, - playtype, - }); - - if (existingGoalsCount > ServerConfig.MAX_GOAL_SUBSCRIPTIONS) { - return res.status(400).json({ - success: false, - description: `You already have ${ServerConfig.MAX_GOAL_SUBSCRIPTIONS} goals. You cannot have anymore.`, - }); - } - - const gptConfig = GetGamePTConfig(game, playtype); - - const validCriteria = [ - ...Object.keys(gptConfig.providedMetrics), - ...Object.keys(gptConfig.derivedMetrics), - ]; - - if (!validCriteria.includes(req.body.criteria.key)) { - return res.status(400).json({ - success: false, - description: `Invalid criteria '${ - req.body.criteria.key - }', expected any of ${validCriteria.join(", ")}.`, - }); - } - - const data = req.safeBody as GoalCreationBody; - - let goal; - - try { - goal = await ConstructGoal(data.charts, data.criteria, game, playtype); - } catch (e) { - const err = e as Error; - - log.info({ err }, err.message); - - return res.status(400).json({ - success: false, - description: err.message, - }); - } - - const goalSub = await SubscribeToGoal(user.id, goal, true); - - if (goalSub === SubscribeFailReasons.ALREADY_SUBSCRIBED) { - return res.status(409).json({ - success: false, - description: `You are already subscribed to this goal.`, - }); - } - - if (goalSub === SubscribeFailReasons.ALREADY_ACHIEVED) { - return res.status(400).json({ - success: false, - description: `You can't directly assign goals that you would immediately achieve.`, - }); - } - - return res.status(200).json({ - success: true, - description: `Subscribed to ${goal.name}.`, - body: { - goal, - goalSub, - }, - }); - }, -); - -const GetGoalSubscription: RequestHandler = async (req, res, next) => { - const { user, game, playtype } = GetUGPT(req); - - const goalSub = await db["goal-subs"].findOne({ - userID: user.id, - game, - playtype, - goalID: req.params.goalID, - }); - - if (!goalSub) { - return res.status(404).json({ - success: false, - description: `${user.username} is not subscribed to this goal.`, - }); - } - - AssignToReqTachiData(req, { goalSubDoc: goalSub }); - - next(); -}; - -/** - * Reads information about the users subscription to this goal ID. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/targets/goals/:goalID - */ -router.get("/:goalID", GetGoalSubscription, async (req, res) => { - const { user } = GetUGPT(req); - - const goalSub = GetTachiData(req, "goalSubDoc"); - - const quests: Array = await GetQuestsThatContainGoal(goalSub.goalID); - - const goal = await GetGoalForIDGuaranteed(goalSub.goalID); - - return res.status(200).json({ - success: true, - description: `Returned information about goal '${goal.name}'.`, - body: { - goal, - goalSub, - quests, - user, - }, - }); -}); - -/** - * Unsubscribe from a goal. - * - * @name DELETE /api/v1/users/:userID/games/:game/:playtype/targets/goals/:goalID - */ -router.delete( - "/:goalID", - RequireAuthedAsUser, - GetGoalSubscription, - RequirePermissions("manage_targets"), - async (req, res) => { - const goalSub = GetTachiData(req, "goalSubDoc"); - - const fail = await UnsubscribeFromGoal(goalSub, false); - - if (fail) { - switch (fail.reason) { - case "WAS_STANDALONE": - // can't happen. mightaswell handle it though. - return res.status(400).json({ - success: false, - description: `This goal was assigned by you and can't be removed as a consequence of another action.`, - }); - - case "HAS_QUEST_DEPENDENCIES": - return res.status(400).json({ - success: false, - description: `This goal is part of a quest you are subscribed to. It can only be removed by unsubscribing from the relevant quests: ${fail.parentQuests - .map((e) => `'${e.quest.name}'`) - .join(", ")}.`, - }); - } - } - - return res.status(200).json({ - success: true, - description: `Removed this goal from your subscriptions.`, - body: {}, - }); - }, -); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/targets/quests/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/targets/quests/router.ts deleted file mode 100644 index d6c3d5a73..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/targets/quests/router.ts +++ /dev/null @@ -1,245 +0,0 @@ -import { SubscribeFailReasons } from "#lib/constants/err-codes"; -import { log } from "#lib/log/log.js"; -import { ServerConfig } from "#lib/setup/config"; -import { - EvaluateQuestProgress, - GetGoalsInQuests, - SubscribeToQuest, - UnsubscribeFromQuest, -} from "#lib/targets/quests"; -import { RequirePermissions } from "#server/middleware/auth"; -import db from "#services/mongo/db"; -import { AssignToReqTachiData, GetGPT, GetTachiData, GetUGPT } from "#utils/req-tachi-data"; -import { FormatUserDoc } from "#utils/user"; -import { type RequestHandler, Router } from "express"; - -import { RequireAuthedAsUser } from "../../../../../middleware"; - -const router: Router = Router({ mergeParams: true }); - -/** - * Retrieves this user's subscribed quests. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/targets/quests - */ -router.get("/", async (req, res) => { - const { user, game, playtype } = GetUGPT(req); - - const questSubs = await db["quest-subs"].find({ - userID: user.id, - game, - playtype, - }); - - const quests = await db.quests.find({ - questID: { $in: questSubs.map((e) => e.questID) }, - }); - - if (quests.length !== questSubs.length) { - log.error( - `Found ${questSubs.length} subscriptions, but got ${quests.length} parents. This is a state desync.`, - ); - throw new Error("Failed to fetch quests"); - } - - const goals = await GetGoalsInQuests(quests); - - return res.status(200).json({ - success: true, - description: `Retrieved ${questSubs.length} quest(s).`, - body: { - quests, - questSubs, - goals, - }, - }); -}); - -const GetQuestSubscription: RequestHandler = async (req, res, next) => { - const { user, game, playtype } = GetUGPT(req); - - const questSub = await db["quest-subs"].findOne({ - userID: user.id, - game, - playtype, - questID: req.params.questID, - }); - - if (!questSub) { - return res.status(404).json({ - success: false, - description: `${user.username} is not subscribed to this quest.`, - }); - } - - AssignToReqTachiData(req, { questSubDoc: questSub }); - - next(); -}; - -const GetQuest: RequestHandler = async (req, res, next) => { - const { game, playtype } = GetGPT(req); - - const quest = await db.quests.findOne({ - game, - playtype, - questID: req.params.questID, - }); - - if (!quest) { - return res.status(404).json({ - success: false, - description: `Can't find a quest with id '${req.params.questID}'.`, - }); - } - - AssignToReqTachiData(req, { questDoc: quest }); - - next(); -}; - -/** - * Returns this user's progress on this quest. - * This also evaluates individual progress on all of the quests goals. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/targets/quests/:questID - */ -router.get("/:questID", GetQuest, GetQuestSubscription, async (req, res) => { - const { user } = GetUGPT(req); - - const questSub = GetTachiData(req, "questSubDoc"); - const quest = GetTachiData(req, "questDoc"); - - // Evaluate each goal for the user. This operation is much faster if the user is - // subscribed to the quest (they are), as we can just read their goalSub - // for each goal. - const { goalResults: results, goals } = await EvaluateQuestProgress(user.id, quest); - - return res.status(200).json({ - success: true, - description: `Returned information about ${FormatUserDoc(user)}'s progress on ${ - quest.name - }.`, - body: { - questSub, - quest, - results, - goals, - }, - }); -}); - -/** - * Subscribe to a quest. - * - * @name PUT /api/v1/users/:userID/games/:game/:playtype/targets/quests/:questID - */ -router.put( - "/:questID", - RequireAuthedAsUser, - GetQuest, - RequirePermissions("manage_targets"), - async (req, res) => { - const { user, game, playtype } = GetUGPT(req); - - const existingQuestsCount = await db["quest-subs"].count({ - userID: user.id, - game, - playtype, - }); - - if (existingQuestsCount > ServerConfig.MAX_QUEST_SUBSCRIPTIONS) { - return res.status(400).json({ - success: false, - description: `You already have ${ServerConfig.MAX_QUEST_SUBSCRIPTIONS} quests. You cannot have anymore for this game.`, - }); - } - - const quest = GetTachiData(req, "questDoc"); - - const alreadySubscibed = await db["quest-subs"].findOne({ - userID: user.id, - questID: quest.questID, - }); - - if (alreadySubscibed) { - return res.status(409).json({ - success: false, - description: `You are already subscribed to this goal.`, - }); - } - - const subResult = await SubscribeToQuest(user.id, quest, false); - - // Users should be able to subscribe to quests EVEN IF they would instantly - // achieve them. - - // if (subResult === SubscribeFailReasons.ALREADY_ACHIEVED) { - // return res.status(400).json({ - // success: false, - // description: `You cannot assign a quest that would be immediately achieved.`, - // }); - // } - - if (subResult === SubscribeFailReasons.ALREADY_SUBSCRIBED) { - return res.status(409).json({ - success: false, - description: `You're already subscribed to this quest.`, - }); - } - - return res.status(200).json({ - success: true, - description: `Subscribed to quest '${quest.name}'.`, - body: { ...subResult, quest }, - }); - }, -); - -/** - * Unsubscribe from a quest. - * - * @name DELETE /api/v1/users/:userID/games/:game/:playtype/targets/quests/:questID - */ -router.delete( - "/:questID", - RequireAuthedAsUser, - GetQuest, - RequirePermissions("manage_targets"), - async (req, res) => { - const { user } = GetUGPT(req); - const quest = GetTachiData(req, "questDoc"); - - log.info( - { - quest, - user, - }, - `User ${FormatUserDoc(user)} is unsubscribing from quest '${quest.name}'.`, - ); - - const questSub = await db["quest-subs"].findOne({ - userID: user.id, - questID: quest.questID, - }); - - if (!questSub) { - return res.status(409).json({ - success: false, - description: `Can't unsubscribe from a quest you were never subscribed to.`, - }); - } - - await UnsubscribeFromQuest(questSub, quest); - - return res.status(200).json({ - success: true, - description: `Unsubscribed from quest.`, - body: { - quest, - }, - }); - }, -); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/targets/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/targets/router.ts deleted file mode 100644 index 29de5ecbf..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/games/_game/_playtype/targets/router.ts +++ /dev/null @@ -1,217 +0,0 @@ -import { log } from "#lib/log/log.js"; -import { GetRelevantGoals } from "#lib/targets/goals"; -import { GetParentQuests } from "#lib/targets/quests"; -import db from "#services/mongo/db"; -import { - GetRecentlyAchievedGoals, - GetRecentlyAchievedQuests, - GetRecentlyInteractedGoals, - GetRecentlyInteractedQuests, -} from "#utils/db"; -import { GetFolderChartIDs } from "#utils/folder"; -import { GetUGPT } from "#utils/req-tachi-data"; -import { Router } from "express"; - -import goalsRouter from "./goals/router"; -import questsRouter from "./quests/router"; - -const router: Router = Router({ mergeParams: true }); - -/** - * Return a user's recently achieved goals and quests. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/targets/recently-achieved - */ -router.get("/recently-achieved", async (req, res) => { - const { game, playtype, user } = GetUGPT(req); - - const userID = user.id; - - const [{ goals, goalSubs }, { quests, questSubs }] = await Promise.all([ - GetRecentlyAchievedGoals({ userID, game, playtype }), - GetRecentlyAchievedQuests({ userID, game, playtype }), - ]); - - return res.status(200).json({ - success: true, - description: `Returned ${user.username}'s recently achieved targets.`, - body: { - goals, - quests, - goalSubs, - questSubs, - user, - }, - }); -}); - -/** - * Returns a user's recently interacted with (raised, etc.) goals and quests. - * Note that this does not include recently achieved. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/targets/recently-raised - */ -router.get("/recently-raised", async (req, res) => { - const { game, playtype, user } = GetUGPT(req); - - const userID = user.id; - - const [{ goals, goalSubs }, { quests, questSubs }] = await Promise.all([ - GetRecentlyInteractedGoals({ userID, game, playtype }), - GetRecentlyInteractedQuests({ userID, game, playtype }), - ]); - - return res.status(200).json({ - success: true, - description: `Returned ${user.username}'s recently achieved targets.`, - body: { - goals, - quests, - goalSubs, - questSubs, - user, - }, - }); -}); - -/** - * Find what unachieved targets this user has set that consider this chart. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/targets/on-chart/:chartID - */ -router.get("/on-chart/:chartID", async (req, res) => { - const { game, playtype, user } = GetUGPT(req); - - const chartID = req.params.chartID; - - const chart = await db.anyCharts[game].findOne({ chartID, playtype }); - - if (!chart) { - return res.status(404).json({ - success: false, - description: `Failed to find a chart with chartID '${chartID}'.`, - }); - } - - const { goals, goalSubsMap } = await GetRelevantGoals( - game, - user.id, - new Set([chartID]), - log, - false, - ); - - const goalSubs = [...goalSubsMap.values()]; - - const quests = await GetParentQuests(user.id, game, playtype, goalSubs); - - const questSubs = await db["quest-subs"].find({ - questID: { $in: quests.map((e) => e.questID) }, - }); - - return res.status(200).json({ - success: true, - description: `Found pertinent goals`, - body: { - goals, - goalSubs, - quests, - questSubs, - }, - }); -}); - -/** - * Find what unachieved targets this user has set that involve this folder. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/targets/on-folder/:folderID - */ -router.get("/on-folder/:folderID", async (req, res) => { - const { game, playtype, user } = GetUGPT(req); - - const folderID = req.params.folderID; - - const folder = await db.folders.findOne({ folderID, playtype }); - - if (!folder) { - return res.status(404).json({ - success: false, - description: `Failed to find a folder with folderID '${folderID}'.`, - }); - } - - const folderChartIDs = await GetFolderChartIDs(folderID); - - const allGoalSubs = await db["goal-subs"].find({ - userID: user.id, - game, - playtype, - }); - - const goalIDs = allGoalSubs.map((e) => e.goalID); - - // goals are relevant to a folder if it's part of a folder type goal - // or if it's on any of the charts in the folder. - // this is convenient for the UI, atleast. - const goals = await Promise.all([ - db.goals.find({ - "charts.type": { $in: ["single", "multi"] }, - "charts.data": { $in: folderChartIDs }, - goalID: { $in: goalIDs }, - }), - db.goals.find({ - "charts.type": "folder", - "charts.data": folderID, - goalID: { $in: goalIDs }, - }), - ]).then((r) => r.flat()); - - const goalSubs = await db["goal-subs"].find({ - goalID: { $in: goals.map((e) => e.goalID) }, - userID: user.id, - game, - playtype, - }); - - const quests = await GetParentQuests(user.id, game, playtype, goalSubs); - - const questSubs = await db["quest-subs"].find({ - questID: { $in: quests.map((e) => e.questID) }, - }); - - return res.status(200).json({ - success: true, - description: `Found pertinent goals`, - body: { - goals, - goalSubs, - quests, - questSubs, - }, - }); -}); - -/** - * Retrieve all of this user's target subscriptions. - * - * @name GET /api/v1/users/:userID/games/:game/:playtype/targets/all-subs - */ -router.get("/all-subs", async (req, res) => { - const { user, game, playtype } = GetUGPT(req); - - const [goalSubs, questSubs] = await Promise.all([ - db["goal-subs"].find({ userID: user.id, game, playtype }), - db["quest-subs"].find({ userID: user.id, game, playtype }), - ]); - - return res.status(200).json({ - success: true, - description: `Returned all target subscriptions.`, - body: { goalSubs, questSubs }, - }); -}); - -router.use("/goals", goalsRouter); -router.use("/quests", questsRouter); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/imports/router.test.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/imports/router.test.ts deleted file mode 100644 index 6e17f8b43..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/imports/router.test.ts +++ /dev/null @@ -1,4 +0,0 @@ -import t from "tap"; - -t.todo("GET /api/v1/users/:userID/imports"); -t.todo("GET /api/v1/users/:userID/imports/failed"); diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/imports/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/imports/router.ts deleted file mode 100644 index 93ab5b681..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/imports/router.ts +++ /dev/null @@ -1,104 +0,0 @@ -import type { FilterQuery } from "mongodb"; -import type { ImportTrackerDocument, ImportTypes } from "tachi-common"; - -import { TachiConfig } from "#lib/setup/config"; -import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; -import { DeleteUndefinedProps } from "#utils/misc"; -import { GetTachiData } from "#utils/req-tachi-data"; -import { Router } from "express"; -import { p } from "prudence"; - -const router: Router = Router({ mergeParams: true }); - -/** - * Query this user's imports. Returns the 500 most recently-finished imports. - * - * @param importType - Optionally, limit the returns to only this import type. - * @param userIntent - Optionally, limit returns to only those with or without userIntent. - * - * @name GET /api/v1/users/:userID/imports - */ -router.get( - "/", - prValidate({ - importType: p.optional(p.isIn(TachiConfig.IMPORT_TYPES)), - userIntent: p.optional(p.isIn("true", "false")), - }), - async (req, res) => { - const userID = GetTachiData(req, "requestedUser").id; - const importType = req.query.importType as ImportTypes | undefined; - - // all query input ends up as strings, so we need convert it into an optional - // boolean - const userIntent = - req.query.userIntent === undefined ? undefined : req.query.userIntent === "true"; - - const query = { - userIntent, - userID, - importType, - }; - - DeleteUndefinedProps(query); - - const imports = await db.imports.find(query, { - sort: { timeFinished: -1 }, - limit: 500, - }); - - return res.status(200).json({ - success: true, - description: `Found ${imports.length} imports.`, - body: imports, - }); - }, -); - -/** - * Return this users 500 most recent failed imports. - * - * @param userIntent - Optionally, Whether to limit returns to only those with userIntent or without. - * @param importType - Optionally, Whether to limit returns to only a specific importType. - * - * @name GET /api/v1/users/:userID/imports/failed - */ -router.get( - "/failed", - prValidate({ - importType: p.optional(p.isIn(TachiConfig.IMPORT_TYPES)), - userIntent: p.optional(p.isIn("true", "false")), - }), - async (req, res) => { - const userID = GetTachiData(req, "requestedUser").id; - - const importType = req.query.importType as ImportTypes | undefined; - - // all query input ends up as strings, so we need convert it into an optional - // boolean - const userIntent = - req.query.userIntent === undefined ? undefined : req.query.userIntent === "true"; - - const query: FilterQuery = { - type: "FAILED", - userIntent, - userID, - importType, - }; - - DeleteUndefinedProps(query); - - const trackers = await db["import-trackers"].find(query, { - sort: { timeStarted: -1 }, - limit: 500, - }); - - return res.status(200).json({ - success: true, - description: `Found ${trackers.length} failed imports.`, - body: trackers, - }); - }, -); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/cg/_cgType/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/cg/_cgType/router.ts deleted file mode 100644 index 432a3741c..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/cg/_cgType/router.ts +++ /dev/null @@ -1,132 +0,0 @@ -import type { CGServices } from "#lib/score-import/import-types/common/api-cg/types"; -import type { RequestHandler } from "express-serve-static-core"; -import type { CGCardInfo } from "tachi-common"; - -import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; -import { GetTachiData } from "#utils/req-tachi-data"; -import { Router } from "express"; -import { p } from "prudence"; - -import { RequireSelfRequestFromUser } from "../../../middleware"; - -const router: Router = Router({ mergeParams: true }); - -const ValidateCGType: RequestHandler = (req, res, next) => { - if (req.params.cgType === "dev" || req.params.cgType === "nag" || req.params.cgType === "gan") { - next(); - return; - } - - return res.status(404).json({ - success: false, - description: `No such service 'cg/${req.params.cgType}' is supported.`, - }); -}; - -/** - * Retrieve this user's card info (cardID). - * - * @name GET /api/v1/users/:userID/integrations/cg/:cgType - */ -router.get("/", ValidateCGType, RequireSelfRequestFromUser, async (req, res) => { - const user = GetTachiData(req, "requestedUser"); - const cgType = req.params.cgType as CGServices; - - const cardInfo = await db["cg-card-info"].findOne({ - userID: user.id, - service: cgType, - }); - - if (!cardInfo) { - return res.status(200).json({ - success: true, - description: `User has no card info set.`, - body: null, - }); - } - - return res.status(200).json({ - success: true, - description: `Found card info.`, - body: cardInfo, - }); -}); - -/** - * Write new card details for this CG integration. - * - * @name PUT /api/v1/users/:userID/integrations/cg/:cgType - */ -router.put( - "/", - ValidateCGType, - RequireSelfRequestFromUser, - prValidate( - { - cardID: p.regex(/^[a-zA-Z0-9]{16}$/u), - pin: p.regex(/^[0-9]{4}$/u), - }, - { - cardID: "Expected 16 characters.", - pin: "Expected 4 digits.", - }, - ), - async (req, res) => { - const user = GetTachiData(req, "requestedUser"); - const cgType = req.params.cgType as CGServices; - - const { cardID, pin } = req.safeBody as { - cardID: string; - pin: string; - }; - - const newCardInfo: CGCardInfo = { - service: cgType, - cardID, - pin, - userID: user.id, - }; - - await db["cg-card-info"].update( - { - userID: user.id, - service: cgType, - }, - { $set: newCardInfo }, - { - // insert new card info if the user doesn't have it yet. - upsert: true, - }, - ); - - return res.status(200).json({ - success: true, - description: `Updated cardID and pin.`, - body: {}, - }); - }, -); - -/** - * Unset this user's card details for this CG integration. - * - * @name DELETE /api/v1/users/:userID/integrations/cg/:cgType - */ -router.delete("/", ValidateCGType, RequireSelfRequestFromUser, async (req, res) => { - const user = GetTachiData(req, "requestedUser"); - const cgType = req.params.cgType as CGServices; - - await db["cg-card-info"].remove({ - userID: user.id, - service: cgType, - }); - - return res.status(200).json({ - success: true, - description: `Deleted stored card info.`, - body: {}, - }); -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/fervidex/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/fervidex/router.ts deleted file mode 100644 index fa1f54813..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/fervidex/router.ts +++ /dev/null @@ -1,92 +0,0 @@ -import prValidate from "#server/middleware/prudence-validate"; -import { RequireKamaitachi } from "#server/middleware/type-require"; -import db from "#services/mongo/db"; -import { DeleteUndefinedProps } from "#utils/misc"; -import { optNull } from "#utils/prudence"; -import { GetTachiData } from "#utils/req-tachi-data"; -import { Router } from "express"; - -import { RequireSelfRequestFromUser } from "../../middleware"; - -const router: Router = Router({ mergeParams: true }); - -router.use(RequireKamaitachi); -router.use(RequireSelfRequestFromUser); - -/** - * Retrieve your fervidex settings. - * - * @name GET /api/v1/users/:userID/integrations/fervidex/settings - */ -router.get("/settings", async (req, res) => { - const user = GetTachiData(req, "requestedUser"); - - const settingsDoc = await db["fer-settings"].findOne({ - userID: user.id, - }); - - return res.status(200).json({ - success: true, - description: `Retrieved Fervidex settings.`, - body: settingsDoc ?? null, - }); -}); - -/** - * Update your fervidex configuration. - * - * @param cards - An array of strings to be used as a cards whitelist. - * @param forceStaticImport - Whether or whether not to force a static import on non-INF2 clients. - * - * @name PUT /api/v1/users/:userID/integrations/fervidex/settings - */ -router.patch( - "/settings", - prValidate({ cards: optNull(["string"]), forceStaticImport: "*?boolean" }), - async (req, res) => { - const body = req.safeBody as { - cards?: Array | null; - forceStaticImport?: boolean | null; - }; - - if (body.cards && body.cards.length > 6) { - return res.status(400).json({ - success: false, - description: `You cannot have more than 6 card filters at once.`, - }); - } - - const user = GetTachiData(req, "requestedUser"); - - const modifyDocument = req.safeBody; - - DeleteUndefinedProps(modifyDocument); - - if (Object.keys(modifyDocument).length === 0) { - return res.status(400).json({ - success: false, - description: `No modifications sent.`, - }); - } - - await db["fer-settings"].update( - { userID: user.id }, - { - $set: modifyDocument, - }, - { - upsert: true, - }, - ); - - const settings = await db["fer-settings"].findOne({ userID: user.id }); - - return res.status(200).json({ - success: true, - description: `Successfully updated settings.`, - body: settings, - }); - }, -); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/kai/_kaiType/middleware.test.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/kai/_kaiType/middleware.test.ts deleted file mode 100644 index ce2c4a427..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/kai/_kaiType/middleware.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { expressRequestMock } from "#test-utils/mock-request"; -import t from "tap"; - -import { ValidateKaiType } from "./middleware"; - -t.test("#ValidateKaiType", (t) => { - const k = async (k: string) => { - const { res } = await expressRequestMock(ValidateKaiType, { params: { kaiType: k } }); - - return res.statusCode; - }; - - t.test("Should allow flo, eag or min, case insensitively.", async (t) => { - t.equal(await k("flo"), 200); - t.equal(await k("eag"), 200); - t.equal(await k("min"), 200); - t.equal(await k("FLO"), 200); - t.equal(await k("EAG"), 200); - t.equal(await k("MIN"), 200); - t.equal(await k("FlO"), 200); - t.equal(await k("EaG"), 200); - t.equal(await k("MiN"), 200); - t.equal(await k("nonsense"), 400); - t.equal(await k("bad"), 400); - t.equal(await k(""), 400); - t.equal(await k("FLO2"), 400); - t.equal(await k("2FLO"), 400); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/kai/_kaiType/middleware.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/kai/_kaiType/middleware.ts deleted file mode 100644 index d3fa54abb..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/kai/_kaiType/middleware.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { RequestHandler } from "express"; - -export const ValidateKaiType: RequestHandler = (req, res, next) => { - const kaiType = req.params.kaiType; - - if (kaiType === undefined) { - throw new Error( - `Expected to find kaiType parameter inside ValidateKaiType on route ${req.originalUrl}.`, - ); - } - - if (!["eag", "flo", "min"].includes(kaiType.toLowerCase())) { - return res.status(400).json({ - success: false, - description: `Invalid kaiType - Expected min, flo or eag.`, - }); - } - - next(); -}; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/kai/_kaiType/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/kai/_kaiType/router.ts deleted file mode 100644 index 310e7ca9e..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/kai/_kaiType/router.ts +++ /dev/null @@ -1,220 +0,0 @@ -import { log } from "#lib/log/log.js"; -import { - GetKaiTypeClientCredentials, - KaiTypeToBaseURL, -} from "#lib/score-import/import-types/common/api-kai/utils"; -import prValidate from "#server/middleware/prudence-validate"; -import { RequireKamaitachi } from "#server/middleware/type-require"; -import db from "#services/mongo/db"; -import fetch from "#utils/fetch"; -import { NotNullish } from "#utils/misc"; -import { GetKaiAuth, RevokeKaiAuth } from "#utils/queries/auth"; -import { GetTachiData } from "#utils/req-tachi-data"; -import { FormatUserDoc } from "#utils/user"; -import { Router } from "express"; -import { p } from "prudence"; - -import { RequireSelfRequestFromUser } from "../../../middleware"; -import { ValidateKaiType } from "./middleware"; - -const router: Router = Router({ mergeParams: true }); - -router.use(RequireKamaitachi, RequireSelfRequestFromUser, ValidateKaiType); - -/** - * Return the authentication status for this kaiType. - * @note - Express's types infer arg0 of "/" to mean no params, for some reason. - * the generic overrides this behaviour. - * - * @name GET /api/v1/users/:userID/integrations/kai/:kaiType - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -router.get("/", async (req, res) => { - const user = GetTachiData(req, "requestedUser"); - const kaiType = NotNullish(req.params.kaiType).toUpperCase() as "EAG" | "FLO" | "MIN"; - - const authDoc = await GetKaiAuth(user.id, kaiType); - - return res.status(200).json({ - success: true, - description: authDoc ? `User is authenticated.` : `User is unauthenticated.`, - body: { - authStatus: !!authDoc, - }, - }); -}); - -/** - * Revoke your authentication for this kaiType. - * @note - Express's types infer arg0 of "/" to mean no params, for some reason. - * the generic overrides this behaviour. - * - * @name DELETE /api/v1/users/:userID/integrations/kai/:kaiType - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -router.delete("/", async (req, res) => { - const user = GetTachiData(req, "requestedUser"); - const kaiType = NotNullish(req.params.kaiType).toUpperCase() as "EAG" | "FLO" | "MIN"; - - const authDoc = await GetKaiAuth(user.id, kaiType); - - if (!authDoc) { - return res.status(409).json({ - success: false, - description: `You are not authorised with this service.`, - }); - } - - await RevokeKaiAuth(user.id, kaiType); - - return res.status(200).json({ - success: true, - description: `Revoked authentication for ${kaiType}.`, - body: {}, - }); -}); - -const KAI_OAUTH2_RETURN_SCHEMA = { - access_token: "string", - refresh_token: "string", -}; - -/** - * The OAuth2 callback used by Kai to send an intermediate token to. - * @note The way this is implemented is *really* weird due to the fact that - * the tachi-server code cannot have any knowledge of the tachi-client code, - * and the two must be agnostic. - * - * This means the tachi-client will handle the redirecting, and will check - * query params for ?code=12345 to know when to POST us with the code - * to perform an update. - * - * @param code - An intermediate code to use to get the real auth token. - * - * @name POST /api/v1/users/:userID/integrations/kai/:kaiType/oauth2callback - */ -router.post( - "/oauth2callback", - prValidate({ code: "string" }, {}, { allowExcessKeys: true }), - async (req, res) => { - const user = GetTachiData(req, "requestedUser"); - const kaiType = NotNullish(req.params.kaiType).toUpperCase() as "EAG" | "FLO" | "MIN"; - - const body = req.safeBody as { - code: string; - }; - - const baseUrl = KaiTypeToBaseURL(kaiType); - - const maybeCredentials = GetKaiTypeClientCredentials(kaiType); - - if (!maybeCredentials) { - log.error( - `Attempted to /callback ${kaiType}, but this server has no oauth2 credentials configured for that type.`, - ); - return res.status(500).json({ - success: false, - description: `A fatal error has occured, This has been reported.`, - }); - } - - const { CLIENT_SECRET, CLIENT_ID, REDIRECT_URI } = maybeCredentials; - - const url = `${baseUrl}/oauth/token`; - - const params = new URLSearchParams(); - - params.append("code", body.code); - params.append("grant_type", "authorization_code"); - params.append("client_secret", CLIENT_SECRET); - params.append("client_id", CLIENT_ID); - params.append("redirect_uri", REDIRECT_URI); - - log.info(`Making token reify request from ${baseUrl}/oauth/token`); - let getTokenRes; - - try { - getTokenRes = await fetch(url, { - body: params.toString(), - headers: { - "Content-Type": "application/x-www-form-urlencoded", - }, - method: "POST", - }); - } catch (err) { - log.error(err, `Completely failed to getTokenRes from ${url}.`); - return res.status(500).json({ - success: false, - description: `We failed to reach this site. Are they down?`, - }); - } - - if (getTokenRes.status !== 200) { - log.error(`Unexpected status of ${getTokenRes.status} from ${url} oauth2 flow.`); - - return res.status(getTokenRes.status < 500 ? 400 : 500).json({ - success: false, - description: `The server you requested returned a status of ${getTokenRes.status}. Either your request was malformed, or the server is malfunctioning.`, - }); - } - - let json: unknown; - - try { - json = await getTokenRes.json(); - } catch (err) { - log.error( - { - res: getTokenRes, - err, - }, - `Error parsing JSON in response body from getTokenRes.`, - ); - - return res.status(500).json({ - success: false, - description: `Failed to parse JSON returned from this service. Is their server malfunctioning?`, - }); - } - - const err = p(json, KAI_OAUTH2_RETURN_SCHEMA, {}, { allowExcessKeys: true }); - - if (err) { - log.error({ err }, `Validation error in JSON return from ${url}.`); - return res.status(500).json({ - success: false, - description: `Failed to validate JSON returned from this service. Is their server malfunctioning?`, - }); - } - - const j = json as { access_token: string; refresh_token: string }; - - await db["kai-auth-tokens"].update( - { - userID: user.id, - service: kaiType, - }, - { - $set: { - userID: user.id, - service: kaiType, - refreshToken: j.refresh_token, - token: j.access_token, - }, - }, - { - upsert: true, - }, - ); - - log.info(`Updated Auth for ${kaiType} for user ${FormatUserDoc(user)}.`); - - return res.status(200).json({ - success: true, - description: `Successfully updated auth for ${kaiType}`, - body: {}, - }); - }, -); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/kshook-sv6c/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/kshook-sv6c/router.ts deleted file mode 100644 index a73afcb08..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/kshook-sv6c/router.ts +++ /dev/null @@ -1,68 +0,0 @@ -import prValidate from "#server/middleware/prudence-validate"; -import { RequireKamaitachi } from "#server/middleware/type-require"; -import db from "#services/mongo/db"; -import { GetTachiData } from "#utils/req-tachi-data"; -import { Router } from "express"; - -import { RequireSelfRequestFromUser } from "../../middleware"; - -const router: Router = Router({ mergeParams: true }); - -router.use(RequireKamaitachi); -router.use(RequireSelfRequestFromUser); - -/** - * Retrieve your KsHook SV6C settings. - * - * @name GET /api/v1/users/:userID/integrations/kshook-sv6c/settings - */ -router.get("/settings", async (req, res) => { - const user = GetTachiData(req, "requestedUser"); - - const settingsDoc = await db["kshook-sv6c-settings"].findOne({ - userID: user.id, - }); - - return res.status(200).json({ - success: true, - description: `Retrieved KsHook (S6VC) settings.`, - body: settingsDoc ?? null, - }); -}); - -/** - * Update your KsHook SV6C configuration. - * - * @param forceStaticImport - Whether or whether not to statically import data. - * - * @name PUT /api/v1/users/:userID/integrations/kshook-sv6c/settings - */ -router.patch("/settings", prValidate({ forceStaticImport: "boolean" }), async (req, res) => { - const body = req.safeBody as { - forceStaticImport: boolean; - }; - - const user = GetTachiData(req, "requestedUser"); - - await db["kshook-sv6c-settings"].update( - { userID: user.id }, - { - $set: { - forceStaticImport: body.forceStaticImport, - }, - }, - { - upsert: true, - }, - ); - - const settings = await db["kshook-sv6c-settings"].findOne({ userID: user.id }); - - return res.status(200).json({ - success: true, - description: `Successfully updated settings.`, - body: settings, - }); -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/myt/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/myt/router.ts deleted file mode 100644 index b4365f73a..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/myt/router.ts +++ /dev/null @@ -1,99 +0,0 @@ -import type { MytCardInfo } from "tachi-common"; - -import prValidate from "#server/middleware/prudence-validate"; -import { RequireKamaitachi } from "#server/middleware/type-require"; -import db from "#services/mongo/db"; -import { GetTachiData } from "#utils/req-tachi-data"; -import { Router } from "express"; -import { p } from "prudence"; - -import { RequireSelfRequestFromUser } from "../../middleware"; - -const router: Router = Router({ mergeParams: true }); - -router.use(RequireKamaitachi); -router.use(RequireSelfRequestFromUser); - -/** - * Retrieve this user's card info (cardAccessCode). - * - * @name GET /api/v1/users/:userID/integrations/myt - */ -router.get("/", async (req, res) => { - const user = GetTachiData(req, "requestedUser"); - - const cardInfo = await db["myt-card-info"].findOne({ - userID: user.id, - }); - - if (!cardInfo) { - return res.status(200).json({ - success: true, - description: `User has no card info set.`, - body: null, - }); - } - - return res.status(200).json({ - success: true, - description: `Found card info.`, - body: cardInfo, - }); -}); - -/** - * Write new card details for Myt. - * - * @name PUT /api/v1/users/:userID/integrations/myt - */ -router.put( - "/", - prValidate( - { cardAccessCode: p.regex(/^[0-9]{20}$/u) }, - { cardAccessCode: "Expected 20 digits." }, - ), - async (req, res) => { - const user = GetTachiData(req, "requestedUser"); - - const { cardAccessCode } = req.safeBody as { cardAccessCode: string }; - - const newCardInfo: MytCardInfo = { - userID: user.id, - cardAccessCode, - }; - - await db["myt-card-info"].update( - { userID: user.id }, - { $set: newCardInfo }, - { - // insert new card info if the user doesn't have it yet. - upsert: true, - }, - ); - - return res.status(200).json({ - success: true, - description: `Updated cardAccessCode.`, - body: {}, - }); - }, -); - -/** - * Unset this user's card details for Myt. - * - * @name DELETE /api/v1/users/:userID/integrations/myt - */ -router.delete("/", async (req, res) => { - const user = GetTachiData(req, "requestedUser"); - - await db["cg-card-info"].remove({ userID: user.id }); - - return res.status(200).json({ - success: true, - description: `Deleted stored card info.`, - body: {}, - }); -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/router.ts deleted file mode 100644 index a15fca56d..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/integrations/router.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Router } from "express"; - -import cgCgTypeRouter from "./cg/_cgType/router"; -import fervidexRouter from "./fervidex/router"; -import kaiKaiTypeRouter from "./kai/_kaiType/router"; -import ksHookSV6CRouter from "./kshook-sv6c/router"; -import mytRouter from "./myt/router"; - -const router: Router = Router({ mergeParams: true }); - -router.use("/cg/:cgType", cgCgTypeRouter); -router.use("/myt", mytRouter); -router.use("/kai/:kaiType", kaiKaiTypeRouter); -router.use("/fervidex", fervidexRouter); -router.use("/kshook-sv6c", ksHookSV6CRouter); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/invites/router.test.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/invites/router.test.ts deleted file mode 100644 index 671a27cf2..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/invites/router.test.ts +++ /dev/null @@ -1,163 +0,0 @@ -import { ONE_MONTH } from "#lib/constants/time"; -import db from "#services/mongo/db"; -import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import { type InviteCodeDocument, UserAuthLevels, type UserDocument } from "tachi-common"; -import t from "tap"; - -t.test("GET /api/v1/users/:userID/invites", async (t) => { - t.beforeEach(ResetDBState); - - const cookie = await CreateFakeAuthCookie(mockApi); - - t.test("Should return all of this users created invites and who used them.", async (t) => { - await db.users.insert({ - id: 2, - username: "other_dude", - usernameLowercase: "other_dude", - } as UserDocument); - - const res = await mockApi.get("/api/v1/users/1/invites").set("Cookie", cookie); - - t.strictSame( - (res.body.body.invites as Array).sort( - (a, b) => a.createdAt - b.createdAt, - ), - [ - { - code: "example_invite", - createdBy: 1, - createdAt: 0, - consumed: false, - consumedBy: null, - consumedAt: null, - }, - { - code: "example_consumed_invite", - createdBy: 1, - createdAt: 1, - consumed: true, - consumedBy: 2, - consumedAt: 123, - }, - ], - ); - - t.strictSame(res.body.body.consumers, [ - { - id: 2, - username: "other_dude", - usernameLowercase: "other_dude", - }, - ]); - - t.end(); - }); - - t.test("Should require self-key level authentication.", async (t) => { - const res = await mockApi.get("/api/v1/users/1/invites"); - - t.equal(res.statusCode, 401); - - const res2 = await mockApi - .get("/api/v1/users/1/invites") - .set("Authorization", "Bearer fake_api_token"); - - t.equal(res2.statusCode, 403); - - t.end(); - }); - - t.end(); -}); - -t.test("GET /api/v1/users/:userID/invites/limit", async (t) => { - t.beforeEach(ResetDBState); - - const cookie = await CreateFakeAuthCookie(mockApi); - - t.test("Should return this users current limit on invites.", async (t) => { - // because this thing is time-based, we need to make sure - // this is always relative to right now. - - await db.users.update({ id: 1 }, { $set: { joinDate: Date.now() - ONE_MONTH * 2.5 } }); - const res = await mockApi.get("/api/v1/users/1/invites/limit").set("Cookie", cookie); - - t.equal(res.body.body.invites, 2); - t.equal(res.body.body.limit, 4); - - t.end(); - }); - - t.test("Should require self-key level authentication.", async (t) => { - const res = await mockApi.get("/api/v1/users/1/invites/limit"); - - t.equal(res.statusCode, 401); - - const res2 = await mockApi - .get("/api/v1/users/1/invites/limit") - .set("Authorization", "Bearer fake_api_token"); - - t.equal(res2.statusCode, 403); - - t.end(); - }); - - t.end(); -}); - -t.test("POST /api/v1/users/:userID/invites/create", async (t) => { - t.beforeEach(ResetDBState); - - const cookie = await CreateFakeAuthCookie(mockApi); - - t.test("Should create a new invite.", async (t) => { - await db.users.update({ id: 1 }, { $set: { joinDate: Date.now() - ONE_MONTH * 2.5 } }); - - const res = await mockApi.post("/api/v1/users/1/invites/create").set("Cookie", cookie); - - t.equal(res.statusCode, 200); - - const dbRes = await db.invites.findOne({ - code: res.body.body.code, - }); - - t.not(dbRes, null, "Should exist in the database."); - - t.end(); - }); - - t.test("Should honor invite limit.", async (t) => { - await db.invites.remove({}); - - // users with less than a month of life in them dont get invites, - // so this will force an invite limit honor. - await db.users.update( - { id: 1 }, - { $set: { joinDate: Date.now(), authLevel: UserAuthLevels.USER } }, - ); - - const res = await mockApi.post("/api/v1/users/1/invites/create").set("Cookie", cookie); - - t.equal(res.statusCode, 400); - - t.end(); - }); - - t.test("Should require self-key level authentication.", async (t) => { - const res = await mockApi.post("/api/v1/users/1/invites/create"); - - t.equal(res.statusCode, 401); - - const res2 = await mockApi - .post("/api/v1/users/1/invites/create") - .set("Authorization", "Bearer fake_api_token"); - - t.equal(res2.statusCode, 403); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/invites/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/invites/router.ts deleted file mode 100644 index ee2240b86..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/invites/router.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { GetTotalAllowedInvites } from "#lib/invites/invites"; -import { RequireInvitesEnabled } from "#server/middleware/type-require"; -import db from "#services/mongo/db"; -import { Random20Hex } from "#utils/misc"; -import { GetTachiData } from "#utils/req-tachi-data"; -import { GetUsersWithIDs } from "#utils/user"; -import { Router } from "express"; -import { type InviteCodeDocument, UserAuthLevels } from "tachi-common"; - -import { RequireSelfRequestFromUser } from "../middleware"; - -const router: Router = Router({ mergeParams: true }); - -router.use(RequireInvitesEnabled); -router.use(RequireSelfRequestFromUser); - -/** - * Retrieve all of this users created invites. - * - * @name GET /api/v1/users/:userID/invites - */ -router.get("/", async (req, res) => { - const user = GetTachiData(req, "requestedUser"); - - const invites = await db.invites.find( - { - createdBy: user.id, - }, - { - sort: { - consumedAt: -1, - }, - }, - ); - - const consumers = await GetUsersWithIDs( - invites.map((e) => e.consumedBy).filter((e) => e !== null) as Array, - ); - - return res.status(200).json({ - success: true, - description: `Found ${invites.length} invites.`, - body: { invites, consumers }, - }); -}); - -/** - * Return how many invites this user can create, and how many they - * have already created. - * - * @name GET /api/v1/users/:userID/invites/limit - */ -router.get("/limit", async (req, res) => { - const user = GetTachiData(req, "requestedUser"); - - const invites = await db.invites.count({ createdBy: user.id }); - const limit = GetTotalAllowedInvites(user); - - return res.status(200).json({ - success: true, - description: `Calculated invite limit.`, - body: { - invites, - limit, - }, - }); -}); - -/** - * Create a new invite. - * - * @name POST /api/v1/users/:userID/invites/create - */ -router.post("/create", async (req, res) => { - const user = GetTachiData(req, "requestedUser"); - const userID = user.id; - - try { - const lockExists = await db["invite-locks"].findOne({ - userID, - }); - - if (!lockExists) { - await db["invite-locks"].insert({ - userID, - locked: false, - }); - } - - const isNotLocked = await db["invite-locks"].findOneAndUpdate( - { - userID, - locked: false, - }, - { - $set: { locked: true }, - }, - ); - - // race condition protection - // to avoid users double-creating invites. - if (!isNotLocked) { - return res.status(409).json({ - success: false, - description: `You already have an outgoing invite creation request.`, - }); - } - - const existingInvites = await db.invites.count({ createdBy: user.id }); - - if ( - existingInvites >= GetTotalAllowedInvites(user) && - user.authLevel !== UserAuthLevels.ADMIN - ) { - return res.status(400).json({ - success: false, - description: `You already have your maximum amount of outgoing invites.`, - }); - } - - const inviteDoc: InviteCodeDocument = { - code: Random20Hex(), - consumed: false, - consumedAt: null, - consumedBy: null, - createdAt: Date.now(), - createdBy: user.id, - }; - - await db.invites.insert(inviteDoc); - - return res.status(200).json({ - success: true, - description: `Created Invite.`, - body: inviteDoc, - }); - } finally { - await db["invite-locks"].findOneAndUpdate( - { - userID, - }, - { - $set: { locked: false }, - }, - ); - } -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/middleware.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/middleware.ts deleted file mode 100644 index 8e87f14b2..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/middleware.ts +++ /dev/null @@ -1,118 +0,0 @@ -import type { RequestHandler } from "express"; - -import { SYMBOL_TACHI_API_AUTH } from "#lib/constants/tachi"; -import { log } from "#lib/log/log.js"; -import { IsNullish } from "#utils/misc"; -import { AssignToReqTachiData, GetTachiData } from "#utils/req-tachi-data"; -import { GetUserWithID, ResolveUser } from "#utils/user"; -import { UserAuthLevels } from "tachi-common"; - -export const GetUserFromParam: RequestHandler = async (req, res, next) => { - if (!req.params.userID) { - return res.status(400).json({ - success: false, - description: "No userID given.", - }); - } - - let userID = req.params.userID; - - if (req.params.userID === "me") { - const authUserID = req[SYMBOL_TACHI_API_AUTH].userID; - - if (authUserID === null) { - return res.status(401).json({ - success: false, - description: "Cannot use 'me' userID with no authentication.", - }); - } - - // fast assign using JWT - if (req.session.tachi?.user) { - AssignToReqTachiData(req, { requestedUser: req.session.tachi.user }); - next(); - return; - } - - userID = authUserID.toString(); - } - - const user = await ResolveUser(userID); - - if (!user) { - return res.status(404).json({ - success: false, - description: `The user ${userID} does not exist.`, - }); - } - - AssignToReqTachiData(req, { requestedUser: user }); - - next(); -}; - -/** - * Require the user making this request to also be the user in the :userID param. - */ -export const RequireAuthedAsUser: RequestHandler = async (req, res, next) => { - const user = GetTachiData(req, "requestedUser"); - - const requestingUserID = req[SYMBOL_TACHI_API_AUTH].userID; - - if (requestingUserID === null) { - return res.status(401).json({ - success: false, - description: `Authentication is required for this endpoint.`, - }); - } - - const requestingUser = await GetUserWithID(requestingUserID); - - if (!requestingUser) { - log.error(`${requestingUserID} is signed in as someone who does not exist.`); - return res.status(500).json({ - success: false, - description: `You are signed in as someone who does not exist.`, - }); - } - - // admins can do whatever. - if (requestingUser.authLevel === UserAuthLevels.ADMIN) { - next(); - return; - } - - if (requestingUserID !== user.id) { - return res.status(403).json({ - success: false, - description: "You are not authorised as this user.", - }); - } - - next(); -}; - -/** - * Require that this request is made with a Cookie, instead of any - * API key. This is for things that services should not be allowed to - * alter/access, like integration information. - */ -export const RequireSelfRequestFromUser: RequestHandler = (req, res, next) => { - const user = GetTachiData(req, "requestedUser"); - - if (req[SYMBOL_TACHI_API_AUTH].userID === null) { - return res.status(401).json({ - success: false, - description: `This endpoint requires session-level authentication.`, - }); - } - - if (IsNullish(req.session.tachi?.user.id) || req[SYMBOL_TACHI_API_AUTH].userID !== user.id) { - return res.status(403).json({ - success: false, - description: `This request cannot be performed by an API key, and requires authentication.`, - }); - } - - next(); -}; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/notifications/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/notifications/router.ts deleted file mode 100644 index b18a13898..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/notifications/router.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { ONE_SECOND } from "#lib/constants/time"; -import db from "#services/mongo/db"; -import { GetTachiData } from "#utils/req-tachi-data"; -import { Router } from "express"; - -import { RequireSelfRequestFromUser } from "../middleware"; - -const router: Router = Router({ mergeParams: true }); - -// Notifications aren't really for anyone else to interact with. Only the requesting user -// should be able to see their notifications. -router.use(RequireSelfRequestFromUser); - -/** - * Return all of this user's notifications, this is sorted on most recently sent first. - * - * @name GET /api/v1/users/:userID/notifications - */ -router.get("/", async (req, res) => { - const user = GetTachiData(req, "requestedUser"); - - const notifs = await db.notifications.find( - { - sentTo: user.id, - }, - { - sort: { - sentAt: -1, - }, - }, - ); - - return res.status(200).json({ - success: true, - description: `Found ${notifs.length} notifications.`, - body: notifs, - }); -}); - -/** - * Mark all notifications in this user's inbox as read. - * - * @name POST /api/v1/users/:userID/notifications/mark-all-read - */ -router.post("/mark-all-read", async (req, res) => { - const user = GetTachiData(req, "requestedUser"); - - const updateRes = await db.notifications.update( - { - sentTo: user.id, - - // insanely rare edge case, but if someone submits an empty-my-inbox - // request, and then gets a notif at the same time, they run the risk of - // emptying something so immediately they don't actually ever see it. - // This hack mitigates that, slightly. - sentAt: { - $lt: Date.now() - ONE_SECOND * 2, - }, - }, - { - $set: { read: true }, - }, - { - multi: true, - }, - ); - - return res.status(200).json({ - success: true, - description: `Marked ${updateRes.n} notifications as read.`, - body: {}, - }); -}); - -/** - * Clear all notifications from your inbox. - * - * @name POST /api/v1/users/:userID/notifications/delete-all - */ -router.post("/delete-all", async (req, res) => { - const user = GetTachiData(req, "requestedUser"); - - const deleted = await db.notifications.remove({ - sentTo: user.id, - - // See mark-all-read for an explanation of this behaviour. - sentAt: { - $lt: Date.now() - ONE_SECOND * 2, - }, - }); - - return res.status(200).json({ - success: true, - description: `Deleted ${deleted.deletedCount ?? 0} notification(s).`, - body: {}, - }); -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/pfp/router.test.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/pfp/router.test.ts deleted file mode 100644 index dbea92d20..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/pfp/router.test.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { CDNStoreOrOverwrite } from "#lib/cdn/cdn"; -import { GetProfilePictureURL } from "#lib/cdn/url-format"; -import db from "#services/mongo/db"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import { GetKTDataBuffer } from "#test-utils/test-data"; -import t from "tap"; - -t.test("GET /api/v1/users/:userID/pfp", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should return the default profile picture if user has no custom pfp", async (t) => { - await CDNStoreOrOverwrite("/users/default/pfp", "test"); - - // we have to follow redirs here lol - const res = await mockApi.get("/api/v1/users/1/pfp").redirects(1); - - t.equal(res.body.toString(), "test"); - - t.end(); - }); - - t.test("Should work with a .png extension", async (t) => { - await CDNStoreOrOverwrite("/users/default/pfp", "test"); - - // we have to follow redirs here lol - const res = await mockApi.get("/api/v1/users/1/pfp.png").redirects(2); - - t.equal(res.body.toString(), "test"); - - t.end(); - }); - - t.test("Should return a custom profile picture if one is set", async (t) => { - await CDNStoreOrOverwrite(GetProfilePictureURL(1, "checksum"), "foo"); - await db.users.update({ id: 1 }, { $set: { customPfpLocation: "checksum" } }); - const res = await mockApi.get("/api/v1/users/1/pfp").redirects(1); - - t.equal(res.body.toString(), "foo"); - - t.end(); - }); - - t.end(); -}); - -t.test("PUT /api/v1/users/:userID/pfp", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should set a profile picture if user has no custom pfp", async (t) => { - const img = GetKTDataBuffer("/images/acorn.png"); - - const res = await mockApi - .put("/api/v1/users/1/pfp") - .set("Authorization", "Bearer fake_api_token") - .attach("pfp", img, "file.jpg"); - - t.equal(res.statusCode, 200); - - const get = await mockApi.get(res.body.body.get).redirects(1); - - t.strictSame(img, get.body, "Profile picture should be stored."); - - t.end(); - }); - - t.test("Should set a profile picture if user has custom pfp", async (t) => { - await db.users.update({ id: 1 }, { $set: { customPfpLocation: "checksum" } }); - - const img = GetKTDataBuffer("/images/acorn.png"); - - const res = await mockApi - .put("/api/v1/users/1/pfp") - .set("Authorization", "Bearer fake_api_token") - .attach("pfp", img, "file.png"); - - t.equal(res.statusCode, 200); - - const get = await mockApi.get(res.body.body.get).redirects(1); - - t.strictSame(img, get.body, "Profile picture should be stored."); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/pfp/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/pfp/router.ts deleted file mode 100644 index cb891a368..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/pfp/router.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { CDNDelete, CDNRedirect, CDNStoreOrOverwrite } from "#lib/cdn/cdn"; -import { GetProfilePictureURL } from "#lib/cdn/url-format"; -import { ONE_MEGABYTE } from "#lib/constants/filesize"; -import { log } from "#lib/log/log.js"; -import { RequirePermissions } from "#server/middleware/auth"; -import { CreateMulterSingleUploadMiddleware } from "#server/middleware/multer-upload"; -import db from "#services/mongo/db"; -import { HashSHA256 } from "#utils/crypto"; -import { GetTachiData } from "#utils/req-tachi-data"; -import { FormatUserDoc } from "#utils/user"; -import { Router } from "express"; - -import { RequireAuthedAsUser } from "../middleware"; - -const router: Router = Router({ mergeParams: true }); - -/** - * Sets a profile picture. - * - * @param pfp - A JPG, PNG or GIF file less than 1mb. - * @note although GIFs are supported, this functionality isn't documented on the site. - * this is kind of an easter egg. - * - * @name PUT /api/v1/users/:userID/pfp - */ -router.put( - "/", - RequireAuthedAsUser, - RequirePermissions("customise_profile"), - CreateMulterSingleUploadMiddleware("pfp", ONE_MEGABYTE), - async (req, res) => { - const user = GetTachiData(req, "requestedUser"); - - if (!user.customPfpLocation) { - log.debug(`User ${FormatUserDoc(user)} set a custom profile picture.`); - } else { - log.debug(`User ${FormatUserDoc(user)} updated their profile picture.`); - } - - if (!req.file) { - log.error( - `Conflicting state - no req.file has been populated but passed middleware? (${FormatUserDoc( - user, - )})`, - ); - return res.status(500).json({ - success: false, - description: `An internal error has occured.`, - }); - } - - const contentHash = HashSHA256(req.file.buffer); - - if ( - req.file.mimetype === "image/jpeg" || - req.file.mimetype === "image/png" || - req.file.mimetype === "image/gif" - ) { - await CDNStoreOrOverwrite(GetProfilePictureURL(user.id, contentHash), req.file.buffer); - } else { - return res.status(400).json({ - success: false, - description: `Invalid file - only JPG and PNG files are supported.`, - }); - } - - if (req.session.tachi?.user) { - req.session.tachi.user.customPfpLocation = contentHash; - } - - await db.users.update({ id: user.id }, { $set: { customPfpLocation: contentHash } }); - - return res.status(200).json({ - success: true, - description: `Stored profile picture.`, - body: { - get: req.originalUrl, - }, - }); - }, -); - -/** - * Returns this user's profile picture. If the user does not have a custom profile picture, - * return the default profile picture. - * - * @name GET /api/v1/users/:userID/pfp - */ -router.get("/", (req, res) => { - const user = GetTachiData(req, "requestedUser"); - - log.debug(user, "User Info for /:userID/pfp request is "); - - if (!user.customPfpLocation) { - res.setHeader("Content-Type", "image/png"); - CDNRedirect(res, "/users/default/pfp"); - return; - } - - CDNRedirect(res, GetProfilePictureURL(user.id, user.customPfpLocation)); -}); - -/** - * Deletes this user's profile picture, and go back to the default profile picture. - * - * @name DELETE /api/v1/users/:userID/pfp - */ -router.delete( - "/", - RequireAuthedAsUser, - RequirePermissions("customise_profile"), - async (req, res) => { - const user = GetTachiData(req, "requestedUser"); - - if (!user.customPfpLocation) { - return res.status(404).json({ - success: false, - description: `You do not have a custom profile picture to delete.`, - }); - } - - await CDNDelete(GetProfilePictureURL(user.id, user.customPfpLocation)); - - await db.users.update({ id: user.id }, { $set: { customPfpLocation: null } }); - - return res.status(200).json({ - success: true, - description: `Removed custom profile picture.`, - body: {}, - }); - }, -); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/router.test.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/router.test.ts deleted file mode 100644 index dbb15833e..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/router.test.ts +++ /dev/null @@ -1,481 +0,0 @@ -import type { GameGroup, ImportDocument, ImportTypes, UserGameStats } from "tachi-common"; - -import { ONE_DAY, ONE_YEAR } from "#lib/constants/time"; -import db from "#services/mongo/db"; -import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import { Random20Hex } from "#utils/misc"; -import t from "tap"; - -import { PasswordCompare } from "../../auth/auth"; - -t.test("GET /api/v1/users/:userID", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should retrieve a user on their ID.", async (t) => { - const res = await mockApi.get("/api/v1/users/1"); - - t.hasStrict(res.body, { - success: true, - description: "Found user test_zkldi.", - body: { - id: 1, - username: "test_zkldi", - }, - }); - - t.end(); - }); - - t.test("Should retrieve a user on their name.", async (t) => { - const res = await mockApi.get("/api/v1/users/test_zkldi"); - - t.hasStrict(res.body, { - success: true, - description: "Found user test_zkldi.", - body: { - id: 1, - username: "test_zkldi", - }, - }); - - t.end(); - }); - - t.test("Should retrieve a user on their name case insensitively.", async (t) => { - const res = await mockApi.get("/api/v1/users/TeSt_ZklDI"); - - t.hasStrict(res.body, { - success: true, - description: "Found user test_zkldi.", - body: { - id: 1, - username: "test_zkldi", - }, - }); - - t.end(); - }); - - t.test("Should return 404 on users that don't exist.", async (t) => { - const res = await mockApi.get("/api/v1/users/someguy"); - - t.hasStrict(res.body, { - success: false, - description: "The user someguy does not exist.", - }); - - t.equal(res.statusCode, 404); - - t.end(); - }); - - t.end(); -}); - -t.test("PATCH /api/v1/users/:userID", async (t) => { - t.beforeEach(ResetDBState); - - const cookie = await CreateFakeAuthCookie(mockApi); - - t.test("Should require authentication", async (t) => { - const res = await mockApi.patch("/api/v1/users/1"); - - t.equal(res.statusCode, 401); - - // needs self key auth - await db["api-tokens"].insert({ - token: "noperm", - permissions: {}, - identifier: "No permissions token", - userID: 1, - fromAPIClient: null, - }); - - const res2 = await mockApi.patch("/api/v1/users/1").set("Authorization", "Bearer noperm"); - - t.equal(res2.statusCode, 403); - - t.end(); - }); - - t.test("Should reject empty updates.", async (t) => { - const res = await mockApi.patch("/api/v1/users/1").set("Cookie", cookie); - - t.equal(res.statusCode, 400); - - t.end(); - }); - - t.test("Should update the user doc.", async (t) => { - const res = await mockApi.patch("/api/v1/users/1").set("Cookie", cookie).send({ - status: "Hello World!", - }); - - t.equal(res.body.body.status, "Hello World!"); - - const dbUser = await db.users.findOne({ id: 1 }); - - t.equal(dbUser?.status, "Hello World!"); - - t.end(); - }); - - t.test("Should allow empty strings for about me.", async (t) => { - const res = await mockApi.patch("/api/v1/users/1").set("Cookie", cookie).send({ - about: "", - }); - - t.equal(res.statusCode, 200); - - const dbUser = await db.users.findOne({ id: 1 }); - - t.equal(dbUser?.about, ""); - - t.end(); - }); - - t.test("Shouldn't allow about me to be set to null.", async (t) => { - const res = await mockApi.patch("/api/v1/users/1").set("Cookie", cookie).send({ - about: null, - }); - - t.equal(res.statusCode, 400); - - const dbUser = await db.users.findOne({ id: 1 }); - - t.equal(dbUser?.about, "test_user_not_real"); - - t.end(); - }); - - t.test("Shouldn't alter other properties.", async (t) => { - await db.users.update({ id: 1 }, { $set: { "socialMedia.discord": "foo#123" } }); - - const res = await mockApi.patch("/api/v1/users/1").set("Cookie", cookie).send({ - status: "Hello World!", - }); - - t.equal(res.body.body.status, "Hello World!"); - - const dbUser = await db.users.findOne({ id: 1 }); - - t.equal(dbUser?.status, "Hello World!"); - t.equal(dbUser?.about, "test_user_not_real"); - t.equal(dbUser?.socialMedia.discord, "foo#123"); - - t.end(); - }); - - t.test("Should correctly strip twitter urls.", async (t) => { - const res = await mockApi.patch("/api/v1/users/1").set("Cookie", cookie).send({ - twitter: "https://twitter.com/zkldi", - }); - - t.equal(res.body.body.socialMedia.twitter, "zkldi"); - - const dbUser = await db.users.findOne({ id: 1 }); - - t.equal(dbUser?.socialMedia.twitter, "zkldi"); - - t.end(); - }); - - t.test("Should correctly strip youtube urls.", async (t) => { - const res = await mockApi.patch("/api/v1/users/1").set("Cookie", cookie).send({ - youtube: "https://youtube.com/user/zkldi", - }); - - t.equal(res.body.body.socialMedia.youtube, "zkldi"); - - const dbUser = await db.users.findOne({ id: 1 }); - - t.equal(dbUser?.socialMedia.youtube, "zkldi"); - - const res2 = await mockApi.patch("/api/v1/users/1").set("Cookie", cookie).send({ - youtube: "https://youtube.com/channel/zkldi", - }); - - t.equal(res2.body.body.socialMedia.youtube, "zkldi"); - - const dbUser2 = await db.users.findOne({ id: 1 }); - - t.equal(dbUser2?.socialMedia.youtube, "zkldi"); - - const res3 = await mockApi.patch("/api/v1/users/1").set("Cookie", cookie).send({ - youtube: "https://youtube.com/@zkldi", - }); - - t.equal(res3.body.body.socialMedia.youtube, "zkldi"); - - const dbUser3 = await db.users.findOne({ id: 1 }); - - t.equal(dbUser3?.socialMedia.youtube, "zkldi"); - - t.end(); - }); - - t.test("Should correctly strip github urls.", async (t) => { - const res = await mockApi.patch("/api/v1/users/1").set("Cookie", cookie).send({ - github: "https://github.com/zkldi", - }); - - t.equal(res.body.body.socialMedia.github, "zkldi"); - - const dbUser = await db.users.findOne({ id: 1 }); - - t.equal(dbUser?.socialMedia.github, "zkldi"); - - t.end(); - }); - - t.test("Should correctly strip twitch urls.", async (t) => { - const res = await mockApi.patch("/api/v1/users/1").set("Cookie", cookie).send({ - twitch: "https://twitch.tv/zkldi", - }); - - t.equal(res.body.body.socialMedia.twitch, "zkldi"); - - const dbUser = await db.users.findOne({ id: 1 }); - - t.equal(dbUser?.socialMedia.twitch, "zkldi"); - - t.end(); - }); - - t.test("Should correctly strip steam urls.", async (t) => { - const res = await mockApi.patch("/api/v1/users/1").set("Cookie", cookie).send({ - steam: "https://steamcommunity.com/id/zkldi", - }); - - t.equal(res.body.body.socialMedia.steam, "zkldi"); - - const dbUser = await db.users.findOne({ id: 1 }); - - t.equal(dbUser?.socialMedia.steam, "zkldi"); - - t.end(); - }); - - t.end(); -}); - -t.test("POST /api/v1/users/:userID/change-password", async (t) => { - t.beforeEach(ResetDBState); - const cookie = await CreateFakeAuthCookie(mockApi); - - function send(content: Record) { - return mockApi.post("/api/v1/users/1/change-password").set("Cookie", cookie).send(content); - } - - t.test("Must require !password and !oldPassword to be a valid password.", async (t) => { - const res = await send({ - "!password": null, - "!oldPassword": "heres_my_old_password", - }); - - t.equal(res.statusCode, 400); - t.match( - res.body.description, - /!password/gu, - "Should be related to the invalid password field.", - ); - - const res2 = await send({ - "!password": "heres_my_new_password", - "!oldPassword": "short", - }); - - t.equal(res2.statusCode, 400); - t.match( - res2.body.description, - /!oldPassword/gu, - "Should be related to the invalid !oldPassword field.", - ); - - t.end(); - }); - - t.test("Must require !oldPassword to match.", async (t) => { - const res = await send({ - "!password": "new_password", - "!oldPassword": "NOT_MY_PASSWORD", - }); - - t.equal(res.statusCode, 401); - t.match(res.body.description, /old password doesn't match/iu); - - t.end(); - }); - - t.test("Should update password if everything checks out.", async (t) => { - const res = await send({ - "!password": "NEW_PASSWORD", - "!oldPassword": "password", - }); - - t.equal(res.statusCode, 200); - - const newPrivateInfo = await db["user-private-information"].findOne({ - userID: 1, - }); - - t.not(newPrivateInfo, null, "newPrivateInfo should not be null."); - - t.ok( - PasswordCompare("NEW_PASSWORD", newPrivateInfo!.password), - "Should update the users password to the hash of NEW_PASSWORD.", - ); - - t.end(); - }); - - t.end(); -}); - -t.test("GET /api/v1/users/:userID/game-stats", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should return all of a user's game stats.", async (t) => { - await db["game-stats"].remove({}); - - const stats: Array = [ - { - userID: 1, - game: "iidx", - playtype: "SP", - classes: {}, - ratings: { - ktLampRating: 12, - }, - }, - { - userID: 1, - game: "iidx", - playtype: "DP", - classes: {}, - ratings: { - ktLampRating: 11, - }, - }, - { - userID: 1, - game: "gitadora", - playtype: "Dora", - classes: {}, - ratings: { - naiveSkill: 4843, - }, - }, - ]; - - await db["game-stats"].insert(stats); - - const res = await mockApi.get("/api/v1/users/test_zkldi/game-stats"); - - t.hasStrict(res.body, { - success: true, - description: "Returned 3 stats objects.", - }); - - const returns = res.body.body.map((e: UserGameStats) => `${e.game}-${e.playtype}`); - - // amusing small hacks - t.equal(returns.length, 3); - t.ok(returns.includes("iidx-SP")); - t.ok(returns.includes("iidx-DP")); - t.ok(returns.includes("gitadora-Dora")); - - t.end(); - }); - - t.end(); -}); - -t.test("GET /api/v1/users/:userID/recent-imports", (t) => { - t.beforeEach(ResetDBState); - - function mkImport( - game: GameGroup, - importType: ImportTypes, - timeFinished: number, - userIntent: boolean, - ): ImportDocument { - return { - classDeltas: [], - createdSessions: [], - errors: [], - game, - importType, - importID: Random20Hex(), - goalInfo: [], - gptStrings: [], - questInfo: [], - playtypes: [], - scoreIDs: [], - timeFinished, - timeStarted: 0, - userID: 1, - userIntent, - }; - } - - t.test("Should work cross game and ignore imports without userIntent.", async (t) => { - await db.imports.insert([ - mkImport("iidx", "api/flo-iidx", Date.now() - ONE_DAY, true), - mkImport("iidx", "api/flo-iidx", Date.now() - ONE_DAY, true), - mkImport("sdvx", "api/flo-sdvx", Date.now() - ONE_DAY, false), - mkImport("iidx", "api/flo-iidx", Date.now() - ONE_DAY, false), - mkImport("sdvx", "api/flo-sdvx", Date.now() - ONE_DAY, true), - ]); - - const res = await mockApi.get("/api/v1/users/1/recent-imports"); - - t.equal(res.statusCode, 200); - - t.strictSame(res.body.body, [ - { - importType: "api/flo-iidx", - count: 2, - }, - { - importType: "api/flo-sdvx", - count: 1, - }, - ]); - - t.end(); - }); - - t.test("Should ignore imports that were too long ago.", async (t) => { - await db.imports.insert([ - mkImport("iidx", "api/flo-iidx", Date.now() - ONE_DAY, true), - mkImport("iidx", "ir/fervidex", Date.now() - ONE_YEAR, true), - mkImport("iidx", "ir/fervidex", Date.now() - ONE_YEAR, true), - mkImport("iidx", "ir/fervidex", Date.now() - ONE_YEAR, true), - mkImport("iidx", "ir/fervidex", Date.now() - ONE_YEAR, true), - mkImport("sdvx", "api/flo-sdvx", Date.now() - ONE_DAY, true), - mkImport("sdvx", "api/flo-sdvx", Date.now() - ONE_DAY, true), - ]); - - const res = await mockApi.get("/api/v1/users/1/recent-imports"); - - t.equal(res.statusCode, 200); - - t.strictSame(res.body.body, [ - { - importType: "api/flo-sdvx", - count: 2, - }, - { - importType: "api/flo-iidx", - count: 1, - }, - ]); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/router.ts deleted file mode 100644 index 311bdf6d6..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/router.ts +++ /dev/null @@ -1,842 +0,0 @@ -import type { - AnyProfileRatingAlg, - GPTString, - ImportTypes, - integer, - UserGameStats, -} from "tachi-common"; - -import { GetRecentActivity } from "#lib/activity/activity"; -import { ONE_MONTH } from "#lib/constants/time"; -import { SendEmail } from "#lib/email/client"; -import { EmailFormatVerifyEmail } from "#lib/email/formats"; -import { log } from "#lib/log/log.js"; -import { GetRivalIDs } from "#lib/rivals/rivals"; -import { ServerConfig } from "#lib/setup/config"; -import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; -import { DeleteUndefinedProps, IsNonEmptyString, Random20Hex, StripUrl } from "#utils/misc"; -import { optNullFluffStrField } from "#utils/prudence"; -import { - GetGoalSummary, - GetRecentlyViewedFoldersAnyGPT, - GetRecentPlaycount, - GetRecentSessions, -} from "#utils/queries/summary"; -import { GetUser } from "#utils/req-tachi-data"; -import { - CanChangeUsername, - CheckIfEmailInUse, - FormatUserDoc, - GetAllRankings, - GetNextAvailableUsernameChange, - GetUserCaseInsensitive, - GetUserWithID, -} from "#utils/user"; -import { Router } from "express"; -import { p } from "prudence"; - -import { HashPassword, PasswordCompare, ValidateEmail, ValidatePassword } from "../../auth/auth"; -import apiTokensRouter from "./api-tokens/router"; -import bannerRouter from "./banner/router"; -import followingRouter from "./following/router"; -import gameSpecificRouter from "./games/@gameSpecificRoutes/router"; -import gamePTRouter from "./games/_game/_playtype/router"; -import importsRouter from "./imports/router"; -import integrationsRouter from "./integrations/router"; -import invitesRouter from "./invites/router"; -import { GetUserFromParam, RequireSelfRequestFromUser } from "./middleware"; -import notifsRouter from "./notifications/router"; -import pfpRouter from "./pfp/router"; -import sessionsRouter from "./sessions/router"; -import settingsRouter from "./settings/router"; - -const router: Router = Router({ mergeParams: true }); - -router.use(GetUserFromParam); - -/** - * Get the user at this ID or name. - * @name GET /api/v1/users/:userID - */ -router.get("/", (req, res) => { - const user = GetUser(req); - - return res.status(200).json({ - success: true, - description: `Found user ${user.username}.`, - body: user, - }); -}); - -interface UserPatchBody { - about?: string | null; - status?: string | null; - discord?: string | null; - twitter?: string | null; - twitch?: string | null; - youtube?: string | null; - github?: string | null; - steam?: string | null; -} - -/** - * Modify this user document. All parameters are optional. - * - * @param about - An about me, this is rendered as markdown. - * @param status - A user status. This is not rendered as markdown, and is short. - * @param discord - The user's discord tag. - * @param twitter - The user's twitter tag. - * @param github - The user's github. - * @param steam - The user's steamID. - * @param youtube - The user's youtube. - * @param twitch - The user's twitch. - * - * @name PATCH /api/v1/users/:userID - */ -router.patch( - "/", - RequireSelfRequestFromUser, - prValidate( - { - about: p.optional(p.isBoundedString(0, 2000)), - status: optNullFluffStrField, - discord: optNullFluffStrField, - twitter: optNullFluffStrField, - github: optNullFluffStrField, - steam: optNullFluffStrField, - youtube: optNullFluffStrField, - twitch: optNullFluffStrField, - }, - { - about: "Your about me is too long.", - }, - ), - async (req, res) => { - const user = GetUser(req); - - const body = req.safeBody as UserPatchBody; - - if (Object.keys(body).length === 0) { - return res.status(400).json({ - success: false, - description: `Nothing was provided to modify.`, - }); - } - - // Hack stuff for user experience. - // In kt1, users would repeatedly mess up these fields. - if (IsNonEmptyString(body.twitter)) { - body.twitter = StripUrl("twitter.com/", body.twitter); - } - - if (IsNonEmptyString(body.github)) { - body.github = StripUrl("github.com/", body.github); - } - - if (IsNonEmptyString(body.youtube)) { - // youtube has THREE user urls lol - body.youtube = StripUrl("youtube.com/user/", body.youtube); - body.youtube = StripUrl("youtube.com/channel/", body.youtube); - body.youtube = StripUrl("youtube.com/@", body.youtube); - } - - if (IsNonEmptyString(body.twitch)) { - body.twitch = StripUrl("twitch.tv/", body.twitch); - } - - if (IsNonEmptyString(body.steam)) { - body.steam = StripUrl("steamcommunity.com/id/", body.steam); - } - - if (body.about === null) { - return res.status(400).json({ - success: false, - description: `Cannot set about me to null.`, - }); - } - - // :( - const modifyObject: Partial< - { about: string } & Record< - | "status" - | `socialMedia.${ - | "discord" - | "github" - | "steam" - | "twitch" - | "twitter" - | "youtube"}`, - string | null - > - > = { - about: body.about, - status: body.status, - }; - - for (const socMed of [ - "twitch", - "github", - "youtube", - "steam", - "twitter", - "discord", - ] as const) { - modifyObject[`socialMedia.${socMed}` as const] = body[socMed]; - } - - DeleteUndefinedProps(modifyObject); - - await db.users.update( - { - id: user.id, - }, - { - $set: modifyObject, - }, - ); - - const newUser = await GetUserWithID(user.id); - - if (!newUser) { - log.error( - { user }, - `User ${FormatUserDoc(user)} updated profile but user doc no longer exists?`, - ); - - return res.status(500).json({ - success: false, - description: `An internal error has occured.`, - }); - } - - if (req.session.tachi?.user) { - req.session.tachi.user = newUser; - } - - return res.status(200).json({ - success: true, - description: `Successfully updated user.`, - body: newUser, - }); - }, -); - -/** - * Returns all of the game-stats this user has. - * Additionally, adds a __rankingData property, which contains this users - * ranking information. - * This endpoint doubles up as a way of checking what games a user has played. - * - * @name GET /api/v1/users/:userID/game-stats - */ -router.get("/game-stats", async (req, res) => { - const user = GetUser(req); - - // a user has played a game if and only if they have stats for it. - const stats: Array< - { - __rankingData?: Record; - } & UserGameStats - > = await db["game-stats"].find({ userID: user.id }); - - await Promise.all( - stats.map(async (s) => { - const data = await GetAllRankings(s); - - s.__rankingData = data; - }), - ); - - return res.status(200).json({ - success: true, - description: `Returned ${stats.length} stats objects.`, - body: stats, - }); -}); - -/** - * Returns a summary of what the user has achieved in the past 16 hours. - * Used on the main dashboard page to give users quick links to sessions, - * alongside other information. - * - * @name GET /api/v1/users/:userID/recent-summary - */ -router.get("/recent-summary", async (req, res) => { - const user = GetUser(req); - - const [ - recentPlaycount, - recentSessions, - { folders: recentFolders, stats: recentFolderStats }, - { achievedGoals, goals, improvedGoals }, - ] = await Promise.all([ - GetRecentPlaycount(user.id), - GetRecentSessions(user.id), - GetRecentlyViewedFoldersAnyGPT(user.id), - GetGoalSummary(user.id), - ]); - - return res.status(200).json({ - success: true, - description: `Retrieved information about ${FormatUserDoc(user)}.`, - body: { - recentPlaycount, - recentSessions, - recentFolders, - recentFolderStats, - recentAchievedGoals: achievedGoals, - recentGoals: goals, - recentImprovedGoals: improvedGoals, - }, - }); -}); - -/** - * Returns whether the user has verified their email or not. - * Requires self-key level permissions. - * - * @name GET /api/v1/users/:userID/is-email-verified - */ -router.get("/is-email-verified", RequireSelfRequestFromUser, async (req, res) => { - const user = GetUser(req); - - const verifyInfo = await db["verify-email-codes"].findOne({ - userID: user.id, - }); - - if (verifyInfo) { - return res.status(200).json({ - success: true, - description: `User has not verified email.`, - body: false, - }); - } - - return res.status(200).json({ - success: true, - description: `User has verified email.`, - body: true, - }); -}); - -/** - * Get what email this user signed up with. - * - * @name GET /api/v1/users/:userID/email - */ -router.get("/email", RequireSelfRequestFromUser, async (req, res) => { - const user = GetUser(req); - - const email = await db["user-private-information"].findOne({ - userID: user.id, - }); - - if (email) { - return res.status(200).json({ - success: true, - description: `User signed up with this email.`, - body: email.email, - }); - } - - log.error(`User ${user.id} doesn't have private info?`); - - return res.status(500).json({ - success: false, - description: `Internal Server Error`, - }); -}); - -/** - * Change what email is associated with this account. - * - * @name GET /api/v1/users/:userID/email - */ -router.post( - "/change-email", - RequireSelfRequestFromUser, - prValidate({ - email: ValidateEmail, - "!password": ValidatePassword, - }), - async (req, res) => { - const user = GetUser(req); - - const body = req.safeBody as { - "!password": string; - email: string; - }; - - const privateInfo = await db["user-private-information"].findOne({ - userID: user.id, - }); - - if (!privateInfo) { - log.error(`User ${user.id} has no associated private info?`); - return res.status(500).json({ - success: false, - description: `Internal server error.`, - }); - } - - const isPasswordValid = await PasswordCompare(body["!password"], privateInfo.password); - - if (!isPasswordValid) { - return res.status(403).json({ - success: false, - description: `Invalid password.`, - }); - } - - const existingEmail = await CheckIfEmailInUse(body.email); - - if (existingEmail) { - log.info(`User attempted to change to email that was already in use.`); - return res.status(409).json({ - success: false, - description: `This email is already in use.`, - }); - } - - log.info(`User ${user.id} changed email from ${privateInfo.email} to ${body.email}`); - - await db["user-private-information"].update( - { - userID: user.id, - }, - { - $set: { - email: body.email, - }, - }, - ); - - if (ServerConfig.EMAIL_CONFIG) { - const resetEmailCode = Random20Hex(); - - // clear out the previous email code! - await db["verify-email-codes"].remove({ - userID: user.id, - }); - - await db["verify-email-codes"].insert({ - code: resetEmailCode, - userID: user.id, - email: body.email, - }); - - const { text, html } = EmailFormatVerifyEmail(user.username, resetEmailCode); - - void SendEmail(body.email, "Email Verification", html, text); - } - - return res.status(200).json({ - success: true, - description: `Re-sent email verification to new email`, - body: null, - }); - }, -); - -/** - * Changes the users password. - * Requires self-key level permissions. - * - * @param !password - The new password. Must pass password validation rules. - * @param !oldPassword - The old password. - * - * @name POST /api/v1/users/:userID/change-password - */ -router.post( - "/change-password", - RequireSelfRequestFromUser, - prValidate({ - "!password": ValidatePassword, - "!oldPassword": ValidatePassword, - }), - async (req, res) => { - const body = req.safeBody as { - "!oldPassword": string; - "!password": string; - }; - - const user = req.session.tachi?.user; - - /* istanbul ignore next */ - if (!user) { - log.error( - `IP ${req.ip} got to /change-password without a user, but passed RequireSelfRequest?`, - ); - - // this should be a 500, but lie to them. - return res.status(403).json({ - success: false, - description: `You are not authorised to perform this action.`, - }); - } - - const privateInfo = await db["user-private-information"].findOne({ - userID: user.id, - }); - - /* istanbul ignore next */ - if (!privateInfo) { - log.error({ user }, `User ${FormatUserDoc(user)} has no private information?`); - return res.status(500).json({ - success: false, - description: `An internal server error has occured.`, - }); - } - - const isLastPasswordValid = await PasswordCompare( - body["!oldPassword"], - privateInfo.password, - ); - - if (!isLastPasswordValid) { - return res.status(401).json({ - success: false, - description: `Old Password doesn't match what we have in our records.`, - }); - } - - const newPasswordHash = await HashPassword(body["!password"]); - - await db["user-private-information"].update( - { - userID: user.id, - }, - { - $set: { - password: newPasswordHash, - }, - }, - ); - - return res.status(200).json({ - success: true, - description: `Updated Password.`, - body: {}, - }); - }, -); - -/** - * Changes the users username. - * Requires self-key level permissions. - * - * @param !password - The new password. Must pass password validation rules. - * @param newUsername - The new username. Must pass username validation rules. - * - * @name POST /api/v1/users/:userID/change-username - */ -router.post( - "/change-username", - RequireSelfRequestFromUser, - prValidate( - { - "!password": ValidatePassword, - newUsername: p.regex(/^[a-zA-Z_-][a-zA-Z0-9_-]{2,20}$/u), - }, - { - newUsername: - "Username must be between 3 and 20 characters long, can only contain alphanumeric characters and cannot start with a number.", - "!password": "Invalid password.", - }, - ), - async (req, res) => { - const body = req.safeBody as { - "!password": string; - newUsername: string; - }; - - const user = req.session.tachi?.user; - - /* istanbul ignore next */ - if (!user) { - log.error( - `IP ${req.ip} got to /change-username without a user, but passed RequireSelfRequest?`, - ); - - // this should be a 500, but lie to them. - return res.status(403).json({ - success: false, - description: `You are not authorised to perform this action.`, - }); - } - - const privateInfo = await db["user-private-information"].findOne({ - userID: user.id, - }); - - /* istanbul ignore next */ - if (!privateInfo) { - log.error({ user }, `User ${FormatUserDoc(user)} has no private information?`); - return res.status(500).json({ - success: false, - description: `An internal server error has occured.`, - }); - } - - const isPasswordValid = await PasswordCompare(body["!password"], privateInfo.password); - - if (!isPasswordValid) { - return res.status(401).json({ - success: false, - description: `Invalid password.`, - }); - } - - const existingUser = await GetUserCaseInsensitive(body.newUsername); - - if (existingUser) { - log.debug(`Invalid username ${body.newUsername}, already in use.`); - return res.status(409).json({ - success: false, - description: "This username is already in use.", - }); - } - - const canChangeUsername = await CanChangeUsername(user.id); - - if (!canChangeUsername) { - return res.status(403).json({ - success: false, - description: "You can only change your username every 6 months.", - }); - } - - await db.users.update( - { - id: user.id, - }, - { - $set: { - username: body.newUsername, - usernameLowercase: body.newUsername.toLowerCase(), - }, - }, - ); - - await db["user-name-changes"].insert({ - userID: user.id, - username: body.newUsername, - timestamp: Date.now(), - previousUsername: user.username, - }); - - if (req.session.tachi?.user) { - req.session.tachi.user = { - ...user, - username: body.newUsername, - }; - req.session.save(); - } - - return res.status(200).json({ - success: true, - description: `Updated your username!`, - body: {}, - }); - }, -); - -/** - * Get the last time the user changed their username, - * and whether they can change their username again. - * - * @name GET /api/v1/users/:userID/last-username-change - */ -router.get("/last-username-change", async (req, res) => { - const user = GetUser(req); - - const nextAvailableChange = await GetNextAvailableUsernameChange(user.id); - - const canChange = await CanChangeUsername(user.id); - - let body; - - if (canChange) { - body = { - canChange: true, - }; - } else { - body = { - canChange: false, - nextAvailableChange, - }; - } - - return res.status(200).json({ - success: true, - description: `Next available username change.`, - body, - }); -}); -/** - * Get the recent import types this user has used. - * - * @name GET /api/v1/users/:userID/recent-imports - */ -router.get("/recent-imports", async (req, res) => { - const user = GetUser(req); - - const recentImports: Array<{ _id: ImportTypes; count: integer }> = await db.imports.aggregate([ - { - $match: { - userID: user.id, - timeFinished: { $gt: Date.now() - ONE_MONTH }, - userIntent: true, - importType: { - $nin: ["file/mypagescraper-records-csv", "file/mypagescraper-player-csv"], - }, - }, - }, - { - $group: { - _id: "$importType", - count: { $sum: 1 }, - }, - }, - ]); - - // rename _id to importType. - const imports = recentImports.map((e) => ({ importType: e._id, count: e.count })); - - return res.status(200).json({ - success: true, - description: `Found ${recentImports.length} imports.`, - body: imports.sort((a, b) => b.count - a.count), - }); -}); - -/** - * Get stats for this user on all games. - * - * @name GET /api/v1/users/:userID/stats - */ -router.get("/stats", async (req, res) => { - const user = GetUser(req); - - const scoreCount = await db.scores.count({ userID: user.id }); - const sessionCount = await db.sessions.count({ userID: user.id }); - - return res.status(200).json({ - success: true, - description: `Retrieved stats.`, - body: { - scores: scoreCount, - sessions: sessionCount, - }, - }); -}); - -/** - * Fetch this users recent activity, and all of their rivals for each GPT they've played. - * - * @name GET /api/v1/users/:userID/activity - */ -router.get( - "/activity", - prValidate({ - startTime: "*string", - includeRivals: p.optional(p.isIn("true", "false")), - includeFollowers: p.optional(p.isIn("true", "false")), - }), - async (req, res) => { - const qStartTime = req.query.startTime as string | undefined; - - const includeRivals = req.query.includeRivals === "true"; - const includeFollowers = req.query.includeFollowers === "true"; - - const startTime = qStartTime ? Number(qStartTime) : null; - - if (Number.isNaN(startTime)) { - return res.status(400).json({ - success: false, - description: `Invalid startTime, got a non number.`, - }); - } - - const user = GetUser(req); - - const gpts = await db["game-stats"].find({ - userID: user.id, - }); - - const data: Partial> = {}; - - const settings = await db["user-settings"].findOne({ userID: user.id }); - - if (!settings) { - log.error(`User ${FormatUserDoc(user)} doesn't have any settings?`); - return res.status(500).json({ - success: false, - description: `This user has no settings.`, - }); - } - - await Promise.all( - gpts.map(async (e) => { - const userIDs: Array = [user.id]; - - if (includeRivals) { - const rivalIDs = await GetRivalIDs(user.id, e.game, e.playtype); - - userIDs.push(...rivalIDs); - } - - // n.b. it is intentional behaviour that you only get updates for - // people you follow on games you play. - if (includeFollowers) { - userIDs.push(...settings.following); - } - - const activity = await GetRecentActivity( - e.game, - { - game: e.game, - playtype: e.playtype, - userID: { $in: userIDs }, - }, - 30, - startTime, - ); - - data[`${e.game}:${e.playtype}` as GPTString] = activity; - }), - ); - - return res.status(200).json({ - success: true, - description: `Returned recent activity.`, - body: data, - }); - }, -); - -router.use("/games", gameSpecificRouter); -router.use("/games/:game/:playtype", gamePTRouter); -router.use("/pfp", pfpRouter); - -router.use("/banner", bannerRouter); -router.use("/integrations", integrationsRouter); -router.use("/settings", settingsRouter); -router.use("/api-tokens", apiTokensRouter); -router.use("/invites", invitesRouter); -router.use("/imports", importsRouter); -router.use("/notifications", notifsRouter); -router.use("/following", followingRouter); -router.use("/sessions", sessionsRouter); - -// Shims for discord functionality; discord checks that a url ends with ".png" -// to use as an image -router.get("/pfp.png", (req, res) => { - res.redirect("./pfp"); -}); -router.get("/banner.png", (req, res) => { - res.redirect("./banner"); -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/sessions/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/sessions/router.ts deleted file mode 100644 index da499dbab..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/sessions/router.ts +++ /dev/null @@ -1,40 +0,0 @@ -import db from "#services/mongo/db"; -import { GetUser } from "#utils/req-tachi-data"; -import { Router } from "express"; - -const router: Router = Router({ mergeParams: true }); - -/** - * Returns all sessions, FOR ALL GPTs - * but with unecessary properties removed so as to reduce - * bandwidth. This is used for the calendar view in tachi-client, hence the name. - * - * @name GET /api/v1/users/:userID/sessions/calendar - */ -router.get("/calendar", async (req, res) => { - const user = GetUser(req); - - const sessions = await db.sessions.find( - { userID: user.id }, - { - projection: { - sessionID: 1, - name: 1, - desc: 1, - highlight: 1, - timeStarted: 1, - timeEnded: 1, - game: 1, - playtype: 1, - }, - }, - ); - - return res.status(200).json({ - success: true, - description: `Found ${sessions.length} events.`, - body: sessions, - }); -}); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/settings/router.test.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/settings/router.test.ts deleted file mode 100644 index 8efd3937e..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/settings/router.test.ts +++ /dev/null @@ -1,205 +0,0 @@ -import type { UserDocument } from "tachi-common"; - -import db from "#services/mongo/db"; -import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; -import mockApi from "#test-utils/mock-api"; -import ResetDBState from "#test-utils/resets"; -import deepmerge from "deepmerge"; -import t from "tap"; - -t.test("GET /api/v1/users/:userID/settings", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should return the users settings.", async (t) => { - const res = await mockApi.get("/api/v1/users/1/settings"); - - t.strictSame(res.body.body, { - userID: 1, - preferences: { - invisible: false, - developerMode: true, - advancedMode: false, - contentiousContent: false, - deletableScores: false, - }, - }); - - t.end(); - }); - - t.end(); -}); - -t.test("PATCH /api/v1/users/:userID/settings", async (t) => { - t.beforeEach(ResetDBState); - - const cookie = await CreateFakeAuthCookie(mockApi); - - t.test("Should mutate the users settings.", async (t) => { - const res = await mockApi.patch("/api/v1/users/1/settings").set("Cookie", cookie).send({ - developerMode: false, - invisible: true, - }); - - t.strictSame(res.body.body, { - userID: 1, - preferences: { - invisible: true, - developerMode: false, - advancedMode: false, - contentiousContent: false, - deletableScores: false, - }, - }); - - const dbRes = await db["user-settings"].findOne({ userID: 1 }); - - t.strictSame(dbRes, { - userID: 1, - preferences: { - invisible: true, - developerMode: false, - advancedMode: false, - contentiousContent: false, - deletableScores: false, - }, - }); - - t.end(); - }); - - t.test("Should 400 if body is empty.", async (t) => { - const res = await mockApi.patch("/api/v1/users/1/settings").set("Cookie", cookie).send({}); - - t.equal(res.statusCode, 400); - - const dbRes = await db["user-settings"].findOne({ userID: 1 }); - - t.strictSame( - dbRes, - { - userID: 1, - preferences: { - invisible: false, - developerMode: true, - advancedMode: false, - contentiousContent: false, - deletableScores: false, - }, - }, - "User Settings should be unmodified.", - ); - - t.end(); - }); - - t.test("Should validate input.", async (t) => { - const res = await mockApi.patch("/api/v1/users/1/settings").set("Cookie", cookie).send({ - developerMode: "true", - }); - - t.equal(res.statusCode, 400); - - const res2 = await mockApi.patch("/api/v1/users/1/settings").set("Cookie", cookie).send({ - invalid_prop: true, - }); - - t.equal(res2.statusCode, 400); - - const res3 = await mockApi - .patch("/api/v1/users/1/settings") - .set("Cookie", cookie) - .send({ - invisible: { $where: "alert(1)" }, - }); - - t.equal(res3.statusCode, 400); - - t.end(); - }); - - t.test("Must be authenticated as that user.", async (t) => { - const user = await db.users.findOne({}); - - await db.users.insert( - deepmerge(user!, { - id: 2, - username: "something_else", - usernameLowercase: "something_else", - }) as UserDocument, - ); - - const res = await mockApi - .patch("/api/v1/users/2/settings") - - // this token is for user 1, not 2 - .set("Cookie", cookie) - .send({ - developerMode: false, - }); - - t.equal(res.statusCode, 403); - - const dbRes = await db["user-settings"].findOne({ userID: 1 }); - - t.equal( - dbRes?.preferences.developerMode, - true, - "Settings should not be modified in the database.", - ); - - t.end(); - }); - - t.test("Must not work with an API key.", async (t) => { - await db["api-tokens"].insert({ - identifier: "no perm", - permissions: { - // has relevant permissions - customise_profile: true, - }, - token: "no_perm", - userID: 1, - fromAPIClient: null, - }); - - const res = await mockApi - .patch("/api/v1/users/1/settings") - .set("Authorization", "Bearer no_perm") - .send({ - developerMode: false, - }); - - t.equal(res.statusCode, 403); - - const dbRes = await db["user-settings"].findOne({ userID: 1 }); - - t.equal( - dbRes?.preferences.developerMode, - true, - "Settings should not be modified in the database.", - ); - - t.end(); - }); - - t.test("Must be authenticated.", async (t) => { - const res = await mockApi.patch("/api/v1/users/1/settings").send({ - developerMode: false, - }); - - t.equal(res.statusCode, 401); - - const dbRes = await db["user-settings"].findOne({ userID: 1 }); - - t.equal( - dbRes?.preferences.developerMode, - true, - "Settings should not be modified in the database.", - ); - - t.end(); - }); - - t.end(); -}); diff --git a/typescript/server/src/server/router/api/v1postgres/users/_userID/settings/router.ts b/typescript/server/src/server/router/api/v1postgres/users/_userID/settings/router.ts deleted file mode 100644 index 2ccbd1dea..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/_userID/settings/router.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { log } from "#lib/log/log.js"; -import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; -import { GetTachiData } from "#utils/req-tachi-data"; -import { FormatUserDoc, GetSettingsForUser } from "#utils/user"; -import { Router } from "express"; - -import { RequireSelfRequestFromUser } from "../middleware"; - -const router: Router = Router({ mergeParams: true }); - -/** - * Retrieve this users settings. Note that these settings are NOT private. - * - * @name GET /api/v1/users/:userID/settings - */ -router.get("/", async (req, res) => { - const user = GetTachiData(req, "requestedUser"); - - const settings = await db["user-settings"].findOne({ - userID: user.id, - }); - - if (!settings) { - log.error(`User ${FormatUserDoc(user)} has no settings?`); - return res.status(500).json({ - success: false, - description: `An internal server error has occured.`, - }); - } - - return res.status(200).json({ - success: true, - description: `Retrieved settings.`, - body: settings, - }); -}); - -/** - * Update a user's settings. - * - * @param invisible - Whether to set the user to invisible or not. - * @param developerMode - Whether to display developer specific information in the WebUI. - * @param advancedMode - Whether to display more advanced options in the WebUI. - * @param contentiousContent - Whether to display slightly inappropriate splash messages. - * - * @name PATCH /api/v1/users/:userID/settings - */ -router.patch( - "/", - RequireSelfRequestFromUser, - prValidate({ - invisible: "*boolean", - developerMode: "*boolean", - contentiousContent: "*boolean", - advancedMode: "*boolean", - deletableScores: "*boolean", - }), - async (req, res) => { - const user = GetTachiData(req, "requestedUser"); - - const preferences = req.safeBody as { - advancedMode?: boolean; - contentiousContent?: boolean; - deletableScores?: boolean; - developerMode?: boolean; - invisible?: boolean; - }; - - if (Object.keys(preferences).length === 0) { - return res.status(400).json({ - success: false, - description: `Nothing was provided to change!`, - }); - } - - const modifyObject: Record = {}; - - for (const [k, v] of Object.entries(preferences)) { - modifyObject[`preferences.${k}`] = v; - } - - await db["user-settings"].update( - { - userID: user.id, - }, - { $set: modifyObject }, - ); - - const settings = await GetSettingsForUser(user.id); - - if (!settings) { - log.error( - { user }, - `User ${FormatUserDoc(user)} has no settings, yet just successfully updated them?`, - ); - return res.status(500).json({ - success: false, - description: `An internal server error has occured.`, - }); - } - - if (req.session.tachi?.settings) { - req.session.tachi.settings = settings; - } - - return res.status(200).json({ - success: true, - description: `Updated settings.`, - body: settings, - }); - }, -); - -export default router; diff --git a/typescript/server/src/server/router/api/v1postgres/users/router.ts b/typescript/server/src/server/router/api/v1postgres/users/router.ts deleted file mode 100644 index baf655d8d..000000000 --- a/typescript/server/src/server/router/api/v1postgres/users/router.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { SearchUsersRegExp } from "#lib/search/search"; -import db from "#services/mongo/db"; -import { IsString } from "#utils/misc"; -import { GetOnlineCutoff } from "#utils/user"; -import { Router } from "express"; - -import userIDRouter from "./_userID/router"; - -const router: Router = Router({ mergeParams: true }); - -/** - * Search users. - * - * @param online - Restrict returned users to those who are online. - * @param search - Search for users where their name contains this string. If not present, returns - * users sorted by last appearance. - * - * @name GET /api/v1/users - */ -router.get("/", async (req, res) => { - const onlyOnline = req.query.online !== undefined; - - let users; - - const search = req.query.search; - - if (search !== undefined) { - if (!IsString(search)) { - return res.status(400).json({ - success: false, - description: `Search parameter was invalid.`, - }); - } - - users = await SearchUsersRegExp(search, onlyOnline); - } else { - const query = onlyOnline ? { lastSeen: { $gt: GetOnlineCutoff() } } : {}; - - users = await db.users.find(query, { - sort: { lastSeen: -1 }, - limit: 100, - }); - } - - return res.status(200).json({ - success: true, - description: `Returned ${users.length} users.`, - body: users, - }); -}); - -router.use("/:userID", userIDRouter); - -export default router; diff --git a/typescript/server/src/server/router/ir/barbatos/router.test.ts b/typescript/server/src/server/router/ir/barbatos/router.oldtest.ts similarity index 94% rename from typescript/server/src/server/router/ir/barbatos/router.test.ts rename to typescript/server/src/server/router/ir/barbatos/router.oldtest.ts index badd3e74f..d61421f85 100644 --- a/typescript/server/src/server/router/ir/barbatos/router.test.ts +++ b/typescript/server/src/server/router/ir/barbatos/router.oldtest.ts @@ -1,4 +1,4 @@ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { InsertFakeTokenWithAllPerms } from "#test-utils/fake-auth"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; @@ -19,7 +19,7 @@ t.test("POST /ir/barbatos/score/submit", (t) => { t.equal(res.body.body.errors.length, 0, "Should have 0 failed scores."); - const scores = await db.scores.count({ + const scores = await MONGODB_KILL.scores.count({ service: "Barbatos (vivid)", }); diff --git a/typescript/server/src/server/router/ir/beatoraja/auth.test.ts b/typescript/server/src/server/router/ir/beatoraja/auth.oldtest.ts similarity index 100% rename from typescript/server/src/server/router/ir/beatoraja/auth.test.ts rename to typescript/server/src/server/router/ir/beatoraja/auth.oldtest.ts diff --git a/typescript/server/src/server/router/ir/beatoraja/charts/_chartSHA256/convert-scores.test.ts b/typescript/server/src/server/router/ir/beatoraja/charts/_chartSHA256/convert-scores.oldtest.ts similarity index 100% rename from typescript/server/src/server/router/ir/beatoraja/charts/_chartSHA256/convert-scores.test.ts rename to typescript/server/src/server/router/ir/beatoraja/charts/_chartSHA256/convert-scores.oldtest.ts diff --git a/typescript/server/src/server/router/ir/beatoraja/charts/_chartSHA256/router.test.ts b/typescript/server/src/server/router/ir/beatoraja/charts/_chartSHA256/router.oldtest.ts similarity index 91% rename from typescript/server/src/server/router/ir/beatoraja/charts/_chartSHA256/router.test.ts rename to typescript/server/src/server/router/ir/beatoraja/charts/_chartSHA256/router.oldtest.ts index 88aa9261b..e08d7a76f 100644 --- a/typescript/server/src/server/router/ir/beatoraja/charts/_chartSHA256/router.test.ts +++ b/typescript/server/src/server/router/ir/beatoraja/charts/_chartSHA256/router.oldtest.ts @@ -1,6 +1,6 @@ import type { PBScoreDocument, ScoreDocument } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; import t from "tap"; @@ -8,7 +8,7 @@ import t from "tap"; t.test("GET /ir/beatoraja/charts/:chartSHA256/scores", (t) => { t.beforeEach(ResetDBState); t.beforeEach(() => - db["api-tokens"].insert({ + MONGODB_KILL["api-tokens"].insert({ userID: 1, identifier: "Mock API Beatoraja Token", permissions: { @@ -24,7 +24,7 @@ t.test("GET /ir/beatoraja/charts/:chartSHA256/scores", (t) => { t.test("Should return PB scores on a chart", async (t) => { // very lazy fake scores - await db["personal-bests"].insert({ + await MONGODB_KILL["personal-bests"].insert({ composedFrom: [{ name: "Best Lamp", scoreID: "mock_lampPB" }], scoreData: { score: 1234, @@ -40,7 +40,7 @@ t.test("GET /ir/beatoraja/charts/:chartSHA256/scores", (t) => { userID: 1, } as unknown as PBScoreDocument); - await db.scores.insert({ + await MONGODB_KILL.scores.insert({ scoreID: "mock_lampPB", scoreMeta: { inputDevice: "BM_CONTROLLER", diff --git a/typescript/server/src/server/router/ir/beatoraja/charts/_chartSHA256/router.ts b/typescript/server/src/server/router/ir/beatoraja/charts/_chartSHA256/router.ts index 36f74d1f4..7d2224a55 100644 --- a/typescript/server/src/server/router/ir/beatoraja/charts/_chartSHA256/router.ts +++ b/typescript/server/src/server/router/ir/beatoraja/charts/_chartSHA256/router.ts @@ -2,7 +2,7 @@ import type { ChartDocument, integer, PBScoreDocument, UserDocument } from "tach import { SYMBOL_TACHI_API_AUTH } from "#lib/constants/tachi"; import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { AssignToReqTachiData, GetTachiData } from "#utils/req-tachi-data"; import { type RequestHandler, Router } from "express"; @@ -12,14 +12,14 @@ const router: Router = Router({ mergeParams: true }); const GetChartDocument: RequestHandler = async (req, res, next) => { let chart: ChartDocument<"bms:7K" | "bms:14K" | "pms:Controller" | "pms:Keyboard"> | null = - (await db.charts.bms.findOne({ + (await MONGODB_KILL.charts.bms.findOne({ "data.hashSHA256": req.params.chartSHA256, })) as ChartDocument<"bms:7K" | "bms:14K"> | null; // if we dont find the chart in bms, // it's probably a pms chart. if (!chart) { - chart = (await db.charts.pms.findOne({ + chart = (await MONGODB_KILL.charts.pms.findOne({ "data.hashSHA256": req.params.chartSHA256, })) as ChartDocument<"pms:Controller" | "pms:Keyboard"> | null; } @@ -48,11 +48,11 @@ router.get("/scores", async (req, res) => { const chart = GetTachiData(req, "beatorajaChartDoc"); const requestingUserID = req[SYMBOL_TACHI_API_AUTH].userID; - const scores = (await db["personal-bests"].find({ + const scores = (await MONGODB_KILL["personal-bests"].find({ chartID: chart.chartID, })) as Array>; - const userDocs = await db.users.find( + const userDocs = await MONGODB_KILL.users.find( { id: { $in: scores.map((e) => e.userID) }, }, diff --git a/typescript/server/src/server/router/ir/beatoraja/router.test.ts b/typescript/server/src/server/router/ir/beatoraja/router.oldtest.ts similarity index 94% rename from typescript/server/src/server/router/ir/beatoraja/router.test.ts rename to typescript/server/src/server/router/ir/beatoraja/router.oldtest.ts index 2ab657e60..4051ee835 100644 --- a/typescript/server/src/server/router/ir/beatoraja/router.test.ts +++ b/typescript/server/src/server/router/ir/beatoraja/router.oldtest.ts @@ -1,6 +1,6 @@ import type { UserDocument } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; import { MockBeatorajaBMSScore, MockBeatorajaPMSScore } from "#test-utils/test-data"; @@ -13,7 +13,7 @@ const NEW_MD5 = "d0f497c0f955e7edfb0278f446cdb6f8"; t.test("POST /ir/beatoraja/submit-score", (t) => { t.beforeEach(ResetDBState); t.beforeEach(() => - db["api-tokens"].insert({ + MONGODB_KILL["api-tokens"].insert({ userID: 1, identifier: "Mock API Beatoraja Token", permissions: { @@ -50,7 +50,7 @@ t.test("POST /ir/beatoraja/submit-score", (t) => { }, }); - const score = await db.scores.findOne( + const score = await MONGODB_KILL.scores.findOne( { scoreID: res.body.body.score.scoreID }, { projection: { _id: 0 } }, ); @@ -90,7 +90,7 @@ t.test("POST /ir/beatoraja/submit-score", (t) => { }, }); - const score = await db.scores.findOne( + const score = await MONGODB_KILL.scores.findOne( { scoreID: res.body.body.score.scoreID }, { projection: { _id: 0 } }, ); @@ -129,7 +129,7 @@ t.test("POST /ir/beatoraja/submit-score", (t) => { }, }); - const score = await db.scores.findOne( + const score = await MONGODB_KILL.scores.findOne( { scoreID: res.body.body.score.scoreID }, { projection: { _id: 0 } }, ); @@ -233,7 +233,7 @@ t.test("POST /ir/beatoraja/submit-score", (t) => { t.equal(res.body.success, true); t.match(res.body.description, /Chart and score have been orphaned/u); - const orphanChart = await db["orphan-chart-queue"].findOne({ + const orphanChart = await MONGODB_KILL["orphan-chart-queue"].findOne({ "chartDoc.data.hashSHA256": NEW_SHA256, }); @@ -248,7 +248,7 @@ t.test("POST /ir/beatoraja/submit-score", (t) => { }); t.test("Should eventually unorphan a chart.", async (t) => { - await db["api-tokens"].insert([ + await MONGODB_KILL["api-tokens"].insert([ { userID: 2, identifier: "token2", @@ -265,7 +265,7 @@ t.test("POST /ir/beatoraja/submit-score", (t) => { }, ]); - await db.users.insert([ + await MONGODB_KILL.users.insert([ { id: 2, username: "foo", @@ -304,7 +304,7 @@ t.test("POST /ir/beatoraja/submit-score", (t) => { t.equal(res2.statusCode, 202); - const orphanData = await db["orphan-chart-queue"].findOne({ + const orphanData = await MONGODB_KILL["orphan-chart-queue"].findOne({ "chartDoc.data.hashSHA256": NEW_SHA256, }); @@ -323,13 +323,13 @@ t.test("POST /ir/beatoraja/submit-score", (t) => { t.equal(res3.statusCode, 200); - const orphanData2 = await db["orphan-chart-queue"].findOne({ + const orphanData2 = await MONGODB_KILL["orphan-chart-queue"].findOne({ "chartDoc.data.hashSHA256": NEW_SHA256, }); t.equal(orphanData2, null, "Orphan data should be removed from the database."); - const score = await db.scores.findOne({ + const score = await MONGODB_KILL.scores.findOne({ game: "bms", userID: 3, }); @@ -427,7 +427,7 @@ const courseScore = { t.test("POST /ir/beatoraja/submit-course", (t) => { t.beforeEach(ResetDBState); t.beforeEach(() => - db["api-tokens"].insert({ + MONGODB_KILL["api-tokens"].insert({ userID: 1, identifier: "Mock API Beatoraja Token", permissions: { @@ -449,7 +449,11 @@ t.test("POST /ir/beatoraja/submit-course", (t) => { t.equal(res.body.success, true); t.equal(res.body.description, "Successfully updated class."); - const ugs = await db["game-stats"].findOne({ userID: 1, game: "bms", playtype: "7K" }); + const ugs = await MONGODB_KILL["game-stats"].findOne({ + userID: 1, + game: "bms", + playtype: "7K", + }); t.equal(ugs?.classes.genocideDan, "OVERJOY", "Should set their dan to overjoy."); diff --git a/typescript/server/src/server/router/ir/beatoraja/router.ts b/typescript/server/src/server/router/ir/beatoraja/router.ts index b5ed12f8f..c0f01b325 100644 --- a/typescript/server/src/server/router/ir/beatoraja/router.ts +++ b/typescript/server/src/server/router/ir/beatoraja/router.ts @@ -10,7 +10,7 @@ import { ExpressWrappedScoreImportMain } from "#lib/score-import/framework/expre import { ServerConfig } from "#lib/setup/config"; import { RequireNotGuest } from "#server/middleware/auth"; import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { UpdateClassIfGreater } from "#utils/class"; import { IsRecord, NotNullish } from "#utils/misc"; import { Router } from "express"; @@ -48,7 +48,7 @@ router.post("/submit-score", RequireNotGuest, async (req, res) => { if (type === "SongOrChartNotFound") { const { chart } = req.safeBody as { chart: BeatorajaChart }; - const orphanInfo: { userIDs: Array } | null = await db[ + const orphanInfo: { userIDs: Array } | null = await MONGODB_KILL[ "orphan-chart-queue" ].findOne( { @@ -97,7 +97,7 @@ router.post("/submit-score", RequireNotGuest, async (req, res) => { }); } - const scoreDoc = await db.scores.findOne({ + const scoreDoc = await MONGODB_KILL.scores.findOne({ scoreID: importRes.body.body.scoreIDs[0], }); @@ -115,7 +115,7 @@ router.post("/submit-score", RequireNotGuest, async (req, res) => { let chart; if (importRes.body.body.game === "bms") { - chart = await db.charts.bms.findOne({ + chart = await MONGODB_KILL.charts.bms.findOne({ chartID: scoreDoc.chartID, }); @@ -130,11 +130,11 @@ router.post("/submit-score", RequireNotGuest, async (req, res) => { }); } - song = await db.songs.bms.findOne({ + song = await MONGODB_KILL.songs.bms.findOne({ id: chart.songID, }); } else { - chart = await db.charts.pms.findOne({ + chart = await MONGODB_KILL.charts.pms.findOne({ chartID: scoreDoc.chartID, }); @@ -149,7 +149,7 @@ router.post("/submit-score", RequireNotGuest, async (req, res) => { }); } - song = await db.songs.pms.findOne({ + song = await MONGODB_KILL.songs.pms.findOne({ id: chart.songID, }); } @@ -279,7 +279,7 @@ router.post( // Combine the md5s into one string in their order. const combinedMD5s = charts.map((e) => e.md5).join(""); - const course = await db["bms-course-lookup"].findOne({ + const course = await MONGODB_KILL["bms-course-lookup"].findOne({ md5sums: combinedMD5s, }); diff --git a/typescript/server/src/server/router/ir/direct-manual/router.test.ts b/typescript/server/src/server/router/ir/direct-manual/router.oldtest.ts similarity index 95% rename from typescript/server/src/server/router/ir/direct-manual/router.test.ts rename to typescript/server/src/server/router/ir/direct-manual/router.oldtest.ts index ac93120c5..c51b6ff8e 100644 --- a/typescript/server/src/server/router/ir/direct-manual/router.test.ts +++ b/typescript/server/src/server/router/ir/direct-manual/router.oldtest.ts @@ -1,6 +1,6 @@ import type { BatchManual, BatchManualScore } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { CreateFakeAuthCookie } from "#test-utils/fake-auth"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; @@ -18,7 +18,7 @@ t.test("POST /ir/direct-manual/import", async (t) => { t.beforeEach(ResetDBState); t.test("Should require submit_score permissions", async (t) => { - await db["api-tokens"].insert({ + await MONGODB_KILL["api-tokens"].insert({ token: "foo", identifier: "bar", permissions: { @@ -49,7 +49,7 @@ t.test("POST /ir/direct-manual/import", async (t) => { t.equal(res.body.body.errors.length, 0, "Should have 0 failed scores."); - const scoreCount = await db.scores.count({ service: "foobar (DIRECT-MANUAL)" }); + const scoreCount = await MONGODB_KILL.scores.count({ service: "foobar (DIRECT-MANUAL)" }); t.equal(scoreCount, 1, "Should import one score."); @@ -84,7 +84,7 @@ t.test("POST /ir/direct-manual/import", async (t) => { }); t.beforeEach(() => - db["api-tokens"].insert({ + MONGODB_KILL["api-tokens"].insert({ token: "mock_token", fromAPIClient: null, identifier: "Mock CHUNITACHI Token", @@ -103,7 +103,9 @@ t.test("POST /ir/direct-manual/import", async (t) => { t.equal(res.body.body.errors.length, 0, "Should have 0 failed scores."); - const scoreCount = await db.scores.count({ service: "ChunItachi (DIRECT-MANUAL)" }); + const scoreCount = await MONGODB_KILL.scores.count({ + service: "ChunItachi (DIRECT-MANUAL)", + }); t.equal(scoreCount, 1, "Should import one score."); diff --git a/typescript/server/src/server/router/ir/fervidex/router.test.ts b/typescript/server/src/server/router/ir/fervidex/router.oldtest.ts similarity index 90% rename from typescript/server/src/server/router/ir/fervidex/router.test.ts rename to typescript/server/src/server/router/ir/fervidex/router.oldtest.ts index 8bba6d786..d3bf40d22 100644 --- a/typescript/server/src/server/router/ir/fervidex/router.test.ts +++ b/typescript/server/src/server/router/ir/fervidex/router.oldtest.ts @@ -1,6 +1,6 @@ import type { ChartDocument, GPTStrings, SongDocument } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { InsertFakeTokenWithAllPerms } from "#test-utils/fake-auth"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; @@ -17,8 +17,8 @@ import t from "tap"; // eslint-disable-next-line @typescript-eslint/no-explicit-any function TestHeaders(url: string, data: any) { t.test("Should validate against card filters", async (t) => { - await db["fer-settings"].remove({}); - await db["fer-settings"].insert({ + await MONGODB_KILL["fer-settings"].remove({}); + await MONGODB_KILL["fer-settings"].insert({ userID: 1, cards: ["foo"], forceStaticImport: false, @@ -51,8 +51,8 @@ function TestHeaders(url: string, data: any) { }); t.test("Should allow certain models", async (t) => { - await db["fer-settings"].remove({}); - await db["fer-settings"].insert({ + await MONGODB_KILL["fer-settings"].remove({}); + await MONGODB_KILL["fer-settings"].insert({ userID: 1, cards: null, forceStaticImport: false, @@ -83,15 +83,15 @@ function TestHeaders(url: string, data: any) { t.equal(res.body.success, true, `Should allow ${name} clients`); - await db["api-tokens"].remove({ key: token }); + await MONGODB_KILL["api-tokens"].remove({ key: token }); } t.end(); }); t.only("Should allow 30-omni", async (t) => { - await db["fer-settings"].remove({}); - await db["fer-settings"].insert({ + await MONGODB_KILL["fer-settings"].remove({}); + await MONGODB_KILL["fer-settings"].insert({ userID: 1, cards: null, forceStaticImport: false, @@ -256,11 +256,15 @@ t.test("POST /ir/fervidex/class/submit", (t) => { t.equal(res.body.success, true); t.equal(res.body.description, "Dan changed!"); - const ugs = await db["game-stats"].findOne({ userID: 1, game: "iidx", playtype: "SP" }); + const ugs = await MONGODB_KILL["game-stats"].findOne({ + userID: 1, + game: "iidx", + playtype: "SP", + }); t.equal(ugs?.classes.dan, "KAIDEN"); - const recentAchievement = await db["class-achievements"].findOne({ + const recentAchievement = await MONGODB_KILL["class-achievements"].findOne({ userID: 1, game: "iidx", playtype: "SP", @@ -290,7 +294,11 @@ t.test("POST /ir/fervidex/class/submit", (t) => { t.equal(res.body.success, true); t.equal(res.body.description, "Dan changed!"); - const ugs = await db["game-stats"].findOne({ userID: 1, game: "iidx", playtype: "DP" }); + const ugs = await MONGODB_KILL["game-stats"].findOne({ + userID: 1, + game: "iidx", + playtype: "DP", + }); t.equal(ugs?.classes.dan, "CHUUDEN"); @@ -391,7 +399,7 @@ t.test("POST /ir/fervidex/score/submit", (t) => { t.equal(res.body.body.errors.length, 0, "Should have 0 failed scores."); - const scores = await db.scores.count({ + const scores = await MONGODB_KILL.scores.count({ service: "Fervidex", }); @@ -413,7 +421,7 @@ t.test("POST /ir/fervidex/score/submit", (t) => { t.equal(res.body.body.errors.length, 0, "Should have 0 failed scores."); - const scores = await db.scores.count({ + const scores = await MONGODB_KILL.scores.count({ service: "Fervidex", }); @@ -434,7 +442,7 @@ t.test("POST /ir/fervidex/score/submit", (t) => { t.equal(res.body.body.errors.length, 0, "Should have 0 failed scores."); - const scores = await db.scores.count({ + const scores = await MONGODB_KILL.scores.count({ service: "Fervidex", }); @@ -474,7 +482,7 @@ t.test("POST /ir/fervidex/score/submit", (t) => { await Sleep(2000); - const imp = await db.imports.findOne({ + const imp = await MONGODB_KILL.imports.findOne({ importID: res.body.body.importID, }); @@ -503,7 +511,7 @@ Expected PGreat*2 + Great to equal EX score. Got 34*2 + 0 but that wasn't equal await Sleep(2000); - const imp = await db.imports.findOne({ + const imp = await MONGODB_KILL.imports.findOne({ importID: res.body.body.importID, }); @@ -527,12 +535,12 @@ t.test("POST /ir/fervidex/profile/submit", (t) => { TestHeaders("/ir/fervidex/profile/submit", FervidexStaticBase); t.test("Should accept a fervidex-static body", async (t) => { - await db.songs.iidx.remove({}); - await db.songs.iidx.insert( + await MONGODB_KILL.songs.iidx.remove({}); + await MONGODB_KILL.songs.iidx.insert( GetKTDataJSON("./tachi/tachi-songs-iidx.json") as Array>, ); - await db.charts.iidx.remove({}); - await db.charts.iidx.insert( + await MONGODB_KILL.charts.iidx.remove({}); + await MONGODB_KILL.charts.iidx.insert( GetKTDataJSON("./tachi/tachi-charts-iidx.json") as Array< ChartDocument >, @@ -552,13 +560,13 @@ t.test("POST /ir/fervidex/profile/submit", (t) => { // Then we can check whether the database has properly updated. await Sleep(5_000); - const scores = await db.scores.count({ + const scores = await MONGODB_KILL.scores.count({ service: "Fervidex Static", }); t.equal(scores, 3, "Should import 3 scores."); - const ugs = await db["game-stats"].findOne({ + const ugs = await MONGODB_KILL["game-stats"].findOne({ userID: 1, game: "iidx", playtype: "SP", @@ -570,18 +578,18 @@ t.test("POST /ir/fervidex/profile/submit", (t) => { }); t.test("Should allow requests from non INF2 if forceStaticImport is true.", async (t) => { - await db.songs.iidx.remove({}); - await db.songs.iidx.insert( + await MONGODB_KILL.songs.iidx.remove({}); + await MONGODB_KILL.songs.iidx.insert( GetKTDataJSON("./tachi/tachi-songs-iidx.json") as Array>, ); - await db.charts.iidx.remove({}); - await db.charts.iidx.insert( + await MONGODB_KILL.charts.iidx.remove({}); + await MONGODB_KILL.charts.iidx.insert( GetKTDataJSON("./tachi/tachi-charts-iidx.json") as Array< ChartDocument >, ); - await db["fer-settings"].update( + await MONGODB_KILL["fer-settings"].update( { userID: 1 }, { $set: { forceStaticImport: true }, @@ -600,13 +608,13 @@ t.test("POST /ir/fervidex/profile/submit", (t) => { // See above. await Sleep(5_000); - const scores = await db.scores.count({ + const scores = await MONGODB_KILL.scores.count({ service: "Fervidex Static", }); t.equal(scores, 3, "Should import 3 scores."); - const ugs = await db["game-stats"].findOne({ + const ugs = await MONGODB_KILL["game-stats"].findOne({ userID: 1, game: "iidx", playtype: "SP", @@ -614,7 +622,7 @@ t.test("POST /ir/fervidex/profile/submit", (t) => { t.equal(ugs!.classes.dan, "DAN_9", "Should successfully update dan to 9th."); - const dbRes = await db["fer-settings"].findOne({ userID: 1 }); + const dbRes = await MONGODB_KILL["fer-settings"].findOne({ userID: 1 }); t.equal( dbRes?.forceStaticImport, @@ -626,7 +634,10 @@ t.test("POST /ir/fervidex/profile/submit", (t) => { }); t.test("Should allow requests from non INF2 if forceStaticImport is false.", async (t) => { - await db["fer-settings"].update({ userID: 1 }, { $set: { forceStaticImport: false } }); + await MONGODB_KILL["fer-settings"].update( + { userID: 1 }, + { $set: { forceStaticImport: false } }, + ); const res = await mockApi .post("/ir/fervidex/profile/submit") @@ -637,7 +648,7 @@ t.test("POST /ir/fervidex/profile/submit", (t) => { t.equal(res.statusCode, 202, "Should be allowed, but no scores should be imported"); - const scoresNow = await db.scores.count({ + const scoresNow = await MONGODB_KILL.scores.count({ service: "Fervidex Static", }); diff --git a/typescript/server/src/server/router/ir/fervidex/router.ts b/typescript/server/src/server/router/ir/fervidex/router.ts index e9290799b..7a3c51326 100644 --- a/typescript/server/src/server/router/ir/fervidex/router.ts +++ b/typescript/server/src/server/router/ir/fervidex/router.ts @@ -5,7 +5,7 @@ import { ExpressWrappedScoreImportMain } from "#lib/score-import/framework/expre import { SoftwareIDToVersion } from "#lib/score-import/import-types/ir/fervidex/parser"; import { RequirePermissions } from "#server/middleware/auth"; import { PrudenceErrorFormatter } from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { UpdateClassIfGreater } from "#utils/class"; import { ParseEA3SoftID } from "#utils/ea3id"; import { IsNullishOrEmptyStr } from "#utils/misc"; @@ -125,7 +125,7 @@ const ValidateModelHeader: RequestHandler = (req, res, next) => { const ValidateCards: RequestHandler = async (req, res, next) => { const userID = req[SYMBOL_TACHI_API_AUTH].userID!; - const cardFilters = await db["fer-settings"].findOne({ userID }); + const cardFilters = await MONGODB_KILL["fer-settings"].findOne({ userID }); if (!cardFilters?.cards) { next(); @@ -159,7 +159,7 @@ router.use( ); async function ShouldImportScoresFromProfileSubmit(swModel: string, userID: integer) { - const settings = await db["fer-settings"].findOne({ + const settings = await MONGODB_KILL["fer-settings"].findOne({ userID, }); @@ -171,7 +171,7 @@ async function ShouldImportScoresFromProfileSubmit(swModel: string, userID: inte // people want. // FSI should ideally just be used once to get unreachable scores onto Kamaitachi. Otherwise // they're doing something wrong. - await db["fer-settings"].update( + await MONGODB_KILL["fer-settings"].update( { userID: settings.userID, }, diff --git a/typescript/server/src/server/router/ir/kshook/sv6c/router.test.ts b/typescript/server/src/server/router/ir/kshook/sv6c/router.oldtest.ts similarity index 96% rename from typescript/server/src/server/router/ir/kshook/sv6c/router.test.ts rename to typescript/server/src/server/router/ir/kshook/sv6c/router.oldtest.ts index f622e3a1c..18b7ee941 100644 --- a/typescript/server/src/server/router/ir/kshook/sv6c/router.test.ts +++ b/typescript/server/src/server/router/ir/kshook/sv6c/router.oldtest.ts @@ -1,4 +1,4 @@ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { InsertFakeTokenWithAllPerms } from "#test-utils/fake-auth"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; @@ -10,7 +10,7 @@ import t from "tap"; t.test("POST /ir/kshook/sv6c/score/save", (t) => { t.beforeEach(ResetDBState); t.beforeEach(() => - db["api-tokens"].insert({ + MONGODB_KILL["api-tokens"].insert({ userID: 1, identifier: "Mock API Token", permissions: ALL_PERMISSIONS, @@ -121,7 +121,7 @@ t.test("POST /ir/kshook/sv6c/score/save", (t) => { t.skip("POST /ir/kshook/sv6c/score/export", (t) => { t.beforeEach(ResetDBState); t.beforeEach(() => - db["api-tokens"].insert({ + MONGODB_KILL["api-tokens"].insert({ userID: 1, identifier: "Mock API Token", permissions: ALL_PERMISSIONS, @@ -147,7 +147,7 @@ t.skip("POST /ir/kshook/sv6c/score/export", (t) => { t.equal(res.body.body.errors.length, 0, "Should have 0 failed scores."); t.equal(res.body.body.userIntent, false, "Should not have user intent."); - const dbRes = await db["kshook-sv6c-settings"].findOne({ + const dbRes = await MONGODB_KILL["kshook-sv6c-settings"].findOne({ userID: 1, }); @@ -183,7 +183,7 @@ t.skip("POST /ir/kshook/sv6c/score/export", (t) => { }); t.test("Should check force-static-import status.", async (t) => { - await db["kshook-sv6c-settings"].update( + await MONGODB_KILL["kshook-sv6c-settings"].update( { userID: 1 }, { $set: { forceStaticImport: false } }, ); diff --git a/typescript/server/src/server/router/ir/kshook/sv6c/router.ts b/typescript/server/src/server/router/ir/kshook/sv6c/router.ts index 4538adf60..83599a86b 100644 --- a/typescript/server/src/server/router/ir/kshook/sv6c/router.ts +++ b/typescript/server/src/server/router/ir/kshook/sv6c/router.ts @@ -2,7 +2,7 @@ import { MODEL_SDVX3_KONASTE } from "#lib/constants/ea3id"; import { SYMBOL_TACHI_API_AUTH } from "#lib/constants/tachi"; import { log } from "#lib/log/log.js"; import { ExpressWrappedScoreImportMain } from "#lib/score-import/framework/express-wrapper"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { ParseEA3SoftID } from "#utils/ea3id"; import { IsNullishOrEmptyStr } from "#utils/misc"; import { type RequestHandler, Router } from "express"; @@ -105,7 +105,7 @@ router.post("/score/save", async (req, res) => { router.post("/score/export", async (req, res) => { const userID = req[SYMBOL_TACHI_API_AUTH].userID!; - const settings = await db["kshook-sv6c-settings"].findOne({ userID }); + const settings = await MONGODB_KILL["kshook-sv6c-settings"].findOne({ userID }); if (!settings?.forceStaticImport) { return res.status(200).json({ @@ -115,7 +115,7 @@ router.post("/score/export", async (req, res) => { }); } - await db["kshook-sv6c-settings"].update( + await MONGODB_KILL["kshook-sv6c-settings"].update( { userID }, { $set: { forceStaticImport: false }, diff --git a/typescript/server/src/server/router/ir/lr2hook/router.test.ts b/typescript/server/src/server/router/ir/lr2hook/router.oldtest.ts similarity index 93% rename from typescript/server/src/server/router/ir/lr2hook/router.test.ts rename to typescript/server/src/server/router/ir/lr2hook/router.oldtest.ts index 11153f403..d212fb484 100644 --- a/typescript/server/src/server/router/ir/lr2hook/router.test.ts +++ b/typescript/server/src/server/router/ir/lr2hook/router.oldtest.ts @@ -1,4 +1,4 @@ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import mockApi from "#test-utils/mock-api"; import ResetDBState from "#test-utils/resets"; import { TestingLR2HookScore } from "#test-utils/test-data"; @@ -9,7 +9,7 @@ import t from "tap"; t.test("POST /ir/lr2hook/import", (t) => { t.beforeEach(ResetDBState); t.beforeEach(async () => { - await db["api-tokens"].insert({ + await MONGODB_KILL["api-tokens"].insert({ token: "foo", permissions: { submit_score: true }, fromAPIClient: null, @@ -30,7 +30,7 @@ t.test("POST /ir/lr2hook/import", (t) => { }); t.test("Should import a score if the API token has the right permissions.", async (t) => { - await db["api-tokens"].insert({ + await MONGODB_KILL["api-tokens"].insert({ token: "bar", permissions: { submit_score: false }, fromAPIClient: null, @@ -104,7 +104,7 @@ t.test("POST /ir/lr2hook/import/course", (t) => { t.equal(res.body.body.set, "genocideDan"); t.equal(res.body.body.value, "OVERJOY"); - const dbRes = await db["game-stats"].findOne({ + const dbRes = await MONGODB_KILL["game-stats"].findOne({ userID: 1, game: "bms", playtype: "7K", @@ -124,7 +124,7 @@ t.test("POST /ir/lr2hook/import/course", (t) => { t.equal(res.statusCode, 200); t.match(res.body.description, "Class not updated. You failed to clear this course."); - const dbRes = await db["game-stats"].findOne({ + const dbRes = await MONGODB_KILL["game-stats"].findOne({ userID: 1, game: "bms", playtype: "7K", diff --git a/typescript/server/src/server/router/ir/lr2hook/router.ts b/typescript/server/src/server/router/ir/lr2hook/router.ts index 859c0fb28..2cf6142cb 100644 --- a/typescript/server/src/server/router/ir/lr2hook/router.ts +++ b/typescript/server/src/server/router/ir/lr2hook/router.ts @@ -5,7 +5,7 @@ import { ExpressWrappedScoreImportMain } from "#lib/score-import/framework/expre import { PR_LR2HOOK as PR_LR2_HOOK } from "#lib/score-import/import-types/ir/lr2hook/parser"; import { RequirePermissions } from "#server/middleware/auth"; import prValidate from "#server/middleware/prudence-validate"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { UpdateClassIfGreater } from "#utils/class"; import { Router } from "express"; @@ -56,7 +56,7 @@ router.post( }); } - const course = await db["bms-course-lookup"].findOne({ + const course = await MONGODB_KILL["bms-course-lookup"].findOne({ md5sums: score.md5, }); diff --git a/typescript/server/src/server/router/ir/usc/_playtype/router.test.ts b/typescript/server/src/server/router/ir/usc/_playtype/router.oldtest.ts similarity index 92% rename from typescript/server/src/server/router/ir/usc/_playtype/router.test.ts rename to typescript/server/src/server/router/ir/usc/_playtype/router.oldtest.ts index 96347a6d8..fa8bfd55a 100644 --- a/typescript/server/src/server/router/ir/usc/_playtype/router.test.ts +++ b/typescript/server/src/server/router/ir/usc/_playtype/router.oldtest.ts @@ -1,7 +1,7 @@ import type { PBScoreDocument, ScoreDocument, UserDocument } from "tachi-common"; import { CDNRetrieve } from "#lib/cdn/cdn"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { dmf } from "#test-utils/misc.js"; import mockApi from "#test-utils/mock-api"; import ResetDBState, { ResetCDN } from "#test-utils/resets"; @@ -9,7 +9,7 @@ import { GetKTDataBuffer } from "#test-utils/test-data"; import t from "tap"; async function InsertFakeUSCAuth() { - await db["api-tokens"].insert({ + await MONGODB_KILL["api-tokens"].insert({ userID: 1, identifier: "USC Token", permissions: { @@ -44,7 +44,7 @@ function TestAuth(url: string) { t.test("GET /ir/usc/Keyboard", async (t) => { t.beforeEach(ResetDBState); - await db["api-tokens"].insert({ + await MONGODB_KILL["api-tokens"].insert({ userID: 1, identifier: "USC Token", permissions: { @@ -74,7 +74,7 @@ t.test("GET /ir/usc/Keyboard", async (t) => { t.test("GET /ir/usc/Controller", async (t) => { t.beforeEach(ResetDBState); - await db["api-tokens"].insert({ + await MONGODB_KILL["api-tokens"].insert({ userID: 1, identifier: "USC Token", permissions: { @@ -200,7 +200,7 @@ t.test("GET /ir/usc/Controller/:chartHash/record", (t) => { }); t.test("Should return 20 and the Server Record.", async (t) => { - await db["personal-bests"].insert([ + await MONGODB_KILL["personal-bests"].insert([ // empty deepmerge is because monk monkey-patches _id on, // which means this crashes if you try to re-insert this document. dmf(USC_SCORE_PB, {}), @@ -208,7 +208,7 @@ t.test("GET /ir/usc/Controller/:chartHash/record", (t) => { ]); // hack for referencing - await db.scores.insert({ + await MONGODB_KILL.scores.insert({ scoreID: "usc_score_pb", scoreMeta: { noteMod: "NORMAL", gaugeMod: "HARD" }, } as ScoreDocument); @@ -300,7 +300,7 @@ t.test("GET /charts/:chartHash/leaderboard", (t) => { }); t.test("Should return scorePBs for mode = best", async (t) => { - await db["personal-bests"].insert([ + await MONGODB_KILL["personal-bests"].insert([ dmf(USC_SCORE_PB, {}), dmf(USC_SCORE_PB, { userID: 2, @@ -312,13 +312,13 @@ t.test("GET /charts/:chartHash/leaderboard", (t) => { }), ]); - await db.users.insert({ + await MONGODB_KILL.users.insert({ id: 2, username: "not_zkldi", usernameLowercase: "not_zkldi", } as UserDocument); - await db.scores.insert([ + await MONGODB_KILL.scores.insert([ { scoreID: "usc_score_pb", scoreMeta: { noteMod: "NORMAL", gaugeMod: "HARD" }, @@ -401,7 +401,7 @@ t.test("POST /replays", (t) => { t.beforeEach(InsertFakeUSCAuth); t.test("Should successfully upload a file where an identifier matches", async (t) => { - await db.scores.insert({ + await MONGODB_KILL.scores.insert({ game: "usc", userID: 1, scoreID: "MOCK_IDENTIFIER", @@ -431,7 +431,7 @@ t.test("POST /replays", (t) => { }); t.test("Should reject a request with no identifier", async (t) => { - await db.scores.insert({ + await MONGODB_KILL.scores.insert({ game: "usc", userID: 1, scoreID: "MOCK_IDENTIFIER", @@ -455,7 +455,7 @@ t.test("POST /replays", (t) => { }); t.test("Should reject a request with no file", async (t) => { - await db.scores.insert({ + await MONGODB_KILL.scores.insert({ game: "usc", userID: 1, scoreID: "MOCK_IDENTIFIER", @@ -481,7 +481,7 @@ t.test("POST /replays", (t) => { }); t.test("Should reject a request with an invalid identifier", async (t) => { - await db.scores.insert({ + await MONGODB_KILL.scores.insert({ game: "usc", userID: 1, scoreID: "MOCK_IDENTIFIER", @@ -511,7 +511,7 @@ t.test("POST /replays", (t) => { t.test("POST /scores", (t) => { t.beforeEach(ResetDBState); t.beforeEach(async () => { - await db["api-tokens"].insert({ + await MONGODB_KILL["api-tokens"].insert({ userID: 1, identifier: "token", permissions: { submit_score: true }, @@ -565,7 +565,7 @@ t.test("POST /scores", (t) => { t.equal(res.body.statusCode, 20); - const dbScore = await db.scores.findOne({ + const dbScore = await MONGODB_KILL.scores.findOne({ game: "usc", }); @@ -603,13 +603,13 @@ t.test("POST /scores", (t) => { "Should contain (required/outOf) in the response description.", ); - const dbScore = await db.scores.findOne({ + const dbScore = await MONGODB_KILL.scores.findOne({ game: "usc", }); t.equal(dbScore, null, "Should not exist in db."); - const orphanData = await db["orphan-chart-queue"].findOne({ + const orphanData = await MONGODB_KILL["orphan-chart-queue"].findOne({ "chartDoc.data.hashSHA1": "NEW_CHART", }); @@ -639,7 +639,7 @@ t.test("POST /scores", (t) => { }); t.skip("Should unorphan charts on their Nth unique user.", async (t) => { - await db["api-tokens"].insert([ + await MONGODB_KILL["api-tokens"].insert([ { userID: 2, identifier: "token2", @@ -656,7 +656,7 @@ t.test("POST /scores", (t) => { }, ]); - await db.users.insert([ + await MONGODB_KILL.users.insert([ { id: 2, username: "foo", @@ -695,7 +695,7 @@ t.test("POST /scores", (t) => { t.equal(res2.body.statusCode, 22); - const orphanData = await db["orphan-chart-queue"].findOne({ + const orphanData = await MONGODB_KILL["orphan-chart-queue"].findOne({ "chartDoc.data.hashSHA1": "NEW_CHART", }); @@ -714,13 +714,13 @@ t.test("POST /scores", (t) => { t.equal(res3.body.statusCode, 20); - const orphanData2 = await db["orphan-chart-queue"].findOne({ + const orphanData2 = await MONGODB_KILL["orphan-chart-queue"].findOne({ "chartDoc.data.hashSHA1": "NEW_CHART", }); t.equal(orphanData2, null, "Orphan data should be removed from the database."); - const score = await db.scores.findOne({ + const score = await MONGODB_KILL.scores.findOne({ game: "usc", userID: 3, }); @@ -740,7 +740,7 @@ t.test("POST /scores", (t) => { }); t.skip("Should maintain separate orphan queues for the separate playtypes.", async (t) => { - await db["api-tokens"].insert([ + await MONGODB_KILL["api-tokens"].insert([ { userID: 2, identifier: "token2", @@ -764,7 +764,7 @@ t.test("POST /scores", (t) => { }, ]); - await db.users.insert([ + await MONGODB_KILL.users.insert([ { id: 2, username: "foo", @@ -808,7 +808,7 @@ t.test("POST /scores", (t) => { t.equal(res2.body.statusCode, 22); - const orphanData = await db["orphan-chart-queue"].findOne({ + const orphanData = await MONGODB_KILL["orphan-chart-queue"].findOne({ "chartDoc.data.hashSHA1": "NEW_CHART", gptString: "usc:Controller", }); @@ -828,7 +828,7 @@ t.test("POST /scores", (t) => { t.equal(res3.body.statusCode, 22); - const orphanData2 = await db["orphan-chart-queue"].findOne({ + const orphanData2 = await MONGODB_KILL["orphan-chart-queue"].findOne({ "chartDoc.data.hashSHA1": "NEW_CHART", gptString: "usc:Controller", }); @@ -852,14 +852,14 @@ t.test("POST /scores", (t) => { t.equal(res4.body.statusCode, 20); - const orphanData3 = await db["orphan-chart-queue"].findOne({ + const orphanData3 = await MONGODB_KILL["orphan-chart-queue"].findOne({ "chartDoc.data.hashSHA1": "NEW_CHART", gptString: "usc:Controller", }); t.equal(orphanData3, null, "Should have removed the orphan chart from the database."); - const score = await db.scores.findOne({ + const score = await MONGODB_KILL.scores.findOne({ game: "usc", userID: 4, }); diff --git a/typescript/server/src/server/router/ir/usc/_playtype/router.ts b/typescript/server/src/server/router/ir/usc/_playtype/router.ts index 87e150bea..ea6fc4006 100644 --- a/typescript/server/src/server/router/ir/usc/_playtype/router.ts +++ b/typescript/server/src/server/router/ir/usc/_playtype/router.ts @@ -9,7 +9,7 @@ import { ExpressWrappedScoreImportMain } from "#lib/score-import/framework/expre import { ServerConfig, TachiConfig } from "#lib/setup/config"; import { RejectIfBanned, RequirePermissions } from "#server/middleware/auth"; import { CreateMulterSingleUploadMiddleware } from "#server/middleware/multer-upload"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { FormatPrError } from "#utils/prudence"; import { AssignToReqTachiData, GetTachiData } from "#utils/req-tachi-data"; import { type RequestHandler, Router } from "express"; @@ -59,7 +59,7 @@ const ValidateUSCRequest: RequestHandler = async (req, res, next) => { }); } - const uscAuthDoc = await db["api-tokens"].findOne({ + const uscAuthDoc = await MONGODB_KILL["api-tokens"].findOne({ token: splitToken[1], }); @@ -114,7 +114,7 @@ router.get("/", (req, res) => ); const RetrieveChart: RequestHandler = async (req, res, next) => { - const chart = await db.charts.usc.findOne({ + const chart = await MONGODB_KILL.charts.usc.findOne({ "data.hashSHA1": req.params.chartHash, playtype: req.params.playtype as Playtypes["usc"], }); @@ -158,7 +158,7 @@ router.get("/charts/:chartHash", RetrieveChart, (req, res) => router.get("/charts/:chartHash/record", RetrieveChart, async (req, res) => { const chart = GetTachiData(req, "uscChartDoc"); - const serverRecord = (await db["personal-bests"].findOne({ + const serverRecord = (await MONGODB_KILL["personal-bests"].findOne({ chartID: chart.chartID, "rankingData.rank": 1, })) as PBScoreDocument<"usc:Controller" | "usc:Keyboard"> | null; @@ -227,7 +227,7 @@ router.get("/charts/:chartHash/leaderboard", RetrieveChart, async (req, res) => const gptConfig = GetGamePTConfig("usc", chart.playtype); - const bestScores = (await db["personal-bests"].find( + const bestScores = (await MONGODB_KILL["personal-bests"].find( { chartID: chart.chartID, }, @@ -286,7 +286,7 @@ router.post("/scores", RequirePermissions("submit_score"), async (req, res) => { const uscChart = req.safeBody.chart as USCClientChart; - const chartDoc = (await db.charts.usc.findOne({ + const chartDoc = (await MONGODB_KILL.charts.usc.findOne({ "data.hashSHA1": uscChart.chartHash, playtype, })) as ChartDocument<"usc:Controller" | "usc:Keyboard"> | null; @@ -408,7 +408,7 @@ router.post( // MUST also be for USC. // Otherwise, anyone could overwrite anyone elses // score replays! - const correspondingScore = await db.scores.findOne({ + const correspondingScore = await MONGODB_KILL.scores.findOne({ userID: req[SYMBOL_TACHI_API_AUTH].userID!, scoreID: req.safeBody.identifier, game: "usc", diff --git a/typescript/server/src/server/router/ir/usc/_playtype/usc.test.ts b/typescript/server/src/server/router/ir/usc/_playtype/usc.oldtest.ts similarity index 85% rename from typescript/server/src/server/router/ir/usc/_playtype/usc.test.ts rename to typescript/server/src/server/router/ir/usc/_playtype/usc.oldtest.ts index c3d57fa0f..f4a87a66b 100644 --- a/typescript/server/src/server/router/ir/usc/_playtype/usc.test.ts +++ b/typescript/server/src/server/router/ir/usc/_playtype/usc.oldtest.ts @@ -1,6 +1,6 @@ import type { ChartDocument, PBScoreDocument, ScoreDocument, UserDocument } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import ResetDBState from "#test-utils/resets"; import deepmerge from "deepmerge"; import t from "tap"; @@ -58,7 +58,7 @@ t.test("#TachiScoreToServerScore", (t) => { t.beforeEach(ResetDBState); t.test("Should correctly convert a tachiScore to a serverScore", async (t) => { - await db.scores.insert(mockScoreDocument); + await MONGODB_KILL.scores.insert(mockScoreDocument); const res = await TachiScoreToServerScore(mockScorePB); @@ -83,7 +83,7 @@ t.test("#TachiScoreToServerScore", (t) => { }); t.test("Should work for timestamped scores", async (t) => { - await db.scores.insert(mockScoreDocument); + await MONGODB_KILL.scores.insert(mockScoreDocument); const res = await TachiScoreToServerScore( deepmerge(mockScorePB, { timeAchieved: 1_621_844_762_995 }), @@ -245,17 +245,17 @@ const mockUserDocs = [2, 3, 4, 5, 6, 7, 8, 9, 10].map((e) => ({ t.test("#CreatePOSTScoresResponseBody", async (t) => { t.beforeEach(ResetDBState); - const chartDoc = (await db.charts.usc.findOne()) as ChartDocument< + const chartDoc = (await MONGODB_KILL.charts.usc.findOne()) as ChartDocument< "usc:Controller" | "usc:Keyboard" >; t.test("Should correctly return POSTScoresResponseBody", async (t) => { - await db.scores.insert(mockScoreDocument); + await MONGODB_KILL.scores.insert(mockScoreDocument); - await db["personal-bests"].insert(uscScorePBsSet); - await db.users.insert(mockUserDocs); + await MONGODB_KILL["personal-bests"].insert(uscScorePBsSet); + await MONGODB_KILL.users.insert(mockUserDocs); - await db.scores.insert({ + await MONGODB_KILL.scores.insert({ scoreID: "USER_1_SCORE_PB", scoreMeta: { replayID: "foo_bar", @@ -294,12 +294,12 @@ t.test("#CreatePOSTScoresResponseBody", async (t) => { }); t.test("Should not return serverRecord in adjacentAbove", async (t) => { - await db.scores.insert(mockScoreDocument); + await MONGODB_KILL.scores.insert(mockScoreDocument); - await db["personal-bests"].insert(uscScorePBsSet); - await db.users.insert(mockUserDocs); + await MONGODB_KILL["personal-bests"].insert(uscScorePBsSet); + await MONGODB_KILL.users.insert(mockUserDocs); - await db.scores.insert({ + await MONGODB_KILL.scores.insert({ scoreID: "USER_4_SCORE_PB", } as ScoreDocument); @@ -333,12 +333,12 @@ t.test("#CreatePOSTScoresResponseBody", async (t) => { }); t.test("Should trim serverRecord in adjacentAbove", async (t) => { - await db.scores.insert(mockScoreDocument); + await MONGODB_KILL.scores.insert(mockScoreDocument); - await db["personal-bests"].insert(uscScorePBsSet); - await db.users.insert(mockUserDocs); + await MONGODB_KILL["personal-bests"].insert(uscScorePBsSet); + await MONGODB_KILL.users.insert(mockUserDocs); - await db.scores.insert({ + await MONGODB_KILL.scores.insert({ scoreID: "USER_3_SCORE_PB", } as ScoreDocument); @@ -372,12 +372,12 @@ t.test("#CreatePOSTScoresResponseBody", async (t) => { }); t.test("Should throw on no ScorePB", async (t) => { - await db.scores.insert(mockScoreDocument); + await MONGODB_KILL.scores.insert(mockScoreDocument); // await db["personal-bests"].insert(uscScorePBsSet); - await db.users.insert(mockUserDocs); + await MONGODB_KILL.users.insert(mockUserDocs); - await db.scores.insert({ + await MONGODB_KILL.scores.insert({ scoreID: "USER_1_SCORE_PB", scoreMeta: { replayID: "foo_bar", @@ -392,10 +392,10 @@ t.test("#CreatePOSTScoresResponseBody", async (t) => { }); t.test("Should throw on no original score", async (t) => { - await db.scores.insert(mockScoreDocument); + await MONGODB_KILL.scores.insert(mockScoreDocument); - await db["personal-bests"].insert(uscScorePBsSet); - await db.users.insert(mockUserDocs); + await MONGODB_KILL["personal-bests"].insert(uscScorePBsSet); + await MONGODB_KILL.users.insert(mockUserDocs); // await db.scores.insert({ // scoreID: "USER_1_SCORE_PB", diff --git a/typescript/server/src/server/router/ir/usc/_playtype/usc.ts b/typescript/server/src/server/router/ir/usc/_playtype/usc.ts index 4cfccd08f..12aa7a785 100644 --- a/typescript/server/src/server/router/ir/usc/_playtype/usc.ts +++ b/typescript/server/src/server/router/ir/usc/_playtype/usc.ts @@ -9,7 +9,7 @@ import type { GetEnumValue } from "tachi-common/types/metrics"; import { USCIR_ADJACENT_SCORE_N } from "#lib/constants/usc-ir"; import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { MStoS } from "#utils/misc"; import { GetPBOnChart, GetServerRecordOnChart } from "#utils/scores"; @@ -39,7 +39,7 @@ export async function TachiScoreToServerScore( // @optimisable // Repeated calls to this may pre-emptively provide usernames // and score PBs. - const userDoc = await db.users.findOne( + const userDoc = await MONGODB_KILL.users.findOne( { id: tachiScore.userID, }, @@ -61,7 +61,7 @@ export async function TachiScoreToServerScore( const firstScoreID = tachiScore.composedFrom[0].scoreID; - const scorePB = (await db.scores.findOne({ + const scorePB = (await MONGODB_KILL.scores.findOne({ scoreID: firstScoreID, })) as ScoreDocument<"usc:Controller" | "usc:Keyboard"> | null; @@ -135,7 +135,7 @@ export async function CreatePOSTScoresResponseBody( // This returns immediately ranked higher // than the current user. - const adjAbove = (await db["personal-bests"].find( + const adjAbove = (await MONGODB_KILL["personal-bests"].find( { chartID: chartDoc.chartID, "rankingData.rank": { $lt: usersRanking }, @@ -161,7 +161,7 @@ export async function CreatePOSTScoresResponseBody( // Similar to above, this returns the N most immediate // scores below the given user. - const adjBelow = (await db["personal-bests"].find( + const adjBelow = (await MONGODB_KILL["personal-bests"].find( { chartID: chartDoc.chartID, "rankingData.rank": { $gt: usersRanking }, @@ -179,7 +179,7 @@ export async function CreatePOSTScoresResponseBody( Promise.all(adjBelow.map(TachiScoreToServerScore)), ]); - const originalScore = (await db.scores.findOne({ + const originalScore = (await MONGODB_KILL.scores.findOne({ scoreID, })) as ScoreDocument<"usc:Controller" | "usc:Keyboard"> | null; diff --git a/typescript/server/src/server/router/router.ts b/typescript/server/src/server/router/router.ts index eda342eeb..56fc023d4 100644 --- a/typescript/server/src/server/router/router.ts +++ b/typescript/server/src/server/router/router.ts @@ -4,7 +4,7 @@ import { Router } from "express"; import { RejectIfBanned, SetRequestPermissions } from "../middleware/auth"; import { NormalRateLimitMiddleware } from "../middleware/rate-limiter"; -import apiRouterV1Mongo from "./api/v1/router"; +import apiRouterV1 from "./api/v1/router"; import irRouter from "./ir/router"; const router: Router = Router({ mergeParams: true }); @@ -18,7 +18,7 @@ router.use("/ir", NormalRateLimitMiddleware, irRouter); router.use(SetRequestPermissions); router.use(UpdateLastSeen); -router.use("/api/v1mongo", apiRouterV1Mongo); +router.use("/api/v1", apiRouterV1); // if in localdev, add a debug endpoint to tell users when they got a successful fetch // on the root endpoint. diff --git a/typescript/server/src/services/mongo/db.test.ts b/typescript/server/src/services/mongo/db.oldtest.ts similarity index 53% rename from typescript/server/src/services/mongo/db.test.ts rename to typescript/server/src/services/mongo/db.oldtest.ts index c496ca162..f48ea5e45 100644 --- a/typescript/server/src/services/mongo/db.test.ts +++ b/typescript/server/src/services/mongo/db.oldtest.ts @@ -3,32 +3,38 @@ import type { ScoreDocument } from "tachi-common"; import ResetDBState from "#test-utils/resets"; import t from "tap"; -import db from "./db"; +import MONGODB_KILL from "./db"; t.test("ID field autoprojection", async (t) => { await ResetDBState(); - const res = await db.scores.findOne(); + const res = await MONGODB_KILL.scores.findOne(); t.equal(res!._id, undefined); - const res2 = await db.scores.findOne({}, { projectID: true }); + const res2 = await MONGODB_KILL.scores.findOne({}, { projectID: true }); t.not(res2!._id, undefined); - const res3 = await db.scores.findOne({}, { projection: { scoreID: 1 } }); + const res3 = await MONGODB_KILL.scores.findOne({}, { projection: { scoreID: 1 } }); t.equal(res3!._id, undefined); - const res4 = await db.scores.findOne({}, { projection: { scoreID: 1 }, projectID: true }); + const res4 = await MONGODB_KILL.scores.findOne( + {}, + { projection: { scoreID: 1 }, projectID: true }, + ); t.not(res4!._id, undefined); - const res5 = await db.scores.findOne({}, { projection: { scoreID: 0 } }); + const res5 = await MONGODB_KILL.scores.findOne({}, { projection: { scoreID: 0 } }); t.equal(res5!._id, undefined); - const res6 = await db.scores.findOne({}, { projection: { scoreID: 0 }, projectID: true }); + const res6 = await MONGODB_KILL.scores.findOne( + {}, + { projection: { scoreID: 0 }, projectID: true }, + ); t.not(res6!._id, undefined); @@ -40,11 +46,11 @@ t.test("ID field autoprojection", async (t) => { t.test("Don't add stuff onto my objects please", async (t) => { const sc = { scoreID: "foo" } as unknown as ScoreDocument; - await db.scores.insert(sc); + await MONGODB_KILL.scores.insert(sc); t.strictSame(sc, { scoreID: "foo" }, "Shouldn't have _id attached onto it."); - await db.scores.remove({}); + await MONGODB_KILL.scores.remove({}); t.end(); }); diff --git a/typescript/server/src/services/mongo/db.ts b/typescript/server/src/services/mongo/db.ts index dcb277813..2392399d6 100644 --- a/typescript/server/src/services/mongo/db.ts +++ b/typescript/server/src/services/mongo/db.ts @@ -143,7 +143,7 @@ const charts = Object.fromEntries( [G in GameGroup]: ICollection>; }; -const db = { +const MONGODB_KILL = { // i have to handwrite this out for TS... :( // dont worry, it was all macro'd. songs, @@ -215,10 +215,10 @@ const db = { }; export type StaticDatabases = Exclude< - keyof typeof db, + keyof typeof MONGODB_KILL, "anyCharts" | "anySongs" | "charts" | "songs" >; export type Databases = `charts-${GameGroup}` | `songs-${GameGroup}` | StaticDatabases; -export default db; +export default MONGODB_KILL; diff --git a/typescript/server/src/services/mongo/sequence-docs.ts b/typescript/server/src/services/mongo/sequence-docs.ts index 3b2782f30..2ce45105d 100644 --- a/typescript/server/src/services/mongo/sequence-docs.ts +++ b/typescript/server/src/services/mongo/sequence-docs.ts @@ -1,11 +1,11 @@ import { log } from "#lib/log/log.js"; -import db from "./db"; +import MONGODB_KILL from "./db"; export async function InitSequenceDocs() { - await db.counters.remove({}); + await MONGODB_KILL.counters.remove({}); - const userWithLargestID = await db.users.findOne( + const userWithLargestID = await MONGODB_KILL.users.findOne( {}, { sort: { @@ -14,7 +14,7 @@ export async function InitSequenceDocs() { }, ); - const largestBMSSongID = await db.songs.bms.findOne( + const largestBMSSongID = await MONGODB_KILL.songs.bms.findOne( {}, { sort: { @@ -23,7 +23,7 @@ export async function InitSequenceDocs() { }, ); - const largestPMSSongID = await db.songs.pms.findOne( + const largestPMSSongID = await MONGODB_KILL.songs.pms.findOne( {}, { sort: { @@ -51,5 +51,5 @@ export async function InitSequenceDocs() { `Setting Counters -> ${Counters.map((e) => `${e.counterName}: ${e.value}`).join(", ")}`, ); - await db.counters.insert(Counters); + await MONGODB_KILL.counters.insert(Counters); } diff --git a/typescript/server/src/services/pg/db.ts b/typescript/server/src/services/pg/db.ts index ac48c9e6b..cf387b036 100644 --- a/typescript/server/src/services/pg/db.ts +++ b/typescript/server/src/services/pg/db.ts @@ -6,12 +6,12 @@ import { Pool } from "pg"; const pool = new Pool({ connectionString: Env.POSTGRES_URL }); -const db = new Kysely({ +const DB = new Kysely({ dialect: new PostgresDialect({ pool }), }); export async function ClosePgConnection() { - await db.destroy(); + await DB.destroy(); } -export default db; +export default DB; diff --git a/typescript/server/src/test-utils/api-common.ts b/typescript/server/src/test-utils/api-common.ts index d0a6b4ffc..56aec4b82 100644 --- a/typescript/server/src/test-utils/api-common.ts +++ b/typescript/server/src/test-utils/api-common.ts @@ -1,6 +1,6 @@ import type { APIPermissions } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import t from "tap"; import mockApi from "./mock-api"; @@ -23,7 +23,7 @@ export function RequireAuthPerms( // 401 if no auth given t.equal(res.statusCode, 401); - await db["api-tokens"].insert({ + await MONGODB_KILL["api-tokens"].insert({ identifier: "temp_auth_perms", permissions: {}, token: "temp_auth", @@ -37,7 +37,7 @@ export function RequireAuthPerms( const prm = Array.isArray(perms) ? perms : [perms]; - await db["api-tokens"].insert({ + await MONGODB_KILL["api-tokens"].insert({ identifier: "temp_auth_perms2", permissions: Object.fromEntries(prm.map((e) => [e, true])), token: "temp_auth2", diff --git a/typescript/server/src/test-utils/fake-auth.ts b/typescript/server/src/test-utils/fake-auth.ts index f22b7b63e..73cda3c73 100644 --- a/typescript/server/src/test-utils/fake-auth.ts +++ b/typescript/server/src/test-utils/fake-auth.ts @@ -2,7 +2,7 @@ import type supertest from "supertest"; import { log } from "#lib/log/log.js"; import { ClearTestingRateLimitCache } from "#server/middleware/rate-limiter"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { ALL_PERMISSIONS } from "tachi-common"; import ResetDBState from "./resets"; @@ -36,7 +36,7 @@ export async function CreateFakeAuthCookie(mockApi: supertest.SuperTest Promise { return () => - db["api-tokens"].insert({ + MONGODB_KILL["api-tokens"].insert({ userID: 1, identifier: "Mock API Token", permissions: ALL_PERMISSIONS, diff --git a/typescript/server/src/test-utils/resets.ts b/typescript/server/src/test-utils/resets.ts index ece0cc310..773d08c0f 100644 --- a/typescript/server/src/test-utils/resets.ts +++ b/typescript/server/src/test-utils/resets.ts @@ -4,7 +4,7 @@ import type { GameGroup } from "tachi-common"; import { log } from "#lib/log/log.js"; import { Env, ServerConfig } from "#lib/setup/config"; import { ClearTestingRateLimitCache } from "#server/middleware/rate-limiter"; -import db, { type StaticDatabases } from "#services/mongo/db"; +import MONGODB_KILL, { type StaticDatabases } from "#services/mongo/db"; import { SetIndexes } from "#services/mongo/indexes"; import fs from "fs"; import path from "path"; @@ -37,11 +37,11 @@ function GetAndCache( let collection: ICollection; if (filename.startsWith("songs-")) { - collection = db.anySongs[filename.split("-")[1] as GameGroup]; + collection = MONGODB_KILL.anySongs[filename.split("-")[1] as GameGroup]; } else if (filename.startsWith("charts-")) { - collection = db.anyCharts[filename.split("-")[1] as GameGroup]; - } else if (filename in db) { - collection = db[filename as StaticDatabases]; + collection = MONGODB_KILL.anyCharts[filename.split("-")[1] as GameGroup]; + } else if (filename in MONGODB_KILL) { + collection = MONGODB_KILL[filename as StaticDatabases]; } else { throw new Error( `Panicked when trying to get collection for ${filename}. Does this collection exist?`, diff --git a/typescript/server/src/test-utils/single-process-tap.ts b/typescript/server/src/test-utils/single-process-tap.ts index 021a451ab..24b8f3747 100644 --- a/typescript/server/src/test-utils/single-process-tap.ts +++ b/typescript/server/src/test-utils/single-process-tap.ts @@ -5,7 +5,7 @@ import t from "tap"; import { CleanUpAfterTests } from "./cleanup"; -const files = glob.sync(path.join(__dirname, "../", "**/*.test.ts")); +const files = glob.sync(path.join(__dirname, "../", "**/*.oldtest.ts")); process.env.NODE_PATH = path.join(__dirname, "../"); diff --git a/typescript/server/src/test-utils/test-data.ts b/typescript/server/src/test-utils/test-data.ts index fdc5ce05f..d20d5506e 100644 --- a/typescript/server/src/test-utils/test-data.ts +++ b/typescript/server/src/test-utils/test-data.ts @@ -17,7 +17,7 @@ import { USC_DEFAULT_PERFECT, USC_DEFAULT_SLAM, } from "#lib/constants/usc-ir"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { ApplyNTimes, RFA } from "#utils/misc"; import fs from "fs"; import path from "path"; @@ -924,10 +924,10 @@ export async function LoadTachiIIDXData() { KTDATA_CACHE = { songs, charts }; } - await db.songs.iidx.remove({}); - await db.songs.iidx.insert(songs); - await db.charts.iidx.remove({}); - await db.charts.iidx.insert(charts); + await MONGODB_KILL.songs.iidx.remove({}); + await MONGODB_KILL.songs.iidx.insert(songs); + await MONGODB_KILL.charts.iidx.remove({}); + await MONGODB_KILL.charts.iidx.insert(charts); } export const MockBarbatosScore: BarbatosScore = { diff --git a/typescript/server/src/utils/calculations/recalc-game-profiles.ts b/typescript/server/src/utils/calculations/recalc-game-profiles.ts index b9fd166ae..afcf03bbe 100644 --- a/typescript/server/src/utils/calculations/recalc-game-profiles.ts +++ b/typescript/server/src/utils/calculations/recalc-game-profiles.ts @@ -2,14 +2,14 @@ import { log } from "#lib/log/log.js"; import { UpdateUsersGamePlaytypeStats } from "#lib/score-import/framework/ugpt-stats/update-ugpt-stats"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { FormatUserDoc } from "#utils/user"; export async function RecalcGameProfiles(filter = {}) { - const profiles = await db["game-stats"].find(filter); + const profiles = await MONGODB_KILL["game-stats"].find(filter); for (const profile of profiles) { - const user = await db.users.findOne({ + const user = await MONGODB_KILL.users.findOne({ id: profile.userID, }); diff --git a/typescript/server/src/utils/calculations/recalc-scores.ts b/typescript/server/src/utils/calculations/recalc-scores.ts index b3124deff..559a496df 100644 --- a/typescript/server/src/utils/calculations/recalc-scores.ts +++ b/typescript/server/src/utils/calculations/recalc-scores.ts @@ -5,7 +5,7 @@ import { ProcessPBs } from "#lib/score-import/framework/pb/process-pbs"; import { UpdateUsersQuests } from "#lib/score-import/framework/quests/quests"; import { UpdateUsersGamePlaytypeStats } from "#lib/score-import/framework/ugpt-stats/update-ugpt-stats"; import { TachiConfig } from "#lib/setup/config"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { EfficientDBIterate } from "#utils/efficient-db-iterate"; import { FormatUserDoc } from "#utils/user"; import { @@ -26,9 +26,9 @@ export async function RecalcAllScores(filter = {}) { const chartIDs = new Set(); await EfficientDBIterate( - db.scores, + MONGODB_KILL.scores, async (c) => { - const chart = await db.anyCharts[c.game].findOne({ chartID: c.chartID }); + const chart = await MONGODB_KILL.anyCharts[c.game].findOne({ chartID: c.chartID }); if (!chart) { log.error( @@ -51,7 +51,7 @@ export async function RecalcAllScores(filter = {}) { return { scoreID: c.scoreID, calculatedData }; }, async (updates) => { - await db.scores.bulkWrite( + await MONGODB_KILL.scores.bulkWrite( updates.map((e) => ({ updateOne: { filter: { @@ -93,9 +93,9 @@ export async function UpdateAllPBs(userIDs?: Array, filter = {}) { let allUsers: Array; if (!userIDs) { - allUsers = await db.users.find({}); + allUsers = await MONGODB_KILL.users.find({}); } else { - allUsers = await db.users.find({ + allUsers = await MONGODB_KILL.users.find({ id: { $in: userIDs }, }); } @@ -107,7 +107,7 @@ export async function UpdateAllPBs(userIDs?: Array, filter = {}) { const gameConfig = GetGameGroupConfig(game); for (const playtype of gameConfig.playtypes) { - const scores = await db.scores.find( + const scores = await MONGODB_KILL.scores.find( deepmerge(filter, { userID: user.id, game, playtype }), { projection: { chartID: 1 }, diff --git a/typescript/server/src/utils/calculations/recalc-sessions.ts b/typescript/server/src/utils/calculations/recalc-sessions.ts index 8491cf903..625276f86 100644 --- a/typescript/server/src/utils/calculations/recalc-sessions.ts +++ b/typescript/server/src/utils/calculations/recalc-sessions.ts @@ -2,16 +2,16 @@ import { log } from "#lib/log/log.js"; import { CreateSessionCalcData } from "#lib/score-import/framework/calculated-data/session"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetGPTString } from "tachi-common"; export async function RecalcSessions(filter = {}) { - const allSessions = await db.sessions.find(filter); + const allSessions = await MONGODB_KILL.sessions.find(filter); log.info(`Recalcing ${allSessions.length} sessions.`); for (const session of allSessions) { - const scores = await db.scores.find( + const scores = await MONGODB_KILL.scores.find( { scoreID: { $in: session.scoreIDs } }, { projection: { calculatedData: 1 }, @@ -19,7 +19,7 @@ export async function RecalcSessions(filter = {}) { ); if (scores.length === 0) { - await db.sessions.remove({ sessionID: session.sessionID }); + await MONGODB_KILL.sessions.remove({ sessionID: session.sessionID }); continue; } @@ -30,11 +30,14 @@ export async function RecalcSessions(filter = {}) { } catch (err) { log.error({ err }, `Recalcing ${session.game} (${session.playtype}) failed.`); log.warn(`Destroying session!`); - await db.sessions.remove({ sessionID: session.sessionID }); + await MONGODB_KILL.sessions.remove({ sessionID: session.sessionID }); continue; } - await db.sessions.update({ sessionID: session.sessionID }, { $set: { calculatedData: c } }); + await MONGODB_KILL.sessions.update( + { sessionID: session.sessionID }, + { $set: { calculatedData: c } }, + ); } log.info(`Done!`); diff --git a/typescript/server/src/utils/class.ts b/typescript/server/src/utils/class.ts index 7f65869f3..68ff70a2b 100644 --- a/typescript/server/src/utils/class.ts +++ b/typescript/server/src/utils/class.ts @@ -1,7 +1,7 @@ import { CreateGameSettings } from "#lib/game-settings/create-game-settings"; import { log } from "#lib/log/log.js"; import { EmitWebhookEvent } from "#lib/webhooks/webhooks"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { type Classes, type GameGroup, @@ -101,7 +101,7 @@ export async function UpdateClassIfGreater( ) { const gptString = GetGPTString(game, playtype); - const userGameStats = await db["game-stats"].findOne({ userID, game, playtype }); + const userGameStats = await MONGODB_KILL["game-stats"].findOne({ userID, game, playtype }); const isGreater = ReturnClassIfGreater(gptString, classSet, classVal, userGameStats); if (isGreater === false) { @@ -109,14 +109,14 @@ export async function UpdateClassIfGreater( } if (userGameStats) { - await db["game-stats"].update( + await MONGODB_KILL["game-stats"].update( { userID, game, playtype }, { $set: { [`classes.${classSet}`]: classVal } }, ); } else { // insert new game stats for this user - this is an awkward place // to call this - maybe we should call it elsewhere. - await db["game-stats"].insert({ + await MONGODB_KILL["game-stats"].insert({ userID, game, playtype, @@ -131,7 +131,7 @@ export async function UpdateClassIfGreater( await CreateGameSettings(userID, game, playtype); } - await db["class-achievements"].insert({ + await MONGODB_KILL["class-achievements"].insert({ game, playtype, userID, diff --git a/typescript/server/src/utils/conversion.ts b/typescript/server/src/utils/conversion.ts new file mode 100644 index 000000000..aa2cb982e --- /dev/null +++ b/typescript/server/src/utils/conversion.ts @@ -0,0 +1,17 @@ +import { UserAuthLevels } from "tachi-common"; +import { type AuthLevel } from "tachi-db"; + +import { staticAssertUnreachable } from "./misc"; + +export function AuthLevelToInt(authLevel: AuthLevel): UserAuthLevels { + switch (authLevel) { + case "banned": + return UserAuthLevels.BANNED; + case "admin": + return UserAuthLevels.ADMIN; + case "user": + return UserAuthLevels.USER; + default: + staticAssertUnreachable(authLevel); + } +} diff --git a/typescript/server/src/utils/crypto.test.ts b/typescript/server/src/utils/crypto.oldtest.ts similarity index 100% rename from typescript/server/src/utils/crypto.test.ts rename to typescript/server/src/utils/crypto.oldtest.ts diff --git a/typescript/server/src/utils/db.test.ts b/typescript/server/src/utils/db.oldtest.ts similarity index 81% rename from typescript/server/src/utils/db.test.ts rename to typescript/server/src/utils/db.oldtest.ts index fa67b0f02..f1b48f1c3 100644 --- a/typescript/server/src/utils/db.test.ts +++ b/typescript/server/src/utils/db.oldtest.ts @@ -1,4 +1,4 @@ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import ResetDBState from "#test-utils/resets"; import t from "tap"; @@ -13,7 +13,7 @@ t.test("#GetNextCounterValue", (t) => { // database starts with this at one t.equal(response, 2, "Counter should return the current number stored"); - const dbData = await db.counters.findOne({ + const dbData = await MONGODB_KILL.counters.findOne({ counterName: "real-counter", }); @@ -32,13 +32,13 @@ t.test("#DecrementCounterValue", (t) => { t.beforeEach(ResetDBState); t.test("Should decrease a counter.", async (t) => { - await db.counters.insert({ counterName: "foo", value: 3 }); + await MONGODB_KILL.counters.insert({ counterName: "foo", value: 3 }); const res = await DecrementCounterValue("foo"); t.equal(res, 2); - const doc = await db.counters.findOne({ counterName: "foo" }); + const doc = await MONGODB_KILL.counters.findOne({ counterName: "foo" }); t.equal(doc?.value, 2); diff --git a/typescript/server/src/utils/db.ts b/typescript/server/src/utils/db.ts index bd7ce2d92..6317690f1 100644 --- a/typescript/server/src/utils/db.ts +++ b/typescript/server/src/utils/db.ts @@ -1,7 +1,7 @@ import type { FilterQuery } from "mongodb"; import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { FormatChart, type GameGroup, @@ -16,7 +16,7 @@ import { } from "tachi-common"; export async function GetNextCounterValue(counterName: string): Promise { - const sequenceDoc = await db.counters.findOneAndUpdate( + const sequenceDoc = await MONGODB_KILL.counters.findOneAndUpdate( { counterName, }, @@ -43,7 +43,7 @@ export async function GetNextCounterValue(counterName: string): Promise export async function DecrementCounterValue(counterName: string): Promise { log.debug(`Decrementing Counter Value ${counterName}.`); - const sequenceDoc = await db.counters.findOneAndUpdate( + const sequenceDoc = await MONGODB_KILL.counters.findOneAndUpdate( { counterName, }, @@ -70,10 +70,10 @@ export async function GetRelevantSongsAndCharts( game: GameGroup, ) { const [songs, charts] = await Promise.all([ - db.anySongs[game].find({ + MONGODB_KILL.anySongs[game].find({ id: { $in: scores.map((e) => e.songID) }, }), - db.anyCharts[game].find({ + MONGODB_KILL.anyCharts[game].find({ chartID: { $in: scores.map((e) => e.chartID) }, }), ]); @@ -82,7 +82,7 @@ export async function GetRelevantSongsAndCharts( } export async function UpdateGameSongIDCounter(game: "bms" | "pms") { - const latestSong = await db.anySongs[game].findOne( + const latestSong = await MONGODB_KILL.anySongs[game].findOne( {}, { sort: { id: -1 }, @@ -98,7 +98,7 @@ export async function UpdateGameSongIDCounter(game: "bms" | "pms") { const largestSongID = latestSong?.id ?? 0; - await db.counters.update( + await MONGODB_KILL.counters.update( { counterName: `${game}-song-id`, }, @@ -111,7 +111,7 @@ export async function UpdateGameSongIDCounter(game: "bms" | "pms") { } export async function GetChartForIDGuaranteed(game: GameGroup, chartID: string) { - const chart = await db.anyCharts[game].findOne({ chartID }); + const chart = await MONGODB_KILL.anyCharts[game].findOne({ chartID }); if (!chart) { throw new Error(`Couldn't find chart with ID ${chartID} (${game}).`); @@ -121,7 +121,7 @@ export async function GetChartForIDGuaranteed(game: GameGroup, chartID: string) } export async function GetSongForIDGuaranteed(game: GameGroup, songID: integer) { - const song = await db.anySongs[game].findOne({ id: songID }); + const song = await MONGODB_KILL.anySongs[game].findOne({ id: songID }); if (!song) { throw new Error(`Couldn't find song with ID ${songID} (${game}).`); @@ -131,7 +131,7 @@ export async function GetSongForIDGuaranteed(game: GameGroup, songID: integer) { } export async function GetFolderForIDGuaranteed(folderID: string) { - const folder = await db.folders.findOne({ folderID }); + const folder = await MONGODB_KILL.folders.findOne({ folderID }); if (!folder) { throw new Error(`Couldn't find folder with ID ${folderID}.`); @@ -141,7 +141,7 @@ export async function GetFolderForIDGuaranteed(folderID: string) { } export async function GetGoalForIDGuaranteed(goalID: string) { - const goal = await db.goals.findOne({ goalID }); + const goal = await MONGODB_KILL.goals.findOne({ goalID }); if (!goal) { throw new Error(`Couldn't find goal with ID ${goalID}`); @@ -151,7 +151,7 @@ export async function GetGoalForIDGuaranteed(goalID: string) { } export async function GetQuestForIDGuaranteed(questID: string) { - const quest = await db.quests.findOne({ questID }); + const quest = await MONGODB_KILL.quests.findOne({ questID }); if (!quest) { throw new Error(`Couldn't find quest with ID ${questID}`); @@ -184,14 +184,14 @@ export async function GetRecentlyAchievedGoals( achieved: true, }; - const goalSubs = await db["goal-subs"].find(query, { + const goalSubs = await MONGODB_KILL["goal-subs"].find(query, { sort: { timeAchieved: -1, }, limit, }); - const goals = await db.goals.find({ + const goals = await MONGODB_KILL.goals.find({ goalID: { $in: goalSubs.map((e) => e.goalID) }, }); @@ -216,14 +216,14 @@ export async function GetRecentlyInteractedGoals( lastInteraction: { $ne: null }, }; - const goalSubs = await db["goal-subs"].find(query, { + const goalSubs = await MONGODB_KILL["goal-subs"].find(query, { sort: { lastInteraction: -1, }, limit, }); - const goals = await db.goals.find({ + const goals = await MONGODB_KILL.goals.find({ goalID: { $in: goalSubs.map((e) => e.goalID) }, }); @@ -247,14 +247,14 @@ export async function GetRecentlyAchievedQuests( achieved: true, }; - const questSubs = await db["quest-subs"].find(query, { + const questSubs = await MONGODB_KILL["quest-subs"].find(query, { sort: { timeAchieved: -1, }, limit, }); - const quests = await db.quests.find({ + const quests = await MONGODB_KILL.quests.find({ questID: { $in: questSubs.map((e) => e.questID) }, }); @@ -279,14 +279,14 @@ export async function GetRecentlyInteractedQuests( wasInstantlyAchieved: false, }; - const questSubs = await db["quest-subs"].find(query, { + const questSubs = await MONGODB_KILL["quest-subs"].find(query, { sort: { lastInteraction: -1, }, limit, }); - const quests = await db.quests.find({ + const quests = await MONGODB_KILL.quests.find({ questID: { $in: questSubs.map((e) => e.questID) }, }); @@ -297,43 +297,42 @@ export async function GetMostSubscribedGoals( query: FilterQuery, limit = 100, ): Promise> { - const mostSubscribedGoals: Array<{ goal: GoalDocument; subscriptions: integer }> = await db[ - "goal-subs" - ].aggregate([ - { - $match: query, - }, - { - $group: { - _id: "$goalID", - subscriptions: { $sum: 1 }, + const mostSubscribedGoals: Array<{ goal: GoalDocument; subscriptions: integer }> = + await MONGODB_KILL["goal-subs"].aggregate([ + { + $match: query, }, - }, - { - $sort: { - subscriptions: -1, + { + $group: { + _id: "$goalID", + subscriptions: { $sum: 1 }, + }, }, - }, - { - $limit: limit, - }, - { - $lookup: { - from: "goals", - localField: "_id", - foreignField: "goalID", - as: "goal", + { + $sort: { + subscriptions: -1, + }, }, - }, - { - $set: { - goal: { $arrayElemAt: ["$goal", 0] }, + { + $limit: limit, }, - }, - { - $unset: "goal._id", - }, - ]); + { + $lookup: { + from: "goals", + localField: "_id", + foreignField: "goalID", + as: "goal", + }, + }, + { + $set: { + goal: { $arrayElemAt: ["$goal", 0] }, + }, + }, + { + $unset: "goal._id", + }, + ]); return mostSubscribedGoals.map((e) => ({ __subscriptions: e.subscriptions, @@ -346,7 +345,7 @@ export async function GetMostSubscribedQuests( limit = 100, ): Promise> { const mostSubscribedQuests: Array<{ subscriptions: integer } & QuestDocument> = - await db.quests.aggregate([ + await MONGODB_KILL.quests.aggregate([ { $match: query, }, @@ -383,7 +382,7 @@ export async function GetMostSubscribedQuests( } export async function GetChildQuests(questline: QuestlineDocument) { - const quests = await db.quests.find({ + const quests = await MONGODB_KILL.quests.find({ questID: { $in: questline.quests }, }); diff --git a/typescript/server/src/utils/folder.test.ts b/typescript/server/src/utils/folder.oldtest.ts similarity index 96% rename from typescript/server/src/utils/folder.test.ts rename to typescript/server/src/utils/folder.oldtest.ts index 797d47019..021c92ddc 100644 --- a/typescript/server/src/utils/folder.test.ts +++ b/typescript/server/src/utils/folder.oldtest.ts @@ -1,6 +1,6 @@ import type { ChartDocument, FolderDocument } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import ResetDBState from "#test-utils/resets"; import { Testing511SPA } from "#test-utils/test-data"; import deepmerge from "deepmerge"; @@ -110,7 +110,7 @@ t.test("#ResolveFolderToCharts", (t) => { t.test("Filter", async (t) => { // add fake "NORMAL" 511 chart - await db.charts.iidx.insert( + await MONGODB_KILL.charts.iidx.insert( deepmerge(Testing511SPA, { difficulty: "NORMAL", chartID: "FAKE_511_SPN", @@ -180,11 +180,11 @@ t.test("#GetFolderChartIDs", (t) => { t.beforeEach(ResetDBState); t.test("Should use the folder-cache", async (t) => { - await db["folder-chart-lookup"].insert( + await MONGODB_KILL["folder-chart-lookup"].insert( ["a", "b", "c", "d"].map((e) => ({ chartID: e, folderID: "folder1" })), ); - await db["folder-chart-lookup"].insert( + await MONGODB_KILL["folder-chart-lookup"].insert( ["e", "f", "g", "h"].map((e) => ({ chartID: e, folderID: "folder2" })), ); diff --git a/typescript/server/src/utils/folder.ts b/typescript/server/src/utils/folder.ts index 66391f7bd..7a7d9aecc 100644 --- a/typescript/server/src/utils/folder.ts +++ b/typescript/server/src/utils/folder.ts @@ -2,7 +2,7 @@ import type { BulkWriteOperation, FilterQuery } from "mongodb"; import { log } from "#lib/log/log.js"; import { TachiConfig } from "#lib/setup/config"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import deepmerge from "deepmerge"; import fjsh from "fast-json-stable-hash"; import { @@ -45,7 +45,7 @@ export async function ResolveFolderToCharts( switch (folder.type) { case "static": { - charts = await db.anyCharts[folder.game].find( + charts = await MONGODB_KILL.anyCharts[folder.game].find( deepmerge(filter, { // Specifying playtype is mandatory, don't want to catch other charts. playtype: folder.playtype, @@ -56,9 +56,9 @@ export async function ResolveFolderToCharts( } case "songs": { - songs = await db.anySongs[folder.game].find(folder.data); + songs = await MONGODB_KILL.anySongs[folder.game].find(folder.data); - charts = await db.anyCharts[folder.game].find( + charts = await MONGODB_KILL.anyCharts[folder.game].find( deepmerge(filter, { playtype: folder.playtype, songID: { $in: songs.map((e) => e.id) }, @@ -80,7 +80,7 @@ export async function ResolveFolderToCharts( const fx = deepmerge.all([filter, { playtype: folder.playtype }, folderDataTransposed]); - charts = await db.anyCharts[folder.game].find(fx); + charts = await MONGODB_KILL.anyCharts[folder.game].find(fx); break; } } @@ -90,7 +90,7 @@ export async function ResolveFolderToCharts( return { songs, charts }; } - songs = await db.anySongs[folder.game].find({ + songs = await MONGODB_KILL.anySongs[folder.game].find({ id: { $in: charts.map((e) => e.songID) }, }); @@ -142,12 +142,12 @@ export async function GetFolderCharts( ): Promise<{ charts: Array; songs?: Array }> { const chartIDs = await GetFolderChartIDs(folder.folderID); - const charts = await db.anyCharts[folder.game].find( + const charts = await MONGODB_KILL.anyCharts[folder.game].find( deepmerge.all([{ playtype: folder.playtype }, { chartID: { $in: chartIDs } }, filter]), ); if (getSongs) { - const songs = await db.anySongs[folder.game].find({ + const songs = await MONGODB_KILL.anySongs[folder.game].find({ id: { $in: charts.map((e) => e.songID) }, }); @@ -158,7 +158,7 @@ export async function GetFolderCharts( } export async function GetFolderChartIDs(folderID: string) { - const chartIDs = await db["folder-chart-lookup"].find( + const chartIDs = await MONGODB_KILL["folder-chart-lookup"].find( { folderID, }, @@ -177,7 +177,7 @@ export async function CreateFolderChartLookup(folder: FolderDocument, flush = fa const { charts } = await ResolveFolderToCharts(folder, {}, false); if (flush) { - await db["folder-chart-lookup"].remove({ + await MONGODB_KILL["folder-chart-lookup"].remove({ folderID: folder.folderID, }); } @@ -206,7 +206,7 @@ export async function CreateFolderChartLookup(folder: FolderDocument, flush = fa // we do a bulk-upsert here to avoid race conditions if multiple things try to // create a folder-chart-lookup at the same time. - await db["folder-chart-lookup"].bulkWrite(ops); + await MONGODB_KILL["folder-chart-lookup"].bulkWrite(ops); } catch (err) { log.error({ folder, err }, `Failed to create folder chart lookup for ${folder.title}.`); throw err; @@ -219,13 +219,13 @@ export async function CreateFolderChartLookup(folder: FolderDocument, flush = fa */ export async function InitaliseFolderChartLookup() { log.info(`Started InitialiseFolderChartLookup`); - await db["folder-chart-lookup"].remove({}); + await MONGODB_KILL["folder-chart-lookup"].remove({}); log.info(`Flushed Cache.`); // temporary hack -- this will still break if we introduce a new // playtype on staging or something. // We need to have separate seeds for staging and prod! todo #609. - const folders = await db.folders.find({ + const folders = await MONGODB_KILL.folders.find({ game: { $in: TachiConfig.GAMES }, }); @@ -237,7 +237,7 @@ export async function InitaliseFolderChartLookup() { } export async function GetFoldersFromTable(table: TableDocument) { - const folders = await db.folders.find({ + const folders = await MONGODB_KILL.folders.find({ folderID: { $in: table.folders }, }); @@ -290,7 +290,7 @@ export async function GetFolderNamesInOrder(table: TableDocument): Promise e.chartID) }, }); @@ -356,7 +356,7 @@ export async function GetRecentlyViewedFolders( game: GameGroup, playtype: Playtype, ) { - const views = await db["recent-folder-views"].find( + const views = await MONGODB_KILL["recent-folder-views"].find( { userID, game, @@ -374,7 +374,7 @@ export async function GetRecentlyViewedFolders( return { views, folders: [] }; } - const folders = await db.folders.find({ + const folders = await MONGODB_KILL.folders.find({ folderID: { $in: views.map((e) => e.folderID) }, }); @@ -382,7 +382,7 @@ export async function GetRecentlyViewedFolders( } export async function GetTableForIDGuaranteed(tableID: string): Promise { - const table = await db.tables.findOne({ tableID }); + const table = await MONGODB_KILL.tables.findOne({ tableID }); if (!table) { throw new Error(`Couldn't find table with ID '${tableID}'.`); @@ -420,7 +420,7 @@ export async function GetEnumDistForFolderAsOf( } const bestEnumIndexes: Array<{ _id: string } & Record> = - await db.scores.aggregate([ + await MONGODB_KILL.scores.aggregate([ { $match: { chartID: { $in: chartIDs }, diff --git a/typescript/server/src/utils/misc.test.ts b/typescript/server/src/utils/misc.oldtest.ts similarity index 100% rename from typescript/server/src/utils/misc.test.ts rename to typescript/server/src/utils/misc.oldtest.ts diff --git a/typescript/server/src/utils/misc.ts b/typescript/server/src/utils/misc.ts index 64fc63193..3d4fa12bf 100644 --- a/typescript/server/src/utils/misc.ts +++ b/typescript/server/src/utils/misc.ts @@ -32,6 +32,25 @@ export function EscapeStringRegexp(string: string) { return string.replace(/[|\\{}()[\]^$+*?.]/gu, "\\$&").replace(/-/gu, "\\x2d"); } +export function EscapeForILIKE(str: string): string { + // Escape special characters used in ILIKE patterns + return str + .replaceAll("\\", "\\\\") // Escape backslashes first + .replaceAll("%", "\\%") // Escape % wildcard + .replaceAll("_", "\\_"); // Escape _ wildcard +} + +/** + * Stick this in the "default" branch of switch exprs to statically typecheck that your + * switch is exhaustive. + * + * This works because the argument to this function should be "never" because all of its + * variants should be exhausted. + */ +export function staticAssertUnreachable(_: never): never { + throw new Error(`unreachable (Got ${JSON.stringify(_)})`); +} + /** * Takes a process.hrtime.bigint(), and returns the milliseconds elapsed since it. * This function will not work if more than 100(ish) days have passed since the first reference. diff --git a/typescript/server/src/utils/naive-csv-parser.test.ts b/typescript/server/src/utils/naive-csv-parser.oldtest.ts similarity index 100% rename from typescript/server/src/utils/naive-csv-parser.test.ts rename to typescript/server/src/utils/naive-csv-parser.oldtest.ts diff --git a/typescript/server/src/utils/queries/api-clients.ts b/typescript/server/src/utils/queries/api-clients.ts new file mode 100644 index 000000000..462c85b30 --- /dev/null +++ b/typescript/server/src/utils/queries/api-clients.ts @@ -0,0 +1,11 @@ +import { SELECT_API_CLIENT, ToAPIClientDocument } from "#lib/db-formats/api-client.js"; +import DB from "#services/pg/db.js"; +import { type TachiAPIClientDocument } from "tachi-common"; + +export function GetClientByID(clientID: string): Promise { + return DB.selectFrom("priv_api_client") + .select(SELECT_API_CLIENT) + .where("client_id", "=", clientID) + .executeTakeFirst() + .then((res) => (res ? ToAPIClientDocument(res) : null)); +} diff --git a/typescript/server/src/utils/queries/auth.ts b/typescript/server/src/utils/queries/auth.ts index f9520e1b1..f1dcae21a 100644 --- a/typescript/server/src/utils/queries/auth.ts +++ b/typescript/server/src/utils/queries/auth.ts @@ -2,17 +2,17 @@ import type { KtLogger } from "#lib/log/log.js"; import type { integer } from "tachi-common"; import ScoreImportFatalError from "#lib/score-import/framework/score-importing/score-import-error"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; export function GetKaiAuth(userID: integer, service: "EAG" | "FLO" | "MIN") { - return db["kai-auth-tokens"].findOne({ + return MONGODB_KILL["kai-auth-tokens"].findOne({ userID, service, }); } export function RevokeKaiAuth(userID: integer, service: "EAG" | "FLO" | "MIN") { - return db["kai-auth-tokens"].remove({ + return MONGODB_KILL["kai-auth-tokens"].remove({ userID, service, }); diff --git a/typescript/server/src/utils/queries/blacklist.ts b/typescript/server/src/utils/queries/blacklist.ts index 2dddd7427..2b0a989db 100644 --- a/typescript/server/src/utils/queries/blacklist.ts +++ b/typescript/server/src/utils/queries/blacklist.ts @@ -1,7 +1,7 @@ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; export async function GetBlacklist() { - return (await db["score-blacklist"].find({}, { projection: { scoreID: 1 } })).map( + return (await MONGODB_KILL["score-blacklist"].find({}, { projection: { scoreID: 1 } })).map( (e) => e.scoreID, ); } diff --git a/typescript/server/src/utils/queries/charts.ts b/typescript/server/src/utils/queries/charts.ts index ca3c71e3b..42a823bd0 100644 --- a/typescript/server/src/utils/queries/charts.ts +++ b/typescript/server/src/utils/queries/charts.ts @@ -10,10 +10,10 @@ import type { Versions, } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; export function FindChartWithChartID(game: GameGroup, chartID: string) { - return db.anyCharts[game].findOne({ chartID }); + return MONGODB_KILL.anyCharts[game].findOne({ chartID }); } /** @@ -26,7 +26,7 @@ export function FindChartWithPTDF< P extends Playtypes[G] = Playtypes[G], GPT extends GPTString = GPTString, >(game: G, songID: integer, playtype: P, difficulty: Difficulties[GPT]) { - return db.anyCharts[game].findOne({ + return MONGODB_KILL.anyCharts[game].findOne({ songID, playtype, difficulty, @@ -43,7 +43,7 @@ export function FindChartWithPTDFVersion< P extends Playtypes[G] = Playtypes[G], GPT extends GPTString = GPTString, >(game: G, songID: integer, playtype: P, difficulty: Difficulties[GPT], version: Versions[GPT]) { - return db.anyCharts[game].findOne({ + return MONGODB_KILL.anyCharts[game].findOne({ songID, playtype, difficulty, @@ -52,7 +52,7 @@ export function FindChartWithPTDFVersion< } export function FindITGChartOnHash(hash: string) { - return db.charts.itg.findOne({ + return MONGODB_KILL.charts.itg.findOne({ "data.hashGSv3": hash, }); } @@ -62,7 +62,7 @@ export function FindITGChartOnHash(hash: string) { * @param hash The md5 or sha256 hash to look for. */ export function FindBMSChartOnHash(hash: string) { - return db.charts.bms.findOne({ + return MONGODB_KILL.charts.bms.findOne({ $or: [{ "data.hashMD5": hash }, { "data.hashSHA256": hash }], }) as Promise | null>; } @@ -80,7 +80,7 @@ export function FindChartOnInGameID( throw new Error(`Cannot call FindChartOnInGameID for game ${game}.`); } - return db.anyCharts[game].findOne({ + return MONGODB_KILL.anyCharts[game].findOne({ "data.inGameID": inGameID, playtype, difficulty, @@ -96,7 +96,7 @@ export function FindIIDXChartOnInGameID( playtype: Playtypes["iidx"], difficulty: Difficulties["iidx:DP" | "iidx:SP"], ) { - return db.charts.iidx.findOne({ + return MONGODB_KILL.charts.iidx.findOne({ "data.inGameID": inGameID, "data.2dxtraSet": null, isPrimary: true, @@ -115,7 +115,7 @@ export function FindIIDXChartOnInGameIDVersion( difficulty: Difficulties["iidx:DP" | "iidx:SP"], version: Versions["iidx:DP" | "iidx:SP"], ) { - return db.charts.iidx.findOne({ + return MONGODB_KILL.charts.iidx.findOne({ "data.inGameID": inGameID, "data.2dxtraSet": null, playtype, @@ -134,7 +134,7 @@ export function FindChartOnInGameIDVersion( difficulty: Difficulties[GPT], version: Versions[GPT], ) { - return db.anyCharts[game].findOne({ + return MONGODB_KILL.anyCharts[game].findOne({ "data.inGameID": inGameID, versions: version, playtype, @@ -146,7 +146,7 @@ export function FindChartOnInGameIDVersion( * Finds an IIDX chart on its 2dxtra hash, which is the sha256 of the .1 buffer. */ export function FindIIDXChartWith2DXtraHash(hash: string) { - return db.charts.iidx.findOne({ + return MONGODB_KILL.charts.iidx.findOne({ "data.hashSHA256": hash, }); } @@ -166,7 +166,7 @@ export function FindSDVXChartOnInGameID( ? { $in: ["INF", "GRV", "HVN", "VVD", "XCD"] as Array } : difficulty; - return db.charts.sdvx.findOne({ + return MONGODB_KILL.charts.sdvx.findOne({ "data.inGameID": inGameID, difficulty: diffQuery, isPrimary: true, @@ -183,7 +183,7 @@ export function FindSDVXChartOnInGameIDVersion( ? { $in: ["INF", "GRV", "HVN", "VVD", "XCD"] as Array } : difficulty; - return db.charts.sdvx.findOne({ + return MONGODB_KILL.charts.sdvx.findOne({ "data.inGameID": inGameID, difficulty: diffQuery, versions: version, @@ -200,7 +200,7 @@ export function FindSDVXChartOnDFVersion( ? { $in: ["INF", "GRV", "HVN", "VVD", "XCD"] as Array } : difficulty; - return db.charts.sdvx.findOne({ + return MONGODB_KILL.charts.sdvx.findOne({ songID, difficulty: diffQuery, versions: version, @@ -212,7 +212,7 @@ export function FindChartOnSHA256(game: GameGroup, hash: string) { throw new Error(`Cannot call FindChartOnSHA256 for game ${game}.`); } - return db.anyCharts[game].findOne({ + return MONGODB_KILL.anyCharts[game].findOne({ "data.hashSHA256": hash, }); } @@ -222,7 +222,7 @@ export function FindChartOnSHA256Playtype(game: GameGroup, hash: string, playtyp throw new Error(`Cannot call FindChartOnSHA256 for game ${game}.`); } - return db.anyCharts[game].findOne({ + return MONGODB_KILL.anyCharts[game].findOne({ "data.hashSHA256": hash, playtype, }); @@ -257,36 +257,36 @@ export async function FindChartsOnPopularity( // magnitude faster. // Not entirely sure why, but $lookup is incredibly inefficient, // and you should just avoid it. - const charts = (await db.anyCharts[game].find(matchQuery)) as unknown as Array< + const charts = (await MONGODB_KILL.anyCharts[game].find(matchQuery)) as unknown as Array< { __playcount: integer; } & ChartDocument >; - const scoreCounts: Array<{ _id: string; count: integer }> = await db[scoreCollection].aggregate( - [ - { - $match: { chartID: { $in: charts.map((e) => e.chartID) } }, + const scoreCounts: Array<{ _id: string; count: integer }> = await MONGODB_KILL[ + scoreCollection + ].aggregate([ + { + $match: { chartID: { $in: charts.map((e) => e.chartID) } }, + }, + { + $group: { + _id: "$chartID", + count: { $sum: 1 }, }, - { - $group: { - _id: "$chartID", - count: { $sum: 1 }, - }, + }, + { + $sort: { + count: -1, }, - { - $sort: { - count: -1, - }, - }, - { - $skip: skip, - }, - { - $limit: limit, - }, - ], - ); + }, + { + $skip: skip, + }, + { + $limit: limit, + }, + ]); const scoreCountMap = new Map(); diff --git a/typescript/server/src/utils/queries/pbs.ts b/typescript/server/src/utils/queries/pbs.ts index e0c21cbb4..0b2d2fbdc 100644 --- a/typescript/server/src/utils/queries/pbs.ts +++ b/typescript/server/src/utils/queries/pbs.ts @@ -1,9 +1,9 @@ import type { PBScoreDocument } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; export async function GetAdjacentAbove(userPB: PBScoreDocument, size = 5) { - const adjAbove = (await db["personal-bests"].find( + const adjAbove = (await MONGODB_KILL["personal-bests"].find( { chartID: userPB.chartID, "rankingData.rank": { $lt: userPB.rankingData.rank }, @@ -18,7 +18,7 @@ export async function GetAdjacentAbove(userPB: PBScoreDocument, size = 5) { } export async function GetAdjacentBelow(userPB: PBScoreDocument, size = 5) { - const adjAbove = (await db["personal-bests"].find( + const adjAbove = (await MONGODB_KILL["personal-bests"].find( { chartID: userPB.chartID, "rankingData.rank": { $gt: userPB.rankingData.rank }, diff --git a/typescript/server/src/utils/queries/scores.ts b/typescript/server/src/utils/queries/scores.ts index c2331b08c..29b35c1d7 100644 --- a/typescript/server/src/utils/queries/scores.ts +++ b/typescript/server/src/utils/queries/scores.ts @@ -1,6 +1,6 @@ import type { GameGroup, integer, Playtype } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; export async function GetRecentUGPTScores( userID: integer, @@ -8,7 +8,7 @@ export async function GetRecentUGPTScores( playtype: Playtype, limit = 100, ) { - return db.scores.find( + return MONGODB_KILL.scores.find( { userID, game, @@ -29,7 +29,7 @@ export async function GetRecentUGPTHighlights( playtype: Playtype, limit = 100, ) { - return db.scores.find( + return MONGODB_KILL.scores.find( { userID, game, diff --git a/typescript/server/src/utils/queries/settings.ts b/typescript/server/src/utils/queries/settings.ts new file mode 100644 index 000000000..aa5e4b0af --- /dev/null +++ b/typescript/server/src/utils/queries/settings.ts @@ -0,0 +1,11 @@ +import DB from "#services/pg/db.js"; +import { type integer } from "tachi-common"; + +export async function GetFollowingForUser(userID: integer): Promise> { + const results = await DB.selectFrom("account_following") + .select("followee") + .where("user_id", "=", userID) + .execute(); + + return results.map((r) => r.followee); +} diff --git a/typescript/server/src/utils/queries/songs.ts b/typescript/server/src/utils/queries/songs.ts index bf2bb1034..f99313a01 100644 --- a/typescript/server/src/utils/queries/songs.ts +++ b/typescript/server/src/utils/queries/songs.ts @@ -6,7 +6,7 @@ import { AmbiguousTitleFailure, InternalFailure, } from "#lib/score-import/framework/common/converter-failures"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { EscapeStringRegexp } from "../misc"; @@ -23,7 +23,7 @@ export async function FindSongOnTitle( title: string, ): Promise { // @optimisable: Performance should be tested here by having a utility field for all-titles. - const res = await db.anySongs[game].find( + const res = await MONGODB_KILL.anySongs[game].find( { $or: [ { @@ -63,7 +63,7 @@ export async function FindSongOnTitleInsensitive( const regexTitle = new RegExp(`^${EscapeStringRegexp(title)}$`, "iu"); const regexArtist = new RegExp(`^${EscapeStringRegexp(artist ?? "")}$`, "iu"); - const res = await db.anySongs[game].find( + const res = await MONGODB_KILL.anySongs[game].find( { $and: [ { @@ -111,7 +111,7 @@ export function FindSongOnID( game: GameGroup, songID: integer, ): Promise> { - return db.anySongs[game].findOne({ + return MONGODB_KILL.anySongs[game].findOne({ id: songID, }); } diff --git a/typescript/server/src/utils/queries/stats.ts b/typescript/server/src/utils/queries/stats.ts index ca3e942c4..2b7ee4aad 100644 --- a/typescript/server/src/utils/queries/stats.ts +++ b/typescript/server/src/utils/queries/stats.ts @@ -1,7 +1,7 @@ import type { Classes, GameGroup, GPTString, integer, Playtype } from "tachi-common"; import { ONE_HOUR } from "#lib/constants/time"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import NodeCache from "node-cache"; const classDistCache = new NodeCache(); @@ -18,7 +18,7 @@ export async function GetClassDistribution( const distribution: Array<{ _id: string; count: integer; - }> = await db["game-stats"].aggregate([ + }> = await MONGODB_KILL["game-stats"].aggregate([ { $match: { game, diff --git a/typescript/server/src/utils/queries/summary.ts b/typescript/server/src/utils/queries/summary.ts index 3373ea4d4..89f7df692 100644 --- a/typescript/server/src/utils/queries/summary.ts +++ b/typescript/server/src/utils/queries/summary.ts @@ -1,6 +1,6 @@ import type { integer } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { GetEnumDistForFolders } from "#utils/folder"; import { GetTimeXHoursAgo } from "#utils/misc"; @@ -10,13 +10,13 @@ const REASONABLE_HOURS_AGO = 16; export function GetRecentPlaycount(userID: integer) { const time = GetTimeXHoursAgo(REASONABLE_HOURS_AGO); - return db.scores.count({ userID, timeAchieved: { $gte: time } }); + return MONGODB_KILL.scores.count({ userID, timeAchieved: { $gte: time } }); } export function GetRecentSessions(userID: integer) { const time = GetTimeXHoursAgo(REASONABLE_HOURS_AGO); - return db.sessions.find({ + return MONGODB_KILL.sessions.find({ userID, timeEnded: { $gte: time }, }); @@ -25,7 +25,7 @@ export function GetRecentSessions(userID: integer) { export async function GetRecentlyViewedFoldersAnyGPT(userID: integer) { const time = GetTimeXHoursAgo(REASONABLE_HOURS_AGO); - const views = await db["recent-folder-views"].find( + const views = await MONGODB_KILL["recent-folder-views"].find( { userID, lastViewed: { $gte: time }, @@ -38,7 +38,7 @@ export async function GetRecentlyViewedFoldersAnyGPT(userID: integer) { }, ); - const folders = await db.folders.find({ + const folders = await MONGODB_KILL.folders.find({ folderID: { $in: views.map((e) => e.folderID) }, }); @@ -53,19 +53,19 @@ export async function GetRecentlyViewedFoldersAnyGPT(userID: integer) { export async function GetGoalSummary(userID: integer) { const time = GetTimeXHoursAgo(REASONABLE_HOURS_AGO); - const achievedGoals = await db["goal-subs"].find({ + const achievedGoals = await MONGODB_KILL["goal-subs"].find({ timeAchieved: { $gte: time }, wasInstantlyAchieved: false, userID, }); - const improvedGoals = await db["goal-subs"].find({ + const improvedGoals = await MONGODB_KILL["goal-subs"].find({ lastInteraction: { $gte: time }, achieved: false, userID, }); - const goals = await db.goals.find({ + const goals = await MONGODB_KILL.goals.find({ goalID: { $in: [...achievedGoals.map((e) => e.goalID), ...improvedGoals.map((e) => e.goalID)], }, diff --git a/typescript/server/src/utils/reset-state/destroy-ugpt.ts b/typescript/server/src/utils/reset-state/destroy-ugpt.ts index 3e5be25ed..cdc988f29 100644 --- a/typescript/server/src/utils/reset-state/destroy-ugpt.ts +++ b/typescript/server/src/utils/reset-state/destroy-ugpt.ts @@ -1,6 +1,6 @@ import type { GameGroup, integer, Playtype } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { UpdateAllPBs } from "#utils/calculations/recalc-scores"; /** @@ -13,20 +13,20 @@ export default async function DestroyUserGamePlaytypeData( game: GameGroup, playtype: Playtype, ) { - await db["game-stats-snapshots"].remove({ + await MONGODB_KILL["game-stats-snapshots"].remove({ userID, game, playtype, }); - await db.scores.remove({ + await MONGODB_KILL.scores.remove({ userID, game, playtype, }); const chartIDs = ( - await db["personal-bests"].find( + await MONGODB_KILL["personal-bests"].find( { userID, game, @@ -40,7 +40,7 @@ export default async function DestroyUserGamePlaytypeData( ) ).map((e) => e.chartID); - await db["personal-bests"].remove({ + await MONGODB_KILL["personal-bests"].remove({ userID, game, playtype, @@ -50,25 +50,25 @@ export default async function DestroyUserGamePlaytypeData( chartID: { $in: chartIDs }, }); - await db.sessions.remove({ + await MONGODB_KILL.sessions.remove({ userID, game, playtype, }); - await db.imports.remove({ + await MONGODB_KILL.imports.remove({ userID, game, playtype, }); - await db["game-settings"].remove({ + await MONGODB_KILL["game-settings"].remove({ userID, game, playtype, }); - await db["game-stats"].remove({ + await MONGODB_KILL["game-stats"].remove({ userID, game, playtype, diff --git a/typescript/server/src/utils/response.ts b/typescript/server/src/utils/response.ts new file mode 100644 index 000000000..f2bb475eb --- /dev/null +++ b/typescript/server/src/utils/response.ts @@ -0,0 +1,25 @@ +import { type TachiAPIFailResponse } from "./types"; + +export function apiSuccess( + description: string, + data: T, +): { + body: T; + description: string; + success: true; +} { + return { + success: true, + description: description, + body: data, + }; +} + +export function apiFail(description: string): { + description: string; +} & TachiAPIFailResponse { + return { + description: description, + success: false, + }; +} diff --git a/typescript/server/src/utils/scores.ts b/typescript/server/src/utils/scores.ts index 50fc93a2b..369b75269 100644 --- a/typescript/server/src/utils/scores.ts +++ b/typescript/server/src/utils/scores.ts @@ -6,19 +6,19 @@ import type { SongDocument, } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { DedupeArr } from "./misc"; export function GetPBOnChart(userID: integer, chartID: string) { - return db["personal-bests"].findOne({ + return MONGODB_KILL["personal-bests"].findOne({ userID, chartID, }); } export function GetServerRecordOnChart(chartID: string) { - return db["personal-bests"].findOne({ + return MONGODB_KILL["personal-bests"].findOne({ chartID, "rankingData.rank": 1, }); diff --git a/typescript/server/src/utils/session.test.ts b/typescript/server/src/utils/session.oldtest.ts similarity index 84% rename from typescript/server/src/utils/session.test.ts rename to typescript/server/src/utils/session.oldtest.ts index 4aab337c2..af5859ab1 100644 --- a/typescript/server/src/utils/session.test.ts +++ b/typescript/server/src/utils/session.oldtest.ts @@ -1,4 +1,4 @@ -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import ResetDBState from "#test-utils/resets"; import t from "tap"; @@ -9,7 +9,7 @@ t.test("#GetScoresFromSession", async (t) => { await ResetDBState(); - const exampleSession = await db.sessions.findOne(); + const exampleSession = await MONGODB_KILL.sessions.findOne(); const scores = await GetScoresFromSession(exampleSession!); diff --git a/typescript/server/src/utils/session.ts b/typescript/server/src/utils/session.ts index 5dd1c7e67..8180e0454 100644 --- a/typescript/server/src/utils/session.ts +++ b/typescript/server/src/utils/session.ts @@ -1,13 +1,13 @@ import type { ScoreDocument, SessionDocument } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; /** * Returns all the score documents inside a session. * @param session The session to retrieve the score documents of. */ export function GetScoresFromSession(session: SessionDocument) { - return db.scores.find({ + return MONGODB_KILL.scores.find({ scoreID: { $in: session.scoreIDs }, }); } @@ -17,7 +17,7 @@ export function GetScoresFromSession(session: SessionDocument) { * @param score The score to return the associated session of. */ export function GetSessionFromScore(score: ScoreDocument) { - return db.sessions.findOne({ + return MONGODB_KILL.sessions.findOne({ scoreIDs: score.scoreID, }); } diff --git a/typescript/server/src/utils/time.ts b/typescript/server/src/utils/time.ts new file mode 100644 index 000000000..a39174683 --- /dev/null +++ b/typescript/server/src/utils/time.ts @@ -0,0 +1,7 @@ +export function UnixMillisecondsToISO8601(ms: number) { + return new Date(ms).toISOString(); +} + +export function ISO8601ToUnixMilliseconds(iso: string) { + return new Date(iso).getTime(); +} diff --git a/typescript/server/src/utils/user.test.ts b/typescript/server/src/utils/user.oldtest.ts similarity index 94% rename from typescript/server/src/utils/user.test.ts rename to typescript/server/src/utils/user.oldtest.ts index b4b4460ba..91cad2d57 100644 --- a/typescript/server/src/utils/user.test.ts +++ b/typescript/server/src/utils/user.oldtest.ts @@ -1,6 +1,6 @@ import type { UserDocument } from "tachi-common"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; import { DatabaseSchemas } from "#services/mongo/schemas"; import { mkFakeUser } from "#test-utils/misc"; import ResetDBState from "#test-utils/resets"; @@ -54,7 +54,7 @@ t.test("#GetUserCaseInsensitive", (t) => { t.test("#GetUsersWithIDs", (t) => { t.beforeEach(ResetDBState); - t.beforeEach(() => db.users.insert([mkFakeUser(2), mkFakeUser(3), mkFakeUser(4)])); + t.beforeEach(() => MONGODB_KILL.users.insert([mkFakeUser(2), mkFakeUser(3), mkFakeUser(4)])); t.test("Should return users with these IDs.", async (t) => { const res = await GetUsersWithIDs([2, 3]); diff --git a/typescript/server/src/utils/user.ts b/typescript/server/src/utils/user.ts index 643d0fd6d..4942be4d2 100644 --- a/typescript/server/src/utils/user.ts +++ b/typescript/server/src/utils/user.ts @@ -1,8 +1,9 @@ -import type { FindOneResult } from "monk"; - import { ONE_DAY } from "#lib/constants/time"; +import { SELECT_USER, ToUserDocument } from "#lib/db-formats/user.js"; +import { SELECT_USER_SETTINGS, ToUserSettingsDocument } from "#lib/db-formats/user-settings.js"; import { log } from "#lib/log/log.js"; -import db from "#services/mongo/db"; +import MONGODB_KILL from "#services/mongo/db"; +import DB from "#services/pg/db.js"; import { type APITokenDocument, type GameGroup, @@ -14,69 +15,80 @@ import { UserAuthLevels, type UserDocument, type UserGameStats, + type UserSettingsDocument, } from "tachi-common"; +import { GetFollowingForUser } from "./queries/settings"; + /** * Returns a user's username from their ID. Throws if no user with that ID exists. */ export async function GetUsernameFromUserID(userID: integer): Promise { - const partialDoc = await db.users.findOne( - { - id: userID, - }, - { - projection: { - username: 1, - }, - }, - ); + const username = await DB.selectFrom("account") + .select("username") + .where("id", "=", userID) + .executeTakeFirst() + .then((res) => res?.username); - if (!partialDoc) { + if (!username) { throw new Error(`Could not find username for userID ${userID}.`); } - - return partialDoc.username; + return username; } /** * Gets a user based on their username case-insensitively. */ -export function GetUserCaseInsensitive(username: string): Promise> { - return db.users.findOne({ - usernameLowercase: username.toLowerCase(), - }); +export function GetUserCaseInsensitive(username: string): Promise { + return DB.selectFrom("account") + .select(SELECT_USER) + .where("normalized_username", "=", username.toLowerCase()) + .executeTakeFirst() + .then((res) => (res ? ToUserDocument(res) : null)); } export async function CheckIfEmailInUse(email: string) { - const doc = await db["user-private-information"].findOne({ email }); + const exists = await DB.selectFrom("priv_account_credential") + .where("email", "=", email) + .executeTakeFirst() + .then((res) => !!res); - return !!doc; + return exists; } export function GetUserPrivateInfo(userID: integer) { - return db["user-private-information"].findOne({ userID }); + return DB.selectFrom("priv_account_credential") + .selectAll() + .where("user_id", "=", userID) + .executeTakeFirst(); } /** * Gets a user from their userID. */ -export function GetUserWithID(userID: integer): Promise> { - return db.users.findOne({ - id: userID, - }); +export function GetUserWithID(userID: integer): Promise { + return DB.selectFrom("account") + .select(SELECT_USER) + .where("id", "=", userID) + .executeTakeFirst() + .then((res) => (res ? ToUserDocument(res) : null)); } -export function GetSettingsForUser(userID: integer) { - return db["user-settings"].findOne({ - userID, - }); +export async function GetSettingsForUser(userID: integer): Promise { + const following = await GetFollowingForUser(userID); + + return DB.selectFrom("account_settings") + .select(SELECT_USER_SETTINGS) + .where("user_id", "=", userID) + .executeTakeFirstOrThrow() + .then((res) => ToUserSettingsDocument(following, res)); } /** * Gets the users for these user IDs. */ export async function GetUsersWithIDs(userIDs: Array) { - const users = await db.users.find({ + const users = await MONGODB_KILL.users.find({ id: { $in: userIDs }, }); @@ -140,7 +152,7 @@ export function FormatUserDoc(userdoc: UserDocument) { export async function GetUsersRanking(stats: UserGameStats) { const gptConfig = GetGamePTConfig(stats.game, stats.playtype); - const aggRes: [{ _id: null; ranking: integer }] = await db["game-stats"].aggregate([ + const aggRes: [{ _id: null; ranking: integer }] = await MONGODB_KILL["game-stats"].aggregate([ { $match: { game: stats.game, @@ -172,7 +184,7 @@ export async function GetUsersRanking(stats: UserGameStats) { } export function GetUGPTPlaycount(userID: integer, game: GameGroup, playtype: Playtype) { - return db.scores.count({ userID, game, playtype }); + return MONGODB_KILL.scores.count({ userID, game, playtype }); } export async function GetAllRankings(stats: UserGameStats) { @@ -207,7 +219,7 @@ export async function GetUsersRankingAndOutOf( outOf: integer; ranking: integer; }, - ] = await db["game-stats"].aggregate([ + ] = await MONGODB_KILL["game-stats"].aggregate([ { $match: { game: stats.game, @@ -267,11 +279,22 @@ export async function IsRequesterAdmin(request: APITokenDocument) { return user.authLevel === UserAuthLevels.ADMIN; } +export async function IsUserAdmin(userID: integer) { + const exists = await DB.selectFrom("account") + .select("auth_level") + .where("id", "=", userID) + .where("auth_level", "=", "admin") + .executeTakeFirst() + .then((res) => !!res); + + return exists; +} + /** * Return all the GPTs this userID has played. */ export async function GetUserPlayedGPTs(userID: integer) { - const gpts = (await db["game-stats"].find( + const gpts = (await MONGODB_KILL["game-stats"].find( { userID }, { projection: { game: 1, playtype: 1 } }, )) as Array>; @@ -280,7 +303,9 @@ export async function GetUserPlayedGPTs(userID: integer) { } export async function GetAllUserRivals(userID: integer) { - const rivals = (await db["game-settings"].find({ userID }, { projection: { rivals: 1 } })) + const rivals = ( + await MONGODB_KILL["game-settings"].find({ userID }, { projection: { rivals: 1 } }) + ) .map((e) => e.rivals) .flat(); @@ -296,7 +321,7 @@ export async function CanChangeUsername(userID: integer) { } export async function GetNextAvailableUsernameChange(userID: integer): Promise { - const lastChange = await db["user-name-changes"].findOne( + const lastChange = await MONGODB_KILL["user-name-changes"].findOne( { userID }, { sort: { timestamp: -1 } }, ); @@ -307,3 +332,27 @@ export async function GetNextAvailableUsernameChange(userID: integer): Promise { + const admin = await DB.selectFrom("account") + .select(SELECT_USER) + .where("auth_level", "=", "admin") + .orderBy("id", "asc") + .executeTakeFirst(); + + if (!admin) { + throw new Error("There is no admin on this instance of Tachi."); + } + + return ToUserDocument(admin); +} diff --git a/typescript/server/tsconfig.json b/typescript/server/tsconfig.json index 53a3044a1..c330bb552 100644 --- a/typescript/server/tsconfig.json +++ b/typescript/server/tsconfig.json @@ -9,5 +9,5 @@ } }, "include": ["src"], - "exclude": ["node_modules"] + "exclude": ["node_modules", "src/**/*.oldtest.ts"] } diff --git a/typescript/server/vitest.config.ts b/typescript/server/vitest.config.ts new file mode 100644 index 000000000..02151b8c8 --- /dev/null +++ b/typescript/server/vitest.config.ts @@ -0,0 +1,51 @@ +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { defineConfig } from "vitest/config"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +// Read the test server config as a raw string — config.ts will JSON5-parse it at load time. +const tachiConfig = fs.readFileSync(path.join(__dirname, "test.conf.json5"), "utf-8"); + +export default defineConfig({ + resolve: { + // Map #* path aliases to src/* so vite-node resolves them correctly. + alias: [ + { + find: /^#(.+)/u, + replacement: `${path.resolve(__dirname, "src")}/$1`, + }, + ], + }, + + test: { + // Static env vars. POSTGRES_URL is set dynamically per-worker in vitest.setup.ts + // so each worker gets its own isolated database. + env: { + NODE_ENV: "test", + PORT: "8080", + // Still required by config.ts validation; unused by new Postgres-based tests. + MONGO_URL: "tachi-mongo", + REDIS_URL: "tachi-redis", + MIGRATIONS_DIR: "/tachi/db/migrations", + LOG_LEVEL: "warn", + VERSION: "test", + COMMIT_HASH: "test", + TACHI_CONFIG: tachiConfig, + }, + + // Parallel test execution — each worker gets its own isolated Postgres database. + fileParallelism: true, + globalSetup: "./vitest.globalSetup.ts", + setupFiles: "./vitest.setup.ts", + // forks pool gives stronger process isolation between workers. + pool: "forks", + + coverage: { + provider: "v8", + include: ["src/**/*.ts"], + exclude: ["src/**/*.test.ts", "src/**/*.oldtest.ts", "src/test-utils/**"], + }, + }, +}); diff --git a/typescript/server/vitest.globalSetup.ts b/typescript/server/vitest.globalSetup.ts new file mode 100644 index 000000000..7b854d2a9 --- /dev/null +++ b/typescript/server/vitest.globalSetup.ts @@ -0,0 +1,11 @@ +import { execSync } from "node:child_process"; + +/** + * Global vitest setup — runs ONCE before any workers start. + * + * Creates a fully-migrated template database. Workers clone from it + * instead of running migrations themselves, which is much faster. + */ +export default function globalSetup() { + execSync("just server-db-test-template-reset", { stdio: "inherit" }); +} diff --git a/typescript/server/vitest.setup.ts b/typescript/server/vitest.setup.ts new file mode 100644 index 000000000..c525a0cc2 --- /dev/null +++ b/typescript/server/vitest.setup.ts @@ -0,0 +1,112 @@ +/** + * Vitest per-worker setup for parallel test execution. + * + * Each worker gets its own isolated Postgres database cloned from the + * template created in vitest.globalSetup.ts. + * + * IMPORTANT: process.env assignments at the top level of this module run + * before the test file's module graph is resolved, so app code that reads + * env vars at import time (config.ts) sees the right values. + */ + +import crypto from "node:crypto"; + +const WORKER_ID = crypto.randomUUID().slice(0, 8); +const WORKER_DB_NAME = `tachi_server_test_${WORKER_ID}`; + +const POSTGRES_HOST = "tachi-postgres"; +const POSTGRES_USER = "tachi"; +const POSTGRES_PASS = "tachi"; + +// Set POSTGRES_URL before any app code is imported — config.ts reads it at load time. +process.env.POSTGRES_URL = `postgresql://${POSTGRES_USER}:${POSTGRES_PASS}@${POSTGRES_HOST}/${WORKER_DB_NAME}`; + +// Now that env vars are set, we can safely import external packages. +import pg from "pg"; +import { afterAll, beforeAll, beforeEach } from "vitest"; + +const { Client } = pg; + +function adminClient() { + return new Client({ + host: POSTGRES_HOST, + user: POSTGRES_USER, + password: POSTGRES_PASS, + database: "postgres", + }); +} + +async function createWorkerDatabase() { + const client = adminClient(); + + await client.connect(); + + try { + await client.query( + `CREATE DATABASE "${WORKER_DB_NAME}" TEMPLATE tachi_server_test_template`, + ); + } finally { + await client.end(); + } +} + +async function dropWorkerDatabase() { + const client = adminClient(); + + await client.connect(); + + try { + // Terminate any open connections first so DROP DATABASE succeeds. + await client.query(` + SELECT pg_terminate_backend(pid) + FROM pg_stat_activity + WHERE datname = '${WORKER_DB_NAME}' + `); + + await client.query(`DROP DATABASE IF EXISTS "${WORKER_DB_NAME}"`); + } finally { + await client.end(); + } +} + +async function resetDatabase() { + // Lazily import so env vars are definitely set before the pool is created. + const { default: db } = await import("#services/pg/db"); + const { sql } = await import("kysely"); + + await sql` + DO $$ DECLARE + row RECORD; + BEGIN + FOR row IN ( + SELECT table_name + FROM information_schema.tables + WHERE table_schema = 'public' AND table_type = 'BASE TABLE' + ) LOOP + EXECUTE 'TRUNCATE TABLE ' || quote_ident(row.table_name) || ' RESTART IDENTITY CASCADE'; + END LOOP; + END $$; + `.execute(db); +} + +beforeAll(async () => { + await createWorkerDatabase(); +}); + +beforeEach(async () => { + await resetDatabase(); +}); + +afterAll( + async () => { + try { + const { ClosePgConnection } = await import("#services/pg/db"); + await ClosePgConnection(); + } catch { + // Pool may not have been initialised if no test ran a query. + } + + await dropWorkerDatabase(); + }, + process.env.CI ? 60_000 : undefined, +);