diff --git a/bun.lock b/bun.lock index ab93fcc..6903a88 100644 --- a/bun.lock +++ b/bun.lock @@ -3,6 +3,10 @@ "workspaces": { "": { "name": "@vt3e.cat/bbell-backend", + "dependencies": { + "pino": "^10.3.1", + "pino-pretty": "^13.1.3", + }, "devDependencies": { "@typelex/emitter": "^0.4.0", "@types/bun": "latest", @@ -249,6 +253,8 @@ "@optique/run": ["@optique/run@0.6.11", "", { "dependencies": { "@optique/core": "0.6.11" } }, "sha512-tsXBEygGSzNpFK2gjsRlXBn7FiScUeLFWIZNpoAZ8iG85Km0/3K9xgqlQAXoQ+uEZBe4XplnzyCDvmEgbyNT8w=="], + "@pinojs/redact": ["@pinojs/redact@0.4.0", "", {}, "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg=="], + "@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@4.0.0", "", {}, "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ=="], "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], @@ -293,6 +299,8 @@ "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], + "atomic-sleep": ["atomic-sleep@1.0.0", "", {}, "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ=="], + "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=="], @@ -339,6 +347,8 @@ "code-point-at": ["code-point-at@1.1.0", "", {}, "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA=="], + "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=="], "commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], @@ -351,6 +361,8 @@ "dashdash": ["dashdash@1.14.1", "", { "dependencies": { "assert-plus": "^1.0.0" } }, "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g=="], + "dateformat": ["dateformat@4.6.3", "", {}, "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA=="], + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], "default-browser": ["default-browser@5.5.0", "", { "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" } }, "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw=="], @@ -375,6 +387,8 @@ "emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], + "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="], + "env-paths": ["env-paths@4.0.0", "", { "dependencies": { "is-safe-filename": "^0.1.0" } }, "sha512-pxP8eL2SwwaTRi/KHYwLYXinDs7gL3jxFcBYmEdYfZmZXbaVDvdppd0XBU8qVz03rDfKZMXg1omHCbsJjZrMsw=="], "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], @@ -395,12 +409,16 @@ "extsprintf": ["extsprintf@1.3.0", "", {}, "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g=="], + "fast-copy": ["fast-copy@4.0.2", "", {}, "sha512-ybA6PDXIXOXivLJK/z9e+Otk7ve13I4ckBvGO5I2RRmBU1gMHLVDJYEuJYhGwez7YNlYji2M2DvVU+a9mSFDlw=="], + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], + "fast-safe-stringify": ["fast-safe-stringify@2.1.1", "", {}, "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA=="], + "fast-string-truncated-width": ["fast-string-truncated-width@3.0.3", "", {}, "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g=="], "fast-string-width": ["fast-string-width@3.0.2", "", { "dependencies": { "fast-string-truncated-width": "^3.0.2" } }, "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg=="], @@ -449,6 +467,8 @@ "has-ansi": ["has-ansi@2.0.0", "", { "dependencies": { "ansi-regex": "^2.0.0" } }, "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg=="], + "help-me": ["help-me@5.0.0", "", {}, "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg=="], + "http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="], "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=="], @@ -493,6 +513,8 @@ "isstream": ["isstream@0.1.2", "", {}, "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g=="], + "joycon": ["joycon@3.1.1", "", {}, "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw=="], + "js-md4": ["js-md4@0.3.2", "", {}, "sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA=="], "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], @@ -547,6 +569,8 @@ "minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], + "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + "minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], "minizlib": ["minizlib@3.1.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="], @@ -571,6 +595,8 @@ "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + "on-exit-leak-free": ["on-exit-leak-free@2.1.2", "", {}, "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA=="], + "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], "onetime": ["onetime@1.1.0", "", {}, "sha512-GZ+g4jayMqzCRMgB2sol7GiCLjKfS1PINkjmx8spcKce1LiVqcbQreXwqs2YAFXC6R03VIG28ZS31t8M866v6A=="], @@ -587,22 +613,38 @@ "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "pino": ["pino@10.3.1", "", { "dependencies": { "@pinojs/redact": "^0.4.0", "atomic-sleep": "^1.0.0", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^3.0.0", "pino-std-serializers": "^7.0.0", "process-warning": "^5.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", "sonic-boom": "^4.0.1", "thread-stream": "^4.0.0" }, "bin": { "pino": "bin.js" } }, "sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg=="], + + "pino-abstract-transport": ["pino-abstract-transport@3.0.0", "", { "dependencies": { "split2": "^4.0.0" } }, "sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg=="], + + "pino-pretty": ["pino-pretty@13.1.3", "", { "dependencies": { "colorette": "^2.0.7", "dateformat": "^4.6.3", "fast-copy": "^4.0.0", "fast-safe-stringify": "^2.1.1", "help-me": "^5.0.0", "joycon": "^3.1.1", "minimist": "^1.2.6", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^3.0.0", "pump": "^3.0.0", "secure-json-parse": "^4.0.0", "sonic-boom": "^4.0.1", "strip-json-comments": "^5.0.2" }, "bin": { "pino-pretty": "bin.js" } }, "sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg=="], + + "pino-std-serializers": ["pino-std-serializers@7.1.0", "", {}, "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw=="], + "prettier": ["prettier@3.8.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg=="], "process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="], + "process-warning": ["process-warning@5.0.0", "", {}, "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA=="], + "psl": ["psl@1.15.0", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w=="], + "pump": ["pump@3.0.4", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA=="], + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], "qs": ["qs@6.5.5", "", {}, "sha512-mzR4sElr1bfCaPJe7m8ilJ6ZXdDaGoObcYR0ZHSsktM/Lt21MVHj5De30GQH2eiZ1qGRTO7LCAzQsUeXTNexWQ=="], "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + "quick-format-unescaped": ["quick-format-unescaped@4.0.4", "", {}, "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="], + "readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="], "readline2": ["readline2@1.0.1", "", { "dependencies": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", "mute-stream": "0.0.5" } }, "sha512-8/td4MmwUB6PkZUbV25uKz7dfrmjYWxsW8DVfibWdlHRk/l/DfHKn4pU+dfcoGLFgWOdyGCzINRQD7jn+Bv+/g=="], + "real-require": ["real-require@0.2.0", "", {}, "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg=="], + "regenerator-runtime": ["regenerator-runtime@0.9.6", "", {}, "sha512-D0Y/JJ4VhusyMOd/o25a3jdUqN/bC85EFsaoL9Oqmy/O4efCh+xhp7yj2EEOsj974qvMkcW8AwUzJ1jB/MbxCw=="], "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=="], @@ -631,14 +673,22 @@ "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + "safe-stable-stringify": ["safe-stable-stringify@2.5.0", "", {}, "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA=="], + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + "secure-json-parse": ["secure-json-parse@4.1.0", "", {}, "sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA=="], + "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], "slash": ["slash@5.1.0", "", {}, "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg=="], + "sonic-boom": ["sonic-boom@4.2.1", "", { "dependencies": { "atomic-sleep": "^1.0.0" } }, "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q=="], + + "split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="], + "sprintf-js": ["sprintf-js@1.1.3", "", {}, "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA=="], "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=="], @@ -649,6 +699,8 @@ "strip-ansi": ["strip-ansi@3.0.1", "", { "dependencies": { "ansi-regex": "^2.0.0" } }, "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg=="], + "strip-json-comments": ["strip-json-comments@5.0.3", "", {}, "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw=="], + "supports-color": ["supports-color@2.0.0", "", {}, "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g=="], "tar": ["tar@7.5.12", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-9TsuLcdhOn4XztcQqhNyq1KOwOOED/3k58JAvtULiYqbO8B/0IBAAIE1hj0Svmm58k27TmcigyDI0deMlgG3uw=="], @@ -665,6 +717,8 @@ "thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="], + "thread-stream": ["thread-stream@4.0.0", "", { "dependencies": { "real-require": "^0.2.0" } }, "sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA=="], + "through": ["through@2.3.8", "", {}, "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="], "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], diff --git a/package.json b/package.json index 840648d..69c2c47 100644 --- a/package.json +++ b/package.json @@ -13,5 +13,9 @@ }, "peerDependencies": { "typescript": "^5" + }, + "dependencies": { + "pino": "^10.3.1", + "pino-pretty": "^13.1.3" } } diff --git a/pkgs/server/src/auth/middleware.ts b/pkgs/server/src/auth/middleware.ts index b886f3f..cc95019 100644 --- a/pkgs/server/src/auth/middleware.ts +++ b/pkgs/server/src/auth/middleware.ts @@ -3,9 +3,12 @@ import type { OAuthSession } from "@atcute/oauth-node-client"; import { AuthRequiredError } from "@atcute/xrpc-server"; import { sessionStore } from "../db/store"; +import { loggers } from "../logger"; import oauthClient from "./oauth"; import { parseSessionCookie } from "./session"; +const logger = loggers.auth; + export interface AuthContext { did: string; session: OAuthSession; @@ -40,7 +43,7 @@ export async function getAuthContext( client, }; } catch (error) { - console.error("[auth] failed to restore session:", error); + logger.error(error, "failed to restore session"); return null; } } diff --git a/pkgs/server/src/handlers/auth.ts b/pkgs/server/src/handlers/auth.ts index cbbe119..a9216df 100644 --- a/pkgs/server/src/handlers/auth.ts +++ b/pkgs/server/src/handlers/auth.ts @@ -2,12 +2,16 @@ import { type ActorIdentifier, isActorIdentifier, } from "@atcute/lexicons/syntax"; + +import oauthClient from "../auth/oauth"; import { clearSessionCookie, parseSessionCookie, setSessionCookie, } from "../auth/session"; -import oauthClient from "../auth/oauth"; +import { loggers } from "../logger"; + +const logger = loggers.auth; export async function handleOAuthLogin(url: URL): Promise { const identifier = url.searchParams.get("identifier"); @@ -69,7 +73,7 @@ export async function handleOAuthLogin(url: URL): Promise { return Response.redirect(authUrl.url.href); } catch (error) { - console.error("[auth] authorisation failed:", error); + logger.error(error, "authorisation failed"); return new Response( JSON.stringify({ error: "InternalServerError", @@ -83,7 +87,14 @@ export async function handleOAuthLogin(url: URL): Promise { export async function handleOAuthCallback(url: URL): Promise { try { const { session } = await oauthClient.callback(url.searchParams); - console.log("[auth] user authenticated:", session.did); + logger.info( + { + did: session.did, + sub: session.sub, + iss: session.server.issuer, + }, + `user authenticated: ${session.did}`, + ); const headers = new Headers(); headers.set("Set-Cookie", setSessionCookie(session.did)); @@ -94,7 +105,7 @@ export async function handleOAuthCallback(url: URL): Promise { headers, }); } catch (error) { - console.error("[auth] callback failed:", error); + logger.error(error, "callback failed"); const errorMessage = error instanceof Error ? error.message : "Unknown error"; @@ -147,7 +158,9 @@ export async function handleGetSession(request: Request): Promise { return new Response(JSON.stringify({ did: sessionCookie.did }), { headers: { "content-type": "application/json" }, }); - } catch { + } catch (error) { + logger.error(error, "failed to restore session"); + return new Response(JSON.stringify({ did: null }), { headers: { "content-type": "application/json" }, }); diff --git a/pkgs/server/src/handlers/postDeleting.ts b/pkgs/server/src/handlers/postDeleting.ts index 61379cc..9522dec 100644 --- a/pkgs/server/src/handlers/postDeleting.ts +++ b/pkgs/server/src/handlers/postDeleting.ts @@ -12,13 +12,18 @@ import { CatVt3eBbellScheduledDeletionList, } from "@vt3e.cat/bbell-lexicons"; import { and, eq } from "drizzle-orm"; + import { requireAuth } from "../auth/middleware"; import db, { selfDeletingPosts } from "../db"; +import { createScoped, loggers } from "../logger"; import router from "../router"; import { listWithCursor } from "../utils/list"; type SelfDeletingPost = typeof selfDeletingPosts.$inferSelect; +const _logger = loggers.handlers; +const logger = createScoped("postDeleting", _logger); + router.addProcedure(CatVt3eBbellScheduledDeletionCreate, { async handler({ input, request }) { const auth = await requireAuth(request); @@ -45,6 +50,13 @@ router.addProcedure(CatVt3eBbellScheduledDeletionCreate, { const valid = is(AppBskyFeedPost.mainSchema, postRes.data.value); if (!valid) throw new Error(`invalid post: ${postRes.data.value}`); + const base = { + id: postId, + did: auth.did, + uri: input.uri, + }; + + logger.info(base, `creating post deletion ${postId}`); const [result] = await db .insert(selfDeletingPosts) .values({ @@ -59,11 +71,11 @@ router.addProcedure(CatVt3eBbellScheduledDeletionCreate, { .execute(); if (!result) { - console.error("[db] failed to create post deletion"); + logger.error(base, "failed to create post deletion"); throw new Error("internal server error"); } - console.log(`[db] created post deletion ${postId}`); + logger.info(base, `created post deletion ${postId}`); return json({ id: postId, @@ -75,6 +87,10 @@ router.addProcedure(CatVt3eBbellScheduledDeletionCreate, { router.addProcedure(CatVt3eBbellScheduledDeletionCancel, { async handler({ input, request }) { const auth = await requireAuth(request); + const base = { + id: input.id, + did: auth.did, + }; const [deletion] = await db .delete(selfDeletingPosts) @@ -88,14 +104,17 @@ router.addProcedure(CatVt3eBbellScheduledDeletionCancel, { .execute(); if (!deletion) { - console.error( - "[db] failed to cancel post deletion for post ID:", - input.id, + logger.error( + base, + `failed to cancel post deletion for post ID: ${input.id}`, ); throw new Error("internal server error"); } - console.log(`[db] canceled post deletion ${deletion.id}`); + logger.info( + base, + `canceled post deletion ${deletion.id} for ${deletion.uri}`, + ); return json({ id: deletion.id, diff --git a/pkgs/server/src/handlers/postScheduling.ts b/pkgs/server/src/handlers/postScheduling.ts index 8939f0c..fbf79f8 100644 --- a/pkgs/server/src/handlers/postScheduling.ts +++ b/pkgs/server/src/handlers/postScheduling.ts @@ -8,16 +8,27 @@ import { and, eq } from "drizzle-orm"; import { requireAuth } from "../auth/middleware"; import db, { scheduledPosts } from "../db"; +import { createScoped, loggers } from "../logger"; import router from "../router"; import { listWithCursor } from "../utils/list"; type ScheduledPost = typeof scheduledPosts.$inferSelect; +const _logger = loggers.handlers; +const logger = createScoped("postScheduling", _logger); + router.addProcedure(CatVt3eBbellScheduledPostCreate, { async handler({ input, request }) { const auth = await requireAuth(request); const postId = crypto.randomUUID(); + const base = { + id: postId, + did: auth.did, + }; + + logger.info(base, "creating scheduled post"); + const [post] = await db .insert(scheduledPosts) .values({ @@ -31,11 +42,11 @@ router.addProcedure(CatVt3eBbellScheduledPostCreate, { .execute(); if (!post) { - console.error("[db] failed to insert scheduled post"); + logger.error(base, "failed to insert scheduled post"); return new Response("internal server error", { status: 500 }); } - console.log(`[db] inserted scheduled post ${post.id}`); + logger.info(base, "inserted scheduled post"); return json({ id: post.id, @@ -47,6 +58,10 @@ router.addProcedure(CatVt3eBbellScheduledPostCreate, { router.addProcedure(CatVt3eBbellScheduledPostCancel, { async handler({ input, request }) { const auth = await requireAuth(request); + const base = { + id: input.id, + did: auth.did, + }; const [post] = await db .delete(scheduledPosts) @@ -57,11 +72,11 @@ router.addProcedure(CatVt3eBbellScheduledPostCancel, { .execute(); if (!post) { - console.error("[db] failed to cancel scheduled post"); + logger.error(base, "[failed to cancel scheduled post"); return new Response("internal server error", { status: 500 }); } - console.log(`[db] canceled scheduled post ${post.id}`); + logger.info(base, "canceled scheduled post"); return json({ id: post.id, diff --git a/pkgs/server/src/index.ts b/pkgs/server/src/index.ts index 5d90e28..3184b32 100644 --- a/pkgs/server/src/index.ts +++ b/pkgs/server/src/index.ts @@ -4,24 +4,25 @@ import router from "./router"; import { runScheduler } from "./services/scheduler"; import "./handlers/index.ts"; +import { loggers } from "./logger.ts"; let schedulerInterval: number | undefined; schedulerInterval = setInterval( () => { runScheduler().catch((err) => { - console.error("[scheduler] unhandled error:", err); + loggers.scheduler.error(err, "[scheduler] unhandled error"); }); }, config.env === "production" ? 60_000 : 1_000, ) as unknown as number; async function gracefulShutdown(signal: string) { - console.log(`[server] received ${signal}, shutting down gracefully...`); + loggers.server.info(`received ${signal}, shutting down gracefully...`); if (schedulerInterval !== undefined) clearInterval(schedulerInterval); stateStore.dispose(); - console.log("[server] shutdown complete"); + loggers.server.info("shutdown complete"); process.exit(0); } diff --git a/pkgs/server/src/logger.ts b/pkgs/server/src/logger.ts new file mode 100644 index 0000000..97bd227 --- /dev/null +++ b/pkgs/server/src/logger.ts @@ -0,0 +1,31 @@ +import pino from "pino"; + +const level = process.env.LOG_LEVEL?.toLowerCase() ?? "info"; + +const baseLogger = pino({ + level, + timestamp: pino.stdTimeFunctions.isoTime, + transport: { + target: "pino-pretty", + }, + base: { pid: false, hostname: false }, +}); + +export const loggers = { + scheduler: baseLogger.child({ name: "scheduler" }), + requests: baseLogger.child({ name: "requests" }), + server: baseLogger.child({ name: "server" }), + handlers: baseLogger.child({ name: "handlers" }), + auth: baseLogger.child({ name: "auth" }), +}; + +export function createScoped(scope: string, parent?: Logger) { + if (!parent) return baseLogger.child({ name: scope }); + + return parent.child({ + name: `${parent.bindings().name}/${scope}`, + }); +} + +export default baseLogger; +export type Logger = pino.Logger; diff --git a/pkgs/server/src/router.ts b/pkgs/server/src/router.ts index cc5b617..b619f93 100644 --- a/pkgs/server/src/router.ts +++ b/pkgs/server/src/router.ts @@ -1,4 +1,4 @@ -import { XRPCRouter } from "@atcute/xrpc-server"; +import { type FetchMiddleware, XRPCRouter } from "@atcute/xrpc-server"; import { cors } from "@atcute/xrpc-server/middlewares/cors"; import { handleGetSession, @@ -6,6 +6,7 @@ import { handleOAuthCallback, handleOAuthLogin, } from "./handlers/auth"; +import { createScoped } from "./logger"; async function serveStaticFile(path: string, headers?: HeadersInit) { try { @@ -37,8 +38,28 @@ function getContentType(path: string): string { return types[ext] || "application/octet-stream"; } +const requestLogger = createScoped("requests"); +const loggerMiddleware = (): FetchMiddleware => { + return async (request, next) => { + const start = performance.now(); + const response = await next(request); + const duration = performance.now() - start; + + if (response.status >= 400 || duration > 500) { + requestLogger.info({ + method: request.method, + url: request.url, + status: response.status, + duration: Number(duration.toFixed(2)), + }); + } + + return response; + }; +}; + const router = new XRPCRouter({ - middlewares: [cors()], + middlewares: [cors(), loggerMiddleware()], handleNotFound: async (req: Request) => { const url = new URL(req.url); @@ -56,7 +77,7 @@ const router = new XRPCRouter({ return new Response("AuthenticationRequired", { status: 401 }); } - console.error(error); + requestLogger.error(error); return new Response("internal server error", { status: 500 }); }, }); diff --git a/pkgs/server/src/services/scheduler.ts b/pkgs/server/src/services/scheduler.ts index 4d5ea0a..e987f1d 100644 --- a/pkgs/server/src/services/scheduler.ts +++ b/pkgs/server/src/services/scheduler.ts @@ -3,8 +3,13 @@ import { type ActorIdentifier, parseResourceUri } from "@atcute/lexicons"; import { and, eq, lte } from "drizzle-orm"; import db, { scheduledPosts, selfDeletingPosts } from "../db/index.ts"; +import { createScoped, loggers } from "../logger.ts"; import { getClient } from "./atcute.ts"; +const logger = loggers.scheduler; +const postLogger = createScoped("postScheduling", logger); +const deleteLogger = createScoped("postDeletion", logger); + export async function runScheduler() { const now = new Date(); @@ -19,6 +24,8 @@ export async function runScheduler() { ), ); + if (readyPosts.length !== 0) + postLogger.info(`found ${readyPosts.length} posts to publish`); for (const post of readyPosts) await processScheduledPost(post); const readyDeletions = await db @@ -31,17 +38,26 @@ export async function runScheduler() { ), ); + if (readyDeletions.length !== 0) + deleteLogger.info(`found ${readyDeletions.length} posts to delete`); for (const deletion of readyDeletions) await processSelfDeletingPost(deletion); } catch (err) { - console.error("[scheduler] error during run:", err); + logger.error(err, "error during run"); } } async function processScheduledPost(post: typeof scheduledPosts.$inferSelect) { + const base = { + id: post.id, + did: post.did, + }; + try { const client = await getClient(post.did); + postLogger.info(base, "processing post"); + const res = ok( await client.post("com.atproto.repo.createRecord", { input: { @@ -60,8 +76,19 @@ async function processScheduledPost(post: typeof scheduledPosts.$inferSelect) { uri: res.uri, }) .where(eq(scheduledPosts.id, post.id)); + + postLogger.info( + { + ...base, + uri: res.uri, + }, + "processed post", + ); } catch (err) { - console.error(`[scheduler] failed to process post ${post.id}:`, err); + postLogger.error( + { ...base, error: err instanceof Error ? err.message : String(err) }, + `failed to process post ${post.id}`, + ); await db .update(scheduledPosts) @@ -76,8 +103,14 @@ async function processScheduledPost(post: typeof scheduledPosts.$inferSelect) { async function processSelfDeletingPost( deletion: typeof selfDeletingPosts.$inferSelect, ) { + const base = { + id: deletion.id, + did: deletion.did, + }; + try { const client = await getClient(deletion.did); + deleteLogger.info(base, "processing deletion"); const parsedUri = parseResourceUri(deletion.uri); if (!parsedUri.ok) throw new Error(`invalid URI: ${parsedUri.error}`); @@ -98,10 +131,12 @@ async function processSelfDeletingPost( status: "deleted", }) .where(eq(selfDeletingPosts.id, deletion.id)); + + deleteLogger.info(base, `processed deletion ${deletion.id}`); } catch (err) { - console.error( - `[scheduler] failed to process deletion ${deletion.id}:`, - err, + deleteLogger.error( + { ...base, err }, + `failed to process deletion ${deletion.id}`, ); await db