mirror of
https://github.com/nestriness/nestri.git
synced 2025-12-11 00:05:36 +02:00
fix: Clean up and add Neon DB
This commit is contained in:
32
bun.lock
32
bun.lock
@@ -21,6 +21,7 @@
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-iot-data-plane": "^3.758.0",
|
||||
"@aws-sdk/client-sesv2": "^3.753.0",
|
||||
"@neondatabase/serverless": "^1.0.1",
|
||||
"@openauthjs/openauth": "*",
|
||||
"@openauthjs/openevent": "^0.0.27",
|
||||
"@polar-sh/sdk": "^0.26.1",
|
||||
@@ -36,6 +37,7 @@
|
||||
"sanitize-html": "^2.16.0",
|
||||
"sharp": "^0.34.1",
|
||||
"steam-session": "*",
|
||||
"ws": "^8.18.3",
|
||||
"xml2js": "^0.6.2",
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -645,6 +647,8 @@
|
||||
|
||||
"@multiformats/multiaddr-to-uri": ["@multiformats/multiaddr-to-uri@11.0.2", "", { "dependencies": { "@multiformats/multiaddr": "^12.3.0" } }, "sha512-SiLFD54zeOJ0qMgo9xv1Tl9O5YktDKAVDP4q4hL16mSq4O4sfFNagNADz8eAofxd6TfQUzGQ3TkRRG9IY2uHRg=="],
|
||||
|
||||
"@neondatabase/serverless": ["@neondatabase/serverless@1.0.1", "", { "dependencies": { "@types/node": "^22.15.30", "@types/pg": "^8.8.0" } }, "sha512-O6yC5TT0jbw86VZVkmnzCZJB0hfxBl0JJz6f+3KHoZabjb/X08r9eFA+vuY06z1/qaovykvdkrXYq3SPUuvogA=="],
|
||||
|
||||
"@nestri/core": ["@nestri/core@workspace:cloud/packages/core"],
|
||||
|
||||
"@nestri/functions": ["@nestri/functions@workspace:cloud/packages/functions"],
|
||||
@@ -1171,7 +1175,7 @@
|
||||
|
||||
"@types/node": ["@types/node@22.13.10", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw=="],
|
||||
|
||||
"@types/pg": ["@types/pg@8.6.1", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w=="],
|
||||
"@types/pg": ["@types/pg@8.15.5", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-LF7lF6zWEKxuT3/OR8wAZGzkg4ENGXFNyiV/JeOt9z5B+0ZVwbql9McqX5c/WStFq1GaGso7H1AzP/qSzmlCKQ=="],
|
||||
|
||||
"@types/pg-pool": ["@types/pg-pool@2.0.6", "", { "dependencies": { "@types/pg": "*" } }, "sha512-TaAUE5rq2VQYxab5Ts7WZhKNmuN78Q6PiFonTDdpbx8a1H0M1vhy3rhiMjl+e2iHmogyMw7jZF4FrE6eJUy5HQ=="],
|
||||
|
||||
@@ -3053,7 +3057,7 @@
|
||||
|
||||
"write-file-atomic": ["write-file-atomic@5.0.1", "", { "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^4.0.1" } }, "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw=="],
|
||||
|
||||
"ws": ["ws@8.18.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w=="],
|
||||
"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=="],
|
||||
|
||||
"xml2js": ["xml2js@0.6.2", "", { "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" } }, "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA=="],
|
||||
|
||||
@@ -3469,6 +3473,8 @@
|
||||
|
||||
"@fastify/ajv-compiler/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
|
||||
|
||||
"@fastify/websocket/ws": ["ws@8.18.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w=="],
|
||||
|
||||
"@gerrit0/mini-shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.4.0", "", { "dependencies": { "@shikijs/types": "3.4.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-zwcWlZ4OQuJ/+1t32ClTtyTU1AiDkK1lhtviRWoq/hFqPjCNyLj22bIg9rB7BfoZKOEOfrsGz7No33BPCf+WlQ=="],
|
||||
|
||||
"@gerrit0/mini-shiki/@shikijs/langs": ["@shikijs/langs@3.4.0", "", { "dependencies": { "@shikijs/types": "3.4.0" } }, "sha512-bQkR+8LllaM2duU9BBRQU0GqFTx7TuF5kKlw/7uiGKoK140n1xlLAwCgXwSxAjJ7Htk9tXTFwnnsJTCU5nDPXQ=="],
|
||||
@@ -3483,6 +3489,8 @@
|
||||
|
||||
"@httptoolkit/websocket-stream/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
|
||||
|
||||
"@httptoolkit/websocket-stream/ws": ["ws@8.18.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w=="],
|
||||
|
||||
"@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
|
||||
|
||||
"@isaacs/cliui/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
|
||||
@@ -3501,8 +3509,6 @@
|
||||
|
||||
"@libp2p/websockets/@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
|
||||
|
||||
"@libp2p/websockets/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=="],
|
||||
|
||||
"@macaron-css/babel/@emotion/hash": ["@emotion/hash@0.8.0", "", {}, "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow=="],
|
||||
|
||||
"@macaron-css/integration/esbuild": ["esbuild@0.14.54", "", { "optionalDependencies": { "@esbuild/linux-loong64": "0.14.54", "esbuild-android-64": "0.14.54", "esbuild-android-arm64": "0.14.54", "esbuild-darwin-64": "0.14.54", "esbuild-darwin-arm64": "0.14.54", "esbuild-freebsd-64": "0.14.54", "esbuild-freebsd-arm64": "0.14.54", "esbuild-linux-32": "0.14.54", "esbuild-linux-64": "0.14.54", "esbuild-linux-arm": "0.14.54", "esbuild-linux-arm64": "0.14.54", "esbuild-linux-mips64le": "0.14.54", "esbuild-linux-ppc64le": "0.14.54", "esbuild-linux-riscv64": "0.14.54", "esbuild-linux-s390x": "0.14.54", "esbuild-netbsd-64": "0.14.54", "esbuild-openbsd-64": "0.14.54", "esbuild-sunos-64": "0.14.54", "esbuild-windows-32": "0.14.54", "esbuild-windows-64": "0.14.54", "esbuild-windows-arm64": "0.14.54" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA=="],
|
||||
@@ -3519,6 +3525,8 @@
|
||||
|
||||
"@multiformats/dns/buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
|
||||
|
||||
"@neondatabase/serverless/@types/node": ["@types/node@22.18.6", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-r8uszLPpeIWbNKtvWRt/DbVi5zbqZyj1PTmhRMqBMvDnaz1QpmSKujUtJLrqGZeoM8v72MfYggDceY4K1itzWQ=="],
|
||||
|
||||
"@nestri/functions/@cloudflare/workers-types": ["@cloudflare/workers-types@4.20250522.0", "", {}, "sha512-9RIffHobc35JWeddzBguGgPa4wLDr5x5F94+0/qy7LiV6pTBQ/M5qGEN9VA16IDT3EUpYI0WKh6VpcmeVEtVtw=="],
|
||||
|
||||
"@nestri/functions/@openauthjs/openauth": ["@openauthjs/openauth@0.0.0-20250322224806", "", { "dependencies": { "@standard-schema/spec": "1.0.0-beta.3", "aws4fetch": "1.0.20", "jose": "5.9.6" }, "peerDependencies": { "arctic": "^2.2.2", "hono": "^4.0.0" } }, "sha512-p5IWSRXvABcwocH2dNI0w8c1QJelIOFulwhKk+aLLFfUbs8u1pr7kQbYe8yCSM2+bcLHiwbogpUQc2ovrGwCuw=="],
|
||||
@@ -3721,6 +3729,8 @@
|
||||
|
||||
"@opentelemetry/instrumentation-pg/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.33.0", "", {}, "sha512-TIpZvE8fiEILFfTlfPnltpBaD3d9/+uQHVCyC3vfdh6WfCXKhNFzoP5RyDDIndfvZC5GrA4pyEDNyjPloJud+w=="],
|
||||
|
||||
"@opentelemetry/instrumentation-pg/@types/pg": ["@types/pg@8.6.1", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w=="],
|
||||
|
||||
"@opentelemetry/instrumentation-pino/@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/instrumentation-pino/@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=="],
|
||||
@@ -3835,6 +3845,8 @@
|
||||
|
||||
"@rocicorp/zero/jose": ["jose@5.9.6", "", {}, "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="],
|
||||
|
||||
"@rocicorp/zero/ws": ["ws@8.18.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w=="],
|
||||
|
||||
"@rollup/pluginutils/@types/estree": ["@types/estree@1.0.6", "", {}, "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="],
|
||||
|
||||
"@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
|
||||
@@ -3859,6 +3871,10 @@
|
||||
|
||||
"@types/bun/bun-types": ["bun-types@1.2.22", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-hwaAu8tct/Zn6Zft4U9BsZcXkYomzpHJX28ofvx7k0Zz2HNz54n1n+tDgxoWFGB4PcFvJXJQloPhaV2eP3Q6EA=="],
|
||||
|
||||
"@types/pg/@types/node": ["@types/node@22.18.6", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-r8uszLPpeIWbNKtvWRt/DbVi5zbqZyj1PTmhRMqBMvDnaz1QpmSKujUtJLrqGZeoM8v72MfYggDceY4K1itzWQ=="],
|
||||
|
||||
"@types/pg-pool/@types/pg": ["@types/pg@8.6.1", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w=="],
|
||||
|
||||
"@vanilla-extract/css/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
||||
|
||||
"@vanilla-extract/integration/find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="],
|
||||
@@ -3983,8 +3999,6 @@
|
||||
|
||||
"it-ws/@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
|
||||
|
||||
"it-ws/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=="],
|
||||
|
||||
"markdown-it/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
||||
|
||||
"mdast-util-find-and-replace/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
|
||||
@@ -4003,6 +4017,8 @@
|
||||
|
||||
"mqtt/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
||||
|
||||
"mqtt/ws": ["ws@8.18.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w=="],
|
||||
|
||||
"node-gyp/env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="],
|
||||
|
||||
"ofetch/destr": ["destr@2.0.3", "", {}, "sha512-2N3BOUU4gYMpTP24s5rF5iP7BDr7uNTCs4ozw3kf/eKfvWSIu93GEBi5m427YoyJoeOzQ5smuu4nNAPGb8idSQ=="],
|
||||
@@ -4443,6 +4459,8 @@
|
||||
|
||||
"@multiformats/dns/buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
||||
|
||||
"@neondatabase/serverless/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
|
||||
|
||||
"@nestri/functions/@openauthjs/openauth/jose": ["jose@5.9.6", "", {}, "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="],
|
||||
|
||||
"@openauthjs/solid/@openauthjs/openauth/jose": ["jose@5.9.6", "", {}, "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="],
|
||||
@@ -4535,6 +4553,8 @@
|
||||
|
||||
"@rocicorp/zero/@opentelemetry/sdk-trace-node/@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=="],
|
||||
|
||||
"@types/pg/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
|
||||
|
||||
"@vanilla-extract/integration/find-up/locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
|
||||
|
||||
"@vanilla-extract/integration/find-up/path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-iot-data-plane": "^3.758.0",
|
||||
"@aws-sdk/client-sesv2": "^3.753.0",
|
||||
"@neondatabase/serverless": "^1.0.1",
|
||||
"@openauthjs/openauth": "*",
|
||||
"@openauthjs/openevent": "^0.0.27",
|
||||
"@polar-sh/sdk": "^0.26.1",
|
||||
@@ -44,6 +45,7 @@
|
||||
"sanitize-html": "^2.16.0",
|
||||
"sharp": "^0.34.1",
|
||||
"steam-session": "*",
|
||||
"ws": "^8.18.3",
|
||||
"xml2js": "^0.6.2"
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,21 @@
|
||||
import { AsyncLocalStorage } from "node:async_hooks";
|
||||
|
||||
export function createContext<T>() {
|
||||
const storage = new AsyncLocalStorage<T>();
|
||||
return {
|
||||
use() {
|
||||
const result = storage.getStore();
|
||||
if (!result) {
|
||||
throw new Error("No context available");
|
||||
}
|
||||
return result;
|
||||
},
|
||||
provide<R>(value: T, fn: () => R) {
|
||||
return storage.run<R>(value, fn);
|
||||
},
|
||||
};
|
||||
}
|
||||
export namespace Context {
|
||||
export class NotFound extends Error {}
|
||||
|
||||
export function create<T>() {
|
||||
const storage = new AsyncLocalStorage<T>();
|
||||
return {
|
||||
use() {
|
||||
const result = storage.getStore();
|
||||
if (!result) {
|
||||
throw new NotFound();
|
||||
}
|
||||
return result;
|
||||
},
|
||||
provide<R>(value: T, fn: () => R) {
|
||||
return storage.run<R>(value, fn);
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,102 @@
|
||||
import ws from "ws";
|
||||
import { Resource } from "sst";
|
||||
import postgres from "postgres";
|
||||
import { drizzle } from "drizzle-orm/postgres-js";
|
||||
import { memo } from "../utils";
|
||||
import { Context } from "../context";
|
||||
import { ExtractTablesWithRelations } from "drizzle-orm";
|
||||
import { Pool, neonConfig } from "@neondatabase/serverless";
|
||||
import { PgTransaction, PgTransactionConfig } from "drizzle-orm/pg-core";
|
||||
import { NeonQueryResultHKT, drizzle } from "drizzle-orm/neon-serverless";
|
||||
|
||||
const client = postgres({
|
||||
idle_timeout: 30000,
|
||||
connect_timeout: 30000,
|
||||
host: Resource.Database.host,
|
||||
database: Resource.Database.database,
|
||||
user: Resource.Database.username,
|
||||
password: Resource.Database.password,
|
||||
port: Resource.Database.port,
|
||||
max: parseInt(process.env.POSTGRES_POOL_MAX || "1"),
|
||||
});
|
||||
neonConfig.webSocketConstructor = ws;
|
||||
|
||||
export const db = drizzle(client, {});
|
||||
export namespace Database {
|
||||
function addPoolerSuffix(original: string): string {
|
||||
const firstDotIndex = original.indexOf(".");
|
||||
if (firstDotIndex === -1) return original + "-pooler";
|
||||
return (
|
||||
original.slice(0, firstDotIndex) +
|
||||
"-pooler" +
|
||||
original.slice(firstDotIndex)
|
||||
);
|
||||
}
|
||||
|
||||
const client = memo(() => {
|
||||
const dbHost = addPoolerSuffix(Resource.Database.host);
|
||||
const pool = new Pool({
|
||||
connectionString: `postgres://${Resource.Database.user}:${Resource.Database.password}@${dbHost}/${Resource.Database.name}?sslmode=require`,
|
||||
});
|
||||
const db = drizzle(pool);
|
||||
return db;
|
||||
});
|
||||
|
||||
export type Transaction = PgTransaction<
|
||||
NeonQueryResultHKT,
|
||||
Record<string, never>,
|
||||
ExtractTablesWithRelations<Record<string, never>>
|
||||
>;
|
||||
|
||||
export type TxOrDb = Transaction | ReturnType<typeof client>;
|
||||
|
||||
const TransactionContext = Context.create<{
|
||||
tx: TxOrDb;
|
||||
effects: (() => void | Promise<void>)[];
|
||||
}>();
|
||||
|
||||
export async function use<T>(callback: (trx: TxOrDb) => Promise<T>) {
|
||||
try {
|
||||
const { tx } = TransactionContext.use();
|
||||
return tx.transaction(callback);
|
||||
} catch (err) {
|
||||
if (err instanceof Context.NotFound) {
|
||||
const effects: (() => void | Promise<void>)[] = [];
|
||||
const result = await TransactionContext.provide(
|
||||
{
|
||||
effects,
|
||||
tx: client(),
|
||||
},
|
||||
() => callback(client()),
|
||||
);
|
||||
await Promise.all(effects.map((x) => x()));
|
||||
return result;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export async function fn<Input, T>(
|
||||
callback: (input: Input, trx: TxOrDb) => Promise<T>,
|
||||
) {
|
||||
return (input: Input) => use(async (tx) => callback(input, tx));
|
||||
}
|
||||
|
||||
export async function effect(effect: () => any | Promise<any>) {
|
||||
try {
|
||||
const { effects } = TransactionContext.use();
|
||||
effects.push(effect);
|
||||
} catch {
|
||||
await effect();
|
||||
}
|
||||
}
|
||||
|
||||
export async function transaction<T>(
|
||||
callback: (tx: TxOrDb) => Promise<T>,
|
||||
config?: PgTransactionConfig,
|
||||
) {
|
||||
try {
|
||||
const { tx } = TransactionContext.use();
|
||||
return callback(tx);
|
||||
} catch (err) {
|
||||
if (err instanceof Context.NotFound) {
|
||||
const effects: (() => void | Promise<void>)[] = [];
|
||||
const result = await client().transaction(async (tx) => {
|
||||
return TransactionContext.provide({ tx, effects }, () =>
|
||||
callback(tx),
|
||||
);
|
||||
}, config);
|
||||
await Promise.all(effects.map((x) => x()));
|
||||
return result;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
import { db } from ".";
|
||||
import {
|
||||
PgTransaction,
|
||||
PgTransactionConfig
|
||||
} from "drizzle-orm/pg-core";
|
||||
import {
|
||||
PostgresJsQueryResultHKT
|
||||
} from "drizzle-orm/postgres-js";
|
||||
import { ExtractTablesWithRelations } from "drizzle-orm";
|
||||
import { createContext } from "../context";
|
||||
|
||||
export type Transaction = PgTransaction<
|
||||
PostgresJsQueryResultHKT,
|
||||
Record<string, never>,
|
||||
ExtractTablesWithRelations<Record<string, never>>
|
||||
>;
|
||||
|
||||
type TxOrDb = Transaction | typeof db;
|
||||
|
||||
const TransactionContext = createContext<{
|
||||
tx: Transaction;
|
||||
effects: (() => void | Promise<void>)[];
|
||||
}>();
|
||||
|
||||
export async function useTransaction<T>(callback: (trx: TxOrDb) => Promise<T>) {
|
||||
try {
|
||||
const { tx } = TransactionContext.use();
|
||||
return callback(tx);
|
||||
} catch {
|
||||
return callback(db);
|
||||
}
|
||||
}
|
||||
|
||||
export async function afterTx(effect: () => any | Promise<any>) {
|
||||
try {
|
||||
const { effects } = TransactionContext.use();
|
||||
effects.push(effect);
|
||||
} catch {
|
||||
await effect();
|
||||
}
|
||||
}
|
||||
|
||||
export async function createTransaction<T>(
|
||||
callback: (tx: Transaction) => Promise<T>,
|
||||
isolationLevel?: PgTransactionConfig["isolationLevel"],
|
||||
): Promise<T> {
|
||||
try {
|
||||
const { tx } = TransactionContext.use();
|
||||
return callback(tx);
|
||||
} catch {
|
||||
const effects: (() => void | Promise<void>)[] = [];
|
||||
const result = await db.transaction(
|
||||
async (tx) => {
|
||||
return TransactionContext.provide({ tx, effects }, () => callback(tx));
|
||||
},
|
||||
{
|
||||
isolationLevel: isolationLevel || "read committed",
|
||||
},
|
||||
);
|
||||
await Promise.all(effects.map((x) => x()));
|
||||
return result as T;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,17 @@
|
||||
import { z } from "zod";
|
||||
import { fn } from "../utils";
|
||||
import { Resource } from "sst";
|
||||
import { fn, memo } from "../utils";
|
||||
import { Polar as PolarSdk } from "@polar-sh/sdk";
|
||||
import { validateEvent } from "@polar-sh/sdk/webhooks";
|
||||
|
||||
export namespace Polar {
|
||||
export const client = () =>
|
||||
new PolarSdk({
|
||||
accessToken: Resource.POLAR_API_KEY.value,
|
||||
server: Resource.App.stage !== "production" ? "sandbox" : "production",
|
||||
});
|
||||
export const client = memo(
|
||||
() =>
|
||||
new PolarSdk({
|
||||
accessToken: Resource.POLAR_API_KEY.value,
|
||||
server: Resource.App.stage !== "production" ? "sandbox" : "production",
|
||||
}),
|
||||
);
|
||||
|
||||
export const fromUserEmail = fn(z.string().min(1), async (email) => {
|
||||
try {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { z } from "zod";
|
||||
import { Common } from "../common";
|
||||
import { Database } from "../drizzle";
|
||||
import { createEvent } from "../event";
|
||||
import { Polar } from "../polar/index";
|
||||
import { createID, fn } from "../utils";
|
||||
@@ -7,180 +8,165 @@ import { userTable } from "./user.sql";
|
||||
import { Examples } from "../examples";
|
||||
import { and, eq, isNull, asc } from "drizzle-orm";
|
||||
import { ErrorCodes, VisibleError } from "../error";
|
||||
import { createTransaction, useTransaction } from "../drizzle/transaction";
|
||||
|
||||
export namespace User {
|
||||
export const Info = z
|
||||
.object({
|
||||
id: z.string().openapi({
|
||||
description: Common.IdDescription,
|
||||
example: Examples.User.id,
|
||||
}),
|
||||
name: z.string().regex(/^[a-zA-Z ]{1,32}$/, "Use a friendly name.").openapi({
|
||||
description: "The name of this account",
|
||||
example: Examples.User.name
|
||||
}),
|
||||
polarCustomerID: z.string().nullable().openapi({
|
||||
description: "Associated Polar.sh customer identifier",
|
||||
example: Examples.User.polarCustomerID,
|
||||
}),
|
||||
avatarUrl: z.string().url().nullable().openapi({
|
||||
description: "The url to the profile picture",
|
||||
example: Examples.User.avatarUrl
|
||||
}),
|
||||
email: z.string().openapi({
|
||||
description: "Primary email address for user notifications and authentication",
|
||||
example: Examples.User.email,
|
||||
}),
|
||||
lastLogin: z.date().openapi({
|
||||
description: "Timestamp of user's most recent authentication",
|
||||
example: Examples.User.lastLogin
|
||||
})
|
||||
})
|
||||
export const Info = z
|
||||
.object({
|
||||
id: z.string().openapi({
|
||||
description: Common.IdDescription,
|
||||
example: Examples.User.id,
|
||||
}),
|
||||
name: z
|
||||
.string()
|
||||
.regex(/^[a-zA-Z ]{1,32}$/, "Use a friendly name.")
|
||||
.openapi({
|
||||
ref: "User",
|
||||
description: "User account entity with core identification and authentication details",
|
||||
example: Examples.User,
|
||||
});
|
||||
description: "The name of this account",
|
||||
example: Examples.User.name,
|
||||
}),
|
||||
polarCustomerID: z.string().nullable().openapi({
|
||||
description: "Associated Polar.sh customer identifier",
|
||||
example: Examples.User.polarCustomerID,
|
||||
}),
|
||||
avatarUrl: z.string().url().nullable().openapi({
|
||||
description: "The url to the profile picture",
|
||||
example: Examples.User.avatarUrl,
|
||||
}),
|
||||
email: z.string().openapi({
|
||||
description:
|
||||
"Primary email address for user notifications and authentication",
|
||||
example: Examples.User.email,
|
||||
}),
|
||||
lastLogin: z.date().openapi({
|
||||
description: "Timestamp of user's most recent authentication",
|
||||
example: Examples.User.lastLogin,
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
ref: "User",
|
||||
description:
|
||||
"User account entity with core identification and authentication details",
|
||||
example: Examples.User,
|
||||
});
|
||||
|
||||
export type Info = z.infer<typeof Info>;
|
||||
export type Info = z.infer<typeof Info>;
|
||||
|
||||
export class UserExistsError extends VisibleError {
|
||||
constructor(username: string) {
|
||||
super(
|
||||
"already_exists",
|
||||
ErrorCodes.Validation.ALREADY_EXISTS,
|
||||
`A user with this email ${username} already exists`
|
||||
);
|
||||
}
|
||||
export class UserExistsError extends VisibleError {
|
||||
constructor(username: string) {
|
||||
super(
|
||||
"already_exists",
|
||||
ErrorCodes.Validation.ALREADY_EXISTS,
|
||||
`A user with this email ${username} already exists`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const Events = {
|
||||
Created: createEvent(
|
||||
"user.created",
|
||||
z.object({
|
||||
userID: Info.shape.id,
|
||||
}),
|
||||
),
|
||||
};
|
||||
export const Events = {
|
||||
Created: createEvent(
|
||||
"user.created",
|
||||
z.object({
|
||||
userID: Info.shape.id,
|
||||
}),
|
||||
),
|
||||
};
|
||||
|
||||
export const create = fn(
|
||||
Info
|
||||
.omit({
|
||||
lastLogin: true,
|
||||
polarCustomerID: true,
|
||||
}).partial({
|
||||
avatarUrl: true,
|
||||
id: true
|
||||
}),
|
||||
async (input) => {
|
||||
const userID = createID("user")
|
||||
export const create = fn(
|
||||
Info.omit({
|
||||
lastLogin: true,
|
||||
polarCustomerID: true,
|
||||
}).partial({
|
||||
avatarUrl: true,
|
||||
id: true,
|
||||
}),
|
||||
async (input) => {
|
||||
const userID = createID("user");
|
||||
|
||||
const customer = await Polar.fromUserEmail(input.email)
|
||||
const customer = await Polar.fromUserEmail(input.email);
|
||||
|
||||
const id = input.id ?? userID;
|
||||
const id = input.id ?? userID;
|
||||
|
||||
await createTransaction(async (tx) => {
|
||||
const result = await tx
|
||||
.insert(userTable)
|
||||
.values({
|
||||
id,
|
||||
avatarUrl: input.avatarUrl,
|
||||
email: input.email,
|
||||
name: input.name,
|
||||
polarCustomerID: customer?.id,
|
||||
lastLogin: Common.utc()
|
||||
})
|
||||
.onConflictDoNothing({
|
||||
target: [userTable.email]
|
||||
})
|
||||
|
||||
if (result.count === 0) {
|
||||
throw new UserExistsError(input.email)
|
||||
}
|
||||
})
|
||||
|
||||
return id;
|
||||
})
|
||||
|
||||
export const fromEmail = fn(
|
||||
Info.shape.email.min(1),
|
||||
async (email) =>
|
||||
useTransaction(async (tx) =>
|
||||
tx
|
||||
.select()
|
||||
.from(userTable)
|
||||
.where(
|
||||
and(
|
||||
eq(userTable.email, email),
|
||||
isNull(userTable.timeDeleted)
|
||||
)
|
||||
)
|
||||
.orderBy(asc(userTable.timeCreated))
|
||||
.execute()
|
||||
.then(rows => rows.map(serialize).at(0))
|
||||
)
|
||||
)
|
||||
|
||||
export const fromID = fn(
|
||||
Info.shape.id.min(1),
|
||||
(id) =>
|
||||
useTransaction(async (tx) =>
|
||||
tx
|
||||
.select()
|
||||
.from(userTable)
|
||||
.where(
|
||||
and(
|
||||
eq(userTable.id, id),
|
||||
isNull(userTable.timeDeleted)
|
||||
)
|
||||
)
|
||||
.orderBy(asc(userTable.timeCreated))
|
||||
.execute()
|
||||
.then(rows => rows.map(serialize).at(0))
|
||||
),
|
||||
)
|
||||
|
||||
export const remove = fn(
|
||||
Info.shape.id.min(1),
|
||||
(id) =>
|
||||
useTransaction(async (tx) => {
|
||||
await tx
|
||||
.update(userTable)
|
||||
.set({
|
||||
timeDeleted: Common.utc(),
|
||||
})
|
||||
.where(and(eq(userTable.id, id)))
|
||||
.execute();
|
||||
return id;
|
||||
}),
|
||||
);
|
||||
|
||||
export const acknowledgeLogin = fn(
|
||||
Info.shape.id,
|
||||
(id) =>
|
||||
useTransaction(async (tx) =>
|
||||
tx
|
||||
.update(userTable)
|
||||
.set({
|
||||
lastLogin: Common.utc(),
|
||||
})
|
||||
.where(and(eq(userTable.id, id)))
|
||||
.execute()
|
||||
|
||||
),
|
||||
)
|
||||
|
||||
export function serialize(
|
||||
input: typeof userTable.$inferSelect
|
||||
): z.infer<typeof Info> {
|
||||
return {
|
||||
id: input.id,
|
||||
name: input.name,
|
||||
email: input.email,
|
||||
await Database.transaction(async (tx) => {
|
||||
const result = await tx
|
||||
.insert(userTable)
|
||||
.values({
|
||||
id,
|
||||
avatarUrl: input.avatarUrl,
|
||||
lastLogin: input.lastLogin,
|
||||
polarCustomerID: input.polarCustomerID,
|
||||
email: input.email,
|
||||
name: input.name,
|
||||
polarCustomerID: customer?.id,
|
||||
lastLogin: Common.utc(),
|
||||
})
|
||||
.onConflictDoNothing({
|
||||
target: [userTable.email],
|
||||
});
|
||||
|
||||
if (result.rowCount === 0) {
|
||||
throw new UserExistsError(input.email);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return id;
|
||||
},
|
||||
);
|
||||
|
||||
export const fromEmail = fn(Info.shape.email.min(1), async (email) =>
|
||||
Database.transaction(async (tx) =>
|
||||
tx
|
||||
.select()
|
||||
.from(userTable)
|
||||
.where(and(eq(userTable.email, email), isNull(userTable.timeDeleted)))
|
||||
.orderBy(asc(userTable.timeCreated))
|
||||
.execute()
|
||||
.then((rows) => rows.map(serialize).at(0)),
|
||||
),
|
||||
);
|
||||
|
||||
export const fromID = fn(Info.shape.id.min(1), (id) =>
|
||||
Database.transaction(async (tx) =>
|
||||
tx
|
||||
.select()
|
||||
.from(userTable)
|
||||
.where(and(eq(userTable.id, id), isNull(userTable.timeDeleted)))
|
||||
.orderBy(asc(userTable.timeCreated))
|
||||
.execute()
|
||||
.then((rows) => rows.map(serialize).at(0)),
|
||||
),
|
||||
);
|
||||
|
||||
export const remove = fn(Info.shape.id.min(1), (id) =>
|
||||
Database.transaction(async (tx) => {
|
||||
await tx
|
||||
.update(userTable)
|
||||
.set({
|
||||
timeDeleted: Common.utc(),
|
||||
})
|
||||
.where(and(eq(userTable.id, id)))
|
||||
.execute();
|
||||
return id;
|
||||
}),
|
||||
);
|
||||
|
||||
export const acknowledgeLogin = fn(Info.shape.id, (id) =>
|
||||
Database.transaction(async (tx) =>
|
||||
tx
|
||||
.update(userTable)
|
||||
.set({
|
||||
lastLogin: Common.utc(),
|
||||
})
|
||||
.where(and(eq(userTable.id, id)))
|
||||
.execute(),
|
||||
),
|
||||
);
|
||||
|
||||
export function serialize(
|
||||
input: typeof userTable.$inferSelect,
|
||||
): z.infer<typeof Info> {
|
||||
return {
|
||||
id: input.id,
|
||||
name: input.name,
|
||||
email: input.email,
|
||||
avatarUrl: input.avatarUrl,
|
||||
lastLogin: input.lastLogin,
|
||||
polarCustomerID: input.polarCustomerID,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export * from "./id"
|
||||
export * from "./fn"
|
||||
export * from "./log"
|
||||
export * from "./invite"
|
||||
export * from "./helper"
|
||||
export * from "./id";
|
||||
export * from "./fn";
|
||||
export * from "./log";
|
||||
export * from "./invite";
|
||||
export * from "./helper";
|
||||
export * from "./memo";
|
||||
|
||||
18
cloud/packages/core/src/utils/memo.ts
Normal file
18
cloud/packages/core/src/utils/memo.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
export function memo<T>(fn: () => T, cleanup?: (input: T) => Promise<void>) {
|
||||
let value: T | undefined;
|
||||
let loaded = false;
|
||||
|
||||
const result = (): T => {
|
||||
if (loaded) return value as T;
|
||||
loaded = true;
|
||||
value = fn();
|
||||
return value as T;
|
||||
};
|
||||
result.reset = async () => {
|
||||
if (cleanup && value) await cleanup(value);
|
||||
loaded = false;
|
||||
value = undefined;
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import { DiscordAdapter } from "./adapters";
|
||||
import { issuer } from "@openauthjs/openauth";
|
||||
import { User } from "@nestri/core/user/index";
|
||||
import { patchLogger } from "../utils/patch-logger";
|
||||
import type { KVNamespace } from "@cloudflare/workers-types";
|
||||
import { CloudflareStorage } from "@openauthjs/openauth/storage/cloudflare";
|
||||
|
||||
interface Env {
|
||||
|
||||
Reference in New Issue
Block a user