Monorepo for wisp.place. A static site hosting service built on top of the AT Protocol. wisp.place

gzip and base64 files to get around pds mimetype sniffing as well as serve files as gzip's for faster serving

Changed files
+1148 -168
hosting-service
lexicons
src
lexicon
types
place
wisp
lib
routes
+1 -1
.gitignore
···
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
-
+
.env
# dependencies
/node_modules
/.pnp
+6
bun.lock
···
"tailwind-merge": "^3.3.1",
"tailwindcss": "4",
"tw-animate-css": "^1.4.0",
+
"typescript": "^5.9.3",
+
"zlib": "^1.0.5",
},
"devDependencies": {
"@types/react": "^19.2.2",
···
"type-is": ["type-is@1.6.18", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="],
+
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
+
"uint8array-extras": ["uint8array-extras@1.5.0", "", {}, "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A=="],
"uint8arrays": ["uint8arrays@3.0.0", "", { "dependencies": { "multiformats": "^9.4.2" } }, "sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA=="],
···
"ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
"yesno": ["yesno@0.4.0", "", {}, "sha512-tdBxmHvbXPBKYIg81bMCB7bVeDmHkRzk5rVJyYYXurwKkHq/MCd8rz4HSJUP7hW0H2NlXiq8IFiWvYKEHhlotA=="],
+
+
"zlib": ["zlib@1.0.5", "", {}, "sha512-40fpE2II+Cd3k8HWTWONfeKE2jL+P42iWJ1zzps5W51qcTsOUKM5Q5m2PFb0CLxlmFAaUuUdJGc3OfZy947v0w=="],
"zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
+553
hosting-service/bun.lock
···
+
{
+
"lockfileVersion": 1,
+
"workspaces": {
+
"": {
+
"name": "wisp-hosting-service",
+
"dependencies": {
+
"@atproto/api": "^0.17.4",
+
"@atproto/identity": "^0.4.9",
+
"@atproto/lexicon": "^0.5.1",
+
"@atproto/sync": "^0.1.35",
+
"@atproto/xrpc": "^0.7.5",
+
"@elysiajs/node": "^1.4.1",
+
"@elysiajs/opentelemetry": "latest",
+
"elysia": "latest",
+
"mime-types": "^2.1.35",
+
"multiformats": "^13.4.1",
+
"postgres": "^3.4.5",
+
},
+
"devDependencies": {
+
"@types/mime-types": "^2.1.4",
+
"@types/node": "^22.10.5",
+
"tsx": "^4.19.2",
+
},
+
},
+
},
+
"packages": {
+
"@atproto/api": ["@atproto/api@0.17.4", "", { "dependencies": { "@atproto/common-web": "^0.4.3", "@atproto/lexicon": "^0.5.1", "@atproto/syntax": "^0.4.1", "@atproto/xrpc": "^0.7.5", "await-lock": "^2.2.2", "multiformats": "^9.9.0", "tlds": "^1.234.0", "zod": "^3.23.8" } }, ""],
+
+
"@atproto/common": ["@atproto/common@0.4.12", "", { "dependencies": { "@atproto/common-web": "^0.4.3", "@ipld/dag-cbor": "^7.0.3", "cbor-x": "^1.5.1", "iso-datestring-validator": "^2.2.2", "multiformats": "^9.9.0", "pino": "^8.21.0" } }, ""],
+
+
"@atproto/common-web": ["@atproto/common-web@0.4.3", "", { "dependencies": { "graphemer": "^1.4.0", "multiformats": "^9.9.0", "uint8arrays": "3.0.0", "zod": "^3.23.8" } }, ""],
+
+
"@atproto/crypto": ["@atproto/crypto@0.4.4", "", { "dependencies": { "@noble/curves": "^1.7.0", "@noble/hashes": "^1.6.1", "uint8arrays": "3.0.0" } }, ""],
+
+
"@atproto/identity": ["@atproto/identity@0.4.9", "", { "dependencies": { "@atproto/common-web": "^0.4.3", "@atproto/crypto": "^0.4.4" } }, ""],
+
+
"@atproto/lexicon": ["@atproto/lexicon@0.5.1", "", { "dependencies": { "@atproto/common-web": "^0.4.3", "@atproto/syntax": "^0.4.1", "iso-datestring-validator": "^2.2.2", "multiformats": "^9.9.0", "zod": "^3.23.8" } }, ""],
+
+
"@atproto/repo": ["@atproto/repo@0.8.10", "", { "dependencies": { "@atproto/common": "^0.4.12", "@atproto/common-web": "^0.4.3", "@atproto/crypto": "^0.4.4", "@atproto/lexicon": "^0.5.1", "@ipld/dag-cbor": "^7.0.0", "multiformats": "^9.9.0", "uint8arrays": "3.0.0", "varint": "^6.0.0", "zod": "^3.23.8" } }, ""],
+
+
"@atproto/sync": ["@atproto/sync@0.1.35", "", { "dependencies": { "@atproto/common": "^0.4.12", "@atproto/identity": "^0.4.9", "@atproto/lexicon": "^0.5.1", "@atproto/repo": "^0.8.10", "@atproto/syntax": "^0.4.1", "@atproto/xrpc-server": "^0.9.5", "multiformats": "^9.9.0", "p-queue": "^6.6.2", "ws": "^8.12.0" } }, ""],
+
+
"@atproto/syntax": ["@atproto/syntax@0.4.1", "", {}, ""],
+
+
"@atproto/xrpc": ["@atproto/xrpc@0.7.5", "", { "dependencies": { "@atproto/lexicon": "^0.5.1", "zod": "^3.23.8" } }, ""],
+
+
"@atproto/xrpc-server": ["@atproto/xrpc-server@0.9.5", "", { "dependencies": { "@atproto/common": "^0.4.12", "@atproto/crypto": "^0.4.4", "@atproto/lexicon": "^0.5.1", "@atproto/xrpc": "^0.7.5", "cbor-x": "^1.5.1", "express": "^4.17.2", "http-errors": "^2.0.0", "mime-types": "^2.1.35", "rate-limiter-flexible": "^2.4.1", "uint8arrays": "3.0.0", "ws": "^8.12.0", "zod": "^3.23.8" } }, ""],
+
+
"@borewit/text-codec": ["@borewit/text-codec@0.1.1", "", {}, "sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA=="],
+
+
"@cbor-extract/cbor-extract-darwin-arm64": ["@cbor-extract/cbor-extract-darwin-arm64@2.2.0", "", { "os": "darwin", "cpu": "arm64" }, ""],
+
+
"@elysiajs/node": ["@elysiajs/node@1.4.1", "", { "dependencies": { "crossws": "^0.4.1", "srvx": "^0.8.9" }, "peerDependencies": { "elysia": ">= 1.4.0" } }, "sha512-2wAALwHK3IYi1XJPnxfp1xJsvps5FqqcQqe+QXjYlGQvsmSG+vI5wNDIuvIlB+6p9NE/laLbqV0aFromf3X7yg=="],
+
+
"@elysiajs/opentelemetry": ["@elysiajs/opentelemetry@1.4.6", "", { "dependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/instrumentation": "^0.200.0", "@opentelemetry/sdk-node": "^0.200.0" }, "peerDependencies": { "elysia": ">= 1.4.0" } }, "sha512-jR7t4M6ZvMnBqzzHsNTL6y3sNq9jbGi2vKxbkizi/OO5tlvlKl/rnBGyFjZUjQ1Hte7rCz+2kfmgOQMhkjk+Og=="],
+
+
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.11", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg=="],
+
+
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.11", "", { "os": "android", "cpu": "arm" }, "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg=="],
+
+
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.11", "", { "os": "android", "cpu": "arm64" }, "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ=="],
+
+
"@esbuild/android-x64": ["@esbuild/android-x64@0.25.11", "", { "os": "android", "cpu": "x64" }, "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g=="],
+
+
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w=="],
+
+
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ=="],
+
+
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.11", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA=="],
+
+
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.11", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw=="],
+
+
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.11", "", { "os": "linux", "cpu": "arm" }, "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw=="],
+
+
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA=="],
+
+
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.11", "", { "os": "linux", "cpu": "ia32" }, "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw=="],
+
+
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw=="],
+
+
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ=="],
+
+
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.11", "", { "os": "linux", "cpu": "ppc64" }, "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw=="],
+
+
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww=="],
+
+
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.11", "", { "os": "linux", "cpu": "s390x" }, "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw=="],
+
+
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.11", "", { "os": "linux", "cpu": "x64" }, "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ=="],
+
+
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.11", "", { "os": "none", "cpu": "arm64" }, "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg=="],
+
+
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.11", "", { "os": "none", "cpu": "x64" }, "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A=="],
+
+
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.11", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg=="],
+
+
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.11", "", { "os": "openbsd", "cpu": "x64" }, "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw=="],
+
+
"@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.11", "", { "os": "none", "cpu": "arm64" }, "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ=="],
+
+
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.11", "", { "os": "sunos", "cpu": "x64" }, "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA=="],
+
+
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q=="],
+
+
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.11", "", { "os": "win32", "cpu": "ia32" }, "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA=="],
+
+
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.11", "", { "os": "win32", "cpu": "x64" }, "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA=="],
+
+
"@grpc/grpc-js": ["@grpc/grpc-js@1.14.0", "", { "dependencies": { "@grpc/proto-loader": "^0.8.0", "@js-sdsl/ordered-map": "^4.4.2" } }, "sha512-N8Jx6PaYzcTRNzirReJCtADVoq4z7+1KQ4E70jTg/koQiMoUSN1kbNjPOqpPbhMFhfU1/l7ixspPl8dNY+FoUg=="],
+
+
"@grpc/proto-loader": ["@grpc/proto-loader@0.8.0", "", { "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", "protobufjs": "^7.5.3", "yargs": "^17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" } }, "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ=="],
+
+
"@ipld/dag-cbor": ["@ipld/dag-cbor@7.0.3", "", { "dependencies": { "cborg": "^1.6.0", "multiformats": "^9.5.4" } }, ""],
+
+
"@js-sdsl/ordered-map": ["@js-sdsl/ordered-map@4.4.2", "", {}, "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw=="],
+
+
"@noble/curves": ["@noble/curves@1.9.7", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, ""],
+
+
"@noble/hashes": ["@noble/hashes@1.8.0", "", {}, ""],
+
+
"@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
+
+
"@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.200.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-IKJBQxh91qJ+3ssRly5hYEJ8NDHu9oY/B1PXVSCWf7zytmYO9RNLB0Ox9XQ/fJ8m6gY6Q6NtBWlmXfaXt5Uc4Q=="],
+
+
"@opentelemetry/context-async-hooks": ["@opentelemetry/context-async-hooks@2.0.0", "", { "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-IEkJGzK1A9v3/EHjXh3s2IiFc6L4jfK+lNgKVgUjeUJQRRhnVFMIO3TAvKwonm9O1HebCuoOt98v8bZW7oVQHA=="],
+
+
"@opentelemetry/core": ["@opentelemetry/core@2.0.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ=="],
+
+
"@opentelemetry/exporter-logs-otlp-grpc": ["@opentelemetry/exporter-logs-otlp-grpc@0.200.0", "", { "dependencies": { "@grpc/grpc-js": "^1.7.1", "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-grpc-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0", "@opentelemetry/sdk-logs": "0.200.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-+3MDfa5YQPGM3WXxW9kqGD85Q7s9wlEMVNhXXG7tYFLnIeaseUt9YtCeFhEDFzfEktacdFpOtXmJuNW8cHbU5A=="],
+
+
"@opentelemetry/exporter-logs-otlp-http": ["@opentelemetry/exporter-logs-otlp-http@0.200.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.200.0", "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0", "@opentelemetry/sdk-logs": "0.200.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-KfWw49htbGGp9s8N4KI8EQ9XuqKJ0VG+yVYVYFiCYSjEV32qpQ5qZ9UZBzOZ6xRb+E16SXOSCT3RkqBVSABZ+g=="],
+
+
"@opentelemetry/exporter-logs-otlp-proto": ["@opentelemetry/exporter-logs-otlp-proto@0.200.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.200.0", "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-logs": "0.200.0", "@opentelemetry/sdk-trace-base": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-GmahpUU/55hxfH4TP77ChOfftADsCq/nuri73I/AVLe2s4NIglvTsaACkFVZAVmnXXyPS00Fk3x27WS3yO07zA=="],
+
+
"@opentelemetry/exporter-metrics-otlp-grpc": ["@opentelemetry/exporter-metrics-otlp-grpc@0.200.0", "", { "dependencies": { "@grpc/grpc-js": "^1.7.1", "@opentelemetry/core": "2.0.0", "@opentelemetry/exporter-metrics-otlp-http": "0.200.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-grpc-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-metrics": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-uHawPRvKIrhqH09GloTuYeq2BjyieYHIpiklOvxm9zhrCL2eRsnI/6g9v2BZTVtGp8tEgIa7rCQ6Ltxw6NBgew=="],
+
+
"@opentelemetry/exporter-metrics-otlp-http": ["@opentelemetry/exporter-metrics-otlp-http@0.200.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-metrics": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-5BiR6i8yHc9+qW7F6LqkuUnIzVNA7lt0qRxIKcKT+gq3eGUPHZ3DY29sfxI3tkvnwMgtnHDMNze5DdxW39HsAw=="],
+
+
"@opentelemetry/exporter-metrics-otlp-proto": ["@opentelemetry/exporter-metrics-otlp-proto@0.200.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/exporter-metrics-otlp-http": "0.200.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-metrics": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-E+uPj0yyvz81U9pvLZp3oHtFrEzNSqKGVkIViTQY1rH3TOobeJPSpLnTVXACnCwkPR5XeTvPnK3pZ2Kni8AFMg=="],
+
+
"@opentelemetry/exporter-prometheus": ["@opentelemetry/exporter-prometheus@0.200.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-metrics": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-ZYdlU9r0USuuYppiDyU2VFRA0kFl855ylnb3N/2aOlXrbA4PMCznen7gmPbetGQu7pz8Jbaf4fwvrDnVdQQXSw=="],
+
+
"@opentelemetry/exporter-trace-otlp-grpc": ["@opentelemetry/exporter-trace-otlp-grpc@0.200.0", "", { "dependencies": { "@grpc/grpc-js": "^1.7.1", "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-grpc-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-hmeZrUkFl1YMsgukSuHCFPYeF9df0hHoKeHUthRKFCxiURs+GwF1VuabuHmBMZnjTbsuvNjOB+JSs37Csem/5Q=="],
+
+
"@opentelemetry/exporter-trace-otlp-http": ["@opentelemetry/exporter-trace-otlp-http@0.200.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-Goi//m/7ZHeUedxTGVmEzH19NgqJY+Bzr6zXo1Rni1+hwqaksEyJ44gdlEMREu6dzX1DlAaH/qSykSVzdrdafA=="],
+
+
"@opentelemetry/exporter-trace-otlp-proto": ["@opentelemetry/exporter-trace-otlp-proto@0.200.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-V9TDSD3PjK1OREw2iT9TUTzNYEVWJk4Nhodzhp9eiz4onDMYmPy3LaGbPv81yIR6dUb/hNp/SIhpiCHwFUq2Vg=="],
+
+
"@opentelemetry/exporter-zipkin": ["@opentelemetry/exporter-zipkin@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0" } }, "sha512-icxaKZ+jZL/NHXX8Aru4HGsrdhK0MLcuRXkX5G5IRmCgoRLw+Br6I/nMVozX2xjGGwV7hw2g+4Slj8K7s4HbVg=="],
+
+
"@opentelemetry/instrumentation": ["@opentelemetry/instrumentation@0.200.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.200.0", "@types/shimmer": "^1.2.0", "import-in-the-middle": "^1.8.1", "require-in-the-middle": "^7.1.1", "shimmer": "^1.2.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-pmPlzfJd+vvgaZd/reMsC8RWgTXn2WY1OWT5RT42m3aOn5532TozwXNDhg1vzqJ+jnvmkREcdLr27ebJEQt0Jg=="],
+
+
"@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.200.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-transformer": "0.200.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-IxJgA3FD7q4V6gGq4bnmQM5nTIyMDkoGFGrBrrDjB6onEiq1pafma55V+bHvGYLWvcqbBbRfezr1GED88lacEQ=="],
+
+
"@opentelemetry/otlp-grpc-exporter-base": ["@opentelemetry/otlp-grpc-exporter-base@0.200.0", "", { "dependencies": { "@grpc/grpc-js": "^1.7.1", "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-CK2S+bFgOZ66Bsu5hlDeOX6cvW5FVtVjFFbWuaJP0ELxJKBB6HlbLZQ2phqz/uLj1cWap5xJr/PsR3iGoB7Vqw=="],
+
+
"@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.200.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.200.0", "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-logs": "0.200.0", "@opentelemetry/sdk-metrics": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-+9YDZbYybOnv7sWzebWOeK6gKyt2XE7iarSyBFkwwnP559pEevKOUD8NyDHhRjCSp13ybh9iVXlMfcj/DwF/yw=="],
+
+
"@opentelemetry/propagator-b3": ["@opentelemetry/propagator-b3@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-blx9S2EI49Ycuw6VZq+bkpaIoiJFhsDuvFGhBIoH3vJ5oYjJ2U0s3fAM5jYft99xVIAv6HqoPtlP9gpVA2IZtA=="],
+
+
"@opentelemetry/propagator-jaeger": ["@opentelemetry/propagator-jaeger@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-Mbm/LSFyAtQKP0AQah4AfGgsD+vsZcyreZoQ5okFBk33hU7AquU4TltgyL9dvaO8/Zkoud8/0gEvwfOZ5d7EPA=="],
+
+
"@opentelemetry/resources": ["@opentelemetry/resources@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg=="],
+
+
"@opentelemetry/sdk-logs": ["@opentelemetry/sdk-logs@0.200.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.200.0", "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, "sha512-VZG870063NLfObmQQNtCVcdXXLzI3vOjjrRENmU37HYiPFa0ZXpXVDsTD02Nh3AT3xYJzQaWKl2X2lQ2l7TWJA=="],
+
+
"@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, "sha512-Bvy8QDjO05umd0+j+gDeWcTaVa1/R2lDj/eOvjzpm8VQj1K1vVZJuyjThpV5/lSHyYW2JaHF2IQ7Z8twJFAhjA=="],
+
+
"@opentelemetry/sdk-node": ["@opentelemetry/sdk-node@0.200.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.200.0", "@opentelemetry/core": "2.0.0", "@opentelemetry/exporter-logs-otlp-grpc": "0.200.0", "@opentelemetry/exporter-logs-otlp-http": "0.200.0", "@opentelemetry/exporter-logs-otlp-proto": "0.200.0", "@opentelemetry/exporter-metrics-otlp-grpc": "0.200.0", "@opentelemetry/exporter-metrics-otlp-http": "0.200.0", "@opentelemetry/exporter-metrics-otlp-proto": "0.200.0", "@opentelemetry/exporter-prometheus": "0.200.0", "@opentelemetry/exporter-trace-otlp-grpc": "0.200.0", "@opentelemetry/exporter-trace-otlp-http": "0.200.0", "@opentelemetry/exporter-trace-otlp-proto": "0.200.0", "@opentelemetry/exporter-zipkin": "2.0.0", "@opentelemetry/instrumentation": "0.200.0", "@opentelemetry/propagator-b3": "2.0.0", "@opentelemetry/propagator-jaeger": "2.0.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-logs": "0.200.0", "@opentelemetry/sdk-metrics": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0", "@opentelemetry/sdk-trace-node": "2.0.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-S/YSy9GIswnhYoDor1RusNkmRughipvTCOQrlF1dzI70yQaf68qgf5WMnzUxdlCl3/et/pvaO75xfPfuEmCK5A=="],
+
+
"@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-qQnYdX+ZCkonM7tA5iU4fSRsVxbFGml8jbxOgipRGMFHKaXKHQ30js03rTobYjKjIfnOsZSbHKWF0/0v0OQGfw=="],
+
+
"@opentelemetry/sdk-trace-node": ["@opentelemetry/sdk-trace-node@2.0.0", "", { "dependencies": { "@opentelemetry/context-async-hooks": "2.0.0", "@opentelemetry/core": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-omdilCZozUjQwY3uZRBwbaRMJ3p09l4t187Lsdf0dGMye9WKD4NGcpgZRvqhI1dwcH6og+YXQEtoO9Wx3ykilg=="],
+
+
"@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.37.0", "", {}, "sha512-JD6DerIKdJGmRp4jQyX5FlrQjA4tjOw1cvfsPAZXfOOEErMUHjPcPSICS+6WnM0nB0efSFARh0KAZss+bvExOA=="],
+
+
"@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="],
+
+
"@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="],
+
+
"@protobufjs/codegen": ["@protobufjs/codegen@2.0.4", "", {}, "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="],
+
+
"@protobufjs/eventemitter": ["@protobufjs/eventemitter@1.1.0", "", {}, "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="],
+
+
"@protobufjs/fetch": ["@protobufjs/fetch@1.1.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" } }, "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ=="],
+
+
"@protobufjs/float": ["@protobufjs/float@1.0.2", "", {}, "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="],
+
+
"@protobufjs/inquire": ["@protobufjs/inquire@1.1.0", "", {}, "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="],
+
+
"@protobufjs/path": ["@protobufjs/path@1.1.2", "", {}, "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="],
+
+
"@protobufjs/pool": ["@protobufjs/pool@1.1.0", "", {}, "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="],
+
+
"@protobufjs/utf8": ["@protobufjs/utf8@1.1.0", "", {}, "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="],
+
+
"@sinclair/typebox": ["@sinclair/typebox@0.34.41", "", {}, "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g=="],
+
+
"@tokenizer/inflate": ["@tokenizer/inflate@0.2.7", "", { "dependencies": { "debug": "^4.4.0", "fflate": "^0.8.2", "token-types": "^6.0.0" } }, "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg=="],
+
+
"@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
+
+
"@types/mime-types": ["@types/mime-types@2.1.4", "", {}, "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w=="],
+
+
"@types/node": ["@types/node@22.18.12", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-BICHQ67iqxQGFSzfCFTT7MRQ5XcBjG5aeKh5Ok38UBbPe5fxTyE+aHFxwVrGyr8GNlqFMLKD1D3P2K/1ks8tog=="],
+
+
"@types/shimmer": ["@types/shimmer@1.2.0", "", {}, "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg=="],
+
+
"abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, ""],
+
+
"accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, ""],
+
+
"acorn": ["acorn@8.15.0", "", { "bin": "bin/acorn" }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
+
+
"acorn-import-attributes": ["acorn-import-attributes@1.9.5", "", { "peerDependencies": { "acorn": "^8" } }, "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ=="],
+
+
"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=="],
+
+
"array-flatten": ["array-flatten@1.1.1", "", {}, ""],
+
+
"atomic-sleep": ["atomic-sleep@1.0.0", "", {}, ""],
+
+
"await-lock": ["await-lock@2.2.2", "", {}, ""],
+
+
"base64-js": ["base64-js@1.5.1", "", {}, ""],
+
+
"body-parser": ["body-parser@1.20.3", "", { "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" } }, ""],
+
+
"buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, ""],
+
+
"bytes": ["bytes@3.1.2", "", {}, ""],
+
+
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, ""],
+
+
"call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, ""],
+
+
"cbor-extract": ["cbor-extract@2.2.0", "", { "dependencies": { "node-gyp-build-optional-packages": "5.1.1" }, "optionalDependencies": { "@cbor-extract/cbor-extract-darwin-arm64": "2.2.0" }, "bin": { "download-cbor-prebuilds": "bin/download-prebuilds.js" } }, ""],
+
+
"cbor-x": ["cbor-x@1.6.0", "", { "optionalDependencies": { "cbor-extract": "^2.2.0" } }, ""],
+
+
"cborg": ["cborg@1.10.2", "", { "bin": "cli.js" }, ""],
+
+
"cjs-module-lexer": ["cjs-module-lexer@1.4.3", "", {}, "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q=="],
+
+
"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=="],
+
+
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
+
+
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
+
+
"content-disposition": ["content-disposition@0.5.4", "", { "dependencies": { "safe-buffer": "5.2.1" } }, ""],
+
+
"content-type": ["content-type@1.0.5", "", {}, ""],
+
+
"cookie": ["cookie@1.0.2", "", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="],
+
+
"cookie-signature": ["cookie-signature@1.0.6", "", {}, ""],
+
+
"crossws": ["crossws@0.4.1", "", { "peerDependencies": { "srvx": ">=0.7.1" }, "optionalPeers": ["srvx"] }, "sha512-E7WKBcHVhAVrY6JYD5kteNqVq1GSZxqGrdSiwXR9at+XHi43HJoCQKXcCczR5LBnBquFZPsB3o7HklulKoBU5w=="],
+
+
"debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, ""],
+
+
"depd": ["depd@2.0.0", "", {}, ""],
+
+
"destroy": ["destroy@1.2.0", "", {}, ""],
+
+
"detect-libc": ["detect-libc@2.1.2", "", {}, ""],
+
+
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, ""],
+
+
"ee-first": ["ee-first@1.1.1", "", {}, ""],
+
+
"elysia": ["elysia@1.4.13", "", { "dependencies": { "cookie": "^1.0.2", "exact-mirror": "0.2.2", "fast-decode-uri-component": "^1.0.1", "memoirist": "^0.4.0" }, "peerDependencies": { "@sinclair/typebox": ">= 0.34.0 < 1", "@types/bun": ">= 1.2.0", "exact-mirror": ">= 0.0.9", "file-type": ">= 20.0.0", "openapi-types": ">= 12.0.0", "typescript": ">= 5.0.0" }, "optionalPeers": ["@types/bun", "typescript"] }, "sha512-6QaWQEm7QN1UCo1TPpEjaRJPHUmnM7R29y6LY224frDGk5PrpAnWmdHkoZxkcv+JRWp1j2ROr2IHbxHbG/jRjw=="],
+
+
"emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
+
+
"encodeurl": ["encodeurl@2.0.0", "", {}, ""],
+
+
"es-define-property": ["es-define-property@1.0.1", "", {}, ""],
+
+
"es-errors": ["es-errors@1.3.0", "", {}, ""],
+
+
"es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, ""],
+
+
"esbuild": ["esbuild@0.25.11", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.11", "@esbuild/android-arm": "0.25.11", "@esbuild/android-arm64": "0.25.11", "@esbuild/android-x64": "0.25.11", "@esbuild/darwin-arm64": "0.25.11", "@esbuild/darwin-x64": "0.25.11", "@esbuild/freebsd-arm64": "0.25.11", "@esbuild/freebsd-x64": "0.25.11", "@esbuild/linux-arm": "0.25.11", "@esbuild/linux-arm64": "0.25.11", "@esbuild/linux-ia32": "0.25.11", "@esbuild/linux-loong64": "0.25.11", "@esbuild/linux-mips64el": "0.25.11", "@esbuild/linux-ppc64": "0.25.11", "@esbuild/linux-riscv64": "0.25.11", "@esbuild/linux-s390x": "0.25.11", "@esbuild/linux-x64": "0.25.11", "@esbuild/netbsd-arm64": "0.25.11", "@esbuild/netbsd-x64": "0.25.11", "@esbuild/openbsd-arm64": "0.25.11", "@esbuild/openbsd-x64": "0.25.11", "@esbuild/openharmony-arm64": "0.25.11", "@esbuild/sunos-x64": "0.25.11", "@esbuild/win32-arm64": "0.25.11", "@esbuild/win32-ia32": "0.25.11", "@esbuild/win32-x64": "0.25.11" }, "bin": "bin/esbuild" }, "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q=="],
+
+
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
+
+
"escape-html": ["escape-html@1.0.3", "", {}, ""],
+
+
"etag": ["etag@1.8.1", "", {}, ""],
+
+
"event-target-shim": ["event-target-shim@5.0.1", "", {}, ""],
+
+
"eventemitter3": ["eventemitter3@4.0.7", "", {}, ""],
+
+
"events": ["events@3.3.0", "", {}, ""],
+
+
"exact-mirror": ["exact-mirror@0.2.2", "", { "peerDependencies": { "@sinclair/typebox": "^0.34.15" } }, "sha512-CrGe+4QzHZlnrXZVlo/WbUZ4qQZq8C0uATQVGVgXIrNXgHDBBNFD1VRfssRA2C9t3RYvh3MadZSdg2Wy7HBoQA=="],
+
+
"express": ["express@4.21.2", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.19.0", "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, ""],
+
+
"fast-decode-uri-component": ["fast-decode-uri-component@1.0.1", "", {}, "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="],
+
+
"fast-redact": ["fast-redact@3.5.0", "", {}, ""],
+
+
"fflate": ["fflate@0.8.2", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="],
+
+
"file-type": ["file-type@21.0.0", "", { "dependencies": { "@tokenizer/inflate": "^0.2.7", "strtok3": "^10.2.2", "token-types": "^6.0.0", "uint8array-extras": "^1.4.0" } }, "sha512-ek5xNX2YBYlXhiUXui3D/BXa3LdqPmoLJ7rqEx2bKJ7EAUEfmXgW0Das7Dc6Nr9MvqaOnIqiPV0mZk/r/UpNAg=="],
+
+
"finalhandler": ["finalhandler@1.3.1", "", { "dependencies": { "debug": "2.6.9", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", "statuses": "2.0.1", "unpipe": "~1.0.0" } }, ""],
+
+
"forwarded": ["forwarded@0.2.0", "", {}, ""],
+
+
"fresh": ["fresh@0.5.2", "", {}, ""],
+
+
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
+
+
"function-bind": ["function-bind@1.1.2", "", {}, ""],
+
+
"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" } }, ""],
+
+
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, ""],
+
+
"get-tsconfig": ["get-tsconfig@4.13.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ=="],
+
+
"gopd": ["gopd@1.2.0", "", {}, ""],
+
+
"graphemer": ["graphemer@1.4.0", "", {}, ""],
+
+
"has-symbols": ["has-symbols@1.1.0", "", {}, ""],
+
+
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, ""],
+
+
"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" } }, ""],
+
+
"iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, ""],
+
+
"ieee754": ["ieee754@1.2.1", "", {}, ""],
+
+
"import-in-the-middle": ["import-in-the-middle@1.15.0", "", { "dependencies": { "acorn": "^8.14.0", "acorn-import-attributes": "^1.9.5", "cjs-module-lexer": "^1.2.2", "module-details-from-path": "^1.0.3" } }, "sha512-bpQy+CrsRmYmoPMAE/0G33iwRqwW4ouqdRg8jgbH3aKuCtOc8lxgmYXg2dMM92CRiGP660EtBcymH/eVUpCSaA=="],
+
+
"inherits": ["inherits@2.0.4", "", {}, ""],
+
+
"ipaddr.js": ["ipaddr.js@1.9.1", "", {}, ""],
+
+
"is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="],
+
+
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
+
+
"iso-datestring-validator": ["iso-datestring-validator@2.2.2", "", {}, ""],
+
+
"lodash.camelcase": ["lodash.camelcase@4.3.0", "", {}, "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="],
+
+
"long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="],
+
+
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, ""],
+
+
"media-typer": ["media-typer@0.3.0", "", {}, ""],
+
+
"memoirist": ["memoirist@0.4.0", "", {}, "sha512-zxTgA0mSYELa66DimuNQDvyLq36AwDlTuVRbnQtB+VuTcKWm5Qc4z3WkSpgsFWHNhexqkIooqpv4hdcqrX5Nmg=="],
+
+
"merge-descriptors": ["merge-descriptors@1.0.3", "", {}, ""],
+
+
"methods": ["methods@1.1.2", "", {}, ""],
+
+
"mime": ["mime@1.6.0", "", { "bin": "cli.js" }, ""],
+
+
"mime-db": ["mime-db@1.52.0", "", {}, ""],
+
+
"mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, ""],
+
+
"module-details-from-path": ["module-details-from-path@1.0.4", "", {}, "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w=="],
+
+
"ms": ["ms@2.0.0", "", {}, ""],
+
+
"multiformats": ["multiformats@13.4.1", "", {}, ""],
+
+
"negotiator": ["negotiator@0.6.3", "", {}, ""],
+
+
"node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.1.1", "", { "dependencies": { "detect-libc": "^2.0.1" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-optional": "optional.js", "node-gyp-build-optional-packages-test": "build-test.js" } }, ""],
+
+
"object-inspect": ["object-inspect@1.13.4", "", {}, ""],
+
+
"on-exit-leak-free": ["on-exit-leak-free@2.1.2", "", {}, ""],
+
+
"on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, ""],
+
+
"openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="],
+
+
"p-finally": ["p-finally@1.0.0", "", {}, ""],
+
+
"p-queue": ["p-queue@6.6.2", "", { "dependencies": { "eventemitter3": "^4.0.4", "p-timeout": "^3.2.0" } }, ""],
+
+
"p-timeout": ["p-timeout@3.2.0", "", { "dependencies": { "p-finally": "^1.0.0" } }, ""],
+
+
"parseurl": ["parseurl@1.3.3", "", {}, ""],
+
+
"path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
+
+
"path-to-regexp": ["path-to-regexp@0.1.12", "", {}, ""],
+
+
"pino": ["pino@8.21.0", "", { "dependencies": { "atomic-sleep": "^1.0.0", "fast-redact": "^3.1.1", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^1.2.0", "pino-std-serializers": "^6.0.0", "process-warning": "^3.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", "sonic-boom": "^3.7.0", "thread-stream": "^2.6.0" }, "bin": "bin.js" }, ""],
+
+
"pino-abstract-transport": ["pino-abstract-transport@1.2.0", "", { "dependencies": { "readable-stream": "^4.0.0", "split2": "^4.0.0" } }, ""],
+
+
"pino-std-serializers": ["pino-std-serializers@6.2.2", "", {}, ""],
+
+
"postgres": ["postgres@3.4.7", "", {}, ""],
+
+
"process": ["process@0.11.10", "", {}, ""],
+
+
"process-warning": ["process-warning@3.0.0", "", {}, ""],
+
+
"protobufjs": ["protobufjs@7.5.4", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg=="],
+
+
"proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, ""],
+
+
"qs": ["qs@6.13.0", "", { "dependencies": { "side-channel": "^1.0.6" } }, ""],
+
+
"quick-format-unescaped": ["quick-format-unescaped@4.0.4", "", {}, ""],
+
+
"range-parser": ["range-parser@1.2.1", "", {}, ""],
+
+
"rate-limiter-flexible": ["rate-limiter-flexible@2.4.2", "", {}, ""],
+
+
"raw-body": ["raw-body@2.5.2", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" } }, ""],
+
+
"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" } }, ""],
+
+
"real-require": ["real-require@0.2.0", "", {}, ""],
+
+
"require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="],
+
+
"require-in-the-middle": ["require-in-the-middle@7.5.2", "", { "dependencies": { "debug": "^4.3.5", "module-details-from-path": "^1.0.3", "resolve": "^1.22.8" } }, "sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ=="],
+
+
"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": "bin/resolve" }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="],
+
+
"resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="],
+
+
"safe-buffer": ["safe-buffer@5.2.1", "", {}, ""],
+
+
"safe-stable-stringify": ["safe-stable-stringify@2.5.0", "", {}, ""],
+
+
"safer-buffer": ["safer-buffer@2.1.2", "", {}, ""],
+
+
"send": ["send@0.19.0", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", "http-errors": "2.0.0", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "2.4.1", "range-parser": "~1.2.1", "statuses": "2.0.1" } }, ""],
+
+
"serve-static": ["serve-static@1.16.2", "", { "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "0.19.0" } }, ""],
+
+
"setprototypeof": ["setprototypeof@1.2.0", "", {}, ""],
+
+
"shimmer": ["shimmer@1.2.1", "", {}, "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw=="],
+
+
"side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, ""],
+
+
"side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, ""],
+
+
"side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, ""],
+
+
"side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, ""],
+
+
"sonic-boom": ["sonic-boom@3.8.1", "", { "dependencies": { "atomic-sleep": "^1.0.0" } }, ""],
+
+
"split2": ["split2@4.2.0", "", {}, ""],
+
+
"srvx": ["srvx@0.8.16", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-hmcGW4CgroeSmzgF1Ihwgl+Ths0JqAJ7HwjP2X7e3JzY7u4IydLMcdnlqGQiQGUswz+PO9oh/KtCpOISIvs9QQ=="],
+
+
"statuses": ["statuses@2.0.1", "", {}, ""],
+
+
"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_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, ""],
+
+
"strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
+
+
"strtok3": ["strtok3@10.3.4", "", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg=="],
+
+
"supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
+
+
"thread-stream": ["thread-stream@2.7.0", "", { "dependencies": { "real-require": "^0.2.0" } }, ""],
+
+
"tlds": ["tlds@1.261.0", "", { "bin": "bin.js" }, ""],
+
+
"toidentifier": ["toidentifier@1.0.1", "", {}, ""],
+
+
"token-types": ["token-types@6.1.1", "", { "dependencies": { "@borewit/text-codec": "^0.1.0", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ=="],
+
+
"tsx": ["tsx@4.20.6", "", { "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": "dist/cli.mjs" }, "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg=="],
+
+
"type-is": ["type-is@1.6.18", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, ""],
+
+
"uint8array-extras": ["uint8array-extras@1.5.0", "", {}, "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A=="],
+
+
"uint8arrays": ["uint8arrays@3.0.0", "", { "dependencies": { "multiformats": "^9.4.2" } }, ""],
+
+
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
+
+
"unpipe": ["unpipe@1.0.0", "", {}, ""],
+
+
"utils-merge": ["utils-merge@1.0.1", "", {}, ""],
+
+
"varint": ["varint@6.0.0", "", {}, ""],
+
+
"vary": ["vary@1.1.2", "", {}, ""],
+
+
"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=="],
+
+
"ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, ""],
+
+
"y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
+
+
"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@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
+
+
"zod": ["zod@3.25.76", "", {}, ""],
+
+
"@atproto/api/multiformats": ["multiformats@9.9.0", "", {}, ""],
+
+
"@atproto/common/multiformats": ["multiformats@9.9.0", "", {}, ""],
+
+
"@atproto/common-web/multiformats": ["multiformats@9.9.0", "", {}, ""],
+
+
"@atproto/lexicon/multiformats": ["multiformats@9.9.0", "", {}, ""],
+
+
"@atproto/repo/multiformats": ["multiformats@9.9.0", "", {}, ""],
+
+
"@atproto/sync/multiformats": ["multiformats@9.9.0", "", {}, ""],
+
+
"@ipld/dag-cbor/multiformats": ["multiformats@9.9.0", "", {}, ""],
+
+
"@tokenizer/inflate/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
+
+
"express/cookie": ["cookie@0.7.1", "", {}, ""],
+
+
"require-in-the-middle/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
+
+
"send/encodeurl": ["encodeurl@1.0.2", "", {}, ""],
+
+
"send/ms": ["ms@2.1.3", "", {}, ""],
+
+
"uint8arrays/multiformats": ["multiformats@9.9.0", "", {}, ""],
+
+
"@tokenizer/inflate/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
+
+
"require-in-the-middle/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
+
}
+
}
+2 -2
hosting-service/package.json
···
"@atproto/lexicon": "^0.5.1",
"@atproto/sync": "^0.1.35",
"@atproto/xrpc": "^0.7.5",
-
"@hono/node-server": "^1.13.7",
-
"hono": "^4.6.14",
+
"@elysiajs/opentelemetry": "latest",
+
"elysia": "latest",
"mime-types": "^2.1.35",
"multiformats": "^13.4.1",
"postgres": "^3.4.5"
+10 -14
hosting-service/src/index.ts
···
-
import { serve } from '@hono/node-server';
-
import app from './server.js';
-
import { FirehoseWorker } from './lib/firehose.js';
+
import app from './server';
+
import { FirehoseWorker } from './lib/firehose';
import { mkdirSync, existsSync } from 'fs';
const PORT = process.env.PORT ? parseInt(process.env.PORT) : 3001;
···
firehose.start();
// Add health check endpoint
-
app.get('/health', (c) => {
+
app.get('/health', () => {
const firehoseHealth = firehose.getHealth();
-
return c.json({
+
return {
status: 'ok',
firehose: firehoseHealth,
-
});
+
};
});
// Start HTTP server
-
const server = serve({
-
port: PORT,
-
fetch: app.fetch,
-
});
-
-
console.log(`
+
app.listen(PORT, () => {
+
console.log(`
Wisp Hosting Service
Server: http://localhost:${PORT}
···
Cache: ${CACHE_DIR}
Firehose: Connected to Firehose
`);
+
});
// Graceful shutdown
process.on('SIGINT', async () => {
console.log('\n🛑 Shutting down...');
firehose.stop();
-
server.close();
+
app.stop();
process.exit(0);
});
process.on('SIGTERM', async () => {
console.log('\n🛑 Shutting down...');
firehose.stop();
-
server.close();
+
app.stop();
process.exit(0);
});
+44
hosting-service/src/lexicon/index.ts
···
+
/**
+
* GENERATED CODE - DO NOT MODIFY
+
*/
+
import {
+
type Auth,
+
type Options as XrpcOptions,
+
Server as XrpcServer,
+
type StreamConfigOrHandler,
+
type MethodConfigOrHandler,
+
createServer as createXrpcServer,
+
} from '@atproto/xrpc-server'
+
import { schemas } from './lexicons.js'
+
+
export function createServer(options?: XrpcOptions): Server {
+
return new Server(options)
+
}
+
+
export class Server {
+
xrpc: XrpcServer
+
place: PlaceNS
+
+
constructor(options?: XrpcOptions) {
+
this.xrpc = createXrpcServer(schemas, options)
+
this.place = new PlaceNS(this)
+
}
+
}
+
+
export class PlaceNS {
+
_server: Server
+
wisp: PlaceWispNS
+
+
constructor(server: Server) {
+
this._server = server
+
this.wisp = new PlaceWispNS(server)
+
}
+
}
+
+
export class PlaceWispNS {
+
_server: Server
+
+
constructor(server: Server) {
+
this._server = server
+
}
+
}
+14
hosting-service/src/lexicon/lexicons.ts
···
maxSize: 1000000,
description: 'Content blob ref',
},
+
encoding: {
+
type: 'string',
+
enum: ['gzip'],
+
description: 'Content encoding (e.g., gzip for compressed files)',
+
},
+
mimeType: {
+
type: 'string',
+
description: 'Original MIME type before compression',
+
},
+
base64: {
+
type: 'boolean',
+
description:
+
'True if blob content is base64-encoded (used to bypass PDS content sniffing)',
+
},
},
},
directory: {
+6
hosting-service/src/lexicon/types/place/wisp/fs.ts
···
type: 'file'
/** Content blob ref */
blob: BlobRef
+
/** Content encoding (e.g., gzip for compressed files) */
+
encoding?: 'gzip'
+
/** Original MIME type before compression */
+
mimeType?: string
+
/** True if blob content is base64-encoded (used to bypass PDS content sniffing) */
+
base64?: boolean
}
const hashFile = 'file'
+98 -5
hosting-service/src/lib/db.ts
···
verified: boolean;
}
+
// In-memory cache with TTL
+
interface CacheEntry<T> {
+
data: T;
+
expiry: number;
+
}
+
+
const CACHE_TTL_MS = 10 * 60 * 1000; // 10 minutes
+
+
class SimpleCache<T> {
+
private cache = new Map<string, CacheEntry<T>>();
+
+
get(key: string): T | null {
+
const entry = this.cache.get(key);
+
if (!entry) return null;
+
+
if (Date.now() > entry.expiry) {
+
this.cache.delete(key);
+
return null;
+
}
+
+
return entry.data;
+
}
+
+
set(key: string, data: T): void {
+
this.cache.set(key, {
+
data,
+
expiry: Date.now() + CACHE_TTL_MS,
+
});
+
}
+
+
// Periodic cleanup to prevent memory leaks
+
cleanup(): void {
+
const now = Date.now();
+
for (const [key, entry] of this.cache.entries()) {
+
if (now > entry.expiry) {
+
this.cache.delete(key);
+
}
+
}
+
}
+
}
+
+
// Create cache instances
+
const wispDomainCache = new SimpleCache<DomainLookup | null>();
+
const customDomainCache = new SimpleCache<CustomDomainLookup | null>();
+
const customDomainHashCache = new SimpleCache<CustomDomainLookup | null>();
+
+
// Run cleanup every 5 minutes
+
setInterval(() => {
+
wispDomainCache.cleanup();
+
customDomainCache.cleanup();
+
customDomainHashCache.cleanup();
+
}, 5 * 60 * 1000);
+
export async function getWispDomain(domain: string): Promise<DomainLookup | null> {
+
const key = domain.toLowerCase();
+
+
// Check cache first
+
const cached = wispDomainCache.get(key);
+
if (cached !== null) {
+
return cached;
+
}
+
+
// Query database
const result = await sql<DomainLookup[]>`
-
SELECT did, rkey FROM domains WHERE domain = ${domain.toLowerCase()} LIMIT 1
+
SELECT did, rkey FROM domains WHERE domain = ${key} LIMIT 1
`;
-
return result[0] || null;
+
const data = result[0] || null;
+
+
// Store in cache
+
wispDomainCache.set(key, data);
+
+
return data;
}
export async function getCustomDomain(domain: string): Promise<CustomDomainLookup | null> {
+
const key = domain.toLowerCase();
+
+
// Check cache first
+
const cached = customDomainCache.get(key);
+
if (cached !== null) {
+
return cached;
+
}
+
+
// Query database
const result = await sql<CustomDomainLookup[]>`
SELECT id, domain, did, rkey, verified FROM custom_domains
-
WHERE domain = ${domain.toLowerCase()} AND verified = true LIMIT 1
+
WHERE domain = ${key} AND verified = true LIMIT 1
`;
-
return result[0] || null;
+
const data = result[0] || null;
+
+
// Store in cache
+
customDomainCache.set(key, data);
+
+
return data;
}
export async function getCustomDomainByHash(hash: string): Promise<CustomDomainLookup | null> {
+
// Check cache first
+
const cached = customDomainHashCache.get(hash);
+
if (cached !== null) {
+
return cached;
+
}
+
+
// Query database
const result = await sql<CustomDomainLookup[]>`
SELECT id, domain, did, rkey, verified FROM custom_domains
WHERE id = ${hash} AND verified = true LIMIT 1
`;
-
return result[0] || null;
+
const data = result[0] || null;
+
+
// Store in cache
+
customDomainHashCache.set(hash, data);
+
+
return data;
}
export async function upsertSite(did: string, rkey: string, displayName?: string) {
+1 -1
hosting-service/src/lib/firehose.ts
···
return;
}
-
// Cache the record with verified CID
+
// Cache the record with verified CID (uses atomic swap internally)
await downloadAndCacheSite(did, site, fsRecord, pdsEndpoint, verifiedCid);
// Upsert site to database
+29 -10
hosting-service/src/lib/html-rewriter.ts
···
* Check if a path should be rewritten
*/
function shouldRewritePath(path: string): boolean {
-
// Must start with /
-
if (!path.startsWith('/')) return false;
+
// Don't rewrite empty paths
+
if (!path) return false;
-
// Don't rewrite protocol-relative URLs
-
if (path.startsWith('//')) return false;
+
// Don't rewrite external URLs (http://, https://, //)
+
if (path.startsWith('http://') || path.startsWith('https://') || path.startsWith('//')) {
+
return false;
+
}
-
// Don't rewrite anchors
-
if (path.startsWith('/#')) return false;
+
// Don't rewrite data URIs or other schemes (except file paths)
+
if (path.includes(':') && !path.startsWith('./') && !path.startsWith('../')) {
+
return false;
+
}
-
// Don't rewrite data URIs or other schemes
-
if (path.includes(':')) return false;
+
// Don't rewrite pure anchors
+
if (path.startsWith('#')) return false;
+
// Rewrite absolute paths (/) and relative paths (./ or ../ or plain filenames)
return true;
}
···
return path;
}
-
// Remove leading slash and prepend base path
-
return basePath + path.slice(1);
+
// Handle absolute paths: /file.js -> /base/file.js
+
if (path.startsWith('/')) {
+
return basePath + path.slice(1);
+
}
+
+
// Handle relative paths: ./file.js or ../file.js or file.js -> /base/file.js
+
// Strip leading ./ or ../ and just use the base path
+
let cleanPath = path;
+
if (cleanPath.startsWith('./')) {
+
cleanPath = cleanPath.slice(2);
+
} else if (cleanPath.startsWith('../')) {
+
// For sites.wisp.place, we can't go up from the site root, so just use base path
+
cleanPath = cleanPath.replace(/^(\.\.\/)+/, '');
+
}
+
+
return basePath + cleanPath;
}
/**
+75 -13
hosting-service/src/lib/utils.ts
···
import { AtpAgent } from '@atproto/api';
import type { WispFsRecord, Directory, Entry, File } from './types';
-
import { existsSync, mkdirSync, readFileSync } from 'fs';
-
import { writeFile, readFile } from 'fs/promises';
+
import { existsSync, mkdirSync, readFileSync, rmSync } from 'fs';
+
import { writeFile, readFile, rename } from 'fs/promises';
import { safeFetchJson, safeFetchBlob } from './safe-fetch';
import { CID } from 'multiformats/cid';
···
throw new Error('Invalid record structure: root missing entries array');
}
-
await cacheFiles(did, rkey, record.root.entries, pdsEndpoint, '');
+
// Use a temporary directory with timestamp to avoid collisions
+
const tempSuffix = `.tmp-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
+
const tempDir = `${CACHE_DIR}/${did}/${rkey}${tempSuffix}`;
+
const finalDir = `${CACHE_DIR}/${did}/${rkey}`;
-
await saveCacheMetadata(did, rkey, recordCid);
+
try {
+
// Download to temporary directory
+
await cacheFiles(did, rkey, record.root.entries, pdsEndpoint, '', tempSuffix);
+
await saveCacheMetadata(did, rkey, recordCid, tempSuffix);
+
+
// Atomically replace old cache with new cache
+
// On POSIX systems (Linux/macOS), rename is atomic
+
if (existsSync(finalDir)) {
+
// Rename old directory to backup
+
const backupDir = `${finalDir}.old-${Date.now()}`;
+
await rename(finalDir, backupDir);
+
+
try {
+
// Rename new directory to final location
+
await rename(tempDir, finalDir);
+
+
// Clean up old backup
+
rmSync(backupDir, { recursive: true, force: true });
+
} catch (err) {
+
// If rename failed, restore backup
+
if (existsSync(backupDir) && !existsSync(finalDir)) {
+
await rename(backupDir, finalDir);
+
}
+
throw err;
+
}
+
} else {
+
// No existing cache, just rename temp to final
+
await rename(tempDir, finalDir);
+
}
+
+
console.log('Successfully cached site atomically', did, rkey);
+
} catch (err) {
+
// Clean up temp directory on failure
+
if (existsSync(tempDir)) {
+
rmSync(tempDir, { recursive: true, force: true });
+
}
+
throw err;
+
}
}
async function cacheFiles(
···
site: string,
entries: Entry[],
pdsEndpoint: string,
-
pathPrefix: string
+
pathPrefix: string,
+
dirSuffix: string = ''
): Promise<void> {
for (const entry of entries) {
const currentPath = pathPrefix ? `${pathPrefix}/${entry.name}` : entry.name;
const node = entry.node;
if ('type' in node && node.type === 'directory' && 'entries' in node) {
-
await cacheFiles(did, site, node.entries, pdsEndpoint, currentPath);
+
await cacheFiles(did, site, node.entries, pdsEndpoint, currentPath, dirSuffix);
} else if ('type' in node && node.type === 'file' && 'blob' in node) {
-
await cacheFileBlob(did, site, currentPath, node.blob, pdsEndpoint);
+
const fileNode = node as File;
+
await cacheFileBlob(did, site, currentPath, fileNode.blob, pdsEndpoint, fileNode.encoding, fileNode.mimeType, fileNode.base64, dirSuffix);
}
}
}
···
site: string,
filePath: string,
blobRef: any,
-
pdsEndpoint: string
+
pdsEndpoint: string,
+
encoding?: 'gzip',
+
mimeType?: string,
+
base64?: boolean,
+
dirSuffix: string = ''
): Promise<void> {
const cid = extractBlobCid(blobRef);
if (!cid) {
···
const blobUrl = `${pdsEndpoint}/xrpc/com.atproto.sync.getBlob?did=${encodeURIComponent(did)}&cid=${encodeURIComponent(cid)}`;
// Allow up to 100MB per file blob
-
const content = await safeFetchBlob(blobUrl, { maxSize: 100 * 1024 * 1024 });
+
let content = await safeFetchBlob(blobUrl, { maxSize: 100 * 1024 * 1024 });
-
const cacheFile = `${CACHE_DIR}/${did}/${site}/${filePath}`;
+
// If content is base64-encoded, decode it back to gzipped binary
+
if (base64 && encoding === 'gzip') {
+
// Convert Uint8Array to Buffer for proper string conversion
+
const buffer = Buffer.from(content);
+
const base64String = buffer.toString('utf-8');
+
content = Buffer.from(base64String, 'base64');
+
}
+
+
const cacheFile = `${CACHE_DIR}/${did}/${site}${dirSuffix}/${filePath}`;
const fileDir = cacheFile.substring(0, cacheFile.lastIndexOf('/'));
if (fileDir && !existsSync(fileDir)) {
···
}
await writeFile(cacheFile, content);
-
console.log('Cached file', filePath, content.length, 'bytes');
+
+
// Store metadata if file is compressed
+
if (encoding === 'gzip' && mimeType) {
+
const metaFile = `${cacheFile}.meta`;
+
await writeFile(metaFile, JSON.stringify({ encoding, mimeType }));
+
console.log('Cached file', filePath, content.length, 'bytes (gzipped,', mimeType + ')');
+
} else {
+
console.log('Cached file', filePath, content.length, 'bytes');
+
}
}
/**
···
return existsSync(`${CACHE_DIR}/${did}/${site}`);
}
-
async function saveCacheMetadata(did: string, rkey: string, recordCid: string): Promise<void> {
+
async function saveCacheMetadata(did: string, rkey: string, recordCid: string, dirSuffix: string = ''): Promise<void> {
const metadata: CacheMetadata = {
recordCid,
cachedAt: Date.now(),
···
rkey
};
-
const metadataPath = `${CACHE_DIR}/${did}/${rkey}/.metadata.json`;
+
const metadataPath = `${CACHE_DIR}/${did}/${rkey}${dirSuffix}/.metadata.json`;
const metadataDir = metadataPath.substring(0, metadataPath.lastIndexOf('/'));
if (!existsSync(metadataDir)) {
+188 -106
hosting-service/src/server.ts
···
-
import { Hono } from 'hono';
+
import { Elysia } from 'elysia';
+
import { node } from '@elysiajs/node'
+
import { opentelemetry } from '@elysiajs/opentelemetry';
import { getWispDomain, getCustomDomain, getCustomDomainByHash } from './lib/db';
import { resolveDid, getPdsForDid, fetchSiteRecord, downloadAndCacheSite, getCachedFilePath, isCached, sanitizePath } from './lib/utils';
import { rewriteHtmlPaths, isHtmlContent } from './lib/html-rewriter';
import { existsSync, readFileSync } from 'fs';
import { lookup } from 'mime-types';
-
-
const app = new Hono();
const BASE_HOST = process.env.BASE_HOST || 'wisp.place';
···
if (existsSync(cachedFile)) {
const content = readFileSync(cachedFile);
+
const metaFile = `${cachedFile}.meta`;
+
+
// Check if file has compression metadata
+
if (existsSync(metaFile)) {
+
const meta = JSON.parse(readFileSync(metaFile, 'utf-8'));
+
if (meta.encoding === 'gzip' && meta.mimeType) {
+
// Serve gzipped content with proper headers
+
return new Response(content, {
+
headers: {
+
'Content-Type': meta.mimeType,
+
'Content-Encoding': 'gzip',
+
},
+
});
+
}
+
}
+
+
// Serve non-compressed files normally
const mimeType = lookup(cachedFile) || 'application/octet-stream';
return new Response(content, {
headers: {
···
const indexFile = getCachedFilePath(did, rkey, `${requestPath}/index.html`);
if (existsSync(indexFile)) {
const content = readFileSync(indexFile);
+
const metaFile = `${indexFile}.meta`;
+
+
// Check if file has compression metadata
+
if (existsSync(metaFile)) {
+
const meta = JSON.parse(readFileSync(metaFile, 'utf-8'));
+
if (meta.encoding === 'gzip' && meta.mimeType) {
+
return new Response(content, {
+
headers: {
+
'Content-Type': meta.mimeType,
+
'Content-Encoding': 'gzip',
+
},
+
});
+
}
+
}
+
return new Response(content, {
headers: {
'Content-Type': 'text/html; charset=utf-8',
···
const cachedFile = getCachedFilePath(did, rkey, requestPath);
if (existsSync(cachedFile)) {
-
const mimeType = lookup(cachedFile) || 'application/octet-stream';
+
const metaFile = `${cachedFile}.meta`;
+
let mimeType = lookup(cachedFile) || 'application/octet-stream';
+
let isGzipped = false;
+
+
// Check if file has compression metadata
+
if (existsSync(metaFile)) {
+
const meta = JSON.parse(readFileSync(metaFile, 'utf-8'));
+
if (meta.encoding === 'gzip' && meta.mimeType) {
+
mimeType = meta.mimeType;
+
isGzipped = true;
+
}
+
}
// Check if this is HTML content that needs rewriting
+
// Note: For gzipped HTML with path rewriting, we need to decompress, rewrite, and serve uncompressed
+
// This is a trade-off for the sites.wisp.place domain which needs path rewriting
if (isHtmlContent(requestPath, mimeType)) {
-
const content = readFileSync(cachedFile, 'utf-8');
+
let content: string;
+
if (isGzipped) {
+
const { gunzipSync } = await import('zlib');
+
const compressed = readFileSync(cachedFile);
+
content = gunzipSync(compressed).toString('utf-8');
+
} else {
+
content = readFileSync(cachedFile, 'utf-8');
+
}
const rewritten = rewriteHtmlPaths(content, basePath);
return new Response(rewritten, {
headers: {
···
});
}
-
// Non-HTML files served with proper MIME type
+
// Non-HTML files: serve gzipped content as-is with proper headers
const content = readFileSync(cachedFile);
+
if (isGzipped) {
+
return new Response(content, {
+
headers: {
+
'Content-Type': mimeType,
+
'Content-Encoding': 'gzip',
+
},
+
});
+
}
return new Response(content, {
headers: {
'Content-Type': mimeType,
···
if (!requestPath.includes('.')) {
const indexFile = getCachedFilePath(did, rkey, `${requestPath}/index.html`);
if (existsSync(indexFile)) {
-
const content = readFileSync(indexFile, 'utf-8');
+
const metaFile = `${indexFile}.meta`;
+
let isGzipped = false;
+
+
if (existsSync(metaFile)) {
+
const meta = JSON.parse(readFileSync(metaFile, 'utf-8'));
+
if (meta.encoding === 'gzip') {
+
isGzipped = true;
+
}
+
}
+
+
// HTML needs path rewriting, so decompress if needed
+
let content: string;
+
if (isGzipped) {
+
const { gunzipSync } = await import('zlib');
+
const compressed = readFileSync(indexFile);
+
content = gunzipSync(compressed).toString('utf-8');
+
} else {
+
content = readFileSync(indexFile, 'utf-8');
+
}
const rewritten = rewriteHtmlPaths(content, basePath);
return new Response(rewritten, {
headers: {
···
}
}
-
// Route 4: Direct file serving (no DB) - sites.wisp.place/:identifier/:site/*
-
// This route is now handled in the catch-all route below
+
const app = new Elysia({ adapter: node() })
+
.use(opentelemetry())
+
.get('/*', async ({ request, set }) => {
+
const url = new URL(request.url);
+
const hostname = request.headers.get('host') || '';
+
const rawPath = url.pathname.replace(/^\//, '');
+
const path = sanitizePath(rawPath);
-
// Route 3: DNS routing for custom domains - /hash.dns.wisp.place/*
-
app.get('/*', async (c) => {
-
const hostname = c.req.header('host') || '';
-
const rawPath = c.req.path.replace(/^\//, '');
-
const path = sanitizePath(rawPath);
+
// Check if this is sites.wisp.place subdomain
+
if (hostname === `sites.${BASE_HOST}` || hostname === `sites.${BASE_HOST}:${process.env.PORT || 3000}`) {
+
// Sanitize the path FIRST to prevent path traversal
+
const sanitizedFullPath = sanitizePath(rawPath);
-
console.log('[Request]', { hostname, path });
-
-
// Check if this is sites.wisp.place subdomain
-
if (hostname === `sites.${BASE_HOST}` || hostname === `sites.${BASE_HOST}:${process.env.PORT || 3000}`) {
-
// Sanitize the path FIRST to prevent path traversal
-
const sanitizedFullPath = sanitizePath(rawPath);
+
// Extract identifier and site from sanitized path: did:plc:123abc/sitename/file.html
+
const pathParts = sanitizedFullPath.split('/');
+
if (pathParts.length < 2) {
+
set.status = 400;
+
return 'Invalid path format. Expected: /identifier/sitename/path';
+
}
-
// Extract identifier and site from sanitized path: did:plc:123abc/sitename/file.html
-
const pathParts = sanitizedFullPath.split('/');
-
if (pathParts.length < 2) {
-
return c.text('Invalid path format. Expected: /identifier/sitename/path', 400);
-
}
+
const identifier = pathParts[0];
+
const site = pathParts[1];
+
const filePath = pathParts.slice(2).join('/');
-
const identifier = pathParts[0];
-
const site = pathParts[1];
-
const filePath = pathParts.slice(2).join('/');
+
// Additional validation: identifier must be a valid DID or handle format
+
if (!identifier || identifier.length < 3 || identifier.includes('..') || identifier.includes('\0')) {
+
set.status = 400;
+
return 'Invalid identifier';
+
}
-
console.log('[Sites] Serving', { identifier, site, filePath });
+
// Validate site name (rkey)
+
if (!isValidRkey(site)) {
+
set.status = 400;
+
return 'Invalid site name';
+
}
-
// Additional validation: identifier must be a valid DID or handle format
-
if (!identifier || identifier.length < 3 || identifier.includes('..') || identifier.includes('\0')) {
-
return c.text('Invalid identifier', 400);
-
}
+
// Resolve identifier to DID
+
const did = await resolveDid(identifier);
+
if (!did) {
+
set.status = 400;
+
return 'Invalid identifier';
+
}
-
// Validate site name (rkey)
-
if (!isValidRkey(site)) {
-
return c.text('Invalid site name', 400);
-
}
+
// Ensure site is cached
+
const cached = await ensureSiteCached(did, site);
+
if (!cached) {
+
set.status = 404;
+
return 'Site not found';
+
}
-
// Resolve identifier to DID
-
const did = await resolveDid(identifier);
-
if (!did) {
-
return c.text('Invalid identifier', 400);
+
// Serve with HTML path rewriting to handle absolute paths
+
const basePath = `/${identifier}/${site}/`;
+
return serveFromCacheWithRewrite(did, site, filePath, basePath);
}
-
// Ensure site is cached
-
const cached = await ensureSiteCached(did, site);
-
if (!cached) {
-
return c.text('Site not found', 404);
-
}
+
// Check if this is a DNS hash subdomain
+
const dnsMatch = hostname.match(/^([a-f0-9]{16})\.dns\.(.+)$/);
+
if (dnsMatch) {
+
const hash = dnsMatch[1];
+
const baseDomain = dnsMatch[2];
-
// Serve with HTML path rewriting to handle absolute paths
-
const basePath = `/${identifier}/${site}/`;
-
return serveFromCacheWithRewrite(did, site, filePath, basePath);
-
}
+
if (baseDomain !== BASE_HOST) {
+
set.status = 400;
+
return 'Invalid base domain';
+
}
-
// Check if this is a DNS hash subdomain
-
const dnsMatch = hostname.match(/^([a-f0-9]{16})\.dns\.(.+)$/);
-
if (dnsMatch) {
-
const hash = dnsMatch[1];
-
const baseDomain = dnsMatch[2];
+
const customDomain = await getCustomDomainByHash(hash);
+
if (!customDomain) {
+
set.status = 404;
+
return 'Custom domain not found or not verified';
+
}
-
console.log('[DNS Hash] Looking up', { hash, baseDomain });
+
const rkey = customDomain.rkey || 'self';
+
if (!isValidRkey(rkey)) {
+
set.status = 500;
+
return 'Invalid site configuration';
+
}
-
if (baseDomain !== BASE_HOST) {
-
return c.text('Invalid base domain', 400);
-
}
+
const cached = await ensureSiteCached(customDomain.did, rkey);
+
if (!cached) {
+
set.status = 404;
+
return 'Site not found';
+
}
-
const customDomain = await getCustomDomainByHash(hash);
-
if (!customDomain) {
-
return c.text('Custom domain not found or not verified', 404);
+
return serveFromCache(customDomain.did, rkey, path);
}
-
const rkey = customDomain.rkey || 'self';
-
if (!isValidRkey(rkey)) {
-
return c.text('Invalid site configuration', 500);
-
}
+
// Route 2: Registered subdomains - /*.wisp.place/*
+
if (hostname.endsWith(`.${BASE_HOST}`)) {
+
const subdomain = hostname.replace(`.${BASE_HOST}`, '');
-
const cached = await ensureSiteCached(customDomain.did, rkey);
-
if (!cached) {
-
return c.text('Site not found', 404);
-
}
+
const domainInfo = await getWispDomain(hostname);
+
if (!domainInfo) {
+
set.status = 404;
+
return 'Subdomain not registered';
+
}
-
return serveFromCache(customDomain.did, rkey, path);
-
}
+
const rkey = domainInfo.rkey || 'self';
+
if (!isValidRkey(rkey)) {
+
set.status = 500;
+
return 'Invalid site configuration';
+
}
-
// Route 2: Registered subdomains - /*.wisp.place/*
-
if (hostname.endsWith(`.${BASE_HOST}`)) {
-
const subdomain = hostname.replace(`.${BASE_HOST}`, '');
+
const cached = await ensureSiteCached(domainInfo.did, rkey);
+
if (!cached) {
+
set.status = 404;
+
return 'Site not found';
+
}
-
console.log('[Subdomain] Looking up', { subdomain, fullDomain: hostname });
+
return serveFromCache(domainInfo.did, rkey, path);
+
}
-
const domainInfo = await getWispDomain(hostname);
-
if (!domainInfo) {
-
return c.text('Subdomain not registered', 404);
+
// Route 1: Custom domains - /*
+
const customDomain = await getCustomDomain(hostname);
+
if (!customDomain) {
+
set.status = 404;
+
return 'Custom domain not found or not verified';
}
-
const rkey = domainInfo.rkey || 'self';
+
const rkey = customDomain.rkey || 'self';
if (!isValidRkey(rkey)) {
-
return c.text('Invalid site configuration', 500);
+
set.status = 500;
+
return 'Invalid site configuration';
}
-
const cached = await ensureSiteCached(domainInfo.did, rkey);
+
const cached = await ensureSiteCached(customDomain.did, rkey);
if (!cached) {
-
return c.text('Site not found', 404);
+
set.status = 404;
+
return 'Site not found';
}
-
return serveFromCache(domainInfo.did, rkey, path);
-
}
-
-
// Route 1: Custom domains - /*
-
console.log('[Custom Domain] Looking up', { hostname });
-
-
const customDomain = await getCustomDomain(hostname);
-
if (!customDomain) {
-
return c.text('Custom domain not found or not verified', 404);
-
}
-
-
const rkey = customDomain.rkey || 'self';
-
if (!isValidRkey(rkey)) {
-
return c.text('Invalid site configuration', 500);
-
}
-
-
const cached = await ensureSiteCached(customDomain.did, rkey);
-
if (!cached) {
-
return c.text('Site not found', 404);
-
}
-
-
return serveFromCache(customDomain.did, rkey, path);
-
});
+
return serveFromCache(customDomain.did, rkey, path);
+
});
export default app;
+4 -1
lexicons/fs.json
···
"required": ["type", "blob"],
"properties": {
"type": { "type": "string", "const": "file" },
-
"blob": { "type": "blob", "accept": ["*/*"], "maxSize": 1000000, "description": "Content blob ref" }
+
"blob": { "type": "blob", "accept": ["*/*"], "maxSize": 1000000, "description": "Content blob ref" },
+
"encoding": { "type": "string", "enum": ["gzip"], "description": "Content encoding (e.g., gzip for compressed files)" },
+
"mimeType": { "type": "string", "description": "Original MIME type before compression" },
+
"base64": { "type": "boolean", "description": "True if blob content is base64-encoded (used to bypass PDS content sniffing)" }
}
},
"directory": {
+3 -1
package.json
···
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "bun run --watch src/index.ts",
+
"start": "bun run src/index.ts",
"build": "bun build --compile --target bun --outfile server src/index.ts"
},
"dependencies": {
···
"tailwind-merge": "^3.3.1",
"tailwindcss": "4",
"tw-animate-css": "^1.4.0",
-
"typescript": "^5.9.3"
+
"typescript": "^5.9.3",
+
"zlib": "^1.0.5"
},
"devDependencies": {
"@types/react": "^19.2.2",
+14
src/lexicon/lexicons.ts
···
maxSize: 1000000,
description: 'Content blob ref',
},
+
encoding: {
+
type: 'string',
+
enum: ['gzip'],
+
description: 'Content encoding (e.g., gzip for compressed files)',
+
},
+
mimeType: {
+
type: 'string',
+
description: 'Original MIME type before compression',
+
},
+
base64: {
+
type: 'boolean',
+
description:
+
'True if blob content is base64-encoded (used to bypass PDS content sniffing)',
+
},
},
},
directory: {
+6
src/lexicon/types/place/wisp/fs.ts
···
type: 'file'
/** Content blob ref */
blob: BlobRef
+
/** Content encoding (e.g., gzip for compressed files) */
+
encoding?: 'gzip'
+
/** Original MIME type before compression */
+
mimeType?: string
+
/** True if blob content is base64-encoded (used to bypass PDS content sniffing) */
+
base64?: boolean
}
const hashFile = 'file'
+41 -2
src/lib/wisp-utils.ts
···
import type { BlobRef } from "@atproto/api";
import type { Record, Directory, File, Entry } from "../lexicon/types/place/wisp/fs";
import { validateRecord } from "../lexicon/types/place/wisp/fs";
+
import { gzipSync } from 'zlib';
export interface UploadedFile {
name: string;
content: Buffer;
mimeType: string;
size: number;
+
compressed?: boolean;
+
originalMimeType?: string;
}
export interface FileUploadResult {
hash: string;
blobRef: BlobRef;
+
encoding?: 'gzip';
+
mimeType?: string;
+
base64?: boolean;
}
export interface ProcessedDirectory {
directory: Directory;
fileCount: number;
+
}
+
+
/**
+
* Determine if a file should be gzip compressed based on its MIME type
+
*/
+
export function shouldCompressFile(mimeType: string): boolean {
+
// Compress text-based files
+
const compressibleTypes = [
+
'text/html',
+
'text/css',
+
'text/javascript',
+
'application/javascript',
+
'application/json',
+
'image/svg+xml',
+
'text/xml',
+
'application/xml',
+
'text/plain',
+
'application/x-javascript'
+
];
+
+
// Check if mime type starts with any compressible type
+
return compressibleTypes.some(type => mimeType.startsWith(type));
+
}
+
+
/**
+
* Compress a file using gzip
+
*/
+
export function compressFile(content: Buffer): Buffer {
+
return gzipSync(content, { level: 9 });
}
/**
···
});
if (fileIndex !== -1 && uploadResults[fileIndex]) {
-
const blobRef = uploadResults[fileIndex].blobRef;
+
const result = uploadResults[fileIndex];
+
const blobRef = result.blobRef;
return {
...entry,
node: {
$type: 'place.wisp.fs#file' as const,
type: 'file' as const,
-
blob: blobRef
+
blob: blobRef,
+
...(result.encoding && { encoding: result.encoding }),
+
...(result.mimeType && { mimeType: result.mimeType }),
+
...(result.base64 && { base64: result.base64 })
}
};
} else {
+53 -12
src/routes/wisp.ts
···
type FileUploadResult,
processUploadedFiles,
createManifest,
-
updateFileBlobs
+
updateFileBlobs,
+
shouldCompressFile,
+
compressFile
} from '../lib/wisp-utils'
import { upsertSite } from '../lib/db'
import { logger } from '../lib/logger'
···
}
const arrayBuffer = await file.arrayBuffer();
-
uploadedFiles.push({
-
name: file.name,
-
content: Buffer.from(arrayBuffer),
-
mimeType: 'application/octet-stream',
-
size: file.size
-
});
+
const originalContent = Buffer.from(arrayBuffer);
+
const originalMimeType = file.type || 'application/octet-stream';
+
+
// Determine if we should compress this file
+
const shouldCompress = shouldCompressFile(originalMimeType);
+
+
if (shouldCompress) {
+
const compressedContent = compressFile(originalContent);
+
// Base64 encode the gzipped content to prevent PDS content sniffing
+
const base64Content = Buffer.from(compressedContent.toString('base64'), 'utf-8');
+
const compressionRatio = (compressedContent.length / originalContent.length * 100).toFixed(1);
+
logger.info(`[Wisp] Compressing ${file.name}: ${originalContent.length} -> ${compressedContent.length} bytes (${compressionRatio}%), base64: ${base64Content.length} bytes`);
+
+
uploadedFiles.push({
+
name: file.name,
+
content: base64Content,
+
mimeType: originalMimeType,
+
size: base64Content.length,
+
compressed: true,
+
originalMimeType
+
});
+
} else {
+
uploadedFiles.push({
+
name: file.name,
+
content: originalContent,
+
mimeType: originalMimeType,
+
size: file.size,
+
compressed: false
+
});
+
}
}
// Check total size limit (300MB)
···
// Process files into directory structure
const { directory, fileCount } = processUploadedFiles(uploadedFiles);
-
// Upload files as blobs in parallel (always as octet-stream)
+
// Upload files as blobs in parallel
+
// For compressed files, we upload as octet-stream and store the original MIME type in metadata
+
// For text/html files, we also use octet-stream as a workaround for PDS image pipeline issues
const uploadPromises = uploadedFiles.map(async (file, i) => {
try {
+
// If compressed, always upload as octet-stream
+
// Otherwise, workaround: PDS incorrectly processes text/html through image pipeline
+
const uploadMimeType = file.compressed || file.mimeType.startsWith('text/html')
+
? 'application/octet-stream'
+
: file.mimeType;
+
+
const compressionInfo = file.compressed ? ' (gzipped)' : '';
+
logger.info(`[Wisp] Uploading file: ${file.name} (original: ${file.mimeType}, sending as: ${uploadMimeType}, ${file.size} bytes${compressionInfo})`);
+
const uploadResult = await agent.com.atproto.repo.uploadBlob(
file.content,
{
-
encoding: 'application/octet-stream'
+
encoding: uploadMimeType
}
);
-
const sentMimeType = file.mimeType;
const returnedBlobRef = uploadResult.data.blob;
// Use the blob ref exactly as returned from PDS
return {
result: {
hash: returnedBlobRef.ref.toString(),
-
blobRef: returnedBlobRef
+
blobRef: returnedBlobRef,
+
...(file.compressed && {
+
encoding: 'gzip' as const,
+
mimeType: file.originalMimeType || file.mimeType,
+
base64: true
+
})
},
filePath: file.name,
-
sentMimeType,
+
sentMimeType: file.mimeType,
returnedMimeType: returnedBlobRef.mimeType
};
} catch (uploadError) {