untested WIP

This commit is contained in:
2026-01-24 16:59:50 -06:00
parent 935c7296f6
commit 4be36e6ca0
56 changed files with 8645 additions and 3 deletions
+48
View File
@@ -0,0 +1,48 @@
version: "3"
services:
pgsql:
image: postgres
restart: unless-stopped
environment:
POSTGRES_USER: duxcore
POSTGRES_PASSWORD: 123web123
POSTGRES_DB: duxcore
volumes:
- ./postgres:/var/lib/postgresql/data
ports:
- 5432:5432
pgsql-boss:
image: postgres
restart: unless-stopped
environment:
POSTGRES_USER: pg-boss
POSTGRES_PASSWORD: 123web123
POSTGRES_DB: pg-boss
volumes:
- ./postgres-pgb:/var/lib/postgresql/data
ports:
- 2345:5432
redis:
image: redis:6.2-alpine
restart: always
ports:
- "6379:6379"
command: redis-server --save 20 1 --loglevel warning --requirepass iamatotallysecurepassworddonttestmebrox
volumes:
- ./redis:/data
adminer:
image: adminer
restart: unless-stopped
ports:
- 8080:8080
depends_on:
- pgsql
redisinsight:
image: redis/redisinsight:latest
container_name: redisinsight
ports:
- "5540:5540"
restart: unless-stopped
+15
View File
@@ -0,0 +1,15 @@
meta {
name: Teapot
type: http
seq: 1
}
get {
url: http://localhost:3000/v1/teapot
body: none
auth: inherit
}
settings {
encodeUrl: true
}
+9
View File
@@ -0,0 +1,9 @@
{
"version": "1",
"name": "ttscm",
"type": "collection",
"ignore": [
"node_modules",
".git"
]
}
+286
View File
@@ -4,8 +4,22 @@
"workspaces": { "workspaces": {
"": { "": {
"name": "ttscm-api", "name": "ttscm-api",
"dependencies": {
"@discordjs/collection": "^2.1.1",
"@duxcore/eventra": "^1.1.0",
"@prisma/adapter-pg": "^7.3.0",
"cors": "^2.8.6",
"cuid": "^3.0.0",
"hono": "^4.11.5",
"jsonwebtoken": "^9.0.3",
"keypair": "^1.0.4",
"prisma": "^7.3.0",
"zod": "^4.3.6",
"zon": "^1.0.3",
},
"devDependencies": { "devDependencies": {
"@types/bun": "latest", "@types/bun": "latest",
"@types/jsonwebtoken": "^9.0.10",
}, },
"peerDependencies": { "peerDependencies": {
"typescript": "^5", "typescript": "^5",
@@ -13,14 +27,286 @@
}, },
}, },
"packages": { "packages": {
"@chevrotain/cst-dts-gen": ["@chevrotain/cst-dts-gen@10.5.0", "", { "dependencies": { "@chevrotain/gast": "10.5.0", "@chevrotain/types": "10.5.0", "lodash": "4.17.21" } }, "sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw=="],
"@chevrotain/gast": ["@chevrotain/gast@10.5.0", "", { "dependencies": { "@chevrotain/types": "10.5.0", "lodash": "4.17.21" } }, "sha512-pXdMJ9XeDAbgOWKuD1Fldz4ieCs6+nLNmyVhe2gZVqoO7v8HXuHYs5OV2EzUtbuai37TlOAQHrTDvxMnvMJz3A=="],
"@chevrotain/types": ["@chevrotain/types@10.5.0", "", {}, "sha512-f1MAia0x/pAVPWH/T73BJVyO2XU5tI4/iE7cnxb7tqdNTNhQI3Uq3XkqcoteTmD4t1aM0LbHCJOhgIDn07kl2A=="],
"@chevrotain/utils": ["@chevrotain/utils@10.5.0", "", {}, "sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ=="],
"@discordjs/collection": ["@discordjs/collection@2.1.1", "", {}, "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg=="],
"@duxcore/eventra": ["@duxcore/eventra@1.1.0", "", {}, "sha512-XYLksXvOjr3uGw9oh0wgKVF0POiz1Gjk/Ety8chEQdEwzDAiyydztB8TtjF/Pfr+vdM/ceMV/OOQliBZLnfONA=="],
"@electric-sql/pglite": ["@electric-sql/pglite@0.3.15", "", {}, "sha512-Cj++n1Mekf9ETfdc16TlDi+cDDQF0W7EcbyRHYOAeZdsAe8M/FJg18itDTSwyHfar2WIezawM9o0EKaRGVKygQ=="],
"@electric-sql/pglite-socket": ["@electric-sql/pglite-socket@0.0.20", "", { "peerDependencies": { "@electric-sql/pglite": "0.3.15" }, "bin": { "pglite-server": "dist/scripts/server.js" } }, "sha512-J5nLGsicnD9wJHnno9r+DGxfcZWh+YJMCe0q/aCgtG6XOm9Z7fKeite8IZSNXgZeGltSigM9U/vAWZQWdgcSFg=="],
"@electric-sql/pglite-tools": ["@electric-sql/pglite-tools@0.2.20", "", { "peerDependencies": { "@electric-sql/pglite": "0.3.15" } }, "sha512-BK50ZnYa3IG7ztXhtgYf0Q7zijV32Iw1cYS8C+ThdQlwx12V5VZ9KRJ42y82Hyb4PkTxZQklVQA9JHyUlex33A=="],
"@hono/node-server": ["@hono/node-server@1.19.9", "", { "peerDependencies": { "hono": "^4" } }, "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw=="],
"@mrleebo/prisma-ast": ["@mrleebo/prisma-ast@0.13.1", "", { "dependencies": { "chevrotain": "^10.5.0", "lilconfig": "^2.1.0" } }, "sha512-XyroGQXcHrZdvmrGJvsA9KNeOOgGMg1Vg9OlheUsBOSKznLMDl+YChxbkboRHvtFYJEMRYmlV3uoo/njCw05iw=="],
"@prisma/adapter-pg": ["@prisma/adapter-pg@7.3.0", "", { "dependencies": { "@prisma/driver-adapter-utils": "7.3.0", "pg": "^8.16.3", "postgres-array": "3.0.4" } }, "sha512-iuYQMbIPO6i9O45Fv8TB7vWu00BXhCaNAShenqF7gLExGDbnGp5BfFB4yz1K59zQ59jF6tQ9YHrg0P6/J3OoLg=="],
"@prisma/config": ["@prisma/config@7.3.0", "", { "dependencies": { "c12": "3.1.0", "deepmerge-ts": "7.1.5", "effect": "3.18.4", "empathic": "2.0.0" } }, "sha512-QyMV67+eXF7uMtKxTEeQqNu/Be7iH+3iDZOQZW5ttfbSwBamCSdwPszA0dum+Wx27I7anYTPLmRmMORKViSW1A=="],
"@prisma/debug": ["@prisma/debug@7.3.0", "", {}, "sha512-yh/tHhraCzYkffsI1/3a7SHX8tpgbJu1NPnuxS4rEpJdWAUDHUH25F1EDo6PPzirpyLNkgPPZdhojQK804BGtg=="],
"@prisma/dev": ["@prisma/dev@0.20.0", "", { "dependencies": { "@electric-sql/pglite": "0.3.15", "@electric-sql/pglite-socket": "0.0.20", "@electric-sql/pglite-tools": "0.2.20", "@hono/node-server": "1.19.9", "@mrleebo/prisma-ast": "0.13.1", "@prisma/get-platform": "7.2.0", "@prisma/query-plan-executor": "7.2.0", "foreground-child": "3.3.1", "get-port-please": "3.2.0", "hono": "4.11.4", "http-status-codes": "2.3.0", "pathe": "2.0.3", "proper-lockfile": "4.1.2", "remeda": "2.33.4", "std-env": "3.10.0", "valibot": "1.2.0", "zeptomatch": "2.1.0" } }, "sha512-ovlBYwWor0OzG+yH4J3Ot+AneD818BttLA+Ii7wjbcLHUrnC4tbUPVGyNd3c/+71KETPKZfjhkTSpdS15dmXNQ=="],
"@prisma/driver-adapter-utils": ["@prisma/driver-adapter-utils@7.3.0", "", { "dependencies": { "@prisma/debug": "7.3.0" } }, "sha512-Wdlezh1ck0Rq2dDINkfSkwbR53q53//Eo1vVqVLwtiZ0I6fuWDGNPxwq+SNAIHnsU+FD/m3aIJKevH3vF13U3w=="],
"@prisma/engines": ["@prisma/engines@7.3.0", "", { "dependencies": { "@prisma/debug": "7.3.0", "@prisma/engines-version": "7.3.0-16.9d6ad21cbbceab97458517b147a6a09ff43aa735", "@prisma/fetch-engine": "7.3.0", "@prisma/get-platform": "7.3.0" } }, "sha512-cWRQoPDXPtR6stOWuWFZf9pHdQ/o8/QNWn0m0zByxf5Kd946Q875XdEJ52pEsX88vOiXUmjuPG3euw82mwQNMg=="],
"@prisma/engines-version": ["@prisma/engines-version@7.3.0-16.9d6ad21cbbceab97458517b147a6a09ff43aa735", "", {}, "sha512-IH2va2ouUHihyiTTRW889LjKAl1CusZOvFfZxCDNpjSENt7g2ndFsK0vdIw/72v7+jCN6YgkHmdAP/BI7SDgyg=="],
"@prisma/fetch-engine": ["@prisma/fetch-engine@7.3.0", "", { "dependencies": { "@prisma/debug": "7.3.0", "@prisma/engines-version": "7.3.0-16.9d6ad21cbbceab97458517b147a6a09ff43aa735", "@prisma/get-platform": "7.3.0" } }, "sha512-Mm0F84JMqM9Vxk70pzfNpGJ1lE4hYjOeLMu7nOOD1i83nvp8MSAcFYBnHqLvEZiA6onUR+m8iYogtOY4oPO5lQ=="],
"@prisma/get-platform": ["@prisma/get-platform@7.2.0", "", { "dependencies": { "@prisma/debug": "7.2.0" } }, "sha512-k1V0l0Td1732EHpAfi2eySTezyllok9dXb6UQanajkJQzPUGi3vO2z7jdkz67SypFTdmbnyGYxvEvYZdZsMAVA=="],
"@prisma/query-plan-executor": ["@prisma/query-plan-executor@7.2.0", "", {}, "sha512-EOZmNzcV8uJ0mae3DhTsiHgoNCuu1J9mULQpGCh62zN3PxPTd+qI9tJvk5jOst8WHKQNwJWR3b39t0XvfBB0WQ=="],
"@prisma/studio-core": ["@prisma/studio-core@0.13.1", "", { "peerDependencies": { "@types/react": "^18.0.0 || ^19.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-agdqaPEePRHcQ7CexEfkX1RvSH9uWDb6pXrZnhCRykhDFAV0/0P3d07WtfiY8hZWb7oRU4v+NkT4cGFHkQJIPg=="],
"@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
"@types/bun": ["@types/bun@1.3.6", "", { "dependencies": { "bun-types": "1.3.6" } }, "sha512-uWCv6FO/8LcpREhenN1d1b6fcspAB+cefwD7uti8C8VffIv0Um08TKMn98FynpTiU38+y2dUO55T11NgDt8VAA=="], "@types/bun": ["@types/bun@1.3.6", "", { "dependencies": { "bun-types": "1.3.6" } }, "sha512-uWCv6FO/8LcpREhenN1d1b6fcspAB+cefwD7uti8C8VffIv0Um08TKMn98FynpTiU38+y2dUO55T11NgDt8VAA=="],
"@types/jsonwebtoken": ["@types/jsonwebtoken@9.0.10", "", { "dependencies": { "@types/ms": "*", "@types/node": "*" } }, "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA=="],
"@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="],
"@types/node": ["@types/node@25.0.10", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg=="], "@types/node": ["@types/node@25.0.10", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg=="],
"@types/react": ["@types/react@19.2.9", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA=="],
"aws-ssl-profiles": ["aws-ssl-profiles@1.1.2", "", {}, "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g=="],
"buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="],
"bun-types": ["bun-types@1.3.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-OlFwHcnNV99r//9v5IIOgQ9Uk37gZqrNMCcqEaExdkVq3Avwqok1bJFmvGMCkCE0FqzdY8VMOZpfpR3lwI+CsQ=="], "bun-types": ["bun-types@1.3.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-OlFwHcnNV99r//9v5IIOgQ9Uk37gZqrNMCcqEaExdkVq3Avwqok1bJFmvGMCkCE0FqzdY8VMOZpfpR3lwI+CsQ=="],
"c12": ["c12@3.1.0", "", { "dependencies": { "chokidar": "^4.0.3", "confbox": "^0.2.2", "defu": "^6.1.4", "dotenv": "^16.6.1", "exsolve": "^1.0.7", "giget": "^2.0.0", "jiti": "^2.4.2", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^1.0.0", "pkg-types": "^2.2.0", "rc9": "^2.1.2" }, "peerDependencies": { "magicast": "^0.3.5" }, "optionalPeers": ["magicast"] }, "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw=="],
"chevrotain": ["chevrotain@10.5.0", "", { "dependencies": { "@chevrotain/cst-dts-gen": "10.5.0", "@chevrotain/gast": "10.5.0", "@chevrotain/types": "10.5.0", "@chevrotain/utils": "10.5.0", "lodash": "4.17.21", "regexp-to-ast": "0.5.0" } }, "sha512-Pkv5rBY3+CsHOYfV5g/Vs5JY9WTHHDEKOlohI2XeygaZhUeqhAlldZ8Hz9cRmxu709bvS08YzxHdTPHhffc13A=="],
"chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
"citty": ["citty@0.1.6", "", { "dependencies": { "consola": "^3.2.3" } }, "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ=="],
"confbox": ["confbox@0.2.2", "", {}, "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="],
"consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="],
"cors": ["cors@2.8.6", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="],
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
"cuid": ["cuid@3.0.0", "", {}, "sha512-WZYYkHdIDnaxdeP8Misq3Lah5vFjJwGuItJuV+tvMafosMzw0nF297T7mrm8IOWiPJkV6gc7sa8pzx27+w25Zg=="],
"deepmerge-ts": ["deepmerge-ts@7.1.5", "", {}, "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw=="],
"defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="],
"denque": ["denque@2.1.0", "", {}, "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="],
"destr": ["destr@2.0.5", "", {}, "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA=="],
"dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
"ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="],
"effect": ["effect@3.18.4", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA=="],
"empathic": ["empathic@2.0.0", "", {}, "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA=="],
"exsolve": ["exsolve@1.0.8", "", {}, "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA=="],
"fast-check": ["fast-check@3.23.2", "", { "dependencies": { "pure-rand": "^6.1.0" } }, "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A=="],
"foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
"generate-function": ["generate-function@2.3.1", "", { "dependencies": { "is-property": "^1.0.2" } }, "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ=="],
"get-port-please": ["get-port-please@3.2.0", "", {}, "sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A=="],
"giget": ["giget@2.0.0", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.0", "defu": "^6.1.4", "node-fetch-native": "^1.6.6", "nypm": "^0.6.0", "pathe": "^2.0.3" }, "bin": { "giget": "dist/cli.mjs" } }, "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA=="],
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
"grammex": ["grammex@3.1.12", "", {}, "sha512-6ufJOsSA7LcQehIJNCO7HIBykfM7DXQual0Ny780/DEcJIpBlHRvcqEBWGPYd7hrXL2GJ3oJI1MIhaXjWmLQOQ=="],
"graphmatch": ["graphmatch@1.1.0", "", {}, "sha512-0E62MaTW5rPZVRLyIJZG/YejmdA/Xr1QydHEw3Vt+qOKkMIOE8WDLc9ZX2bmAjtJFZcId4lEdrdmASsEy7D1QA=="],
"hono": ["hono@4.11.5", "", {}, "sha512-WemPi9/WfyMwZs+ZUXdiwcCh9Y+m7L+8vki9MzDw3jJ+W9Lc+12HGsd368Qc1vZi1xwW8BWMMsnK5efYKPdt4g=="],
"http-status-codes": ["http-status-codes@2.3.0", "", {}, "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA=="],
"iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
"is-property": ["is-property@1.0.2", "", {}, "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g=="],
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
"jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
"jsonwebtoken": ["jsonwebtoken@9.0.3", "", { "dependencies": { "jws": "^4.0.1", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" } }, "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g=="],
"jwa": ["jwa@2.0.1", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg=="],
"jws": ["jws@4.0.1", "", { "dependencies": { "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA=="],
"keypair": ["keypair@1.0.4", "", {}, "sha512-zwhgOhhniaL7oxMgUMKKw5219PWWABMO+dgMnzJOQ2/5L3XJtTJGhW2PEXlxXj9zaccdReZJZ83+4NPhVfNVDg=="],
"lilconfig": ["lilconfig@2.1.0", "", {}, "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ=="],
"lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
"lodash.includes": ["lodash.includes@4.3.0", "", {}, "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="],
"lodash.isboolean": ["lodash.isboolean@3.0.3", "", {}, "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="],
"lodash.isinteger": ["lodash.isinteger@4.0.4", "", {}, "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="],
"lodash.isnumber": ["lodash.isnumber@3.0.3", "", {}, "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="],
"lodash.isplainobject": ["lodash.isplainobject@4.0.6", "", {}, "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="],
"lodash.isstring": ["lodash.isstring@4.0.1", "", {}, "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="],
"lodash.once": ["lodash.once@4.1.1", "", {}, "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="],
"long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="],
"lru.min": ["lru.min@1.1.3", "", {}, "sha512-Lkk/vx6ak3rYkRR0Nhu4lFUT2VDnQSxBe8Hbl7f36358p6ow8Bnvr8lrLt98H8J1aGxfhbX4Fs5tYg2+FTwr5Q=="],
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"mysql2": ["mysql2@3.15.3", "", { "dependencies": { "aws-ssl-profiles": "^1.1.1", "denque": "^2.1.0", "generate-function": "^2.3.1", "iconv-lite": "^0.7.0", "long": "^5.2.1", "lru.min": "^1.0.0", "named-placeholders": "^1.1.3", "seq-queue": "^0.0.5", "sqlstring": "^2.3.2" } }, "sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg=="],
"named-placeholders": ["named-placeholders@1.1.6", "", { "dependencies": { "lru.min": "^1.1.0" } }, "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w=="],
"node-fetch-native": ["node-fetch-native@1.6.7", "", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="],
"nypm": ["nypm@0.6.4", "", { "dependencies": { "citty": "^0.2.0", "pathe": "^2.0.3", "tinyexec": "^1.0.2" }, "bin": { "nypm": "dist/cli.mjs" } }, "sha512-1TvCKjZyyklN+JJj2TS3P4uSQEInrM/HkkuSXsEzm1ApPgBffOn8gFguNnZf07r/1X6vlryfIqMUkJKQMzlZiw=="],
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
"ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="],
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
"pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
"perfect-debounce": ["perfect-debounce@1.0.0", "", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="],
"pg": ["pg@8.17.2", "", { "dependencies": { "pg-connection-string": "^2.10.1", "pg-pool": "^3.11.0", "pg-protocol": "^1.11.0", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.3.0" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-vjbKdiBJRqzcYw1fNU5KuHyYvdJ1qpcQg1CeBrHFqV1pWgHeVR6j/+kX0E1AAXfyuLUGY1ICrN2ELKA/z2HWzw=="],
"pg-cloudflare": ["pg-cloudflare@1.3.0", "", {}, "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ=="],
"pg-connection-string": ["pg-connection-string@2.10.1", "", {}, "sha512-iNzslsoeSH2/gmDDKiyMqF64DATUCWj3YJ0wP14kqcsf2TUklwimd+66yYojKwZCA7h2yRNLGug71hCBA2a4sw=="],
"pg-int8": ["pg-int8@1.0.1", "", {}, "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="],
"pg-pool": ["pg-pool@3.11.0", "", { "peerDependencies": { "pg": ">=8.0" } }, "sha512-MJYfvHwtGp870aeusDh+hg9apvOe2zmpZJpyt+BMtzUWlVqbhFmMK6bOBXLBUPd7iRtIF9fZplDc7KrPN3PN7w=="],
"pg-protocol": ["pg-protocol@1.11.0", "", {}, "sha512-pfsxk2M9M3BuGgDOfuy37VNRRX3jmKgMjcvAcWqNDpZSf4cUmv8HSOl5ViRQFsfARFn0KuUQTgLxVMbNq5NW3g=="],
"pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="],
"pgpass": ["pgpass@1.0.5", "", { "dependencies": { "split2": "^4.1.0" } }, "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug=="],
"pkg-types": ["pkg-types@2.3.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig=="],
"postgres": ["postgres@3.4.7", "", {}, "sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw=="],
"postgres-array": ["postgres-array@3.0.4", "", {}, "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ=="],
"postgres-bytea": ["postgres-bytea@1.0.1", "", {}, "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ=="],
"postgres-date": ["postgres-date@1.0.7", "", {}, "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="],
"postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="],
"prisma": ["prisma@7.3.0", "", { "dependencies": { "@prisma/config": "7.3.0", "@prisma/dev": "0.20.0", "@prisma/engines": "7.3.0", "@prisma/studio-core": "0.13.1", "mysql2": "3.15.3", "postgres": "3.4.7" }, "peerDependencies": { "better-sqlite3": ">=9.0.0", "typescript": ">=5.4.0" }, "optionalPeers": ["better-sqlite3", "typescript"], "bin": { "prisma": "build/index.js" } }, "sha512-ApYSOLHfMN8WftJA+vL6XwAPOh/aZ0BgUyyKPwUFgjARmG6EBI9LzDPf6SWULQMSAxydV9qn5gLj037nPNlg2w=="],
"proper-lockfile": ["proper-lockfile@4.1.2", "", { "dependencies": { "graceful-fs": "^4.2.4", "retry": "^0.12.0", "signal-exit": "^3.0.2" } }, "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA=="],
"pure-rand": ["pure-rand@6.1.0", "", {}, "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA=="],
"rc9": ["rc9@2.1.2", "", { "dependencies": { "defu": "^6.1.4", "destr": "^2.0.3" } }, "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg=="],
"react": ["react@19.2.3", "", {}, "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA=="],
"react-dom": ["react-dom@19.2.3", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.3" } }, "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg=="],
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
"regexp-to-ast": ["regexp-to-ast@0.5.0", "", {}, "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw=="],
"remeda": ["remeda@2.33.4", "", {}, "sha512-ygHswjlc/opg2VrtiYvUOPLjxjtdKvjGz1/plDhkG66hjNjFr1xmfrs2ClNFo/E6TyUFiwYNh53bKV26oBoMGQ=="],
"retry": ["retry@0.12.0", "", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="],
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
"semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
"seq-queue": ["seq-queue@0.0.5", "", {}, "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q=="],
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
"signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
"split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="],
"sqlstring": ["sqlstring@2.3.3", "", {}, "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg=="],
"std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="],
"tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="],
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
"valibot": ["valibot@1.2.0", "", { "peerDependencies": { "typescript": ">=5" }, "optionalPeers": ["typescript"] }, "sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg=="],
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
"xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="],
"zeptomatch": ["zeptomatch@2.1.0", "", { "dependencies": { "grammex": "^3.1.11", "graphmatch": "^1.1.0" } }, "sha512-KiGErG2J0G82LSpniV0CtIzjlJ10E04j02VOudJsPyPwNZgGnRKQy7I1R7GMyg/QswnE4l7ohSGrQbQbjXPPDA=="],
"zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
"zon": ["zon@1.0.3", "", {}, "sha512-FKLlkaI8bU0ORFgZIzbqgvT7ei7aHob2jvhDPLgkwxccDurBRviDlD2eC2px6kEox0VlW0rK3VBwjsvtGV6g0w=="],
"@prisma/dev/hono": ["hono@4.11.4", "", {}, "sha512-U7tt8JsyrxSRKspfhtLET79pU8K+tInj5QZXs1jSugO1Vq5dFj3kmZsRldo29mTBfcjDRVRXrEZ6LS63Cog9ZA=="],
"@prisma/engines/@prisma/get-platform": ["@prisma/get-platform@7.3.0", "", { "dependencies": { "@prisma/debug": "7.3.0" } }, "sha512-N7c6m4/I0Q6JYmWKP2RCD/sM9eWiyCPY98g5c0uEktObNSZnugW2U/PO+pwL0UaqzxqTXt7gTsYsb0FnMnJNbg=="],
"@prisma/fetch-engine/@prisma/get-platform": ["@prisma/get-platform@7.3.0", "", { "dependencies": { "@prisma/debug": "7.3.0" } }, "sha512-N7c6m4/I0Q6JYmWKP2RCD/sM9eWiyCPY98g5c0uEktObNSZnugW2U/PO+pwL0UaqzxqTXt7gTsYsb0FnMnJNbg=="],
"@prisma/get-platform/@prisma/debug": ["@prisma/debug@7.2.0", "", {}, "sha512-YSGTiSlBAVJPzX4ONZmMotL+ozJwQjRmZweQNIq/ER0tQJKJynNkRB3kyvt37eOfsbMCXk3gnLF6J9OJ4QWftw=="],
"nypm/citty": ["citty@0.2.0", "", {}, "sha512-8csy5IBFI2ex2hTVpaHN2j+LNE199AgiI7y4dMintrr8i0lQiFn+0AWMZrWdHKIgMOer65f8IThysYhoReqjWA=="],
"pg-types/postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="],
"proper-lockfile/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
} }
} }
+34
View File
@@ -0,0 +1,34 @@
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
/* eslint-disable */
// biome-ignore-all lint: generated file
// @ts-nocheck
/*
* This file should be your main import to use Prisma-related types and utilities in a browser.
* Use it to get access to models, enums, and input types.
*
* This file does not contain a `PrismaClient` class, nor several other helpers that are intended as server-side only.
* See `client.ts` for the standard, server-side entry point.
*
* 🟢 You can import this file directly.
*/
import * as Prisma from './internal/prismaNamespaceBrowser.ts'
export { Prisma }
export * as $Enums from './enums.ts'
export * from './enums.ts';
/**
* Model Session
*
*/
export type Session = Prisma.SessionModel
/**
* Model User
*
*/
export type User = Prisma.UserModel
/**
* Model Role
*
*/
export type Role = Prisma.RoleModel
+56
View File
@@ -0,0 +1,56 @@
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
/* eslint-disable */
// biome-ignore-all lint: generated file
// @ts-nocheck
/*
* This file should be your main import to use Prisma. Through it you get access to all the models, enums, and input types.
* If you're looking for something you can import in the client-side of your application, please refer to the `browser.ts` file instead.
*
* 🟢 You can import this file directly.
*/
import * as process from 'node:process'
import * as path from 'node:path'
import { fileURLToPath } from 'node:url'
globalThis['__dirname'] = path.dirname(fileURLToPath(import.meta.url))
import * as runtime from "@prisma/client/runtime/client"
import * as $Enums from "./enums.ts"
import * as $Class from "./internal/class.ts"
import * as Prisma from "./internal/prismaNamespace.ts"
export * as $Enums from './enums.ts'
export * from "./enums.ts"
/**
* ## Prisma Client
*
* Type-safe database client for TypeScript
* @example
* ```
* const prisma = new PrismaClient()
* // Fetch zero or more Sessions
* const sessions = await prisma.session.findMany()
* ```
*
* Read more in our [docs](https://pris.ly/d/client).
*/
export const PrismaClient = $Class.getPrismaClientClass()
export type PrismaClient<LogOpts extends Prisma.LogLevel = never, OmitOpts extends Prisma.PrismaClientOptions["omit"] = Prisma.PrismaClientOptions["omit"], ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = $Class.PrismaClient<LogOpts, OmitOpts, ExtArgs>
export { Prisma }
/**
* Model Session
*
*/
export type Session = Prisma.SessionModel
/**
* Model User
*
*/
export type User = Prisma.UserModel
/**
* Model Role
*
*/
export type Role = Prisma.RoleModel
+352
View File
@@ -0,0 +1,352 @@
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
/* eslint-disable */
// biome-ignore-all lint: generated file
// @ts-nocheck
/*
* This file exports various common sort, input & filter types that are not directly linked to a particular model.
*
* 🟢 You can import this file directly.
*/
import type * as runtime from "@prisma/client/runtime/client"
import * as $Enums from "./enums.ts"
import type * as Prisma from "./internal/prismaNamespace.ts"
export type StringFilter<$PrismaModel = never> = {
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
mode?: Prisma.QueryMode
not?: Prisma.NestedStringFilter<$PrismaModel> | string
}
export type DateTimeFilter<$PrismaModel = never> = {
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
not?: Prisma.NestedDateTimeFilter<$PrismaModel> | Date | string
}
export type BoolFilter<$PrismaModel = never> = {
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
not?: Prisma.NestedBoolFilter<$PrismaModel> | boolean
}
export type DateTimeNullableFilter<$PrismaModel = never> = {
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> | null
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
not?: Prisma.NestedDateTimeNullableFilter<$PrismaModel> | Date | string | null
}
export type SortOrderInput = {
sort: Prisma.SortOrder
nulls?: Prisma.NullsOrder
}
export type StringWithAggregatesFilter<$PrismaModel = never> = {
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
mode?: Prisma.QueryMode
not?: Prisma.NestedStringWithAggregatesFilter<$PrismaModel> | string
_count?: Prisma.NestedIntFilter<$PrismaModel>
_min?: Prisma.NestedStringFilter<$PrismaModel>
_max?: Prisma.NestedStringFilter<$PrismaModel>
}
export type DateTimeWithAggregatesFilter<$PrismaModel = never> = {
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
not?: Prisma.NestedDateTimeWithAggregatesFilter<$PrismaModel> | Date | string
_count?: Prisma.NestedIntFilter<$PrismaModel>
_min?: Prisma.NestedDateTimeFilter<$PrismaModel>
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>
}
export type BoolWithAggregatesFilter<$PrismaModel = never> = {
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
not?: Prisma.NestedBoolWithAggregatesFilter<$PrismaModel> | boolean
_count?: Prisma.NestedIntFilter<$PrismaModel>
_min?: Prisma.NestedBoolFilter<$PrismaModel>
_max?: Prisma.NestedBoolFilter<$PrismaModel>
}
export type DateTimeNullableWithAggregatesFilter<$PrismaModel = never> = {
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> | null
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
not?: Prisma.NestedDateTimeNullableWithAggregatesFilter<$PrismaModel> | Date | string | null
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
_min?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
_max?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
}
export type StringNullableFilter<$PrismaModel = never> = {
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
mode?: Prisma.QueryMode
not?: Prisma.NestedStringNullableFilter<$PrismaModel> | string | null
}
export type IntFilter<$PrismaModel = never> = {
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
notIn?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
not?: Prisma.NestedIntFilter<$PrismaModel> | number
}
export type StringNullableWithAggregatesFilter<$PrismaModel = never> = {
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
mode?: Prisma.QueryMode
not?: Prisma.NestedStringNullableWithAggregatesFilter<$PrismaModel> | string | null
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
_min?: Prisma.NestedStringNullableFilter<$PrismaModel>
_max?: Prisma.NestedStringNullableFilter<$PrismaModel>
}
export type IntWithAggregatesFilter<$PrismaModel = never> = {
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
notIn?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
not?: Prisma.NestedIntWithAggregatesFilter<$PrismaModel> | number
_count?: Prisma.NestedIntFilter<$PrismaModel>
_avg?: Prisma.NestedFloatFilter<$PrismaModel>
_sum?: Prisma.NestedIntFilter<$PrismaModel>
_min?: Prisma.NestedIntFilter<$PrismaModel>
_max?: Prisma.NestedIntFilter<$PrismaModel>
}
export type NestedStringFilter<$PrismaModel = never> = {
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
not?: Prisma.NestedStringFilter<$PrismaModel> | string
}
export type NestedDateTimeFilter<$PrismaModel = never> = {
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
not?: Prisma.NestedDateTimeFilter<$PrismaModel> | Date | string
}
export type NestedBoolFilter<$PrismaModel = never> = {
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
not?: Prisma.NestedBoolFilter<$PrismaModel> | boolean
}
export type NestedDateTimeNullableFilter<$PrismaModel = never> = {
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> | null
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
not?: Prisma.NestedDateTimeNullableFilter<$PrismaModel> | Date | string | null
}
export type NestedStringWithAggregatesFilter<$PrismaModel = never> = {
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
not?: Prisma.NestedStringWithAggregatesFilter<$PrismaModel> | string
_count?: Prisma.NestedIntFilter<$PrismaModel>
_min?: Prisma.NestedStringFilter<$PrismaModel>
_max?: Prisma.NestedStringFilter<$PrismaModel>
}
export type NestedIntFilter<$PrismaModel = never> = {
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
notIn?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
not?: Prisma.NestedIntFilter<$PrismaModel> | number
}
export type NestedDateTimeWithAggregatesFilter<$PrismaModel = never> = {
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
not?: Prisma.NestedDateTimeWithAggregatesFilter<$PrismaModel> | Date | string
_count?: Prisma.NestedIntFilter<$PrismaModel>
_min?: Prisma.NestedDateTimeFilter<$PrismaModel>
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>
}
export type NestedBoolWithAggregatesFilter<$PrismaModel = never> = {
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
not?: Prisma.NestedBoolWithAggregatesFilter<$PrismaModel> | boolean
_count?: Prisma.NestedIntFilter<$PrismaModel>
_min?: Prisma.NestedBoolFilter<$PrismaModel>
_max?: Prisma.NestedBoolFilter<$PrismaModel>
}
export type NestedDateTimeNullableWithAggregatesFilter<$PrismaModel = never> = {
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> | null
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
not?: Prisma.NestedDateTimeNullableWithAggregatesFilter<$PrismaModel> | Date | string | null
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
_min?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
_max?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
}
export type NestedIntNullableFilter<$PrismaModel = never> = {
equals?: number | Prisma.IntFieldRefInput<$PrismaModel> | null
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> | null
notIn?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> | null
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
not?: Prisma.NestedIntNullableFilter<$PrismaModel> | number | null
}
export type NestedStringNullableFilter<$PrismaModel = never> = {
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
not?: Prisma.NestedStringNullableFilter<$PrismaModel> | string | null
}
export type NestedStringNullableWithAggregatesFilter<$PrismaModel = never> = {
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
not?: Prisma.NestedStringNullableWithAggregatesFilter<$PrismaModel> | string | null
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
_min?: Prisma.NestedStringNullableFilter<$PrismaModel>
_max?: Prisma.NestedStringNullableFilter<$PrismaModel>
}
export type NestedIntWithAggregatesFilter<$PrismaModel = never> = {
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
notIn?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
not?: Prisma.NestedIntWithAggregatesFilter<$PrismaModel> | number
_count?: Prisma.NestedIntFilter<$PrismaModel>
_avg?: Prisma.NestedFloatFilter<$PrismaModel>
_sum?: Prisma.NestedIntFilter<$PrismaModel>
_min?: Prisma.NestedIntFilter<$PrismaModel>
_max?: Prisma.NestedIntFilter<$PrismaModel>
}
export type NestedFloatFilter<$PrismaModel = never> = {
equals?: number | Prisma.FloatFieldRefInput<$PrismaModel>
in?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel>
notIn?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel>
lt?: number | Prisma.FloatFieldRefInput<$PrismaModel>
lte?: number | Prisma.FloatFieldRefInput<$PrismaModel>
gt?: number | Prisma.FloatFieldRefInput<$PrismaModel>
gte?: number | Prisma.FloatFieldRefInput<$PrismaModel>
not?: Prisma.NestedFloatFilter<$PrismaModel> | number
}
+15
View File
@@ -0,0 +1,15 @@
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
/* eslint-disable */
// biome-ignore-all lint: generated file
// @ts-nocheck
/*
* This file exports all enum related types from the schema.
*
* 🟢 You can import this file directly.
*/
// This file is empty because there are no enums in the schema.
export {}
+212
View File
@@ -0,0 +1,212 @@
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
/* eslint-disable */
// biome-ignore-all lint: generated file
// @ts-nocheck
/*
* WARNING: This is an internal file that is subject to change!
*
* 🛑 Under no circumstances should you import this file directly! 🛑
*
* Please import the `PrismaClient` class from the `client.ts` file instead.
*/
import * as runtime from "@prisma/client/runtime/client"
import type * as Prisma from "./prismaNamespace.ts"
const config: runtime.GetPrismaClientConfig = {
"previewFeatures": [],
"clientVersion": "7.3.0",
"engineVersion": "9d6ad21cbbceab97458517b147a6a09ff43aa735",
"activeProvider": "postgresql",
"inlineSchema": "// This is your Prisma schema file,\n// learn more about it in the docs: https://pris.ly/d/prisma-schema\n\n// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?\n// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init\n\ngenerator client {\n provider = \"prisma-client\"\n output = \"../generated/prisma\"\n}\n\ndatasource db {\n provider = \"postgresql\"\n}\n\nmodel Session {\n id String @id @default(uuid())\n sessionKey String @unique @default(cuid())\n userId String\n expires DateTime\n refreshTokenGenerated Boolean @default(false)\n refreshedAt DateTime?\n invalidatedAt DateTime?\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n}\n\nmodel User {\n id String @id @default(cuid())\n roles Role[]\n permissions String?\n login String @unique\n name String?\n email String @unique\n emailVerified DateTime?\n image String?\n\n userId Int @unique\n token String?\n\n sessions Session[]\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel Role {\n id String @id @default(uuid())\n title String\n moniker String @unique // e.g. admin, super_admin, moderator\n\n permissions String\n users User[]\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n",
"runtimeDataModel": {
"models": {},
"enums": {},
"types": {}
}
}
config.runtimeDataModel = JSON.parse("{\"models\":{\"Session\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"sessionKey\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"expires\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"refreshTokenGenerated\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"refreshedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"invalidatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"SessionToUser\"}],\"dbName\":null},\"User\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"roles\",\"kind\":\"object\",\"type\":\"Role\",\"relationName\":\"RoleToUser\"},{\"name\":\"permissions\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"login\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"emailVerified\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"image\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"token\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"sessions\",\"kind\":\"object\",\"type\":\"Session\",\"relationName\":\"SessionToUser\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"Role\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"title\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"moniker\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"permissions\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"users\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"RoleToUser\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null}},\"enums\":{},\"types\":{}}")
async function decodeBase64AsWasm(wasmBase64: string): Promise<WebAssembly.Module> {
const { Buffer } = await import('node:buffer')
const wasmArray = Buffer.from(wasmBase64, 'base64')
return new WebAssembly.Module(wasmArray)
}
config.compilerWasm = {
getRuntime: async () => await import("@prisma/client/runtime/query_compiler_fast_bg.postgresql.mjs"),
getQueryCompilerWasmModule: async () => {
const { wasm } = await import("@prisma/client/runtime/query_compiler_fast_bg.postgresql.wasm-base64.mjs")
return await decodeBase64AsWasm(wasm)
},
importName: "./query_compiler_fast_bg.js"
}
export type LogOptions<ClientOptions extends Prisma.PrismaClientOptions> =
'log' extends keyof ClientOptions ? ClientOptions['log'] extends Array<Prisma.LogLevel | Prisma.LogDefinition> ? Prisma.GetEvents<ClientOptions['log']> : never : never
export interface PrismaClientConstructor {
/**
* ## Prisma Client
*
* Type-safe database client for TypeScript
* @example
* ```
* const prisma = new PrismaClient()
* // Fetch zero or more Sessions
* const sessions = await prisma.session.findMany()
* ```
*
* Read more in our [docs](https://pris.ly/d/client).
*/
new <
Options extends Prisma.PrismaClientOptions = Prisma.PrismaClientOptions,
LogOpts extends LogOptions<Options> = LogOptions<Options>,
OmitOpts extends Prisma.PrismaClientOptions['omit'] = Options extends { omit: infer U } ? U : Prisma.PrismaClientOptions['omit'],
ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs
>(options: Prisma.Subset<Options, Prisma.PrismaClientOptions> ): PrismaClient<LogOpts, OmitOpts, ExtArgs>
}
/**
* ## Prisma Client
*
* Type-safe database client for TypeScript
* @example
* ```
* const prisma = new PrismaClient()
* // Fetch zero or more Sessions
* const sessions = await prisma.session.findMany()
* ```
*
* Read more in our [docs](https://pris.ly/d/client).
*/
export interface PrismaClient<
in LogOpts extends Prisma.LogLevel = never,
in out OmitOpts extends Prisma.PrismaClientOptions['omit'] = undefined,
in out ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs
> {
[K: symbol]: { types: Prisma.TypeMap<ExtArgs>['other'] }
$on<V extends LogOpts>(eventType: V, callback: (event: V extends 'query' ? Prisma.QueryEvent : Prisma.LogEvent) => void): PrismaClient;
/**
* Connect with the database
*/
$connect(): runtime.Types.Utils.JsPromise<void>;
/**
* Disconnect from the database
*/
$disconnect(): runtime.Types.Utils.JsPromise<void>;
/**
* Executes a prepared raw query and returns the number of affected rows.
* @example
* ```
* const result = await prisma.$executeRaw`UPDATE User SET cool = ${true} WHERE email = ${'user@email.com'};`
* ```
*
* Read more in our [docs](https://pris.ly/d/raw-queries).
*/
$executeRaw<T = unknown>(query: TemplateStringsArray | Prisma.Sql, ...values: any[]): Prisma.PrismaPromise<number>;
/**
* Executes a raw query and returns the number of affected rows.
* Susceptible to SQL injections, see documentation.
* @example
* ```
* const result = await prisma.$executeRawUnsafe('UPDATE User SET cool = $1 WHERE email = $2 ;', true, 'user@email.com')
* ```
*
* Read more in our [docs](https://pris.ly/d/raw-queries).
*/
$executeRawUnsafe<T = unknown>(query: string, ...values: any[]): Prisma.PrismaPromise<number>;
/**
* Performs a prepared raw query and returns the `SELECT` data.
* @example
* ```
* const result = await prisma.$queryRaw`SELECT * FROM User WHERE id = ${1} OR email = ${'user@email.com'};`
* ```
*
* Read more in our [docs](https://pris.ly/d/raw-queries).
*/
$queryRaw<T = unknown>(query: TemplateStringsArray | Prisma.Sql, ...values: any[]): Prisma.PrismaPromise<T>;
/**
* Performs a raw query and returns the `SELECT` data.
* Susceptible to SQL injections, see documentation.
* @example
* ```
* const result = await prisma.$queryRawUnsafe('SELECT * FROM User WHERE id = $1 OR email = $2;', 1, 'user@email.com')
* ```
*
* Read more in our [docs](https://pris.ly/d/raw-queries).
*/
$queryRawUnsafe<T = unknown>(query: string, ...values: any[]): Prisma.PrismaPromise<T>;
/**
* Allows the running of a sequence of read/write operations that are guaranteed to either succeed or fail as a whole.
* @example
* ```
* const [george, bob, alice] = await prisma.$transaction([
* prisma.user.create({ data: { name: 'George' } }),
* prisma.user.create({ data: { name: 'Bob' } }),
* prisma.user.create({ data: { name: 'Alice' } }),
* ])
* ```
*
* Read more in our [docs](https://www.prisma.io/docs/concepts/components/prisma-client/transactions).
*/
$transaction<P extends Prisma.PrismaPromise<any>[]>(arg: [...P], options?: { isolationLevel?: Prisma.TransactionIsolationLevel }): runtime.Types.Utils.JsPromise<runtime.Types.Utils.UnwrapTuple<P>>
$transaction<R>(fn: (prisma: Omit<PrismaClient, runtime.ITXClientDenyList>) => runtime.Types.Utils.JsPromise<R>, options?: { maxWait?: number, timeout?: number, isolationLevel?: Prisma.TransactionIsolationLevel }): runtime.Types.Utils.JsPromise<R>
$extends: runtime.Types.Extensions.ExtendsHook<"extends", Prisma.TypeMapCb<OmitOpts>, ExtArgs, runtime.Types.Utils.Call<Prisma.TypeMapCb<OmitOpts>, {
extArgs: ExtArgs
}>>
/**
* `prisma.session`: Exposes CRUD operations for the **Session** model.
* Example usage:
* ```ts
* // Fetch zero or more Sessions
* const sessions = await prisma.session.findMany()
* ```
*/
get session(): Prisma.SessionDelegate<ExtArgs, { omit: OmitOpts }>;
/**
* `prisma.user`: Exposes CRUD operations for the **User** model.
* Example usage:
* ```ts
* // Fetch zero or more Users
* const users = await prisma.user.findMany()
* ```
*/
get user(): Prisma.UserDelegate<ExtArgs, { omit: OmitOpts }>;
/**
* `prisma.role`: Exposes CRUD operations for the **Role** model.
* Example usage:
* ```ts
* // Fetch zero or more Roles
* const roles = await prisma.role.findMany()
* ```
*/
get role(): Prisma.RoleDelegate<ExtArgs, { omit: OmitOpts }>;
}
export function getPrismaClientClass(): PrismaClientConstructor {
return runtime.getPrismaClient(config) as unknown as PrismaClientConstructor
}
@@ -0,0 +1,964 @@
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
/* eslint-disable */
// biome-ignore-all lint: generated file
// @ts-nocheck
/*
* WARNING: This is an internal file that is subject to change!
*
* 🛑 Under no circumstances should you import this file directly! 🛑
*
* All exports from this file are wrapped under a `Prisma` namespace object in the client.ts file.
* While this enables partial backward compatibility, it is not part of the stable public API.
*
* If you are looking for your Models, Enums, and Input Types, please import them from the respective
* model files in the `model` directory!
*/
import * as runtime from "@prisma/client/runtime/client"
import type * as Prisma from "../models.ts"
import { type PrismaClient } from "./class.ts"
export type * from '../models.ts'
export type DMMF = typeof runtime.DMMF
export type PrismaPromise<T> = runtime.Types.Public.PrismaPromise<T>
/**
* Prisma Errors
*/
export const PrismaClientKnownRequestError = runtime.PrismaClientKnownRequestError
export type PrismaClientKnownRequestError = runtime.PrismaClientKnownRequestError
export const PrismaClientUnknownRequestError = runtime.PrismaClientUnknownRequestError
export type PrismaClientUnknownRequestError = runtime.PrismaClientUnknownRequestError
export const PrismaClientRustPanicError = runtime.PrismaClientRustPanicError
export type PrismaClientRustPanicError = runtime.PrismaClientRustPanicError
export const PrismaClientInitializationError = runtime.PrismaClientInitializationError
export type PrismaClientInitializationError = runtime.PrismaClientInitializationError
export const PrismaClientValidationError = runtime.PrismaClientValidationError
export type PrismaClientValidationError = runtime.PrismaClientValidationError
/**
* Re-export of sql-template-tag
*/
export const sql = runtime.sqltag
export const empty = runtime.empty
export const join = runtime.join
export const raw = runtime.raw
export const Sql = runtime.Sql
export type Sql = runtime.Sql
/**
* Decimal.js
*/
export const Decimal = runtime.Decimal
export type Decimal = runtime.Decimal
export type DecimalJsLike = runtime.DecimalJsLike
/**
* Extensions
*/
export type Extension = runtime.Types.Extensions.UserArgs
export const getExtensionContext = runtime.Extensions.getExtensionContext
export type Args<T, F extends runtime.Operation> = runtime.Types.Public.Args<T, F>
export type Payload<T, F extends runtime.Operation = never> = runtime.Types.Public.Payload<T, F>
export type Result<T, A, F extends runtime.Operation> = runtime.Types.Public.Result<T, A, F>
export type Exact<A, W> = runtime.Types.Public.Exact<A, W>
export type PrismaVersion = {
client: string
engine: string
}
/**
* Prisma Client JS version: 7.3.0
* Query Engine version: 9d6ad21cbbceab97458517b147a6a09ff43aa735
*/
export const prismaVersion: PrismaVersion = {
client: "7.3.0",
engine: "9d6ad21cbbceab97458517b147a6a09ff43aa735"
}
/**
* Utility Types
*/
export type Bytes = runtime.Bytes
export type JsonObject = runtime.JsonObject
export type JsonArray = runtime.JsonArray
export type JsonValue = runtime.JsonValue
export type InputJsonObject = runtime.InputJsonObject
export type InputJsonArray = runtime.InputJsonArray
export type InputJsonValue = runtime.InputJsonValue
export const NullTypes = {
DbNull: runtime.NullTypes.DbNull as (new (secret: never) => typeof runtime.DbNull),
JsonNull: runtime.NullTypes.JsonNull as (new (secret: never) => typeof runtime.JsonNull),
AnyNull: runtime.NullTypes.AnyNull as (new (secret: never) => typeof runtime.AnyNull),
}
/**
* Helper for filtering JSON entries that have `null` on the database (empty on the db)
*
* @see https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-json-fields#filtering-on-a-json-field
*/
export const DbNull = runtime.DbNull
/**
* Helper for filtering JSON entries that have JSON `null` values (not empty on the db)
*
* @see https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-json-fields#filtering-on-a-json-field
*/
export const JsonNull = runtime.JsonNull
/**
* Helper for filtering JSON entries that are `Prisma.DbNull` or `Prisma.JsonNull`
*
* @see https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-json-fields#filtering-on-a-json-field
*/
export const AnyNull = runtime.AnyNull
type SelectAndInclude = {
select: any
include: any
}
type SelectAndOmit = {
select: any
omit: any
}
/**
* From T, pick a set of properties whose keys are in the union K
*/
type Prisma__Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
export type Enumerable<T> = T | Array<T>;
/**
* Subset
* @desc From `T` pick properties that exist in `U`. Simple version of Intersection
*/
export type Subset<T, U> = {
[key in keyof T]: key extends keyof U ? T[key] : never;
};
/**
* SelectSubset
* @desc From `T` pick properties that exist in `U`. Simple version of Intersection.
* Additionally, it validates, if both select and include are present. If the case, it errors.
*/
export type SelectSubset<T, U> = {
[key in keyof T]: key extends keyof U ? T[key] : never
} &
(T extends SelectAndInclude
? 'Please either choose `select` or `include`.'
: T extends SelectAndOmit
? 'Please either choose `select` or `omit`.'
: {})
/**
* Subset + Intersection
* @desc From `T` pick properties that exist in `U` and intersect `K`
*/
export type SubsetIntersection<T, U, K> = {
[key in keyof T]: key extends keyof U ? T[key] : never
} &
K
type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
/**
* XOR is needed to have a real mutually exclusive union type
* https://stackoverflow.com/questions/42123407/does-typescript-support-mutually-exclusive-types
*/
export type XOR<T, U> =
T extends object ?
U extends object ?
(Without<T, U> & U) | (Without<U, T> & T)
: U : T
/**
* Is T a Record?
*/
type IsObject<T extends any> = T extends Array<any>
? False
: T extends Date
? False
: T extends Uint8Array
? False
: T extends BigInt
? False
: T extends object
? True
: False
/**
* If it's T[], return T
*/
export type UnEnumerate<T extends unknown> = T extends Array<infer U> ? U : T
/**
* From ts-toolbelt
*/
type __Either<O extends object, K extends Key> = Omit<O, K> &
{
// Merge all but K
[P in K]: Prisma__Pick<O, P & keyof O> // With K possibilities
}[K]
type EitherStrict<O extends object, K extends Key> = Strict<__Either<O, K>>
type EitherLoose<O extends object, K extends Key> = ComputeRaw<__Either<O, K>>
type _Either<
O extends object,
K extends Key,
strict extends Boolean
> = {
1: EitherStrict<O, K>
0: EitherLoose<O, K>
}[strict]
export type Either<
O extends object,
K extends Key,
strict extends Boolean = 1
> = O extends unknown ? _Either<O, K, strict> : never
export type Union = any
export type PatchUndefined<O extends object, O1 extends object> = {
[K in keyof O]: O[K] extends undefined ? At<O1, K> : O[K]
} & {}
/** Helper Types for "Merge" **/
export type IntersectOf<U extends Union> = (
U extends unknown ? (k: U) => void : never
) extends (k: infer I) => void
? I
: never
export type Overwrite<O extends object, O1 extends object> = {
[K in keyof O]: K extends keyof O1 ? O1[K] : O[K];
} & {};
type _Merge<U extends object> = IntersectOf<Overwrite<U, {
[K in keyof U]-?: At<U, K>;
}>>;
type Key = string | number | symbol;
type AtStrict<O extends object, K extends Key> = O[K & keyof O];
type AtLoose<O extends object, K extends Key> = O extends unknown ? AtStrict<O, K> : never;
export type At<O extends object, K extends Key, strict extends Boolean = 1> = {
1: AtStrict<O, K>;
0: AtLoose<O, K>;
}[strict];
export type ComputeRaw<A extends any> = A extends Function ? A : {
[K in keyof A]: A[K];
} & {};
export type OptionalFlat<O> = {
[K in keyof O]?: O[K];
} & {};
type _Record<K extends keyof any, T> = {
[P in K]: T;
};
// cause typescript not to expand types and preserve names
type NoExpand<T> = T extends unknown ? T : never;
// this type assumes the passed object is entirely optional
export type AtLeast<O extends object, K extends string> = NoExpand<
O extends unknown
? | (K extends keyof O ? { [P in K]: O[P] } & O : O)
| {[P in keyof O as P extends K ? P : never]-?: O[P]} & O
: never>;
type _Strict<U, _U = U> = U extends unknown ? U & OptionalFlat<_Record<Exclude<Keys<_U>, keyof U>, never>> : never;
export type Strict<U extends object> = ComputeRaw<_Strict<U>>;
/** End Helper Types for "Merge" **/
export type Merge<U extends object> = ComputeRaw<_Merge<Strict<U>>>;
export type Boolean = True | False
export type True = 1
export type False = 0
export type Not<B extends Boolean> = {
0: 1
1: 0
}[B]
export type Extends<A1 extends any, A2 extends any> = [A1] extends [never]
? 0 // anything `never` is false
: A1 extends A2
? 1
: 0
export type Has<U extends Union, U1 extends Union> = Not<
Extends<Exclude<U1, U>, U1>
>
export type Or<B1 extends Boolean, B2 extends Boolean> = {
0: {
0: 0
1: 1
}
1: {
0: 1
1: 1
}
}[B1][B2]
export type Keys<U extends Union> = U extends unknown ? keyof U : never
export type GetScalarType<T, O> = O extends object ? {
[P in keyof T]: P extends keyof O
? O[P]
: never
} : never
type FieldPaths<
T,
U = Omit<T, '_avg' | '_sum' | '_count' | '_min' | '_max'>
> = IsObject<T> extends True ? U : T
export type GetHavingFields<T> = {
[K in keyof T]: Or<
Or<Extends<'OR', K>, Extends<'AND', K>>,
Extends<'NOT', K>
> extends True
? // infer is only needed to not hit TS limit
// based on the brilliant idea of Pierre-Antoine Mills
// https://github.com/microsoft/TypeScript/issues/30188#issuecomment-478938437
T[K] extends infer TK
? GetHavingFields<UnEnumerate<TK> extends object ? Merge<UnEnumerate<TK>> : never>
: never
: {} extends FieldPaths<T[K]>
? never
: K
}[keyof T]
/**
* Convert tuple to union
*/
type _TupleToUnion<T> = T extends (infer E)[] ? E : never
type TupleToUnion<K extends readonly any[]> = _TupleToUnion<K>
export type MaybeTupleToUnion<T> = T extends any[] ? TupleToUnion<T> : T
/**
* Like `Pick`, but additionally can also accept an array of keys
*/
export type PickEnumerable<T, K extends Enumerable<keyof T> | keyof T> = Prisma__Pick<T, MaybeTupleToUnion<K>>
/**
* Exclude all keys with underscores
*/
export type ExcludeUnderscoreKeys<T extends string> = T extends `_${string}` ? never : T
export type FieldRef<Model, FieldType> = runtime.FieldRef<Model, FieldType>
type FieldRefInputType<Model, FieldType> = Model extends never ? never : FieldRef<Model, FieldType>
export const ModelName = {
Session: 'Session',
User: 'User',
Role: 'Role'
} as const
export type ModelName = (typeof ModelName)[keyof typeof ModelName]
export interface TypeMapCb<GlobalOmitOptions = {}> extends runtime.Types.Utils.Fn<{extArgs: runtime.Types.Extensions.InternalArgs }, runtime.Types.Utils.Record<string, any>> {
returns: TypeMap<this['params']['extArgs'], GlobalOmitOptions>
}
export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs, GlobalOmitOptions = {}> = {
globalOmitOptions: {
omit: GlobalOmitOptions
}
meta: {
modelProps: "session" | "user" | "role"
txIsolationLevel: TransactionIsolationLevel
}
model: {
Session: {
payload: Prisma.$SessionPayload<ExtArgs>
fields: Prisma.SessionFieldRefs
operations: {
findUnique: {
args: Prisma.SessionFindUniqueArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload> | null
}
findUniqueOrThrow: {
args: Prisma.SessionFindUniqueOrThrowArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload>
}
findFirst: {
args: Prisma.SessionFindFirstArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload> | null
}
findFirstOrThrow: {
args: Prisma.SessionFindFirstOrThrowArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload>
}
findMany: {
args: Prisma.SessionFindManyArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload>[]
}
create: {
args: Prisma.SessionCreateArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload>
}
createMany: {
args: Prisma.SessionCreateManyArgs<ExtArgs>
result: BatchPayload
}
createManyAndReturn: {
args: Prisma.SessionCreateManyAndReturnArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload>[]
}
delete: {
args: Prisma.SessionDeleteArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload>
}
update: {
args: Prisma.SessionUpdateArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload>
}
deleteMany: {
args: Prisma.SessionDeleteManyArgs<ExtArgs>
result: BatchPayload
}
updateMany: {
args: Prisma.SessionUpdateManyArgs<ExtArgs>
result: BatchPayload
}
updateManyAndReturn: {
args: Prisma.SessionUpdateManyAndReturnArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload>[]
}
upsert: {
args: Prisma.SessionUpsertArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload>
}
aggregate: {
args: Prisma.SessionAggregateArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.AggregateSession>
}
groupBy: {
args: Prisma.SessionGroupByArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.SessionGroupByOutputType>[]
}
count: {
args: Prisma.SessionCountArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.SessionCountAggregateOutputType> | number
}
}
}
User: {
payload: Prisma.$UserPayload<ExtArgs>
fields: Prisma.UserFieldRefs
operations: {
findUnique: {
args: Prisma.UserFindUniqueArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload> | null
}
findUniqueOrThrow: {
args: Prisma.UserFindUniqueOrThrowArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>
}
findFirst: {
args: Prisma.UserFindFirstArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload> | null
}
findFirstOrThrow: {
args: Prisma.UserFindFirstOrThrowArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>
}
findMany: {
args: Prisma.UserFindManyArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>[]
}
create: {
args: Prisma.UserCreateArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>
}
createMany: {
args: Prisma.UserCreateManyArgs<ExtArgs>
result: BatchPayload
}
createManyAndReturn: {
args: Prisma.UserCreateManyAndReturnArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>[]
}
delete: {
args: Prisma.UserDeleteArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>
}
update: {
args: Prisma.UserUpdateArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>
}
deleteMany: {
args: Prisma.UserDeleteManyArgs<ExtArgs>
result: BatchPayload
}
updateMany: {
args: Prisma.UserUpdateManyArgs<ExtArgs>
result: BatchPayload
}
updateManyAndReturn: {
args: Prisma.UserUpdateManyAndReturnArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>[]
}
upsert: {
args: Prisma.UserUpsertArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>
}
aggregate: {
args: Prisma.UserAggregateArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.AggregateUser>
}
groupBy: {
args: Prisma.UserGroupByArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.UserGroupByOutputType>[]
}
count: {
args: Prisma.UserCountArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.UserCountAggregateOutputType> | number
}
}
}
Role: {
payload: Prisma.$RolePayload<ExtArgs>
fields: Prisma.RoleFieldRefs
operations: {
findUnique: {
args: Prisma.RoleFindUniqueArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RolePayload> | null
}
findUniqueOrThrow: {
args: Prisma.RoleFindUniqueOrThrowArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RolePayload>
}
findFirst: {
args: Prisma.RoleFindFirstArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RolePayload> | null
}
findFirstOrThrow: {
args: Prisma.RoleFindFirstOrThrowArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RolePayload>
}
findMany: {
args: Prisma.RoleFindManyArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RolePayload>[]
}
create: {
args: Prisma.RoleCreateArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RolePayload>
}
createMany: {
args: Prisma.RoleCreateManyArgs<ExtArgs>
result: BatchPayload
}
createManyAndReturn: {
args: Prisma.RoleCreateManyAndReturnArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RolePayload>[]
}
delete: {
args: Prisma.RoleDeleteArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RolePayload>
}
update: {
args: Prisma.RoleUpdateArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RolePayload>
}
deleteMany: {
args: Prisma.RoleDeleteManyArgs<ExtArgs>
result: BatchPayload
}
updateMany: {
args: Prisma.RoleUpdateManyArgs<ExtArgs>
result: BatchPayload
}
updateManyAndReturn: {
args: Prisma.RoleUpdateManyAndReturnArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RolePayload>[]
}
upsert: {
args: Prisma.RoleUpsertArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RolePayload>
}
aggregate: {
args: Prisma.RoleAggregateArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.AggregateRole>
}
groupBy: {
args: Prisma.RoleGroupByArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.RoleGroupByOutputType>[]
}
count: {
args: Prisma.RoleCountArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.RoleCountAggregateOutputType> | number
}
}
}
}
} & {
other: {
payload: any
operations: {
$executeRaw: {
args: [query: TemplateStringsArray | Sql, ...values: any[]],
result: any
}
$executeRawUnsafe: {
args: [query: string, ...values: any[]],
result: any
}
$queryRaw: {
args: [query: TemplateStringsArray | Sql, ...values: any[]],
result: any
}
$queryRawUnsafe: {
args: [query: string, ...values: any[]],
result: any
}
}
}
}
/**
* Enums
*/
export const TransactionIsolationLevel = runtime.makeStrictEnum({
ReadUncommitted: 'ReadUncommitted',
ReadCommitted: 'ReadCommitted',
RepeatableRead: 'RepeatableRead',
Serializable: 'Serializable'
} as const)
export type TransactionIsolationLevel = (typeof TransactionIsolationLevel)[keyof typeof TransactionIsolationLevel]
export const SessionScalarFieldEnum = {
id: 'id',
sessionKey: 'sessionKey',
userId: 'userId',
expires: 'expires',
refreshTokenGenerated: 'refreshTokenGenerated',
refreshedAt: 'refreshedAt',
invalidatedAt: 'invalidatedAt'
} as const
export type SessionScalarFieldEnum = (typeof SessionScalarFieldEnum)[keyof typeof SessionScalarFieldEnum]
export const UserScalarFieldEnum = {
id: 'id',
permissions: 'permissions',
login: 'login',
name: 'name',
email: 'email',
emailVerified: 'emailVerified',
image: 'image',
userId: 'userId',
token: 'token',
createdAt: 'createdAt',
updatedAt: 'updatedAt'
} as const
export type UserScalarFieldEnum = (typeof UserScalarFieldEnum)[keyof typeof UserScalarFieldEnum]
export const RoleScalarFieldEnum = {
id: 'id',
title: 'title',
moniker: 'moniker',
permissions: 'permissions',
createdAt: 'createdAt',
updatedAt: 'updatedAt'
} as const
export type RoleScalarFieldEnum = (typeof RoleScalarFieldEnum)[keyof typeof RoleScalarFieldEnum]
export const SortOrder = {
asc: 'asc',
desc: 'desc'
} as const
export type SortOrder = (typeof SortOrder)[keyof typeof SortOrder]
export const QueryMode = {
default: 'default',
insensitive: 'insensitive'
} as const
export type QueryMode = (typeof QueryMode)[keyof typeof QueryMode]
export const NullsOrder = {
first: 'first',
last: 'last'
} as const
export type NullsOrder = (typeof NullsOrder)[keyof typeof NullsOrder]
/**
* Field references
*/
/**
* Reference to a field of type 'String'
*/
export type StringFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'String'>
/**
* Reference to a field of type 'String[]'
*/
export type ListStringFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'String[]'>
/**
* Reference to a field of type 'DateTime'
*/
export type DateTimeFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'DateTime'>
/**
* Reference to a field of type 'DateTime[]'
*/
export type ListDateTimeFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'DateTime[]'>
/**
* Reference to a field of type 'Boolean'
*/
export type BooleanFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Boolean'>
/**
* Reference to a field of type 'Int'
*/
export type IntFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Int'>
/**
* Reference to a field of type 'Int[]'
*/
export type ListIntFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Int[]'>
/**
* Reference to a field of type 'Float'
*/
export type FloatFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Float'>
/**
* Reference to a field of type 'Float[]'
*/
export type ListFloatFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Float[]'>
/**
* Batch Payload for updateMany & deleteMany & createMany
*/
export type BatchPayload = {
count: number
}
export const defineExtension = runtime.Extensions.defineExtension as unknown as runtime.Types.Extensions.ExtendsHook<"define", TypeMapCb, runtime.Types.Extensions.DefaultArgs>
export type DefaultPrismaClient = PrismaClient
export type ErrorFormat = 'pretty' | 'colorless' | 'minimal'
export type PrismaClientOptions = ({
/**
* Instance of a Driver Adapter, e.g., like one provided by `@prisma/adapter-pg`.
*/
adapter: runtime.SqlDriverAdapterFactory
accelerateUrl?: never
} | {
/**
* Prisma Accelerate URL allowing the client to connect through Accelerate instead of a direct database.
*/
accelerateUrl: string
adapter?: never
}) & {
/**
* @default "colorless"
*/
errorFormat?: ErrorFormat
/**
* @example
* ```
* // Shorthand for `emit: 'stdout'`
* log: ['query', 'info', 'warn', 'error']
*
* // Emit as events only
* log: [
* { emit: 'event', level: 'query' },
* { emit: 'event', level: 'info' },
* { emit: 'event', level: 'warn' }
* { emit: 'event', level: 'error' }
* ]
*
* / Emit as events and log to stdout
* og: [
* { emit: 'stdout', level: 'query' },
* { emit: 'stdout', level: 'info' },
* { emit: 'stdout', level: 'warn' }
* { emit: 'stdout', level: 'error' }
*
* ```
* Read more in our [docs](https://pris.ly/d/logging).
*/
log?: (LogLevel | LogDefinition)[]
/**
* The default values for transactionOptions
* maxWait ?= 2000
* timeout ?= 5000
*/
transactionOptions?: {
maxWait?: number
timeout?: number
isolationLevel?: TransactionIsolationLevel
}
/**
* Global configuration for omitting model fields by default.
*
* @example
* ```
* const prisma = new PrismaClient({
* omit: {
* user: {
* password: true
* }
* }
* })
* ```
*/
omit?: GlobalOmitConfig
/**
* SQL commenter plugins that add metadata to SQL queries as comments.
* Comments follow the sqlcommenter format: https://google.github.io/sqlcommenter/
*
* @example
* ```
* const prisma = new PrismaClient({
* adapter,
* comments: [
* traceContext(),
* queryInsights(),
* ],
* })
* ```
*/
comments?: runtime.SqlCommenterPlugin[]
}
export type GlobalOmitConfig = {
session?: Prisma.SessionOmit
user?: Prisma.UserOmit
role?: Prisma.RoleOmit
}
/* Types for Logging */
export type LogLevel = 'info' | 'query' | 'warn' | 'error'
export type LogDefinition = {
level: LogLevel
emit: 'stdout' | 'event'
}
export type CheckIsLogLevel<T> = T extends LogLevel ? T : never;
export type GetLogType<T> = CheckIsLogLevel<
T extends LogDefinition ? T['level'] : T
>;
export type GetEvents<T extends any[]> = T extends Array<LogLevel | LogDefinition>
? GetLogType<T[number]>
: never;
export type QueryEvent = {
timestamp: Date
query: string
params: string
duration: number
target: string
}
export type LogEvent = {
timestamp: Date
message: string
target: string
}
/* End Types for Logging */
export type PrismaAction =
| 'findUnique'
| 'findUniqueOrThrow'
| 'findMany'
| 'findFirst'
| 'findFirstOrThrow'
| 'create'
| 'createMany'
| 'createManyAndReturn'
| 'update'
| 'updateMany'
| 'updateManyAndReturn'
| 'upsert'
| 'delete'
| 'deleteMany'
| 'executeRaw'
| 'queryRaw'
| 'aggregate'
| 'count'
| 'runCommandRaw'
| 'findRaw'
| 'groupBy'
/**
* `PrismaClient` proxy available in interactive transactions.
*/
export type TransactionClient = Omit<DefaultPrismaClient, runtime.ITXClientDenyList>
@@ -0,0 +1,139 @@
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
/* eslint-disable */
// biome-ignore-all lint: generated file
// @ts-nocheck
/*
* WARNING: This is an internal file that is subject to change!
*
* 🛑 Under no circumstances should you import this file directly! 🛑
*
* All exports from this file are wrapped under a `Prisma` namespace object in the browser.ts file.
* While this enables partial backward compatibility, it is not part of the stable public API.
*
* If you are looking for your Models, Enums, and Input Types, please import them from the respective
* model files in the `model` directory!
*/
import * as runtime from "@prisma/client/runtime/index-browser"
export type * from '../models.ts'
export type * from './prismaNamespace.ts'
export const Decimal = runtime.Decimal
export const NullTypes = {
DbNull: runtime.NullTypes.DbNull as (new (secret: never) => typeof runtime.DbNull),
JsonNull: runtime.NullTypes.JsonNull as (new (secret: never) => typeof runtime.JsonNull),
AnyNull: runtime.NullTypes.AnyNull as (new (secret: never) => typeof runtime.AnyNull),
}
/**
* Helper for filtering JSON entries that have `null` on the database (empty on the db)
*
* @see https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-json-fields#filtering-on-a-json-field
*/
export const DbNull = runtime.DbNull
/**
* Helper for filtering JSON entries that have JSON `null` values (not empty on the db)
*
* @see https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-json-fields#filtering-on-a-json-field
*/
export const JsonNull = runtime.JsonNull
/**
* Helper for filtering JSON entries that are `Prisma.DbNull` or `Prisma.JsonNull`
*
* @see https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-json-fields#filtering-on-a-json-field
*/
export const AnyNull = runtime.AnyNull
export const ModelName = {
Session: 'Session',
User: 'User',
Role: 'Role'
} as const
export type ModelName = (typeof ModelName)[keyof typeof ModelName]
/*
* Enums
*/
export const TransactionIsolationLevel = runtime.makeStrictEnum({
ReadUncommitted: 'ReadUncommitted',
ReadCommitted: 'ReadCommitted',
RepeatableRead: 'RepeatableRead',
Serializable: 'Serializable'
} as const)
export type TransactionIsolationLevel = (typeof TransactionIsolationLevel)[keyof typeof TransactionIsolationLevel]
export const SessionScalarFieldEnum = {
id: 'id',
sessionKey: 'sessionKey',
userId: 'userId',
expires: 'expires',
refreshTokenGenerated: 'refreshTokenGenerated',
refreshedAt: 'refreshedAt',
invalidatedAt: 'invalidatedAt'
} as const
export type SessionScalarFieldEnum = (typeof SessionScalarFieldEnum)[keyof typeof SessionScalarFieldEnum]
export const UserScalarFieldEnum = {
id: 'id',
permissions: 'permissions',
login: 'login',
name: 'name',
email: 'email',
emailVerified: 'emailVerified',
image: 'image',
userId: 'userId',
token: 'token',
createdAt: 'createdAt',
updatedAt: 'updatedAt'
} as const
export type UserScalarFieldEnum = (typeof UserScalarFieldEnum)[keyof typeof UserScalarFieldEnum]
export const RoleScalarFieldEnum = {
id: 'id',
title: 'title',
moniker: 'moniker',
permissions: 'permissions',
createdAt: 'createdAt',
updatedAt: 'updatedAt'
} as const
export type RoleScalarFieldEnum = (typeof RoleScalarFieldEnum)[keyof typeof RoleScalarFieldEnum]
export const SortOrder = {
asc: 'asc',
desc: 'desc'
} as const
export type SortOrder = (typeof SortOrder)[keyof typeof SortOrder]
export const QueryMode = {
default: 'default',
insensitive: 'insensitive'
} as const
export type QueryMode = (typeof QueryMode)[keyof typeof QueryMode]
export const NullsOrder = {
first: 'first',
last: 'last'
} as const
export type NullsOrder = (typeof NullsOrder)[keyof typeof NullsOrder]
+14
View File
@@ -0,0 +1,14 @@
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
/* eslint-disable */
// biome-ignore-all lint: generated file
// @ts-nocheck
/*
* This is a barrel export file for all models and their related types.
*
* 🟢 You can import this file directly.
*/
export type * from './models/Session.ts'
export type * from './models/User.ts'
export type * from './models/Role.ts'
export type * from './commonInputTypes.ts'
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+28 -1
View File
@@ -1,12 +1,39 @@
{ {
"name": "ttscm-api", "name": "ttscm-api",
"homepage": "https://totaltech.net",
"author": {
"name": "Jackson Roberts",
"email": "jackson.roberts@totaltech.net",
"url": "https://totaltech.net"
},
"module": "src/index.ts", "module": "src/index.ts",
"type": "module", "type": "module",
"private": true, "private": true,
"devDependencies": { "devDependencies": {
"@types/bun": "latest" "@types/bun": "latest",
"@types/jsonwebtoken": "^9.0.10"
}, },
"peerDependencies": { "peerDependencies": {
"typescript": "^5" "typescript": "^5"
},
"scripts": {
"dev": "NODE_ENV=development bun --watch src/index.ts",
"db:gen": "prisma generate",
"db:push": "prisma migrate dev --skip-generate",
"utils:dev": "docker compose -f .docker/docker-compose.yml up --build",
"utils:gen_private_keys": "bun ./utils/genPrivateKeys"
},
"dependencies": {
"@discordjs/collection": "^2.1.1",
"@duxcore/eventra": "^1.1.0",
"@prisma/adapter-pg": "^7.3.0",
"cors": "^2.8.6",
"cuid": "^3.0.0",
"hono": "^4.11.5",
"jsonwebtoken": "^9.0.3",
"keypair": "^1.0.4",
"prisma": "^7.3.0",
"zod": "^4.3.6",
"zon": "^1.0.3"
} }
} }
+12
View File
@@ -0,0 +1,12 @@
import 'dotenv/config'
import { defineConfig, env } from 'prisma/config'
export default defineConfig({
schema: 'prisma/schema.prisma',
migrations: {
path: 'prisma/migrations',
},
datasource: {
url: env('DATABASE_URL'),
},
})
+56
View File
@@ -0,0 +1,56 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
generator client {
provider = "prisma-client"
output = "../generated/prisma"
}
datasource db {
provider = "postgresql"
}
model Session {
id String @id @default(uuid())
sessionKey String @unique @default(cuid())
userId String
expires DateTime
refreshTokenGenerated Boolean @default(false)
refreshedAt DateTime?
invalidatedAt DateTime?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
model User {
id String @id @default(cuid())
roles Role[]
permissions String?
login String @unique
name String?
email String @unique
emailVerified DateTime?
image String?
userId Int @unique
token String?
sessions Session[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Role {
id String @id @default(uuid())
title String
moniker String @unique // e.g. admin, super_admin, moderator
permissions String
users User[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
+8
View File
@@ -0,0 +1,8 @@
export default class AuthenticationError extends Error {
constructor(message: string, cause?: string) {
super();
this.name = "AuthenticationError";
this.message = message;
this.cause = cause;
}
}
+11
View File
@@ -0,0 +1,11 @@
export default class AuthorizationError extends Error {
public status: number;
constructor(message: string, cause?: string, status?: number) {
super();
this.name = "AuthorizationError";
this.status = status ?? 401;
this.message = message;
this.cause = cause;
}
}
+8
View File
@@ -0,0 +1,8 @@
export default class BodyError extends Error {
constructor(message: string, cause?: string) {
super();
this.name = "BodyError";
this.message = message;
this.cause = cause;
}
}
+8
View File
@@ -0,0 +1,8 @@
export default class ExpiredAccessTokenError extends Error {
constructor(cause?: string) {
super();
this.name = "ExpiredAccessTokenError";
this.message = "The provided access token has expired.";
this.cause = cause;
}
}
+8
View File
@@ -0,0 +1,8 @@
export default class ExpiredRefreshTokenError extends Error {
constructor(cause?: string) {
super();
this.name = "ExpiredRefreshTokenError";
this.message = "The provided refresh token has expired.";
this.cause = cause;
}
}
+16
View File
@@ -0,0 +1,16 @@
export default class GenericError extends Error {
public status: number;
constructor(info: {
name: string;
message: string;
cause?: string;
status?: number;
}) {
super();
this.name = info.name;
this.status = info.status ?? 400;
this.message = info.message;
this.cause = info.cause;
}
}
+11
View File
@@ -0,0 +1,11 @@
export default class InsufficientPermission extends Error {
public status: number;
constructor(message: string, cause?: string) {
super();
this.name = "InsufficientPermission";
this.status = 403;
this.message = message;
this.cause = cause;
}
}
+9
View File
@@ -0,0 +1,9 @@
export default class MissingBodyValue extends Error {
constructor(valueName: string) {
super();
this.name = "MissingBodyValue";
this.message = `Value '${valueName}' is missing from the body.`;
this.cause =
"A value that was required by the body of this request is missing.";
}
}
@@ -0,0 +1,8 @@
export default class PermissionsVerificationError extends Error {
constructor(message: string, cause?: string) {
super();
this.name = "PermissionsVerificationError";
this.message = message;
this.cause = cause;
}
}
+8
View File
@@ -0,0 +1,8 @@
export default class RoleError extends Error {
constructor(message: string, cause?: string) {
super();
this.name = "RoleError";
this.message = message;
this.cause = cause;
}
}
+8
View File
@@ -0,0 +1,8 @@
export default class SessionError extends Error {
constructor(message: string, cause?: string) {
super();
this.name = "SessionError";
this.message = message;
this.cause = cause;
}
}
+8
View File
@@ -0,0 +1,8 @@
export default class SessionTokenError extends Error {
constructor(message: string, cause?: string) {
super();
this.name = "SessionTokenError";
this.message = message;
this.cause = cause;
}
}
+8
View File
@@ -0,0 +1,8 @@
export default class UserError extends Error {
constructor(message: string, cause?: string) {
super();
this.name = "UserError";
this.message = message;
this.cause = cause;
}
}
+5
View File
@@ -0,0 +1,5 @@
node_modules
# Keep environment variables out of version control
.env
/generated/prisma
+12
View File
@@ -0,0 +1,12 @@
// This file was generated by Prisma, and assumes you run Prisma commands using `bun --bun run prisma [command]`.
import { defineConfig, env } from "prisma/config";
export default defineConfig({
schema: "prisma/schema.prisma",
migrations: {
path: "prisma/migrations",
},
datasource: {
url: env("DATABASE_URL"),
},
});
+53
View File
@@ -0,0 +1,53 @@
import { Hono } from "hono";
import { apiResponse } from "../modules/api-utils/apiResponse";
import { ZodError } from "zod";
import { cors } from "hono/cors";
import GenericError from "../Errors/GenericError";
import teapot from "./teapot";
const app = new Hono();
const v1 = new Hono();
app.onError((err, ctx) => {
const errClassName = err.constructor.name;
if (
errClassName.toLowerCase().includes("prisma") ||
err.message.toLowerCase().includes("prisma") ||
err.name.toLowerCase().includes("prisma")
) {
console.trace(err);
return ctx.json(apiResponse.internalError(), 500);
}
if (err instanceof ZodError) {
return ctx.json(
apiResponse.zodError(err),
//@ts-ignore
apiResponse.zodError(err).status
);
}
const response = apiResponse.error(err);
return ctx.json(response, response.status);
});
app.use("*", cors());
app.notFound((c) => {
const response = apiResponse.error(
new GenericError({
name: "NotFound",
message: `Cannot ${c.req.method.toUpperCase()} ${c.req.path}`,
status: 404,
cause: "Unknown",
})
);
return c.json(response, response.status);
});
v1.route("/teapot", teapot);
app.route("/v1", v1);
export default app;
+12
View File
@@ -0,0 +1,12 @@
import { Hono } from "hono/tiny";
import { createRoute } from "../modules/api-utils/createRoute";
/* /v1/teapot */
export default createRoute("get", ["/"], (c) => {
c.status(418);
return c.json({
status: 418,
message: "I'm not a teapot",
successful: true,
});
});
+34
View File
@@ -0,0 +1,34 @@
import { readFileSync } from "fs";
import { PrismaPg } from '@prisma/adapter-pg'
import { PrismaClient } from '../generated/prisma/client'
const connectionString = `${process.env.DATABASE_URL}`
const adapter = new PrismaPg({ connectionString })
interface EnvKey {
PORT: number;
};
// ENV CONSTANTS
export const PORT = process.env.PORT;
export const prisma = new PrismaClient({ adapter })
export const sessionDuration = 30 * 24 * 60 * 60000;
export const accessTokenDuration = "10min";
export const refreshTokenDuration = "30d";
export const accessTokenPrivateKey = readFileSync(
`${import.meta.dir}/../.accessToken.key`
).toString();
export const refreshTokenPrivateKey = readFileSync(
`${import.meta.dir}/../.refreshToken.key`
).toString();
export const permissionsPrivateKey = readFileSync(
`${import.meta.dir}/../.permissions.key`
);
export const apiKeyTokenPrivateKey = readFileSync(
`${import.meta.dir}/../.apiKeyToken.key`
);
+338
View File
@@ -0,0 +1,338 @@
import UserController from "./UserController";
import { Collection } from "@discordjs/collection";
import jwt, { JsonWebTokenError } from "jsonwebtoken";
import { permissionsPrivateKey, prisma } from "../constants";
import PermissionsVerificationError from "../Errors/PermissionsVerificationError";
import { mergeArrays } from "../modules/tools/mergeArrays";
import { signPermissions } from "../modules/permission-utils/signPermissions";
import { DecodedPermissionsBlock } from "../types/PermissionTypes";
import { permissionValidator } from "../modules/permission-utils/permissionValidator";
import { z } from "zod";
import { roles } from "../managers/roles";
import GenericError from "../Errors/GenericError";
import RoleError from "../Errors/RoleError";
import { Role, User } from "../../generated/prisma/client";
import { events } from "../modules/globalEvents";
/**
* Roles
*
* Roles are for adding onto a users permissions. They are not for defining a default users permissions.
*
* Roles have two forms of identifiers, titles and monikers. The title is something that can be capitalized and publicly displayed,
* so if you make modifications, and you need to be able to see how they were able to do that without having explicit permission to
* make the modifications, the title is what would be shown to you. The moniker is the human readable identifier. So in an api
* response where you need to get the roles of a user, instead of being given a bunch of id's, you will be given a bunch of monikers
* which are easy to read and easy to identify.
*/
export class RoleController {
public readonly id: string;
public title: string;
public moniker: string; // e.g. admin, super_admin, moderator
private _permissionsToken: string;
private _users: (User & { roles: Role[] })[];
public readonly createdAt: Date;
public updatedAt: Date;
public deleted: boolean = false;
constructor(roledata: Role & { users: (User & { roles: Role[] })[] }) {
this.id = roledata.id;
this.title = roledata.title;
this.moniker = roledata.moniker;
this._permissionsToken = roledata.permissions;
this._users = roledata.users;
this.createdAt = roledata.createdAt;
this.updatedAt = roledata.updatedAt;
}
private _signPermissions = signPermissions;
/**
* Verify Permissions
*
* This method is vital to maintining the security of this system. This method is here to ensure
* that the permissions object is authentic and signed by our system.
*
* @param permissionsToken - Signed permissions JWT
* @returns - Verified object with permissions in it.
*/
private _verifyPermissions(permissionsToken: string) {
let perms: DecodedPermissionsBlock;
try {
perms = jwt.verify(permissionsToken, permissionsPrivateKey, {
algorithms: ["RS256"],
issuer: "roles",
subject: this.id,
}) as DecodedPermissionsBlock;
} catch (err) {
events.emit("role:permissions:verification_error", {
currentSigned: this._permissionsToken,
attemptedVerification: permissionsToken,
err: err as Error,
role: this,
});
throw new PermissionsVerificationError(
`Unable to verify permissions for role '${this.title}, it is recommended that you override and rewrite these permissions immediately.`,
(err as Error).message
);
}
return perms as { permissions: string[] };
}
/**
* Get Users
*
* This will get all the users that have this role and return it as a collection dictionary where the key is
* the `id` of the user and the value is the users `UserController`.
*
* @returns {Collection<string, UserController>} - A collection of all the users that are assigned to this role
*/
public getUsers() {
const collection = new Collection<string, UserController>();
this._users.map((v) => collection.set(v.id, new UserController(v)));
return collection;
}
/**
* Check Permission
*
* Check to see if a role has a specified set of permissions.
*
* @param permission - The permission to check for
* @returns {boolean} Does this role have the specified permission
*/
public checkPermission(permission: string): boolean {
const permissions = this._verifyPermissions(this._permissionsToken);
return permissionValidator(permission, permissions.permissions);
}
/**
* Set permissions
*
* This will remove all existing permissions and replate them with the permissions defined in the params
* of this method.
*
* @param permissions - Array of all Permissions
* @returns {void}
*/
public async setPermissions(...permissions: string[]) {
/*
Make sure the current permissions are verified before updating them again. Basically speaking if the permissions
are tampered with in any way, this bricks the further modification of the permissions
*/
const previous = this._verifyPermissions(this._permissionsToken);
const newPermissionsToken = this._signPermissions({
issuer: "roles",
subject: this.id,
permissions,
});
const newRaw = await prisma.role.update({
where: { id: this.id },
data: { permissions: newPermissionsToken },
});
events.emit("role:permissions:updated", {
current: permissions,
currentSigned: newPermissionsToken,
previous: previous.permissions,
previousSigned: this._permissionsToken,
action: "set",
role: this,
});
this._permissionsToken = newPermissionsToken;
this.updatedAt = newRaw.updatedAt;
return;
}
/**
* Add Permissions
*
* This will take the permissions provided in the params of the method and combine them into the pre-existing
* permissions of this role.
*
* @param permissions - Array of added Permissions
* @returns {void}
*/
public async addPermissions(...permissions: string[]) {
/*
Make sure the current permissions are verified before updating them again. Basically speaking if the permissions
are tampered with in any way, this bricks the further modification of the permissions
*/
const previous = this._verifyPermissions(this._permissionsToken);
const newPermissionsToken = this._signPermissions({
issuer: "roles",
subject: this.id,
permissions: mergeArrays(previous.permissions, permissions),
});
const newRaw = await prisma.role.update({
where: { id: this.id },
data: { permissions: newPermissionsToken },
});
events.emit("role:permissions:updated", {
current: permissions,
currentSigned: newPermissionsToken,
previous: previous.permissions,
previousSigned: this._permissionsToken,
action: "added",
role: this,
});
this._permissionsToken = newPermissionsToken;
this.updatedAt = newRaw.updatedAt;
return;
}
/**
* Remove Permissions
*
* This will take the permissions provided in the params of the method and remove them from the exisitng
* permissions object for this role.
*
* @param permissions - Array of removed Permissions
* @returns {void}
*/
public async removePermissions(...permissions: string[]) {
/*
Make sure the current permissions are verified before updating them again. Basically speaking if the permissions
are tampered with in any way, this bricks the further modification of the permissions
*/
const previous = this._verifyPermissions(this._permissionsToken);
const newPermissionsToken = this._signPermissions({
issuer: "roles",
subject: this.id,
permissions: previous.permissions.filter((v) => !permissions.includes(v)),
});
const newRaw = await prisma.role.update({
where: { id: this.id },
data: { permissions: newPermissionsToken },
});
events.emit("role:permissions:updated", {
current: permissions,
currentSigned: newPermissionsToken,
previous: previous.permissions,
previousSigned: this._permissionsToken,
action: "removed",
role: this,
});
this._permissionsToken = newPermissionsToken;
this.updatedAt = newRaw.updatedAt;
return;
}
/**
* Get Current Permissions
*
* Get all of the permissions for this role.
* @returns {string[]} - Existing permissions in an array
*/
public getPermissions() {
const permissions = this._verifyPermissions(this._permissionsToken!);
return permissions;
}
public async update(
data: Partial<{
title: string;
moniker: string;
permissions: string[];
}>
) {
const schema = z
.object({
title: z.string().min(1, "Title cannot be empty."),
moniker: z.string().min(1, "Moniker cannot be empty."),
permissions: z.array(
z.string().min(1, "Permission node cannot be empty")
),
})
.partial()
.strict();
data = schema.parse(data);
if (data.moniker) {
const checkMoniker = await prisma.role.findFirst({
where: { moniker: data.moniker },
});
if (checkMoniker)
throw new RoleError(
"Moniker is already taken.",
"Another role with this moniker already exists in the databse."
);
}
const updatedRole = await prisma.role.update({
where: { id: this.id },
data: {
...data,
permissions: undefined,
},
});
if (data.permissions) await this.setPermissions(...data.permissions);
events.emit("role:updated", { role: this, updateData: data });
this.title = data.title ?? this.title;
this.moniker = data.moniker ?? this.moniker;
return this;
}
/**
* Delete Role
*
* @returns {Promise<RoleController>} The Remains of a deleted role.
*/
public async delete() {
const deletedData = await prisma.role.delete({ where: { id: this.id } });
this.deleted = true;
events.emit("role:deleted", this);
return this;
}
/**
* To JSON
*
* Create a JSON object that can be used in things like API responses.
*
* @param opts - Optional values to include in the response.
* @returns - JSON-Friendly object.
*/
public toJson(opts?: { viewUsers?: boolean; viewPermissions?: boolean }) {
let object = {
id: this.id,
title: this.title,
moniker: this.moniker,
permissions: opts?.viewPermissions
? this._verifyPermissions(this._permissionsToken).permissions
: undefined,
users: opts?.viewUsers
? this._users.map((v) => ({
id: v.id,
name: v.name,
login: v.login,
roles: v.roles.map((r:any) => r.id),
}))
: undefined,
createdAt: this.createdAt,
updatedAt: this.updatedAt,
};
return object;
}
}
+228
View File
@@ -0,0 +1,228 @@
import jwt from "jsonwebtoken";
import {
prisma,
refreshTokenDuration,
accessTokenDuration,
accessTokenPrivateKey,
refreshTokenPrivateKey,
} from "../constants";
import SessionTokenError from "../Errors/SessionTokenError";
import { events } from "../modules/globalEvents";
import UserController from "./UserController";
import { users } from "../managers/users";
import { Session } from "../../generated/prisma/client";
export interface SessionPayloadObject {
userID: string;
sessionKey: string;
}
export interface SessionTokensObject {
accessToken: string;
refreshToken: string;
}
export type DecodedSession = SessionPayloadObject & {
iat: number;
exp: number;
};
/**
* Session Controller
*
* This class is for create a controller that can manage, generate and refresh tokens, self terminate and self delete
* all sessions. This also allows you to access all data about a session.
*/
export class SessionController {
public readonly id: string;
public readonly sessionKey: string;
public readonly userId: string;
public readonly expires: Date;
public refreshedAt: Date | null;
public invalidatedAt: Date | null;
public terminated: boolean = false;
private _refreshTokenGenrated: boolean;
constructor(sessionData: Session) {
this.id = sessionData.id;
this.sessionKey = sessionData.sessionKey;
this.userId = sessionData.userId;
this.expires = sessionData.expires;
this.refreshedAt = sessionData.refreshedAt;
this.invalidatedAt = sessionData.invalidatedAt;
this._refreshTokenGenrated = sessionData.refreshTokenGenerated;
}
/** @ignore */
private _generateAccessToken() {
const payload: SessionPayloadObject = {
sessionKey: this.sessionKey,
userID: this.userId,
};
return jwt.sign(payload, accessTokenPrivateKey, {
algorithm: "RS256",
expiresIn: accessTokenDuration,
});
}
/** @ignore */
private _generateRefreshToken() {
const payload: SessionPayloadObject = {
sessionKey: this.sessionKey,
userID: this.userId,
};
return jwt.sign(payload, refreshTokenPrivateKey, {
algorithm: "RS256",
expiresIn: refreshTokenDuration,
});
}
/**
* Invalidate the Session
*
* The purpose for this function is if you wanted to be able to listen for somebody using an invalid session,
* you just have to invalidate it with this function and go from there.
*
* @returns {Promise<void>} - nothing
*/
public async invalidate() {
const invalidationDate = new Date();
if (this.invalidatedAt)
throw new Error("Session has already been invalidated.");
await prisma.session.update({
data: { invalidatedAt: invalidationDate },
where: { id: this.id },
});
this.invalidatedAt = invalidationDate;
events.emit("session:invalidated", this);
return;
}
/**
* Terminate the session
*
* Terminating the session will immediately delete the session making it impossible for it to be referenced again.
*
* @returns {Promise<void>} - nothing
*/
public async terminate() {
await prisma.session.delete({ where: { id: this.id } });
events.emit("session:terminated", this);
this.terminated = true;
return;
}
/**
* Generate Tokens
*
* **NOTE**: This method can only be ran once per session.
*
* Running this function will allow you to generate an accessToken and a refreshToken. Each token
* will be generated with their own respective private keys.
*
*
* @returns {Promise<SessionTokensObject>} An object containing the `accessToken` and `refreshToken`
*/
public async generateTokens(): Promise<SessionTokensObject> {
if (this._refreshTokenGenrated)
throw new Error("Tokens have alredy been generated for this session.");
const accessToken = this._generateAccessToken();
const refreshToken = this._generateRefreshToken();
const newRefreshDate = new Date();
await prisma.session.update({
data: { refreshTokenGenerated: true, refreshedAt: newRefreshDate },
where: { id: this.id },
});
this._refreshTokenGenrated = true;
this.refreshedAt = newRefreshDate;
let tokens = { accessToken, refreshToken };
events.emit("session:tokens_generated", { session: this, tokens });
return tokens;
}
/**
* Refresh the session
*
* Refreshing the session will generate a new accessToken for the user to authenticate their requests with.
*
* **NOTE**: Best practice when implementing token refreshing into the UI is that if for any reason this method
* throws an error, imediately purge the existing tokens and have the user login again. This way you don't hold
* them up any longer than necessary.
*
* @param refreshToken - The refresh token provided at session generation
* @returns {Promise<string>} The new access token.
*/
public refresh(refreshToken: string): Promise<string> {
return new Promise(async (res, rej) => {
if (this.expires.getTime() <= Date.now()) {
await this.terminate();
throw new SessionTokenError("Session has Expired.");
}
jwt.verify(
refreshToken,
refreshTokenPrivateKey,
{
algorithms: ["RS256"],
},
async (err, decode) => {
if (err) {
if (
err.name == "TokenExpiredError" ||
err.message == "invalid signature"
)
this.terminate();
rej(err);
}
const data: DecodedSession = decode as DecodedSession;
if (
data.sessionKey !== this.sessionKey ||
data.userID !== this.userId
)
rej(
new SessionTokenError(
"Refresh token does not match this session."
)
);
await prisma.session.update({
data: { refreshedAt: new Date() },
where: { id: this.id },
});
const newToken = this._generateAccessToken();
events.emit("session:token_refresh", {
session: this,
tokens: { accessToken: newToken, refreshToken },
});
return res(newToken);
}
);
});
}
/**
* Fetch Session User
*
* Fetch the user controller of the user that created the session.
*
* @returns {Promise<UserController>} The user that created this session.
*/
public async fetchUser(): Promise<UserController> {
return (await users.fetchUser({ id: this.userId }))!;
}
}
+200
View File
@@ -0,0 +1,200 @@
import { Collection } from "@discordjs/collection";
import { z } from "zod";
import { Role } from "../../generated/prisma/client";
import { User } from "../../generated/prisma/browser";
import { SessionTokensObject } from "./SessionController";
import { sessions } from "../managers/sessions";
import BodyError from "../Errors/BodyError";
import { prisma } from "../constants";
import { events } from "../modules/globalEvents";
import { RoleController } from "./RoleController";
import { roles } from "../managers/roles";
export default class UserController {
public id: string;
public name: string | null;
public login: string;
public email: string;
public image: string | null;
private _roles: Collection<string, Role>;
public createdAt: Date;
public updatedAt: Date;
constructor(userdata: User & { roles: Role[] }) {
this.id = userdata.id;
this.name = userdata.name;
this.login = userdata.login;
this.email = userdata.email;
this.image = userdata.image;
this.updatedAt = userdata.updatedAt;
this.createdAt = userdata.createdAt;
this._roles = (() => {
let collection = new Collection<string, Role>();
userdata.roles.map((v: any) => collection.set(v.id, v));
return collection;
})();
}
/**
* Update the internal values
*
* This is an internal method used to update all the internal values when we query the database. This way
* everything stays upto date even when we pass around the user controller.
*
* @param userdata - User object from Prisma
*/
private _updateInternalValues(userdata: User) {
this.id = userdata.id;
this.name = userdata.name;
this.login = userdata.login;
this.email = userdata.email;
this.image = userdata.image;
this.updatedAt = userdata.updatedAt;
this.createdAt = userdata.createdAt;
}
/**
* Create Session
*
* This will create a session in the database that is linked to the user and will then create a pair of access and refresh
* tokens to provide to the user such that they can authorized their api requests.
*
* @returns {Promise<SessionTokensObject>} - Object with an access token and a refresh token.
*/
public async createSession(): Promise<SessionTokensObject> {
return sessions.create({ user: this });
}
/**
* Update the user
*
* Take in a partial of the user data and validate it then updated it if it passes validation and return
* the updated `UserController` object.
*
* @param data - A partial of the user data
* @returns {Promise<UserController>} - The updated user controller
*/
public async update(data: Partial<User>) {
// Parsed Data With Schema
const pData = z
.object({
name: z.string().optional(),
image: z.string().optional(),
})
.strict()
.parse(data);
if (Object.keys(data).length == 0)
throw new BodyError("Body cannot be empty.");
const updatedUser = await prisma.user.update({
where: { id: this.id },
data: pData,
});
this._updateInternalValues(updatedUser);
events.emit("user:updated", { user: this, updatedValues: data });
return this;
}
/**
* Fetch Roles
*
* This method will fetch all of the roles that a user belongs to and will return each of their controllers in a collection
* of role id's and RoleControllers.
*
* @returns {Promise<Collection<string, RoleController>>} A collection of all the roles a user has
*/
public async fetchRoles(): Promise<Collection<string, RoleController>> {
const collection = new Collection<string, RoleController>();
await Promise.all(
this._roles.map(async (v) =>
collection.set(v.id, await roles.fetch(v.id))
)
);
return collection;
}
/**
* Check Permission
*
* Check if this user has this specific permission. This method will not only check explicit permissions defined in
* the database under users and roles, but will also generate implicit permissions for resources that the user has
* access to but doesn't specifically have defined under any given permissions object.
*
* @param permission - The permission to check for
* @returns {boolean} Does this user have the specified permission
*/
public async hasPermission(permission: string) {
let resources = await prisma.user.findFirst({
where: { id: this.id },
select: {
sessions: {
select: { id: true },
},
apiKeys: {
select: { id: true },
},
projects: {
select: { id: true },
},
services: {
select: { id: true },
},
},
});
const implicitPermissions = Object.keys(resources ?? {})
.filter((v) => resources![v].length > 0)
.map(
(v) =>
`resource.${v}.[${(resources![v] as { id: string }[])
.map((o) => o.id)
.join(",")}].user.${this.id}.implicit`
);
console.log(implicitPermissions);
let checks = [
(await this.fetchRoles()).map((v) => v.checkPermission(permission)),
].flatMap((v) => v);
return checks.includes(true);
}
/**
* To JSON
*
* Create an object that can be safely returned to the user of an api request such that when you
* need to return data to the end user, you don't accidently return data that could be harmful
* if leaked.
*
* Options:
* - Safe return is to return only data that is considered "safe", and not detrimental to pass around
*
* @param opts - Options to change the output
* @returns - An object that is JSON friendly
*/
public toJson(opts?: { safeReturn: boolean }) {
return {
id: this.id,
name: this.name,
roles: opts?.safeReturn
? undefined
: this._roles.size > 0
? this._roles.map((v) => v.moniker)
: undefined,
login: opts?.safeReturn ? undefined : this.login,
email: opts?.safeReturn ? undefined : this.email,
image: this.image,
};
}
}
+7 -1
View File
@@ -1 +1,7 @@
console.log("Hello via Bun!"); import app from "./api/server";
import { PORT } from "./constants";
Bun.serve({
port: PORT,
fetch: app.fetch
});
+171
View File
@@ -0,0 +1,171 @@
import { Collection } from "@discordjs/collection";
import GenericError from "../Errors/GenericError";
import RoleError from "../Errors/RoleError";
import { prisma } from "../constants";
import { RoleController } from "../controllers/RoleController";
import { signPermissions } from "../modules/permission-utils/signPermissions";
import cuid from "cuid";
import UserController from "../controllers/UserController";
import InsufficientPermission from "../Errors/InsufficientPermission";
import { z } from "zod";
export const roles = {
/**
* Create a role
*
* @param data - Data required to make a role
* @returns {RoleController} - The new role
*/
async create(data: {
title: string;
moniker: string;
permissions?: string[];
}) {
const schema = z.object({
title: z.string().min(1, "Title cannot be empty"),
moniker: z.string().min(1, "Moniker cannot be empty"),
permissions: z
.array(z.string().min(1, "Cannot have a blank permission node."))
.optional(),
});
data = await schema.parseAsync(data);
const checkMoniker = await prisma.role.findFirst({
where: { moniker: data.moniker },
});
if (checkMoniker)
throw new RoleError(
"Moniker is already taken.",
"Another role with this moniker already exists in the databse."
);
const id = cuid();
const newRole = await prisma.role.create({
data: {
id,
title: data.title,
moniker: data.moniker,
permissions: signPermissions({
issuer: "roles",
subject: id,
permissions: data.permissions ?? [],
}),
},
include: {
users: {
include: {
roles: true,
},
},
},
});
const controller = new RoleController(newRole);
return controller;
},
/**
* Fetch a role
*
* Fetch a role using either it's id or it's moniker.
*
* @param identifier - Role `id` or `moniker`
* @param identifier - Options for fetching a role.
* @returns {RoleController} - Role Controller
*/
async fetch(identifier:string, opt?: { requestingUser?: UserController }) {
const roleData = await prisma.role.findFirst({
where: { OR: [{ id: identifier }, { moniker: identifier }] },
include: {
users: {
include: {
roles: true,
},
},
},
});
if (!roleData)
throw new GenericError({
name: "UnknownRole",
message: "Unknown role...",
status: 404,
});
if (
opt?.requestingUser &&
!(await opt.requestingUser.hasPermission(
this._buildPermissionNode(roleData.id, "read")
))
)
throw new InsufficientPermission(
"You do not have permission to access this role."
);
const controller = new RoleController(roleData);
return controller;
},
/**
* Fetch all roles
*
* This will give you all of the roles and their respective controllers
*
* @param opt - Options for fetching all roles.
* @returns {Collection<string, RoleController>} A collection of all the roles and their id's
*/
async fetchAllRoles(opt?: { requestingUser?: UserController }) {
let collection = new Collection<string, RoleController>();
const roles = await prisma.role.findMany({
include: { users: { include: { roles: true } } },
});
roles. map((v:any) => collection.set(v.id, new RoleController(v)));
if (opt?.requestingUser) {
const permittedRoles = await Promise.all(
collection.map(async (v) =>
(await opt.requestingUser?.hasPermission(
this._buildPermissionNode(v.id, "read")
))
? v.id
: null
)
);
collection = collection.filter((v) =>
permittedRoles.filter((x) => x !== null).includes(v.id)
);
}
return collection;
},
/**
* Build Permissino Node
*
* Build a role centric permission node with a role id and permission scope.
*
* **FORMAT**: `roles.{id}.{scope}
*
* @param id - Role ID
* @param scope - Scope of the permission node
* @returns {string} Constructed Permission Node
*/
_buildPermissionNode(id: string, scope: "read" | "write") {
return ["roles", id, scope].join(".");
},
};
/**
* @TODO automatic string transformation for monikers to be all lower case with no spaces and only underscores.
*
* @TODO create and inheritance and ordering system @see https://www.prisma.io/docs/concepts/components/prisma-schema/relations/self-relations
*
* @TODO go through and make sure all the zod schemas have mins, maxes, etc.
*
* @TODO Limit those who can give out the `*` permission to those with the `*` permission.
* - Maybe also limit the use of `*` all together?
*/
+159
View File
@@ -0,0 +1,159 @@
import {
prisma,
refreshTokenDuration,
sessionDuration,
accessTokenDuration,
accessTokenPrivateKey,
refreshTokenPrivateKey,
} from "../constants";
import UserController from "../controllers/UserController";
import {
SessionController,
DecodedSession,
SessionPayloadObject,
SessionTokensObject,
} from "../controllers/SessionController";
import jwt from "jsonwebtoken";
import SessionError from "../Errors/SessionError";
import SessionTokenError from "../Errors/SessionTokenError";
import ExpiredAccessTokenError from "../Errors/ExpiredAccessTokenError";
import ExpiredRefreshTokenError from "../Errors/ExpiredRefreshTokenError";
import { events } from "../modules/globalEvents";
export interface SessionCreationData {
user: UserController;
}
export const sessions = {
/**
* Create a session
*
* This will create a session instance in the databse that will be linked to both the session token
* and the refresh token.
*
* @param data - The params needed to create the session tokens
* @returns {Promise<SessionTokensObject>} Session Token and the Refresh Token
*/
async create(data: SessionCreationData): Promise<SessionTokensObject> {
const session = await prisma.session.create({
data: {
expires: new Date(Date.now() + sessionDuration),
userId: data.user.id,
},
});
const controller = new SessionController(session);
// Trigger Global Event
events.emit("session:created", {
user: data.user,
session: controller,
});
let tokens: SessionTokensObject = await controller.generateTokens();
return tokens;
},
/**
* Fetch a session
*
* This method is designed to be as versitile as possible, if you are looking for a session, use this method with
* your choice of `accessToken`, `refreshToken`, `id`, or `sessionKey`. The identifier value is a partial type.
*
* @param identifier - An object allowing you to put either session token, sessionKey, or id in to fetch the desired session.
* @returns {Promise<SessionController>} The controller for the desired session.
*/
async fetch(
identifier: Partial<{
refreshToken: string;
accessToken: string;
id: string;
sessionKey: string;
}>
) {
if (identifier.refreshToken || identifier.accessToken) {
const decodedJWT = identifier.refreshToken
? ((await new Promise((res, rej) =>
jwt.verify(
identifier.refreshToken!,
refreshTokenPrivateKey,
{
algorithms: ["RS256"],
},
async (err, decode) => {
if (
err &&
(err.name == "TokenExpiredError" ||
err.message == "invalid signature")
) {
let sessionDat = await prisma.session.findFirst({
where: {
sessionKey: (
jwt.decode(identifier.refreshToken!) as DecodedSession
).sessionKey,
},
});
if (!sessionDat)
return rej(new SessionError("Invalid session."));
let session = new SessionController(sessionDat);
await session.terminate();
if (err.message == "invalid signature")
return rej(new SessionError("Invalid session."));
return rej(new ExpiredRefreshTokenError("It epired."));
}
if (err) return rej(err);
return res(decode as DecodedSession);
}
)
)) as DecodedSession)
: ((await new Promise((res, rej) =>
jwt.verify(
identifier.accessToken!,
accessTokenPrivateKey,
{
algorithms: ["RS256"],
},
(err, decode) => {
if (err && err.name == "TokenExpiredError")
return rej(new ExpiredAccessTokenError());
if (err) return rej(err);
return res(decode as DecodedSession);
}
)
)) as DecodedSession);
const sessionData = await prisma.session.findFirst({
where: { sessionKey: decodedJWT.sessionKey },
});
if (!sessionData) throw new SessionError("Invalid Session");
if (identifier.accessToken && decodedJWT.exp > Date.now())
throw new ExpiredAccessTokenError();
if (identifier.refreshToken && decodedJWT.exp > Date.now()) {
let sess = new SessionController(sessionData);
await sess.terminate();
throw new SessionError("Invalid Session...", "Expired Refresh Token");
}
return new SessionController(sessionData);
}
const sessionData = await prisma.session.findFirst({
where: { OR: [{ sessionKey: identifier.sessionKey, id: identifier.id }] },
});
if (!sessionData) throw new SessionError("Invalid Session");
return new SessionController(sessionData);
},
};
/**
* @TODO As a consequence of the above, need to setup pgBoss for cron and event loop.
*/
+118
View File
@@ -0,0 +1,118 @@
import { User } from "../../generated/prisma/client";
import { prisma } from "../constants";
import { SessionTokensObject } from "../controllers/SessionController";
import UserController from "../controllers/UserController";
export const users = {
/**
* Authenticate The User
*
* If the user has already been registered, this function will supply them with a session id, otherwise
* this method will create an account, then create a session id.
*
* @summary It creates a user if one doesn't exist and will supply a session id
*
* @async
* @param ghCode - The code supplied in the callback url of a GitHub oAuth transaction
*/
/* async authenticate(ghCode: string): Promise<SessionTokensObject> {
const token = await ghApp.oauth.createToken({ code: ghCode }).catch((e) => {
throw new AuthenticationError("Invalid OAuth code...");
});
const userOK = await ghApp.oauth.getUserOctokit({
token: token.authentication.token,
});
const ghUser = await userOK.request("GET /user");
let user =
(await this.fetchUser({ userId: ghUser.data.id })) ??
(await this.createUser(token.authentication.token));
const tokens = await sessions.create({ user });
events.emit("user:authenticated", { user, tokens });
return tokens;
}, */
/**
* Check to see if the user exists
*
* @param partial - A partial object that you can feed any value from the database into.
* @returns {Promise<boolean>} Does the user exist?
*/
async userExists(partial: Partial<User>): Promise<boolean> {
const match = await prisma.user.findFirst({ where: partial });
return !!match;
},
/**
* Fetch a user
*
* This method takes in a unique identifier for the user you are trying to fetch, and returns
* the controller for the user if the user exists. If the user doesn't exist, or no identifier
* was provided, this will reutrn `null`.
*
* @param identifier - A partial identifier for a user (e.g. id, email, userID, etc.)
* @returns {Promise<UserController>} The controller for the user
*/
async fetchUser(
identifier: Partial<{
id: string;
email: string;
login: string;
userId: number;
}>
) {
if (Object.keys(identifier).length == 0) return null;
const userData = await prisma.user.findFirst({
where: {
//@ts-ignore
OR: Object.keys(identifier).map((v) => ({ [v]: identifier[v] })),
},
include: { roles: true },
});
if (!userData) return null;
return new UserController(userData);
},
/**
* Create a new user
*
* This method will poll GitHub and get all the information on the user to then create the
* record in our database. On top of that it also pushes it into the user cache.
*
* @param token - The Github token provided by the auth method
* @returns {Promise<UserController>} The new user controller for the user
*/
async createUser(token: string): Promise<UserController> {
const ghUser = await (
await ghApp.oauth.getUserOctokit({ token })
).request("GET /user");
const emails = await (
await ghApp.oauth.getUserOctokit({ token })
).request("GET /user/emails");
const newUser = await prisma.user.create({
data: {
userId: ghUser.data.id,
email: emails.data[0].email,
image: ghUser.data.avatar_url,
name: ghUser.data.name,
login: ghUser.data.login,
token,
},
include: { roles: true },
});
let controller = new UserController(newUser);
events.emit("user:created", controller);
return controller;
},
};
/**
* @TODO Figure out default permissions
*/
+56
View File
@@ -0,0 +1,56 @@
import { ZodError } from "zod";
/**
* @ignore
*/
export const apiResponse = {
successful: (message: string, data?: any) => ({
status: 200,
message,
data,
successful: true,
meta: {
timestamp: Date.now(),
},
}),
created: (message: string, data?: any) => ({
status: 201,
message,
data,
successful: true,
meta: {
timestamp: Date.now(),
},
}),
error: (err: Error) => ({
// @ts-ignore
status: err["status"] ?? 400,
message: err.message,
error: err.name,
successful: false,
meta: {
timestamp: Date.now(),
},
}),
internalError: () => ({
status: 500,
message: "An Internal Server Error has occured...",
error: "InternalServerError",
successful: false,
meta: {
timestamp: Date.now(),
},
}),
zodError: (err: ZodError) => {
const data = JSON.parse(err.message);
return {
status: 400,
message: "TypeError",
error: data,
successful: false,
meta: {
timestamp: Date.now(),
},
};
},
};
+40
View File
@@ -0,0 +1,40 @@
import { Handler, Hono, MiddlewareHandler } from "hono";
import { Variables } from "../../types/HonoTypes";
/**
* Create a route.
*
* This method exists to serve the purpose of allowing us to split all of our api routes into different files and
* easily and quickly be able to rope them back into the main api server instance.
*
* One of the sortfallings of this method is that I was not able to figure out how to integrate the middleware to come
* before the handler, so if somebody feels upto it please figure out a way to have the middleware come before the handler
* method naturally as you would if you using a plain hono method.
*
* @TODO Move middleware handlers to come before primary handler naturally.
*
* @param method - HTTP Method
* @param path - URL Path
* @param handler - Handler function for Hono
* @param middleware - Array of Middleware Handlers for Hono
* @returns {Hono} - A new Hono instance containing the newly created route.
*/
export function createRoute(
method: string | string[],
path: string[],
handler: Handler<{
Variables: Variables;
}>,
...middleware: MiddlewareHandler<{
Variables: Variables;
}>[]
): Hono<{ Variables: Variables }> {
if (middleware)
return new Hono<{ Variables: Variables }>().on(
method as any,
path,
...middleware,
handler
);
return new Hono<{ Variables: Variables }>().on(method as any, path, handler);
}
+64
View File
@@ -0,0 +1,64 @@
import { Eventra } from "@duxcore/eventra";
import UserController from "../controllers/UserController";
import {
SessionController,
SessionTokensObject,
} from "../controllers/SessionController";
import { RoleController } from "../controllers/RoleController";
import { JsonWebTokenError } from "jsonwebtoken";
import { User } from "../../generated/prisma/client";
interface EventTypes {
"api:started": () => void;
"user:created": (user: UserController) => void;
"user:updated": (data: {
user: UserController;
updatedValues: Partial<User>;
}) => void;
"user:authenticated": (data: {
user: UserController;
tokens: SessionTokensObject;
}) => void;
"session:created": (data: {
user: UserController;
session: SessionController;
}) => void;
"session:tokens_generated": (data: {
session: SessionController;
tokens: SessionTokensObject;
}) => void;
"session:token_refresh": (data: {
session: SessionController;
tokens: SessionTokensObject;
}) => void;
"session:invalidated": (session: SessionController) => void;
"session:terminated": (session: SessionController) => void;
"role:created": (role: RoleController) => void;
"role:deleted": (role: RoleController) => void;
"role:updated": (data: {
role: RoleController;
updateData: Parameters<typeof RoleController.prototype.update>["0"];
}) => void;
"role:permissions:updated": (data: {
previous: string[];
previousSigned: string;
current: string[];
currentSigned: string;
action: "set" | "added" | "removed";
role: RoleController;
}) => void;
"role:permissions:verification_error": (data: {
currentSigned: string;
attemptedVerification: string;
err: Error;
role: RoleController;
}) => void;
}
export const events = new Eventra<EventTypes>();
export function setupEventDebugger() {
events.any((eventName, ...args) => {
console.log(`[ Event Debugger ] (${eventName})`);
});
}
@@ -0,0 +1,9 @@
export function genImplicitPerm(
resource: string,
resourceId: string,
userId: string
) {
return ["resource", resource, resourceId, "user", userId, "implicit"].join(
"."
);
}
@@ -0,0 +1,53 @@
/**
* Permission Validator
*
* This method is used for validaing user and role permissions. This method is given a single or and array
* of permission nodes that the user has and it is also given the permission node that is required for whatever
* query they are trying to execute, and this will determine if any of the permission nodes match or will
* verify the given permission node.
*
* Special token types:
* - Asterisk (*): verifies it's token and all following tokens.
* - Question Mark (?): verifies it's token and only it's token.
* - Inclusive List ([a,b,c]): verifies only the tokens in the list.
* - Exclusive List (<a,b,c>): verifies all tokens except for the ones in the list.
*
* @param permission - The required permission
* @param permissionExpressions - The owned permission(s)
* @returns {boolean} Does the user have the permission?
*/
export function permissionValidator(
permission: string,
permissionExpressions: string | string[]
): boolean {
if (typeof permissionExpressions === "string") {
// If the second parameter is a string, treat it as a single expression
permissionExpressions = [permissionExpressions];
}
// Iterate over each expression in the array and check if any of them match the permission
for (const expression of permissionExpressions) {
const rx = expression
.replace(/\./g, "\\.")
.replace(/\*/g, ".*")
.replace(/\?/g, ".")
.replace(/\[([^\]\[]*)\]/g, "($1)")
.replace(/<([^<>]+)>/g, "(?:(?!$1)[^.])*")
.replace(/,/g, "|");
if (new RegExp(`^${rx}$`).test(permission)) {
return true;
}
}
return false;
}
/**
* @TODO It's okay, you can't always get everything done and that is fine.
* Just take a breath and move on, come back if you feel upto it.
* What you make is good and whilst you can always do more,
* you can't do everything. Nothing will ever be perfect,
* so stop trying to be perfect and allow your self to move on
* even if you know there is more you can do.
*/
@@ -0,0 +1,24 @@
import jwt from "jsonwebtoken";
import { permissionsPrivateKey } from "../../constants";
import { PermissionIssuers } from "../../types/PermissionTypes";
/**
* Sign Permissions
*
* This will sign the array of permissions with the private key for permissions, and then return
* a JWT which will be stored in the databse.
*
* @param permissions - All the permissions to be signed
* @returns {string} - The signed permissions object
*/
export function signPermissions(data: {
issuer: PermissionIssuers;
subject: string;
permissions: string[];
}) {
return jwt.sign({ permissions: data.permissions }, permissionsPrivateKey, {
algorithm: "RS256",
issuer: data.issuer,
subject: data.subject,
});
}
+34
View File
@@ -0,0 +1,34 @@
import { blake2sHex } from "blakets";
import crypto from "crypto";
export default class Password {
public static generateSalt(options?: GenerateSaltOptions): string {
const length = options?.length ?? 12;
const randomBytes = crypto.randomBytes(Math.ceil(length / 2));
return randomBytes.toString("hex").slice(0, length);
}
public static hash(password: string, options?: HashPasswordOptions): string {
const salt =
options?.overrideSalt ?? Password.generateSalt(options?.saltOpts);
const hash = blake2sHex(`$BLAKE2s$${password}$${salt}`);
return `BLAKE2s$${hash}$${salt}`;
}
public static validate(newPass: string, hashed: string): boolean {
const [algo, oldHash, salt] = hashed.split(/\$/g);
return crypto.timingSafeEqual(
Buffer.from(hashed),
Buffer.from(Password.hash(newPass, { overrideSalt: salt }))
);
}
}
export interface HashPasswordOptions {
overrideSalt?: string;
saltOpts?: GenerateSaltOptions;
}
export interface GenerateSaltOptions {
length?: number; // default 12
}
+8
View File
@@ -0,0 +1,8 @@
export const mergeArrays = (a, b, predicate = (a, b) => a === b) => {
const c = [...a]; // copy to avoid side effects
// add all items from B to copy C if they're not already present
b.forEach((bItem) =>
c.some((cItem) => predicate(bItem, cItem)) ? null : c.push(bItem)
);
return c;
};
+3
View File
@@ -0,0 +1,3 @@
export type Variables = {
foo: "bar"
};
+7
View File
@@ -0,0 +1,7 @@
export type PermissionIssuers = "roles" | "user" | "api_key";
export interface DecodedPermissionsBlock {
permissions: string[];
iat: number; // Issued at
iss: PermissionIssuers; // Issuer
sub: string; // Subject
}
+1 -1
View File
@@ -11,7 +11,7 @@
// Bundler mode // Bundler mode
"moduleResolution": "bundler", "moduleResolution": "bundler",
"allowImportingTsExtensions": true, "allowImportingTsExtensions": true,
"verbatimModuleSyntax": true, "verbatimModuleSyntax": false,
"noEmit": true, "noEmit": true,
// Best practices // Best practices
+37
View File
@@ -0,0 +1,37 @@
import keypair from "keypair";
console.log(`
Generating Private Keys
-----------------
This script will go through and genrate all the keys necessary for running the Credential Manager API locally.
This process might take several minutes.
-----------------`);
await Promise.all(
[
".accessToken.key",
".refreshToken.key",
".permissions.key",
".apiKeyToken.key",
].map(async (v) => {
if (
await Bun.file(v)
.exists()
.then((bool) => {
if (bool) {
console.log(`'${v}' already exists`);
return false;
}
return true;
})
) {
console.log(`Generating '${v}'...`);
const keys = keypair({ bits: 4096 });
await Bun.write(v, keys.private);
}
return;
})
);
console.log("\nGenerated All Keys Successfully!");