A discord bot for teal.fm
discord tealfm music

yuh

besaid.zone 0dece825 bdf95bc4

verified
Changed files
+209 -3
.tangled
workflows
commands
+17
.tangled/workflows/lint.yml
··· 1 + when: 2 + - event: ["push"] 3 + branch: main 4 + 5 + engine: nixery 6 + 7 + dependencies: 8 + nixpkgs: 9 + - nodejs_24 10 + - pnpm 11 + 12 + steps: 13 + - name: install 14 + command: pnpm i --frozen-lockfile 15 + 16 + - name: lint 17 + command: pnpm typecheck
+6
PLAN.md
··· 4 4 - ``/teal auth`` sends user a link to log in with atproto account 5 5 - after auth success, we take did and send http request to tap instance to start backfilling for repo 6 6 - user can now use bot commands 7 + 8 + ## planned commands 9 + 10 + - ``teal auth`` sends user a link to log in with atproto account 11 + - ``top <artist>``: top 10 listeners for artist (total amount of plays across all songs / albums) 12 + - ``recent``: most recent play
+12
commands/auth.ts
··· 1 + import { CommandInteraction, SlashCommandBuilder } from "discord.js"; 2 + import { logger } from "../logger.ts"; 3 + 4 + export default { 5 + data: new SlashCommandBuilder().setName("auth").setDescription( 6 + "Authenticate your account with the teal.fm bot to start tracking your listens", 7 + ), 8 + async execute(interaction: CommandInteraction) { 9 + await interaction.reply("placeholder"); 10 + logger.info("auth command sent") 11 + }, 12 + };
+12
commands/top.ts
··· 1 + import { CommandInteraction, SlashCommandBuilder } from "discord.js"; 2 + import { logger } from "../logger.ts"; 3 + 4 + export default { 5 + data: new SlashCommandBuilder().setName("top").setDescription( 6 + "Find the top listeners for the specified artist(s)", 7 + ), 8 + async execute(interaction: CommandInteraction) { 9 + await interaction.reply("placeholder"); 10 + logger.info("top command sent") 11 + }, 12 + };
+2
commands/utility/ping.ts commands/ping.ts
··· 1 1 import { CommandInteraction, SlashCommandBuilder } from "discord.js"; 2 + import { logger } from "../logger.ts"; 2 3 3 4 export default { 4 5 data: new SlashCommandBuilder().setName("ping").setDescription( ··· 6 7 ), 7 8 async execute(interaction: CommandInteraction) { 8 9 await interaction.reply("pong lol"); 10 + logger.info("ping command sent") 9 11 }, 10 12 };
+10
logger.ts
··· 1 + import pino from "pino" 2 + 3 + export const logger = pino({ 4 + transport: { 5 + target: "pino-pretty", 6 + options: { 7 + colorize: true 8 + } 9 + } 10 + })
+3 -2
main.ts
··· 8 8 import fs from "node:fs"; 9 9 import path from "node:path"; 10 10 import { DISCORD_BOT_TOKEN } from "./constants.ts"; 11 + import { logger } from "./logger.ts"; 11 12 12 13 const client = new Client({ intents: [GatewayIntentBits.Guilds] }); 13 14 ··· 44 45 45 46 client.commands = new Collection(); 46 47 47 - const commandPaths = fs.globSync("commands/**/*.ts"); 48 + const commandPaths = fs.globSync("commands/*.ts"); 48 49 for await (const file of commandPaths) { 49 50 const absoluteCommandPath = path.join(import.meta.dirname, file); 50 51 const command = await import(absoluteCommandPath); 51 52 if ("data" in command.default && "execute" in command.default) { 52 53 client.commands.set(command.default.data.name, command.default); 53 54 } else { 54 - console.warn( 55 + logger.warn( 55 56 `[Warning] The command at ${absoluteCommandPath} is missing a required "data" or "execute" property.`, 56 57 ); 57 58 }
+3 -1
package.json
··· 26 26 "@atproto/tap": "^0.0.2", 27 27 "discord.js": "^14.25.1", 28 28 "kysely": "^0.28.9", 29 - "pg": "^8.16.3" 29 + "pg": "^8.16.3", 30 + "pino": "^10.1.0" 30 31 }, 31 32 "devDependencies": { 32 33 "@types/node": "^25.0.3", 33 34 "@types/pg": "^8.16.0", 34 35 "kysely-codegen": "^0.19.0", 36 + "pino-pretty": "^13.1.3", 35 37 "tsx": "^4.21.0", 36 38 "typescript": "^5.9.3" 37 39 }
+144
pnpm-lock.yaml
··· 20 20 pg: 21 21 specifier: ^8.16.3 22 22 version: 8.16.3 23 + pino: 24 + specifier: ^10.1.0 25 + version: 10.1.0 23 26 devDependencies: 24 27 '@types/node': 25 28 specifier: ^25.0.3 ··· 30 33 kysely-codegen: 31 34 specifier: ^0.19.0 32 35 version: 0.19.0(kysely@0.28.9)(pg@8.16.3)(typescript@5.9.3) 36 + pino-pretty: 37 + specifier: ^13.1.3 38 + version: 13.1.3 33 39 tsx: 34 40 specifier: ^4.21.0 35 41 version: 4.21.0 ··· 258 264 cpu: [x64] 259 265 os: [win32] 260 266 267 + '@pinojs/redact@0.4.0': 268 + resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} 269 + 261 270 '@sapphire/async-queue@1.5.5': 262 271 resolution: {integrity: sha512-cvGzxbba6sav2zZkH8GPf2oGk9yYoD5qrNWdu9fRehifgnFZJMV+nuy2nON2roRO4yQQ+v7MK/Pktl/HgfsUXg==} 263 272 engines: {node: '>=v14.0.0', npm: '>=7.0.0'} ··· 343 352 color-name@1.1.4: 344 353 resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 345 354 355 + colorette@2.0.20: 356 + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} 357 + 346 358 concat-map@0.0.1: 347 359 resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} 348 360 ··· 354 366 peerDependenciesMeta: 355 367 typescript: 356 368 optional: true 369 + 370 + dateformat@4.6.3: 371 + resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} 357 372 358 373 diff@3.5.0: 359 374 resolution: {integrity: sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==} ··· 377 392 dotenv@17.2.3: 378 393 resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==} 379 394 engines: {node: '>=12'} 395 + 396 + end-of-stream@1.4.5: 397 + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} 380 398 381 399 env-paths@2.2.1: 382 400 resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} ··· 402 420 resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} 403 421 engines: {node: '>=0.8.x'} 404 422 423 + fast-copy@4.0.2: 424 + resolution: {integrity: sha512-ybA6PDXIXOXivLJK/z9e+Otk7ve13I4ckBvGO5I2RRmBU1gMHLVDJYEuJYhGwez7YNlYji2M2DvVU+a9mSFDlw==} 425 + 405 426 fast-deep-equal@3.1.3: 406 427 resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} 407 428 408 429 fast-redact@3.5.0: 409 430 resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} 410 431 engines: {node: '>=6'} 432 + 433 + fast-safe-stringify@2.1.1: 434 + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} 411 435 412 436 fill-range@7.1.1: 413 437 resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} ··· 447 471 resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} 448 472 engines: {node: '>= 0.4'} 449 473 474 + help-me@5.0.0: 475 + resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} 476 + 450 477 ieee754@1.2.1: 451 478 resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} 452 479 ··· 478 505 479 506 iso-datestring-validator@2.2.2: 480 507 resolution: {integrity: sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA==} 508 + 509 + joycon@3.1.1: 510 + resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} 511 + engines: {node: '>=10'} 481 512 482 513 js-tokens@4.0.0: 483 514 resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} ··· 623 654 pino-abstract-transport@1.2.0: 624 655 resolution: {integrity: sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==} 625 656 657 + pino-abstract-transport@2.0.0: 658 + resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} 659 + 660 + pino-abstract-transport@3.0.0: 661 + resolution: {integrity: sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==} 662 + 663 + pino-pretty@13.1.3: 664 + resolution: {integrity: sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==} 665 + hasBin: true 666 + 626 667 pino-std-serializers@6.2.2: 627 668 resolution: {integrity: sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==} 669 + 670 + pino-std-serializers@7.0.0: 671 + resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==} 672 + 673 + pino@10.1.0: 674 + resolution: {integrity: sha512-0zZC2ygfdqvqK8zJIr1e+wT1T/L+LF6qvqvbzEQ6tiMAoTqEVK9a1K3YRu8HEUvGEvNqZyPJTtb2sNIoTkB83w==} 675 + hasBin: true 628 676 629 677 pino@8.21.0: 630 678 resolution: {integrity: sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==} ··· 653 701 process-warning@3.0.0: 654 702 resolution: {integrity: sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==} 655 703 704 + process-warning@5.0.0: 705 + resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} 706 + 656 707 process@0.11.10: 657 708 resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} 658 709 engines: {node: '>= 0.6.0'} 659 710 711 + pump@3.0.3: 712 + resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} 713 + 660 714 quick-format-unescaped@4.0.4: 661 715 resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} 662 716 ··· 691 745 resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} 692 746 engines: {node: '>=10'} 693 747 748 + secure-json-parse@4.1.0: 749 + resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==} 750 + 694 751 shelljs.exec@1.1.8: 695 752 resolution: {integrity: sha512-vFILCw+lzUtiwBAHV8/Ex8JsFjelFMdhONIsgKNLgTzeRckp2AOYRQtHJE/9LhNvdMmE27AGtzWx0+DHpwIwSw==} 696 753 engines: {node: '>= 4.0.0'} ··· 702 759 703 760 sonic-boom@3.8.1: 704 761 resolution: {integrity: sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==} 762 + 763 + sonic-boom@4.2.0: 764 + resolution: {integrity: sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==} 705 765 706 766 split2@4.2.0: 707 767 resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} ··· 710 770 string_decoder@1.3.0: 711 771 resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} 712 772 773 + strip-json-comments@5.0.3: 774 + resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} 775 + engines: {node: '>=14.16'} 776 + 713 777 supports-color@5.5.0: 714 778 resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} 715 779 engines: {node: '>=4'} ··· 724 788 725 789 thread-stream@2.7.0: 726 790 resolution: {integrity: sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==} 791 + 792 + thread-stream@3.1.0: 793 + resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} 727 794 728 795 to-regex-range@5.0.1: 729 796 resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} ··· 975 1042 '@esbuild/win32-x64@0.27.2': 976 1043 optional: true 977 1044 1045 + '@pinojs/redact@0.4.0': {} 1046 + 978 1047 '@sapphire/async-queue@1.5.5': {} 979 1048 980 1049 '@sapphire/shapeshift@4.0.0': ··· 1059 1128 1060 1129 color-name@1.1.4: {} 1061 1130 1131 + colorette@2.0.20: {} 1132 + 1062 1133 concat-map@0.0.1: {} 1063 1134 1064 1135 cosmiconfig@9.0.0(typescript@5.9.3): ··· 1070 1141 optionalDependencies: 1071 1142 typescript: 5.9.3 1072 1143 1144 + dateformat@4.6.3: {} 1145 + 1073 1146 diff@3.5.0: {} 1074 1147 1075 1148 discord-api-types@0.38.37: {} ··· 1100 1173 dotenv@16.6.1: {} 1101 1174 1102 1175 dotenv@17.2.3: {} 1176 + 1177 + end-of-stream@1.4.5: 1178 + dependencies: 1179 + once: 1.4.0 1103 1180 1104 1181 env-paths@2.2.1: {} 1105 1182 ··· 1142 1219 1143 1220 events@3.3.0: {} 1144 1221 1222 + fast-copy@4.0.2: {} 1223 + 1145 1224 fast-deep-equal@3.1.3: {} 1146 1225 1147 1226 fast-redact@3.5.0: {} 1227 + 1228 + fast-safe-stringify@2.1.1: {} 1148 1229 1149 1230 fill-range@7.1.1: 1150 1231 dependencies: ··· 1186 1267 dependencies: 1187 1268 function-bind: 1.1.2 1188 1269 1270 + help-me@5.0.0: {} 1271 + 1189 1272 ieee754@1.2.1: {} 1190 1273 1191 1274 import-fresh@3.3.1: ··· 1211 1294 is-number@7.0.0: {} 1212 1295 1213 1296 iso-datestring-validator@2.2.2: {} 1297 + 1298 + joycon@3.1.1: {} 1214 1299 1215 1300 js-tokens@4.0.0: {} 1216 1301 ··· 1327 1412 readable-stream: 4.7.0 1328 1413 split2: 4.2.0 1329 1414 1415 + pino-abstract-transport@2.0.0: 1416 + dependencies: 1417 + split2: 4.2.0 1418 + 1419 + pino-abstract-transport@3.0.0: 1420 + dependencies: 1421 + split2: 4.2.0 1422 + 1423 + pino-pretty@13.1.3: 1424 + dependencies: 1425 + colorette: 2.0.20 1426 + dateformat: 4.6.3 1427 + fast-copy: 4.0.2 1428 + fast-safe-stringify: 2.1.1 1429 + help-me: 5.0.0 1430 + joycon: 3.1.1 1431 + minimist: 1.2.8 1432 + on-exit-leak-free: 2.1.2 1433 + pino-abstract-transport: 3.0.0 1434 + pump: 3.0.3 1435 + secure-json-parse: 4.1.0 1436 + sonic-boom: 4.2.0 1437 + strip-json-comments: 5.0.3 1438 + 1330 1439 pino-std-serializers@6.2.2: {} 1440 + 1441 + pino-std-serializers@7.0.0: {} 1442 + 1443 + pino@10.1.0: 1444 + dependencies: 1445 + '@pinojs/redact': 0.4.0 1446 + atomic-sleep: 1.0.0 1447 + on-exit-leak-free: 2.1.2 1448 + pino-abstract-transport: 2.0.0 1449 + pino-std-serializers: 7.0.0 1450 + process-warning: 5.0.0 1451 + quick-format-unescaped: 4.0.4 1452 + real-require: 0.2.0 1453 + safe-stable-stringify: 2.5.0 1454 + sonic-boom: 4.2.0 1455 + thread-stream: 3.1.0 1331 1456 1332 1457 pino@8.21.0: 1333 1458 dependencies: ··· 1357 1482 1358 1483 process-warning@3.0.0: {} 1359 1484 1485 + process-warning@5.0.0: {} 1486 + 1360 1487 process@0.11.10: {} 1488 + 1489 + pump@3.0.3: 1490 + dependencies: 1491 + end-of-stream: 1.4.5 1492 + once: 1.4.0 1361 1493 1362 1494 quick-format-unescaped@4.0.4: {} 1363 1495 ··· 1389 1521 1390 1522 safe-stable-stringify@2.5.0: {} 1391 1523 1524 + secure-json-parse@4.1.0: {} 1525 + 1392 1526 shelljs.exec@1.1.8: {} 1393 1527 1394 1528 shelljs@0.8.5: ··· 1401 1535 dependencies: 1402 1536 atomic-sleep: 1.0.0 1403 1537 1538 + sonic-boom@4.2.0: 1539 + dependencies: 1540 + atomic-sleep: 1.0.0 1541 + 1404 1542 split2@4.2.0: {} 1405 1543 1406 1544 string_decoder@1.3.0: 1407 1545 dependencies: 1408 1546 safe-buffer: 5.2.1 1547 + 1548 + strip-json-comments@5.0.3: {} 1409 1549 1410 1550 supports-color@5.5.0: 1411 1551 dependencies: ··· 1418 1558 supports-preserve-symlinks-flag@1.0.0: {} 1419 1559 1420 1560 thread-stream@2.7.0: 1561 + dependencies: 1562 + real-require: 0.2.0 1563 + 1564 + thread-stream@3.1.0: 1421 1565 dependencies: 1422 1566 real-require: 0.2.0 1423 1567