From 1a5577f8e6d443a8c272bdb41b32d9df457c18bf Mon Sep 17 00:00:00 2001 From: Ly Tuan Kiet Date: Sat, 2 Nov 2024 16:27:28 +0700 Subject: [PATCH] fix some bug and produce many problem to solve later --- package-lock.json | 492 +++++++++++++++------------- package.json | 7 +- src/AppConfig/appconfig.constant.ts | 20 +- src/AppConfig/appconfig.service.ts | 18 +- src/Graphql/graphql.builder.ts | 39 ++- src/Graphql/graphql.module.ts | 60 +++- src/Graphql/graphql.service.ts | 8 +- src/Message/message.schema.ts | 17 +- src/Realtime/realtime.module.ts | 8 + src/Realtime/realtime.service.ts | 7 + src/Schedule/schedule.d.ts | 65 ++++ src/Schedule/schedule.schema.ts | 70 ++-- src/Schedule/schedule.service.ts | 295 +++++++---------- src/common/utils/datetime.utils.ts | 141 ++++++-- src/common/utils/string.utils.ts | 13 + 15 files changed, 751 insertions(+), 509 deletions(-) create mode 100644 src/Realtime/realtime.module.ts create mode 100644 src/Realtime/realtime.service.ts create mode 100644 src/Schedule/schedule.d.ts create mode 100644 src/common/utils/string.utils.ts diff --git a/package-lock.json b/package-lock.json index 4e4d79f..f1adfce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,11 +24,11 @@ "@nestjs/jwt": "^10.2.0", "@nestjs/platform-express": "^10.0.0", "@nestjs/swagger": "^7.4.2", - "@pothos/core": "^4.2.0", + "@pothos/core": "^4.3.0", "@pothos/plugin-add-graphql": "^4.1.0", "@pothos/plugin-authz": "^3.5.10", "@pothos/plugin-errors": "^4.2.0", - "@pothos/plugin-prisma": "^4.2.1", + "@pothos/plugin-prisma": "^4.3.0", "@pothos/plugin-prisma-utils": "^1.2.0", "@pothos/plugin-relay": "^4.3.0", "@pothos/plugin-scope-auth": "^4.1.0", @@ -59,7 +59,8 @@ "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1", "swagger-ui-express": "^5.0.1", - "type-graphql": "^2.0.0-rc.2" + "type-graphql": "^2.0.0-rc.2", + "yjs": "^13.6.20" }, "devDependencies": { "@biomejs/biome": "1.9.4", @@ -360,9 +361,9 @@ } }, "node_modules/@apollo/server": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@apollo/server/-/server-4.11.0.tgz", - "integrity": "sha512-SWDvbbs0wl2zYhKG6aGLxwTJ72xpqp0awb2lotNpfezd9VcAvzaUizzKQqocephin2uMoaA8MguoyBmgtPzNWw==", + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/@apollo/server/-/server-4.11.2.tgz", + "integrity": "sha512-WUTHY7DDek8xAMn4Woa9Bl8duQUDzRYQkosX/d1DtCsBWESZyApR7ndnI5d6+W4KSTtqBHhJFkusEI7CWuIJXg==", "license": "MIT", "dependencies": { "@apollo/cache-control-types": "^1.0.3", @@ -381,7 +382,7 @@ "@types/node-fetch": "^2.6.1", "async-retry": "^1.2.1", "cors": "^2.8.5", - "express": "^4.17.1", + "express": "^4.21.1", "loglevel": "^1.6.8", "lru-cache": "^7.10.1", "negotiator": "^0.6.3", @@ -753,9 +754,9 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.0.tgz", - "integrity": "sha512-INCKxTtbXtcNbUZ3YXutwMpEleqttcswhAdee7dhuoVrD2cnuc3PqtERBtxkX5nziX9vnBL8WXmSGwv8CuPV6g==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", @@ -767,9 +768,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.0.tgz", - "integrity": "sha512-qETICbZSLe7uXv9VE8T/RWOdIE5qqyTucOt4zLYMafj2MRO271VGgLd4RACJMeBO37UPWhXiKMBk7YlJ0fOzQA==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", + "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -806,12 +807,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.0.tgz", - "integrity": "sha512-/AIkAmInnWwgEAJGQr9vY0c66Mj6kjkE2ZPB1PurTRaRAh3U+J45sAQMjQDJdh4WbR3l0x5xkimXBKyBXXAu2w==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", + "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.0", + "@babel/parser": "^7.26.2", "@babel/types": "^7.26.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", @@ -1027,9 +1028,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.26.1", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.1.tgz", - "integrity": "sha512-reoQYNiAJreZNsJzyrDNzFQ+IQ5JFiIzAHJg9bn94S3l+4++J7RsIhNMoB+lgP/9tpmiAQqspv+xfdxTSzREOw==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", "license": "MIT", "dependencies": { "@babel/types": "^7.26.0" @@ -1910,13 +1911,13 @@ } }, "node_modules/@clerk/backend": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/@clerk/backend/-/backend-1.15.1.tgz", - "integrity": "sha512-yoBCji0bJFn2bUxBOO0+6XmlN6Tb5M2CiW+DAX7V3pFQ7g7DnHjSZ/LVkt9yB0AmqHKPv1ISXWM/NFYSDBRVuA==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@clerk/backend/-/backend-1.15.2.tgz", + "integrity": "sha512-EuurgNAizmM25BQYmPYtaCpbgROfsNtfqHX6AhlgmBwtVtKWfPJcmLTGwOuwVWxZjeVdaNdEPq4aKp2rYZl9CA==", "license": "MIT", "dependencies": { - "@clerk/shared": "2.10.1", - "@clerk/types": "4.28.0", + "@clerk/shared": "2.11.0", + "@clerk/types": "4.29.0", "cookie": "0.7.0", "snakecase-keys": "5.4.4", "tslib": "2.4.1" @@ -1926,14 +1927,14 @@ } }, "node_modules/@clerk/express": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@clerk/express/-/express-1.3.3.tgz", - "integrity": "sha512-PsXNt10bZrCm/RpEaVYkd7xF3H4G+PglZRQ3cdcawm+v3K6g+34N4+Uo5sibsrMphJY4CwfyiOsWrzVqzsfRxA==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@clerk/express/-/express-1.3.4.tgz", + "integrity": "sha512-IiFLDjhG/+DN8olBoTQ0VD2meDHEM8DTMI/RCOKWQSkribFeypmdc+DvC++UDiTFQ2kPD1XBoqkFjWV4NdrHGg==", "license": "MIT", "dependencies": { - "@clerk/backend": "^1.15.1", - "@clerk/shared": "^2.10.1", - "@clerk/types": "4.28.0", + "@clerk/backend": "^1.15.2", + "@clerk/shared": "^2.11.0", + "@clerk/types": "4.29.0", "tslib": "2.4.1" }, "engines": { @@ -1944,13 +1945,13 @@ } }, "node_modules/@clerk/shared": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/@clerk/shared/-/shared-2.10.1.tgz", - "integrity": "sha512-9dPuCcTd2qaK+YU9BiO5mPPnet9B38ZSp0gutnaUQmve9013qO0p9Lx7ympiPSulwkTG4NAfYxjr/pyIUUFqCQ==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@clerk/shared/-/shared-2.11.0.tgz", + "integrity": "sha512-JW6DVXrvZPb0edFH5ZY+yeexeEzIFsXeh+/BYD40AqiVW2KLwAjpH9SiVaiWwVf9LuKZz9ei+5opnBu9iRG6IQ==", "hasInstallScript": true, "license": "MIT", "dependencies": { - "@clerk/types": "4.28.0", + "@clerk/types": "4.29.0", "glob-to-regexp": "0.4.1", "js-cookie": "3.0.5", "std-env": "^3.7.0", @@ -1973,9 +1974,9 @@ } }, "node_modules/@clerk/types": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@clerk/types/-/types-4.28.0.tgz", - "integrity": "sha512-RPdrUs8HYfhXaZ0MOVBkzy7lilsU9lDVSC88a5o/cEMmTML+BTDfLHMlLG81kgvagSLCKKbl28iocb8y7stm1Q==", + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/@clerk/types/-/types-4.29.0.tgz", + "integrity": "sha512-VSqxXWUewMrHeXUxd4pzou6288DFaMy+qUeRtld6oMnts9R/iUXE1qHgFvzRDgYBHBYmrviasubGh2KhvIFXUQ==", "license": "MIT", "dependencies": { "csstype": "3.1.1" @@ -2305,9 +2306,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.13.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.13.0.tgz", - "integrity": "sha512-IFLyoY4d72Z5y/6o/BazFBezupzI/taV8sGumxTAVw3lXG9A6md1Dc34T9s1FoD/an9pJH8RHbAxsaEbBed9lA==", + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.14.0.tgz", + "integrity": "sha512-pFoEtFWCPyDOl+C6Ift+wC7Ro89otjigCf5vcuWqWgqNSQbRrpjSvdeE6ofLz4dHmyxD5f7gIdGT4+p36L6Twg==", "dev": true, "license": "MIT", "engines": { @@ -2325,9 +2326,9 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.1.tgz", - "integrity": "sha512-HFZ4Mp26nbWk9d/BpvP0YNL6W4UoZF0VFcTw/aPPA8RpOxeFQgK+ClABGgAUXs9Y/RGX/l1vOmrqz1MQt9MNuw==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.2.tgz", + "integrity": "sha512-CXtq5nR4Su+2I47WPOlWud98Y5Lv8Kyxp2ukhgFx/eW6Blm18VXJO5WuQylPugRo8nbluoi6GvvxBLqHcvqUUw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2438,21 +2439,21 @@ } }, "node_modules/@graphql-codegen/client-preset": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@graphql-codegen/client-preset/-/client-preset-4.4.0.tgz", - "integrity": "sha512-Q0NHFK7KXLhEaRC/k82ge0dHDfeHJEvvDeV0vV3+oSurHNa/lpxQtbK2BqknZe+JDfZ1YOOvYT93XsAkYD+SQg==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@graphql-codegen/client-preset/-/client-preset-4.5.0.tgz", + "integrity": "sha512-0fFGSjpDhB7Jp6v+FQWDIeNJhL8VEiy3zeazyus3mGUELPaRQsoos2NczkDWnyMjSB1NHn4GrI53DB4TXkTAog==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.20.2", "@babel/template": "^7.20.7", "@graphql-codegen/add": "^5.0.3", - "@graphql-codegen/gql-tag-operations": "4.0.10", - "@graphql-codegen/plugin-helpers": "^5.0.4", - "@graphql-codegen/typed-document-node": "^5.0.10", - "@graphql-codegen/typescript": "^4.1.0", - "@graphql-codegen/typescript-operations": "^4.3.0", - "@graphql-codegen/visitor-plugin-common": "^5.4.0", + "@graphql-codegen/gql-tag-operations": "4.0.11", + "@graphql-codegen/plugin-helpers": "^5.1.0", + "@graphql-codegen/typed-document-node": "^5.0.11", + "@graphql-codegen/typescript": "^4.1.1", + "@graphql-codegen/typescript-operations": "^4.3.1", + "@graphql-codegen/visitor-plugin-common": "^5.5.0", "@graphql-tools/documents": "^1.0.0", "@graphql-tools/utils": "^10.0.0", "@graphql-typed-document-node/core": "3.2.0", @@ -2466,15 +2467,15 @@ } }, "node_modules/@graphql-codegen/client-preset/node_modules/@graphql-codegen/typescript": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-4.1.0.tgz", - "integrity": "sha512-/fS53Nh6U6c58GTOxqfyKTLQfQv36P8II/vPw/fg0cdcWbALhRPls69P8vXUWjrElmLKzCrdusBWPp/r+AKUBQ==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-4.1.1.tgz", + "integrity": "sha512-+o5LOT71K9hdO4lDVnRGkkET5RdlKvxlQGug8dZgRGrhE2/xoPBsKfLhg9AoJGYMauNZxKj3blABQxHOKEku6Q==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-codegen/plugin-helpers": "^5.0.4", + "@graphql-codegen/plugin-helpers": "^5.1.0", "@graphql-codegen/schema-ast": "^4.0.2", - "@graphql-codegen/visitor-plugin-common": "5.4.0", + "@graphql-codegen/visitor-plugin-common": "5.5.0", "auto-bind": "~4.0.0", "tslib": "~2.6.0" }, @@ -2552,14 +2553,14 @@ "license": "0BSD" }, "node_modules/@graphql-codegen/gql-tag-operations": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/@graphql-codegen/gql-tag-operations/-/gql-tag-operations-4.0.10.tgz", - "integrity": "sha512-WsBEVL3XQdBboFJJL5WxrUjkuo3B7Sa51R9NbT7PKBe0HCNstoouGZIvQJRUubttFCqTTyoFtNsoRSKB+rsRug==", + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/@graphql-codegen/gql-tag-operations/-/gql-tag-operations-4.0.11.tgz", + "integrity": "sha512-EUQuBsYB5RtNlzBb/O0nJvbWC8HvPRWwVTHRf0ElOoQlJfRgfDom2GWmEM5hXa2afzMqB7AWxOH24ibOqiYnMQ==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-codegen/plugin-helpers": "^5.0.4", - "@graphql-codegen/visitor-plugin-common": "5.4.0", + "@graphql-codegen/plugin-helpers": "^5.1.0", + "@graphql-codegen/visitor-plugin-common": "5.5.0", "@graphql-tools/utils": "^10.0.0", "auto-bind": "~4.0.0", "tslib": "~2.6.0" @@ -2599,9 +2600,9 @@ "license": "0BSD" }, "node_modules/@graphql-codegen/plugin-helpers": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@graphql-codegen/plugin-helpers/-/plugin-helpers-5.0.4.tgz", - "integrity": "sha512-MOIuHFNWUnFnqVmiXtrI+4UziMTYrcquljaI5f/T/Bc7oO7sXcfkAvgkNWEEi9xWreYwvuer3VHCuPI/lAFWbw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@graphql-codegen/plugin-helpers/-/plugin-helpers-5.1.0.tgz", + "integrity": "sha512-Y7cwEAkprbTKzVIe436TIw4w03jorsMruvCvu0HJkavaKMQbWY+lQ1RIuROgszDbxAyM35twB5/sUvYG5oW+yg==", "license": "MIT", "dependencies": { "@graphql-tools/utils": "^10.0.0", @@ -2611,6 +2612,9 @@ "lodash": "~4.17.0", "tslib": "~2.6.0" }, + "engines": { + "node": ">=16" + }, "peerDependencies": { "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" } @@ -2642,14 +2646,14 @@ "license": "0BSD" }, "node_modules/@graphql-codegen/typed-document-node": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/@graphql-codegen/typed-document-node/-/typed-document-node-5.0.10.tgz", - "integrity": "sha512-YPDUNs6x0muoVWlbY2yEs0lGxFHMTszlGDh6klT/5rqiTDTZg3zz8Wd1ZTihkcH8+V6T0AT9qDWwcx9fcS2tvQ==", + "version": "5.0.11", + "resolved": "https://registry.npmjs.org/@graphql-codegen/typed-document-node/-/typed-document-node-5.0.11.tgz", + "integrity": "sha512-btENKrSIUZ5UllS8sFhVZ+Y91VL0knK9gHxW/6/WzaCTxBQ+wOk07vQNeoWlvMrkl0QeUsGt6YvSo0SoPtsKdA==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-codegen/plugin-helpers": "^5.0.4", - "@graphql-codegen/visitor-plugin-common": "5.4.0", + "@graphql-codegen/plugin-helpers": "^5.1.0", + "@graphql-codegen/visitor-plugin-common": "5.5.0", "auto-bind": "~4.0.0", "change-case-all": "1.0.15", "tslib": "~2.6.0" @@ -2686,14 +2690,14 @@ } }, "node_modules/@graphql-codegen/typescript-operations": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript-operations/-/typescript-operations-4.3.0.tgz", - "integrity": "sha512-ZORwMy8OgsiYd9EZUhTMd4/g5LvTFpx6Fh6dNN0cxFkqSc6KhjX0vhzWsyK8N9+ILaHSutT8UTrLMdJi35HzDQ==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript-operations/-/typescript-operations-4.3.1.tgz", + "integrity": "sha512-yW5Iia6IK1VKiPm3oeukYMQN5pEBLwRlG8ZzQA9beeLQ8PskKyz6mjar6U7dJ2hc8pv/qT4R8kcJOQ2RloniAQ==", "license": "MIT", "dependencies": { - "@graphql-codegen/plugin-helpers": "^5.0.4", - "@graphql-codegen/typescript": "^4.1.0", - "@graphql-codegen/visitor-plugin-common": "5.4.0", + "@graphql-codegen/plugin-helpers": "^5.1.0", + "@graphql-codegen/typescript": "^4.1.1", + "@graphql-codegen/visitor-plugin-common": "5.5.0", "auto-bind": "~4.0.0", "tslib": "~2.6.0" }, @@ -2705,14 +2709,14 @@ } }, "node_modules/@graphql-codegen/typescript-operations/node_modules/@graphql-codegen/typescript": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-4.1.0.tgz", - "integrity": "sha512-/fS53Nh6U6c58GTOxqfyKTLQfQv36P8II/vPw/fg0cdcWbALhRPls69P8vXUWjrElmLKzCrdusBWPp/r+AKUBQ==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-4.1.1.tgz", + "integrity": "sha512-+o5LOT71K9hdO4lDVnRGkkET5RdlKvxlQGug8dZgRGrhE2/xoPBsKfLhg9AoJGYMauNZxKj3blABQxHOKEku6Q==", "license": "MIT", "dependencies": { - "@graphql-codegen/plugin-helpers": "^5.0.4", + "@graphql-codegen/plugin-helpers": "^5.1.0", "@graphql-codegen/schema-ast": "^4.0.2", - "@graphql-codegen/visitor-plugin-common": "5.4.0", + "@graphql-codegen/visitor-plugin-common": "5.5.0", "auto-bind": "~4.0.0", "tslib": "~2.6.0" }, @@ -2806,12 +2810,12 @@ "license": "0BSD" }, "node_modules/@graphql-codegen/visitor-plugin-common": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-5.4.0.tgz", - "integrity": "sha512-tL7hOrO+4MiNfDiHewhRQCiH9GTAh0M9Y/BZxYGGEdnrfGgqK5pCxtjq7EY/L19VGIyU7hhzYTQ0r1HzEbB4Jw==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-5.5.0.tgz", + "integrity": "sha512-FSkxe/o4qKbpK+ipIT/jxZLYH0+3+XdIrJWsKlCW9wwJMF9mEJLJtzZNcxHSjz7+Eny6SUElAT2dqZ5XByxkog==", "license": "MIT", "dependencies": { - "@graphql-codegen/plugin-helpers": "^5.0.4", + "@graphql-codegen/plugin-helpers": "^5.1.0", "@graphql-tools/optimize": "^2.0.0", "@graphql-tools/relay-operation-optimizer": "^7.0.0", "@graphql-tools/utils": "^10.0.0", @@ -2894,9 +2898,9 @@ } }, "node_modules/@graphql-tools/delegate": { - "version": "10.0.27", - "resolved": "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-10.0.27.tgz", - "integrity": "sha512-cHz9d+RoW7I4nlxhv5JBf8g88YMkJsWMvFJqM+XSyPEOCjivw4UaXotcid4Y9gfCJY50yfGbbECXLiystAXdWA==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-10.1.1.tgz", + "integrity": "sha512-Ee2olw3MGpH9KDrQo0KDn7+oxOf8mrq17aCFojsnumGyUaD33LyKn7Gl2bjwEhXa7PN0dEJQhxSaRPyNtCKzCw==", "dev": true, "license": "MIT", "dependencies": { @@ -2906,6 +2910,7 @@ "@graphql-tools/utils": "^10.5.5", "@repeaterjs/repeater": "^3.0.6", "dataloader": "^2.2.2", + "dset": "^3.1.2", "tslib": "^2.5.0" }, "engines": { @@ -2952,9 +2957,9 @@ } }, "node_modules/@graphql-tools/delegate/node_modules/tslib": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", - "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true, "license": "0BSD" }, @@ -3305,13 +3310,13 @@ } }, "node_modules/@graphql-tools/prisma-loader": { - "version": "8.0.10", - "resolved": "https://registry.npmjs.org/@graphql-tools/prisma-loader/-/prisma-loader-8.0.10.tgz", - "integrity": "sha512-My0CM1WPVyrxtTSGtp5M2JYa74Lj2CZLrsS54qHbfypb74dkZEevtW72Fpe2HglPINsiHGLGm/v5xvliGoGlZQ==", + "version": "8.0.15", + "resolved": "https://registry.npmjs.org/@graphql-tools/prisma-loader/-/prisma-loader-8.0.15.tgz", + "integrity": "sha512-kqmqGpE7DqDWLK7RsHpX7ckDqKcGWi5xWOzLgZwWXtgPQIJ/D50R9e6xIr6FpkeL9KYa+DJ8A91WPnwKCqYe/w==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/url-loader": "^8.0.8", + "@graphql-tools/url-loader": "^8.0.13", "@graphql-tools/utils": "^10.5.5", "@types/js-yaml": "^4.0.0", "@whatwg-node/fetch": "^0.9.0", @@ -3381,19 +3386,18 @@ } }, "node_modules/@graphql-tools/url-loader": { - "version": "8.0.8", - "resolved": "https://registry.npmjs.org/@graphql-tools/url-loader/-/url-loader-8.0.8.tgz", - "integrity": "sha512-xgNevPZUF180CAS0QRzWB+PPRG6Qszx+7+5TuOz/VGyZnhrCtorElPF4h/mZeMPR14u13zsMTg1jo4EJ3FNWOA==", + "version": "8.0.13", + "resolved": "https://registry.npmjs.org/@graphql-tools/url-loader/-/url-loader-8.0.13.tgz", + "integrity": "sha512-O7RwIh8Iv60epiV/Smnu3wWQddGEbz2W5sLTF4gW/4/23OLaQIAwR0E8MvOneXPQ5MScbUKXeFmyw97vve10qw==", "dev": true, "license": "MIT", "dependencies": { "@ardatan/sync-fetch": "^0.0.1", - "@graphql-tools/delegate": "^10.0.27", "@graphql-tools/executor-graphql-ws": "^1.3.1", "@graphql-tools/executor-http": "^1.1.7", "@graphql-tools/executor-legacy-ws": "^1.1.1", "@graphql-tools/utils": "^10.5.5", - "@graphql-tools/wrap": "^10.0.11", + "@graphql-tools/wrap": "^10.0.15", "@types/ws": "^8.0.0", "@whatwg-node/fetch": "^0.9.0", "isomorphic-ws": "^5.0.0", @@ -3427,13 +3431,13 @@ } }, "node_modules/@graphql-tools/wrap": { - "version": "10.0.11", - "resolved": "https://registry.npmjs.org/@graphql-tools/wrap/-/wrap-10.0.11.tgz", - "integrity": "sha512-NeINmsDUnonj1J/5kQK8PfGLOSBjn0igw2H9C3GpV93kVuHXNNXACOQ4qP0ATouw7p1IEWwEZQJ3XMAU+nASqQ==", + "version": "10.0.15", + "resolved": "https://registry.npmjs.org/@graphql-tools/wrap/-/wrap-10.0.15.tgz", + "integrity": "sha512-HeR7q0kGAEtbewymnA2Kpqc39q6uUDFx3CNNG552TztJr7uuYu8Wte/4Rcb00CzW1D65JsmfwTksbnc/vs9HmQ==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/delegate": "^10.0.27", + "@graphql-tools/delegate": "^10.1.1", "@graphql-tools/schema": "^10.0.7", "@graphql-tools/utils": "^10.5.5", "tslib": "^2.4.0", @@ -3507,9 +3511,9 @@ } }, "node_modules/@graphql-yoga/subscription/node_modules/tslib": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", - "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, "node_modules/@graphql-yoga/typed-event-target": { @@ -3526,9 +3530,9 @@ } }, "node_modules/@graphql-yoga/typed-event-target/node_modules/tslib": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", - "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, "node_modules/@humanwhocodes/module-importer": { @@ -4957,9 +4961,9 @@ } }, "node_modules/@pothos/plugin-add-graphql": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@pothos/plugin-add-graphql/-/plugin-add-graphql-4.2.0.tgz", - "integrity": "sha512-jR0SNd8QgiaWVBEHRk9PQ3YqDXyebZkqeLMb8ifRDSNq/RklZURjp0l9oWIsPY0vHYddvostcMnaPXU+J0xgAQ==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@pothos/plugin-add-graphql/-/plugin-add-graphql-4.2.1.tgz", + "integrity": "sha512-Q2nRvA0iJHIqOf3GnzmZeHQUSIW3EkUstLIdZBE0rLhnxC/9/opwYbWJSGVcqx+OpAMjsrh/lA7ckcxTXZaw1g==", "license": "ISC", "peerDependencies": { "@pothos/core": "*", @@ -4987,9 +4991,9 @@ } }, "node_modules/@pothos/plugin-prisma": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@pothos/plugin-prisma/-/plugin-prisma-4.3.0.tgz", - "integrity": "sha512-OCJYqJ1PCH7pPfqSyedzKEwr9WoJSzo2FMLCbznHv4BsNvhlLCTKhU/Zuwveq/cGBP3pk0YC8u9D2xtvwt9drg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@pothos/plugin-prisma/-/plugin-prisma-4.3.1.tgz", + "integrity": "sha512-5maJe2o8VCBye5m/fXG+TsvQzhEmF9YpXnLtR9/3T8Z3H51VgthU60xZoN8N+oMKFOqOYEMX1i21J/ojXrTj9A==", "license": "ISC", "dependencies": { "@prisma/generator-helper": "^5.19.1" @@ -5276,14 +5280,14 @@ } }, "node_modules/@stylistic/eslint-plugin-js": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-2.9.0.tgz", - "integrity": "sha512-h08DQybPsXxIvHIvQqU1tFWcu74M7kZK/0S0jVIDdoHSFq7jB+TzxikBWAg5j0lPR17WsGGGHAS8GHFlAAQXHA==", + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-2.10.1.tgz", + "integrity": "sha512-IikL/RKy9Sk2UMDUUpqrEcwDeYzUEt6SaL2/UVCFuVQxKACHSgStT0NxXkxZmBOUforaU52FPf2Su07FYH5s5g==", "dev": true, "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^4.1.0", - "espree": "^10.2.0" + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5588,9 +5592,9 @@ "optional": true }, "node_modules/@types/node": { - "version": "20.17.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.1.tgz", - "integrity": "sha512-j2VlPv1NnwPJbaCNv69FO/1z4lId0QmGvpT41YxitRtWlg96g/j8qcv2RKsLKe2F6OJgyXhupN1Xo17b2m139Q==", + "version": "20.17.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.5.tgz", + "integrity": "sha512-n8FYY/pRxu496441gIcAQFZPKXbhsd6VZygcq+PTSZ75eMh/Ke0hCAROdUa21qiFqKNsPPYic46yXDO1JGiPBQ==", "license": "MIT", "dependencies": { "undici-types": "~6.19.2" @@ -5623,9 +5627,9 @@ "license": "MIT" }, "node_modules/@types/passport": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.16.tgz", - "integrity": "sha512-FD0qD5hbPWQzaM0wHUnJ/T0BBCJBxCeemtnCwc/ThhTg3x9jfrAcRUmj5Dopza+MfFS9acTe3wk7rcVnRIp/0A==", + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.17.tgz", + "integrity": "sha512-aciLyx+wDwT2t2/kJGJR2AEeBz0nJU4WuRX04Wu9Dqc5lSUtwu0WERPHYsLhF9PtseiAMPBGNUOtFjxZ56prsg==", "dev": true, "license": "MIT", "dependencies": { @@ -5765,17 +5769,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.11.0.tgz", - "integrity": "sha512-KhGn2LjW1PJT2A/GfDpiyOfS4a8xHQv2myUagTM5+zsormOmBlYsnQ6pobJ8XxJmh6hnHwa2Mbe3fPrDJoDhbA==", + "version": "8.12.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.12.2.tgz", + "integrity": "sha512-gQxbxM8mcxBwaEmWdtLCIGLfixBMHhQjBqR8sVWNTPpcj45WlYL2IObS/DNMLH1DBP0n8qz+aiiLTGfopPEebw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.11.0", - "@typescript-eslint/type-utils": "8.11.0", - "@typescript-eslint/utils": "8.11.0", - "@typescript-eslint/visitor-keys": "8.11.0", + "@typescript-eslint/scope-manager": "8.12.2", + "@typescript-eslint/type-utils": "8.12.2", + "@typescript-eslint/utils": "8.12.2", + "@typescript-eslint/visitor-keys": "8.12.2", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -5799,16 +5803,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.11.0.tgz", - "integrity": "sha512-lmt73NeHdy1Q/2ul295Qy3uninSqi6wQI18XwSpm8w0ZbQXUpjCAWP1Vlv/obudoBiIjJVjlztjQ+d/Md98Yxg==", + "version": "8.12.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.12.2.tgz", + "integrity": "sha512-MrvlXNfGPLH3Z+r7Tk+Z5moZAc0dzdVjTgUgwsdGweH7lydysQsnSww3nAmsq8blFuRD5VRlAr9YdEFw3e6PBw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "8.11.0", - "@typescript-eslint/types": "8.11.0", - "@typescript-eslint/typescript-estree": "8.11.0", - "@typescript-eslint/visitor-keys": "8.11.0", + "@typescript-eslint/scope-manager": "8.12.2", + "@typescript-eslint/types": "8.12.2", + "@typescript-eslint/typescript-estree": "8.12.2", + "@typescript-eslint/visitor-keys": "8.12.2", "debug": "^4.3.4" }, "engines": { @@ -5828,14 +5832,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.11.0.tgz", - "integrity": "sha512-Uholz7tWhXmA4r6epo+vaeV7yjdKy5QFCERMjs1kMVsLRKIrSdM6o21W2He9ftp5PP6aWOVpD5zvrvuHZC0bMQ==", + "version": "8.12.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.12.2.tgz", + "integrity": "sha512-gPLpLtrj9aMHOvxJkSbDBmbRuYdtiEbnvO25bCMza3DhMjTQw0u7Y1M+YR5JPbMsXXnSPuCf5hfq0nEkQDL/JQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.11.0", - "@typescript-eslint/visitor-keys": "8.11.0" + "@typescript-eslint/types": "8.12.2", + "@typescript-eslint/visitor-keys": "8.12.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5846,14 +5850,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.11.0.tgz", - "integrity": "sha512-ItiMfJS6pQU0NIKAaybBKkuVzo6IdnAhPFZA/2Mba/uBjuPQPet/8+zh5GtLHwmuFRShZx+8lhIs7/QeDHflOg==", + "version": "8.12.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.12.2.tgz", + "integrity": "sha512-bwuU4TAogPI+1q/IJSKuD4shBLc/d2vGcRT588q+jzayQyjVK2X6v/fbR4InY2U2sgf8MEvVCqEWUzYzgBNcGQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.11.0", - "@typescript-eslint/utils": "8.11.0", + "@typescript-eslint/typescript-estree": "8.12.2", + "@typescript-eslint/utils": "8.12.2", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -5871,9 +5875,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.11.0.tgz", - "integrity": "sha512-tn6sNMHf6EBAYMvmPUaKaVeYvhUsrE6x+bXQTxjQRp360h1giATU0WvgeEys1spbvb5R+VpNOZ+XJmjD8wOUHw==", + "version": "8.12.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.12.2.tgz", + "integrity": "sha512-VwDwMF1SZ7wPBUZwmMdnDJ6sIFk4K4s+ALKLP6aIQsISkPv8jhiw65sAK6SuWODN/ix+m+HgbYDkH+zLjrzvOA==", "dev": true, "license": "MIT", "engines": { @@ -5885,14 +5889,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.11.0.tgz", - "integrity": "sha512-yHC3s1z1RCHoCz5t06gf7jH24rr3vns08XXhfEqzYpd6Hll3z/3g23JRi0jM8A47UFKNc3u/y5KIMx8Ynbjohg==", + "version": "8.12.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.12.2.tgz", + "integrity": "sha512-mME5MDwGe30Pq9zKPvyduyU86PH7aixwqYR2grTglAdB+AN8xXQ1vFGpYaUSJ5o5P/5znsSBeNcs5g5/2aQwow==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "8.11.0", - "@typescript-eslint/visitor-keys": "8.11.0", + "@typescript-eslint/types": "8.12.2", + "@typescript-eslint/visitor-keys": "8.12.2", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -5953,16 +5957,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.11.0.tgz", - "integrity": "sha512-CYiX6WZcbXNJV7UNB4PLDIBtSdRmRI/nb0FMyqHPTQD1rMjA0foPLaPUV39C/MxkTd/QKSeX+Gb34PPsDVC35g==", + "version": "8.12.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.12.2.tgz", + "integrity": "sha512-UTTuDIX3fkfAz6iSVa5rTuSfWIYZ6ATtEocQ/umkRSyC9O919lbZ8dcH7mysshrCdrAM03skJOEYaBugxN+M6A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.11.0", - "@typescript-eslint/types": "8.11.0", - "@typescript-eslint/typescript-estree": "8.11.0" + "@typescript-eslint/scope-manager": "8.12.2", + "@typescript-eslint/types": "8.12.2", + "@typescript-eslint/typescript-estree": "8.12.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5976,13 +5980,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.11.0.tgz", - "integrity": "sha512-EaewX6lxSjRJnc+99+dqzTeoDZUfyrA52d2/HRrkI830kgovWsmIiTfmr0NZorzqic7ga+1bS60lRBUgR3n/Bw==", + "version": "8.12.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.12.2.tgz", + "integrity": "sha512-PChz8UaKQAVNHghsHcPyx1OMHoFRUEA7rJSK/mDhdq85bk+PLsUHUBqTQTFt18VJZbmxBovM65fezlheQRsSDA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.11.0", + "@typescript-eslint/types": "8.12.2", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -6180,9 +6184,9 @@ } }, "node_modules/@whatwg-node/events/node_modules/tslib": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", - "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, "node_modules/@whatwg-node/fetch": { @@ -6216,9 +6220,9 @@ } }, "node_modules/@whatwg-node/node-fetch/node_modules/tslib": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", - "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true, "license": "0BSD" }, @@ -7648,9 +7652,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001673", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001673.tgz", - "integrity": "sha512-WTrjUCSMp3LYX0nE12ECkV0a+e6LC85E0Auz75555/qr78Oc8YWhEPNfDd6SHdtlCMSzqtuXY0uyEMNRcsKpKw==", + "version": "1.0.30001676", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001676.tgz", + "integrity": "sha512-Qz6zwGCiPghQXGJvgQAem79esjitvJ+CxSbSQkW9H/UX5hg8XM88d4lp2W+MEQ81j+Hip58Il+jGVdazk1z9cw==", "funding": [ { "type": "opencollective", @@ -8890,9 +8894,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.47", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.47.tgz", - "integrity": "sha512-zS5Yer0MOYw4rtK2iq43cJagHZ8sXN0jDHDKzB+86gSBSAI4v07S97mcq+Gs2vclAxSh1j7vOAHxSVgduiiuVQ==", + "version": "1.5.50", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.50.tgz", + "integrity": "sha512-eMVObiUQ2LdgeO1F/ySTXsvqvxb6ZH2zPGaMYsWzRDdOddUa77tdmI0ltg+L16UpbWdhPmuF3wIQYyQq65WfZw==", "license": "ISC" }, "node_modules/emittery": { @@ -9161,9 +9165,9 @@ } }, "node_modules/eslint-scope": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.1.0.tgz", - "integrity": "sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -9178,9 +9182,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz", - "integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "dev": true, "license": "Apache-2.0", "engines": { @@ -9214,15 +9218,15 @@ } }, "node_modules/espree": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.2.0.tgz", - "integrity": "sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.12.0", + "acorn": "^8.14.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.1.0" + "eslint-visitor-keys": "^4.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -10259,9 +10263,9 @@ } }, "node_modules/graphql-config/node_modules/jiti": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.3.3.tgz", - "integrity": "sha512-EX4oNDwcXSivPrw2qKH2LB5PoFxEvgtv2JgwW0bU858HoLQ+kutSvjLMUqBd0PeJYEinLWhoI9Ol0eYMqj/wNQ==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.0.tgz", + "integrity": "sha512-H5UpaUI+aHOqZXlYOaFP/8AzKsg+guWu+Pr3Y8i7+Y3zr1aXAvCvTAQ1RxSc6oVD8R8c7brgNtTVP91E7upH/g==", "dev": true, "license": "MIT", "bin": { @@ -10326,9 +10330,9 @@ } }, "node_modules/graphql-scalars/node_modules/tslib": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", - "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, "node_modules/graphql-subscriptions": { @@ -11371,6 +11375,16 @@ "ws": "*" } }, + "node_modules/isomorphic.js": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.5.tgz", + "integrity": "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==", + "license": "MIT", + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + } + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -12522,6 +12536,27 @@ "node": ">= 0.8.0" } }, + "node_modules/lib0": { + "version": "0.2.98", + "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.98.tgz", + "integrity": "sha512-XteTiNO0qEXqqweWx+b21p/fBnNHUA1NwAtJNJek1oPrewEZs2uiT4gWivHKr9GqCjDPAhchz0UQO8NwU3bBNA==", + "license": "MIT", + "dependencies": { + "isomorphic.js": "^0.2.4" + }, + "bin": { + "0ecdsa-generate-keypair": "bin/0ecdsa-generate-keypair.js", + "0gentesthtml": "bin/gentesthtml.js", + "0serve": "bin/0serve.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + } + }, "node_modules/libbase64": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/libbase64/-/libbase64-1.3.0.tgz", @@ -13974,9 +14009,9 @@ "license": "MIT" }, "node_modules/nodemailer": { - "version": "6.9.15", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.15.tgz", - "integrity": "sha512-AHf04ySLC6CIfuRtRiEYtGEXgRfa6INgWGluDhnxTZhHSKvrBu7lc1VVchQ0d8nPc4cFaZoPq8vkyNoZr0TpGQ==", + "version": "6.9.16", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.16.tgz", + "integrity": "sha512-psAuZdTIRN08HKVd/E8ObdV6NO7NTBY3KsC30F7M4H1OnmLCUNaS56FpYxyb26zWLSyYF9Ozch9KYHhHegsiOQ==", "license": "MIT-0", "engines": { "node": ">=6.0.0" @@ -14124,9 +14159,9 @@ } }, "node_modules/openai": { - "version": "4.68.4", - "resolved": "https://registry.npmjs.org/openai/-/openai-4.68.4.tgz", - "integrity": "sha512-LRinV8iU9VQplkr25oZlyrsYGPGasIwYN8KFMAAFTHHLHjHhejtJ5BALuLFrkGzY4wfbKhOhuT+7lcHZ+F3iEA==", + "version": "4.70.2", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.70.2.tgz", + "integrity": "sha512-Q2ymi/KPUYv+LJ9rFxeYxpkVAhcrZFTVvnJbdF1pUHg9eMC6lY8PU4TO1XOK5UZzOZuuVicouRwVMi1iDrT4qw==", "license": "Apache-2.0", "dependencies": { "@types/node": "^18.11.18", @@ -14150,9 +14185,9 @@ } }, "node_modules/openai/node_modules/@types/node": { - "version": "18.19.59", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.59.tgz", - "integrity": "sha512-vizm2EqwV/7Zay+A6J3tGl9Lhr7CjZe2HmWS988sefiEmsyP9CeXEleho6i4hJk/8UtZAo0bWN4QPZZr83RxvQ==", + "version": "18.19.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.63.tgz", + "integrity": "sha512-hcUB7THvrGmaEcPcvUZCZtQ2Z3C+UR/aOcraBLCvTsFMh916Gc1kCCYcfcMuB76HM2pSerxl1PoP3KnmHzd9Lw==", "license": "MIT", "dependencies": { "undici-types": "~5.26.4" @@ -14418,9 +14453,9 @@ } }, "node_modules/parse5": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.0.tgz", - "integrity": "sha512-ZkDsAOcxsUMZ4Lz5fVciOehNcJ+Gb8gTzcA4yl3wnc273BAybYWrQ+Ks/OjCjSEpjvQkDSeZbybK9qj2VHHdGA==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", + "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", "license": "MIT", "optional": true, "dependencies": { @@ -16472,9 +16507,9 @@ } }, "node_modules/synckit/node_modules/tslib": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", - "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true, "license": "0BSD" }, @@ -16702,9 +16737,9 @@ } }, "node_modules/ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.0.tgz", + "integrity": "sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==", "dev": true, "license": "MIT", "engines": { @@ -17006,9 +17041,9 @@ } }, "node_modules/type-graphql/node_modules/tslib": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", - "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, "node_modules/type-is": { @@ -17916,6 +17951,23 @@ "node": ">=12" } }, + "node_modules/yjs": { + "version": "13.6.20", + "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.20.tgz", + "integrity": "sha512-Z2YZI+SYqK7XdWlloI3lhMiKnCdFCVC4PchpdO+mCYwtiTwncjUbnRK9R1JmkNfdmHyDXuWN3ibJAt0wsqTbLQ==", + "license": "MIT", + "dependencies": { + "lib0": "^0.2.98" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + }, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + } + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/package.json b/package.json index 3d7fb02..78bfb7e 100644 --- a/package.json +++ b/package.json @@ -46,11 +46,11 @@ "@nestjs/jwt": "^10.2.0", "@nestjs/platform-express": "^10.0.0", "@nestjs/swagger": "^7.4.2", - "@pothos/core": "^4.2.0", + "@pothos/core": "^4.3.0", "@pothos/plugin-add-graphql": "^4.1.0", "@pothos/plugin-authz": "^3.5.10", "@pothos/plugin-errors": "^4.2.0", - "@pothos/plugin-prisma": "^4.2.1", + "@pothos/plugin-prisma": "^4.3.0", "@pothos/plugin-prisma-utils": "^1.2.0", "@pothos/plugin-relay": "^4.3.0", "@pothos/plugin-scope-auth": "^4.1.0", @@ -81,7 +81,8 @@ "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1", "swagger-ui-express": "^5.0.1", - "type-graphql": "^2.0.0-rc.2" + "type-graphql": "^2.0.0-rc.2", + "yjs": "^13.6.20" }, "devDependencies": { "@biomejs/biome": "1.9.4", diff --git a/src/AppConfig/appconfig.constant.ts b/src/AppConfig/appconfig.constant.ts index d9bbcd0..ab61e83 100644 --- a/src/AppConfig/appconfig.constant.ts +++ b/src/AppConfig/appconfig.constant.ts @@ -22,25 +22,25 @@ export const ConfigConstants: Record< MID_DAY_BREAK_TIME_START: { name: 'Mid Day Break Time Start', key: 'MID_DAY_BREAK_TIME_START', - value: new Date(new Date().setUTCHours(12, 0, 0, 0)).toISOString(), + value: '12:00:00', visible: true, }, MID_DAY_BREAK_TIME_END: { name: 'Mid Day Break Time End', key: 'MID_DAY_BREAK_TIME_END', - value: new Date(new Date().setUTCHours(13, 0, 0, 0)).toISOString(), + value: '13:00:00', visible: true, }, - SLOT_START_TIME: { - name: 'Slot Start Time', - key: 'SLOT_START_TIME', - value: new Date(new Date().setUTCHours(8, 0, 0, 0)).toISOString(), + DAY_START_TIME: { + name: 'Day Start Time', + key: 'DAY_START_TIME', + value: '08:00:00', visible: true, }, - SLOT_END_TIME: { - name: 'Slot End Time', - key: 'SLOT_END_TIME', - value: new Date(new Date().setUTCHours(22, 0, 0, 0)).toISOString(), + DAY_END_TIME: { + name: 'Day End Time', + key: 'DAY_END_TIME', + value: '22:00:00', visible: true, }, } diff --git a/src/AppConfig/appconfig.service.ts b/src/AppConfig/appconfig.service.ts index a06d4f6..02e42f8 100644 --- a/src/AppConfig/appconfig.service.ts +++ b/src/AppConfig/appconfig.service.ts @@ -3,6 +3,7 @@ import { Injectable, OnModuleInit } from '@nestjs/common' import { PrismaService } from 'src/Prisma/prisma.service' import { ConfigConstants } from './appconfig.constant' import { Config } from '@prisma/client' +import { StringUtils } from 'src/common/utils/string.utils' @Injectable() export class AppConfigService implements OnModuleInit { @@ -37,9 +38,14 @@ export class AppConfigService implements OnModuleInit { } async getVisibleConfigs() { - return await this.prisma.config.findMany({ + const configs = await this.prisma.config.findMany({ where: { visible: true }, }) + // map to camelCase + return configs.map((config) => ({ + ...config, + key: StringUtils.sneakyCaseToCamelCase(config.key), + })) } async updateConfig(key: string, value: string) { @@ -63,6 +69,16 @@ export class AppConfigService implements OnModuleInit { }) } + async getDefaultConfig() { + return Object.entries(ConfigConstants).reduce((acc, [key, value]) => { + if (value.visible) { + // @ts-ignore + acc[key] = value.value + } + return acc + }, {}) + } + async resetAllConfigs() { // reset all configs to default values Object.entries(ConfigConstants).forEach(async ([_key, value]) => { diff --git a/src/Graphql/graphql.builder.ts b/src/Graphql/graphql.builder.ts index 4589752..59aba78 100644 --- a/src/Graphql/graphql.builder.ts +++ b/src/Graphql/graphql.builder.ts @@ -22,6 +22,9 @@ import SchemaBuilder from '@pothos/core' import SimpleObjectPlugin from '@pothos/plugin-simple-objects' import { User } from '@prisma/client' import { getDatamodel } from '../types/pothos.generated' +import { DateTime } from 'luxon' +import { Kind } from 'graphql' +import { DateTimeUtils } from '../common/utils/datetime.utils' // import { rules } from '../common/graphql/common.graphql.auth-rule'; @@ -29,6 +32,7 @@ export type SchemaContext = | { isSubscription: true websocket: { + req: Request pubSub: PubSub me: User generator: PrismaCrudGenerator @@ -57,8 +61,8 @@ export interface SchemaBuilderOption { // AuthZRule: keyof typeof rules; Scalars: { DateTime: { - Input: Date - Output: Date + Input: string | DateTime | Date + Output: string | DateTime | Date } Json: { Input: JSON @@ -112,7 +116,36 @@ export class Builder extends SchemaBuilder { }, }) this.generator = new PrismaCrudGenerator(this) - this.addScalarType('DateTime', DateTimeResolver) + this.scalarType('DateTime', { + serialize: (value) => { + // Serialize outgoing DateTime to ISO string + if (typeof value === 'string') { + return value + } + if (typeof value === 'object' && value !== null && 'toISO' in value) { + return value + } + // if value = Date, convert to DateTime + if (value instanceof Date) { + return DateTimeUtils.toIsoString(DateTimeUtils.fromDate(value)) + } + throw new Error('Invalid DateTime') + }, + parseValue: (value) => { + // Parse incoming ISO string to Luxon DateTime + if (typeof value === 'string') { + return DateTimeUtils.fromIsoString(value) + } + throw new Error('Invalid DateTime') + }, + parseLiteral: (ast) => { + // parse string to DateTime + if (ast.kind === Kind.STRING) { + return DateTimeUtils.fromIsoString(ast.value) + } + throw new Error('Invalid DateTime') + }, + }) this.addScalarType('Json', JSONObjectResolver) this.addScalarType('Upload', GraphQLUpload) diff --git a/src/Graphql/graphql.module.ts b/src/Graphql/graphql.module.ts index b01058b..bf3e77f 100644 --- a/src/Graphql/graphql.module.ts +++ b/src/Graphql/graphql.module.ts @@ -39,6 +39,8 @@ import { WorkshopModule } from '../Workshop/workshop.module' import { WorkshopOrganizationModule } from '../WorkshopOrganization/workshoporganization.module' import { WorkshopSubscriptionModule } from '../WorkshopSubscription/workshopsubscription.module' import { initContextCache } from '@pothos/core' +import { PubSub } from 'graphql-subscriptions' +import { isSubscription } from 'rxjs/internal/Subscription' @Global() @Module({ @@ -81,8 +83,8 @@ import { initContextCache } from '@pothos/core' }), GraphQLModule.forRootAsync({ driver: PothosApolloDriver, - inject: [GraphqlService], - useFactory: async (graphqlService: GraphqlService) => ({ + inject: [GraphqlService, 'PUB_SUB'], + useFactory: async (graphqlService: GraphqlService, pubsub: PubSub) => ({ path: process.env.API_PATH + '/graphql', debug: process.env.NODE_ENV === 'development' || false, playground: process.env.NODE_ENV === 'development' || false, @@ -91,18 +93,36 @@ import { initContextCache } from '@pothos/core' subscriptions: { 'graphql-ws': true, }, - context: async ({ req }: { req: Request }) => ({ - ...initContextCache(), - isSubscription: false, - http: { - req, - me: await graphqlService.acquireContext(req), - invalidateCache: () => - graphqlService.invalidateCache( - req.headers['x-session-id'] as string, - ), - }, - }), + context: async ({ + req, + subscriptions, + extra, + // biome-ignore lint/suspicious/noExplicitAny: + }: { req?: Request; subscriptions?: any; extra?: any }) => { + if (subscriptions) { + return { + isSubscription: true, + websocket: { + req: extra.request, + pubSub: pubsub, + me: await graphqlService.acquireContext( + extra.request.headers['x-session-id'], + ), + generator: extra.schemaBuilder, + }, + } + } + return { + isSubscription: false, + http: { + req, + me: req ? await graphqlService.acquireContext(req) : null, + pubSub: pubsub, + invalidateCache: () => Promise.resolve(), + generator: extra.schemaBuilder, + }, + } + }, }), }), ], @@ -124,7 +144,17 @@ import { initContextCache } from '@pothos/core' useFactory: (builder: Builder) => new PrismaCrudGenerator(builder), inject: [Builder], }, + { + provide: 'PUB_SUB', + useFactory: () => new PubSub(), + }, + ], + exports: [ + Builder, + PrismaCrudGenerator, + GraphqlService, + RedisService, + 'PUB_SUB', ], - exports: [Builder, PrismaCrudGenerator, GraphqlService, RedisService], }) export class GraphqlModule {} diff --git a/src/Graphql/graphql.service.ts b/src/Graphql/graphql.service.ts index df63a02..2ddae93 100644 --- a/src/Graphql/graphql.service.ts +++ b/src/Graphql/graphql.service.ts @@ -24,12 +24,8 @@ export class GraphqlService { const disableAuth = process.env.DISABLE_AUTH === 'true' try { sessionId = req.headers['x-session-id'] as string - } catch (error) { - Logger.error('Error acquiring context', error) - if (disableAuth) { - return null - } - throw new UnauthorizedException('Must provide a session ID') + } catch (_error) { + return null } if (disableAuth) { return null diff --git a/src/Message/message.schema.ts b/src/Message/message.schema.ts index 889a01c..0f6f24b 100644 --- a/src/Message/message.schema.ts +++ b/src/Message/message.schema.ts @@ -118,22 +118,17 @@ export class MessageSchema extends PothosSchema { }), })) - // subscriptions - /* The code snippet `subscriptions` is currently commented out in the provided TypeScript class. It - appears to be a placeholder or a section where subscription-related logic or fields could be - defined. In GraphQL, subscriptions are used to listen for real-time events or changes in data - and receive updates when those events occur. */ - this.builder.subscriptionFields((t) => ({ messageSent: t.field({ - subscribe: (_, __, ctx) => { + description: 'Subscribe to messages sent by users.', + args: {}, + subscribe: async (_, __, ctx) => { if (!ctx.isSubscription) { throw new Error('Not allowed') } - return { - [Symbol.asyncIterator]: () => - ctx.websocket.pubSub.asyncIterator('MESSAGE_SENT'), - } + return (await ctx.websocket.pubSub.asyncIterator( + 'MESSAGE_SENT', + )) as unknown as AsyncIterable }, type: this.message(), // Add the type property resolve: (payload) => diff --git a/src/Realtime/realtime.module.ts b/src/Realtime/realtime.module.ts new file mode 100644 index 0000000..df3d159 --- /dev/null +++ b/src/Realtime/realtime.module.ts @@ -0,0 +1,8 @@ +import { Module } from '@nestjs/common' +import { RealtimeService } from './realtime.service' + +@Module({ + providers: [RealtimeService], + exports: [RealtimeService], +}) +export class RealtimeModule {} diff --git a/src/Realtime/realtime.service.ts b/src/Realtime/realtime.service.ts new file mode 100644 index 0000000..ceb988a --- /dev/null +++ b/src/Realtime/realtime.service.ts @@ -0,0 +1,7 @@ +import { Injectable } from '@nestjs/common' +// @ts-ignore +import * as Y from 'yjs' +@Injectable() +export class RealtimeService { + yjs = new Y.Doc() +} diff --git a/src/Schedule/schedule.d.ts b/src/Schedule/schedule.d.ts new file mode 100644 index 0000000..12ae74c --- /dev/null +++ b/src/Schedule/schedule.d.ts @@ -0,0 +1,65 @@ +export interface ScheduleDateInput { + scheduleId: string + start: string + end: string + dayOfWeek: number + slot: number + serviceId: string + orderId: string | null +} + +export interface ScheduleConfigType { + midDayBreakTimeStart: string + midDayBreakTimeEnd: string + slotDuration: string + slotBreakDuration: string + dayStartTime: string + dayEndTime: string +} + +export interface ScheduleConfigTypeForCenter { + startDate: string + endDate: string + slots: number[] + days: number[] +} + +export interface ScheduleSlotType { + slot: string + dayOfWeek: number + start: string + end: string +} + +export interface PreviewScheduleType { + totalSlots: number + slots: ScheduleSlotType[] +} + +export interface ScheduleConfigType { + midDayBreakTimeStart: string + midDayBreakTimeEnd: string + slotDuration: string + slotBreakDuration: string + dayStartTime: string + dayEndTime: string +} + +export interface ScheduleConfigTypeForCenter { + startDate: string + endDate: string + slots: number[] + days: number[] +} + +export interface ScheduleSlotType { + slot: string + dayOfWeek: number + start: string + end: string +} + +export interface PreviewScheduleType { + totalSlots: number + slots: ScheduleSlotType[] +} diff --git a/src/Schedule/schedule.schema.ts b/src/Schedule/schedule.schema.ts index 86d61fb..530ac8b 100644 --- a/src/Schedule/schedule.schema.ts +++ b/src/Schedule/schedule.schema.ts @@ -10,37 +10,7 @@ import { PrismaService } from '../Prisma/prisma.service' import { ScheduleStatus } from '@prisma/client' import { ScheduleService } from './schedule.service' import { AppConfigService } from '../AppConfig/appconfig.service' -import { forEach } from 'lodash' - -export type ScheduleConfigType = - | { - midDayBreakTimeStart?: string | null | undefined - midDayBreakTimeEnd?: string | null | undefined - slotDuration?: string | null | undefined - slotBreakDuration?: string | null | undefined - slotEndTime?: string | null | undefined - slotStartTime?: string | null | undefined - } - | null - | undefined - -export type ScheduleConfigTypeForCenter = { - startDate: string - endDate: string - slots: number[] - days: number[] -} - -export type ScheduleSlotType = { - slot: string - start: string - end: string -} - -export type PreviewScheduleType = { - totalSlots: number - slots: ScheduleSlotType[] -} +import { ScheduleConfigType } from './schedule' @Injectable() export class ScheduleSchema extends PothosSchema { @@ -109,6 +79,7 @@ export class ScheduleSchema extends PothosSchema { slot: t.string({}), start: t.string({}), end: t.string({}), + dayOfWeek: t.int({}), }), }) } @@ -168,12 +139,24 @@ export class ScheduleSchema extends PothosSchema { return this.builder.inputType('ScheduleConfigInput', { description: 'A schedule config in the system.', fields: (t) => ({ - midDayBreakTimeStart: t.string(), - midDayBreakTimeEnd: t.string(), - slotDuration: t.string(), - slotBreakDuration: t.string(), - slotEndTime: t.string(), - slotStartTime: t.string(), + midDayBreakTimeStart: t.string({ + required: true, + }), + midDayBreakTimeEnd: t.string({ + required: true, + }), + slotDuration: t.string({ + required: true, + }), + slotBreakDuration: t.string({ + required: true, + }), + dayStartTime: t.string({ + required: true, + }), + dayEndTime: t.string({ + required: true, + }), }), }) } @@ -254,7 +237,17 @@ export class ScheduleSchema extends PothosSchema { }), }, resolve: async (_parent, args, _context, _info) => { - return await this.scheduleService.createSchedulePreview( + // if no scheduleConfig, use default config + if (!args.scheduleConfig) { + args.scheduleConfig = ( + await this.appConfigService.getVisibleConfigs() + ).reduce((acc, curr) => { + // @ts-ignore + acc[curr.key] = curr.value + return acc + }, {} as ScheduleConfigType) + } + return await this.scheduleService.createSchedulePreviewForSingleDay( args.scheduleConfig, ) }, @@ -278,6 +271,7 @@ d72a864e-2f41-45ab-9c9b-bf0512a31883,e9be51fd-2382-4e43-9988-74e76fde4b56,2024-1 'status', 'customerId', 'orderId', + 'dates', ]), required: true, }), diff --git a/src/Schedule/schedule.service.ts b/src/Schedule/schedule.service.ts index ebff78a..e644bb0 100644 --- a/src/Schedule/schedule.service.ts +++ b/src/Schedule/schedule.service.ts @@ -1,4 +1,4 @@ -import * as DateTimeUtils from '../common/utils/datetime.utils' +import { DateTimeUtils } from '../common/utils/datetime.utils' import { Injectable, Logger } from '@nestjs/common' import { PrismaService } from 'src/Prisma/prisma.service' @@ -9,28 +9,12 @@ import { ScheduleConfigType, ScheduleConfigTypeForCenter, ScheduleSlotType, -} from './schedule.schema' +} from './schedule.d' import { Config, Schedule, ScheduleDate } from '@prisma/client' import { DateTime, Settings, Zone } from 'luxon' import * as _ from 'lodash' +import { ScheduleDateInput } from './schedule' -Settings.defaultLocale = 'en-US' -Settings.defaultZone = 'utc' -Settings.defaultWeekSettings = { - firstDay: 2, - minimalDays: 1, - weekend: [6, 7], -} - -interface ScheduleDateInput { - scheduleId: string - start: string - end: string - dayOfWeek: number - slot: number - serviceId: string - orderId: string | null -} @Injectable() export class ScheduleService { constructor( @@ -38,17 +22,11 @@ export class ScheduleService { private readonly appConfigService: AppConfigService, ) {} - async createSchedulePreview( + async createSchedulePreviewForSingleDay( scheduleConfig: ScheduleConfigType, ): Promise { - const config: Config[] = await this.appConfigService.getVisibleConfigs() - // process scheduleConfig input by filling with default values from config - const scheduleConfigFilled = this.processScheduleConfig( - scheduleConfig, - config, - ) // generate Slot By config - const slots = this.generateSlots(scheduleConfigFilled) + const slots = this.generateSlots(scheduleConfig) return { totalSlots: slots.length, @@ -60,7 +38,13 @@ export class ScheduleService { async createSchedulePreviewForCenter( scheduleConfig: ScheduleConfigTypeForCenter, ): Promise { - const config: Config[] = await this.appConfigService.getVisibleConfigs() + const config: ScheduleConfigType = ( + await this.appConfigService.getVisibleConfigs() + ).reduce((acc, curr) => { + // @ts-ignore + acc[curr.key] = curr.value + return acc + }, {} as ScheduleConfigType) const slots = this.generateSlotsPreviewForCenter(scheduleConfig, config) return { totalSlots: slots.length, @@ -70,30 +54,41 @@ export class ScheduleService { async generateScheduleDates(schedule: Schedule): Promise { // generate schedule dates based on data and config - const config: Config[] = await this.appConfigService.getVisibleConfigs() + const config: ScheduleConfigType = ( + await this.appConfigService.getVisibleConfigs() + ).reduce((acc, curr) => { + // @ts-ignore + acc[curr.key] = curr.value + return acc + }, {} as ScheduleConfigType) + const daysOfWeeks = schedule.daysOfWeek const slots = schedule.slots - const scheduleStart = schedule.scheduleStart - const scheduleEnd = schedule.scheduleEnd - const slotDuration = config.find((c) => c.key === 'SLOT_DURATION')?.value - const slotBreakDuration = config.find( - (c) => c.key === 'SLOT_BREAK_DURATION', - )?.value - const slotStartTime = config.find((c) => c.key === 'SLOT_START_TIME')?.value + const scheduleStart = DateTime.fromJSDate(schedule.scheduleStart) + const scheduleEnd = DateTime.fromJSDate(schedule.scheduleEnd) + const slotDuration = parseInt(config.slotDuration) + const slotBreakDuration = parseInt(config.slotBreakDuration) const scheduleDates: ScheduleDateInput[] = [] + // loop each day from scheduleStart to scheduleEnd - let date = DateTime.fromJSDate(scheduleStart) - while (date <= DateTime.fromJSDate(scheduleEnd)) { + for ( + let date = scheduleStart; + date <= scheduleEnd; + date = date.plus({ days: 1 }) + ) { // Check if the current date matches one of the specified days of the week if (daysOfWeeks.includes(date.weekday)) { // loop through slots for (const slot of slots) { const { startTime, endTime } = this.getSlotStartAndEndTime( slot, - slotDuration ?? '', - slotBreakDuration ?? '', - slotStartTime ?? '', + slotDuration.toString(), + slotBreakDuration.toString(), + DateTimeUtils.getATimeWithDateB( + DateTime.fromISO(config.dayStartTime), + date, + ).toISO() ?? '', ) scheduleDates.push({ scheduleId: schedule.id, @@ -106,9 +101,8 @@ export class ScheduleService { }) } } - // Move to the next day - date = date.plus({ days: 1 }) } + const scheduleDatesCreated = await this.prisma.scheduleDate.createManyAndReturn({ data: scheduleDates, @@ -117,100 +111,102 @@ export class ScheduleService { return scheduleDatesCreated } + /* + example query: +query CenterPreviewSchedule { + centerPreviewSchedule( + scheduleConfigInput: { days: [3,5], endDate: "2024-11-22T00:00:00.000Z", slots: [2,6], startDate: "2024-11-02T00:00:00.000Z" } + ) { + totalSlots + slots { + dayOfWeek + end + slot + start + } + } +} + */ + generateSlotsPreviewForCenter( - scheduleConfig: ScheduleConfigTypeForCenter, - config: Config[], + _scheduleConfig: ScheduleConfigTypeForCenter, + _config: ScheduleConfigType, ): ScheduleSlotType[] { - const startDate = DateTime.fromISO(scheduleConfig.startDate) - const endDate = DateTime.fromISO(scheduleConfig.endDate) - const daysOfWeeks = scheduleConfig.days - - // Retrieve slot configuration values once - const slotDuration = - config.find((c) => c.key === 'SLOT_DURATION')?.value ?? '' - const slotBreakDuration = - config.find((c) => c.key === 'SLOT_BREAK_DURATION')?.value ?? '' - const slotStartTime = - config.find((c) => c.key === 'SLOT_START_TIME')?.value ?? '' - const slotEndTime = - config.find((c) => c.key === 'SLOT_END_TIME')?.value ?? '' - - // Calculate the number of slots based on configuration - const numberOfSlots = this.calculateNumberOfSlots( - slotStartTime, - slotEndTime, - slotDuration, - slotBreakDuration, - ) - const slots: ScheduleSlotType[] = [] - - // Loop through each day between start and end dates - for (let date = startDate; date <= endDate; date = date.plus({ days: 1 })) { + const daysOfWeeks = _scheduleConfig.days + const scheduleStart = DateTime.fromISO(_scheduleConfig.startDate) + const scheduleEnd = DateTime.fromISO(_scheduleConfig.endDate) + // loop each day from scheduleStart to scheduleEnd + for ( + let date = scheduleStart; + date <= scheduleEnd; + date = date.plus({ days: 1 }) + ) { + // Check if the current date matches one of the specified days of the week if (daysOfWeeks.includes(date.weekday)) { - Logger.log(`Generating slots for date: ${date.toISO()}`) - // For each slot number, calculate start and end times - for (let i = 1; i <= numberOfSlots; i++) { + // loop through slots + for (const slot of _scheduleConfig.slots) { + // get slot start and end time const { startTime, endTime } = this.getSlotStartAndEndTime( - i, - slotDuration, - slotBreakDuration, - slotStartTime, + slot, + _config.slotDuration, + _config.slotBreakDuration, + DateTimeUtils.getATimeWithDateB( + DateTime.fromISO(_config.dayStartTime), + date, + ).toISO() ?? '', ) - - slots.push({ - slot: i.toString(), - start: startTime.toISO() ?? '', - end: endTime.toISO() ?? '', - }) + // if the slot is not overlapping with mid day break time, add it to the slots + if ( + !DateTimeUtils.isOverlap( + startTime, + endTime, + DateTimeUtils.fromIsoString(_config.midDayBreakTimeStart), + DateTimeUtils.fromIsoString(_config.midDayBreakTimeEnd), + ) + ) { + slots.push({ + slot: slot.toString(), + start: startTime.toString(), + end: endTime.toString(), + dayOfWeek: date.weekday, + }) + } } } } - return slots } - generateSlots(scheduleConfigFilled: ScheduleConfigType): ScheduleSlotType[] { + generateSlots(scheduleConfig: ScheduleConfigType): ScheduleSlotType[] { const slots: ScheduleSlotType[] = [] const numberOfSlots = this.calculateNumberOfSlots( - // @ts-ignore - scheduleConfigFilled?.slotStartTime, - // @ts-ignore - scheduleConfigFilled?.slotEndTime, - // @ts-ignore - scheduleConfigFilled?.slotDuration, - // @ts-ignore - scheduleConfigFilled?.slotBreakDuration, + scheduleConfig.dayStartTime, + scheduleConfig.dayEndTime, + scheduleConfig.slotDuration, + scheduleConfig.slotBreakDuration, ) for (let i = 1; i <= numberOfSlots; i++) { const { startTime, endTime } = this.getSlotStartAndEndTime( i, - // @ts-ignore - scheduleConfigFilled?.slotDuration, - // @ts-ignore - scheduleConfigFilled?.slotBreakDuration, - // @ts-ignore - scheduleConfigFilled?.slotStartTime, + scheduleConfig.slotDuration, + scheduleConfig.slotBreakDuration, + scheduleConfig.dayStartTime, ) // if the slot is not overlapping with mid day break time, add it to the slots if ( - !this.isOverLapping( + !DateTimeUtils.isOverlap( startTime, endTime, - DateTime.fromISO( - // @ts-ignore - scheduleConfigFilled?.midDayBreakTimeStart, - ), - DateTime.fromISO( - // @ts-ignore - scheduleConfigFilled?.midDayBreakTimeEnd, - ), + DateTimeUtils.fromIsoString(scheduleConfig.midDayBreakTimeStart), + DateTimeUtils.fromIsoString(scheduleConfig.midDayBreakTimeEnd), ) ) { slots.push({ slot: i.toString(), start: startTime.toString(), end: endTime.toString(), + dayOfWeek: startTime.weekday, }) } } @@ -235,13 +231,26 @@ export class ScheduleService { slotDuration: string, slotBreakDuration: string, ) { - const startDate = DateTime.fromISO(startTime) - const endDate = DateTime.fromISO(endTime) + const _startTime = DateTimeUtils.toTime(startTime) + const _endTime = DateTimeUtils.toTime(endTime) + const _slotDuration = parseInt(slotDuration) // minutes + const _slotBreakDuration = parseInt(slotBreakDuration) // minutes + + const startDate = DateTime.fromObject({ + hour: _startTime.hour, + minute: _startTime.minute, + second: _startTime.second, + }) + const endDate = DateTime.fromObject({ + hour: _endTime.hour, + minute: _endTime.minute, + second: _endTime.second, + }) const totalMinutes = (endDate.toMillis() - startDate.toMillis()) / (60 * 1000) const numberOfSlots = Math.floor( - totalMinutes / (parseInt(slotDuration) + parseInt(slotBreakDuration)), + totalMinutes / (_slotDuration + _slotBreakDuration), ) return numberOfSlots } @@ -250,12 +259,13 @@ export class ScheduleService { slotNumber: number, slotDuration: string, slotBreakDuration: string, - slotStartTime: string, + dayStartTime: string, ) { const durationInMinutes = parseInt(slotDuration) const breakDurationInMinutes = parseInt(slotBreakDuration) + const initialStartTime = DateTime.fromISO(dayStartTime) - const startTime = DateTime.fromISO(slotStartTime).plus({ + const startTime = initialStartTime.plus({ minutes: (slotNumber - 1) * (durationInMinutes + breakDurationInMinutes), }) @@ -265,63 +275,4 @@ export class ScheduleService { endTime: endTime, } } - - processScheduleConfig( - scheduleConfig: ScheduleConfigType, - config: Config[], - ): ScheduleConfigType { - // if scheduleConfig is undefined, create a new object and seed all the values with default values from config - if (scheduleConfig === undefined) { - scheduleConfig = config.reduce((acc, curr) => { - // biome-ignore lint/suspicious/noExplicitAny: - ;(acc as any)[this.sneakyCaseToCamelCase(curr.key)] = curr.value - return acc - }, {}) - } - // loop through scheduleConfig and fill with default values from config - for (const key in scheduleConfig) { - if ( - // biome-ignore lint/suspicious/noExplicitAny: - (scheduleConfig as any)[key] === undefined || - // biome-ignore lint/suspicious/noExplicitAny: - (scheduleConfig as any)[key] === null || - // biome-ignore lint/suspicious/noExplicitAny: - (scheduleConfig as any)[key] === '' - ) { - // biome-ignore lint/suspicious/noExplicitAny: - ;(scheduleConfig as any)[key] = - // biome-ignore lint/suspicious/noExplicitAny: - config[this.camelCaseToUpperSneakyCase(key) as any] - } - } - return scheduleConfig - } - - camelCaseToUpperSneakyCase(str: string) { - return _.snakeCase(str).toUpperCase() - } - - sneakyCaseToCamelCase(str: string) { - return _.camelCase(str.toLowerCase()) - } - - getTodayWithTime(date: DateTime) { - let today = DateTime.now() - today = today.set({ - hour: date.hour, - minute: date.minute, - second: date.second, - }) - return today - } - - getSpecificDateWithTime(date: DateTime) { - let specificDate = DateTime.now() - specificDate = specificDate.set({ - hour: date.hour, - minute: date.minute, - second: date.second, - }) - return specificDate - } } diff --git a/src/common/utils/datetime.utils.ts b/src/common/utils/datetime.utils.ts index ca55690..5a2c9b8 100644 --- a/src/common/utils/datetime.utils.ts +++ b/src/common/utils/datetime.utils.ts @@ -1,34 +1,115 @@ -// function getOverlapRange( -// startA: number, -// endA: number, -// startB: number, -// endB: number, -// ) { -// const overlapStart = Math.max(startA, startB); -// const overlapEnd = Math.min(endA, endB); +import { Injectable } from '@nestjs/common' +import * as _ from 'lodash' +import { + DateTime, + Settings, + HourNumbers, + MinuteNumbers, + SecondNumbers, + DayNumbers, + WeekdayNumbers, +} from 'luxon' -// return overlapStart < overlapEnd ? { overlapStart, overlapEnd } : null; -// } +Settings.defaultLocale = 'en-US' +Settings.defaultZone = 'utc' +Settings.defaultWeekSettings = { + firstDay: 2, + minimalDays: 1, + weekend: [6, 7], +} -// export function isOverlap( -// startA: number, -// endA: number, -// startB: number, -// endB: number, -// ) { -// return getOverlapRange(startA, endA, startB, endB) !== null; -// } +export type TimeType = { + hour: HourNumbers + minute: MinuteNumbers + second: SecondNumbers +} -// const overlapRange = getOverlapRange(startA, endA, startB, endB); -// if (overlapRange) { -// console.log( -// `Overlap Start: ${new Date(overlapRange.overlapStart).toISOString()}`, -// ); -// console.log( -// `Overlap End: ${new Date(overlapRange.overlapEnd).toISOString()}`, -// ); +@Injectable() +export class DateTimeUtils { + static getOverlapRange( + startA: DateTime, + endA: DateTime, + startB: DateTime, + endB: DateTime, + ): { start: DateTime; end: DateTime } { + const overlapStart = DateTime.max(startA, startB) + const overlapEnd = DateTime.min(endA, endB) + return { + start: overlapStart, + end: overlapEnd, + } + } -// console.log('Is overlap: true'); -// } else { -// console.log('No overlap'); -// } + static isOverlap( + startA: DateTime, + endA: DateTime, + startB: DateTime, + endB: DateTime, + ): boolean { + return ( + this.getOverlapRange(startA, endA, startB, endB).start < + this.getOverlapRange(startA, endA, startB, endB).end + ) + } + + static fromIsoString(isoString: string): DateTime { + const dateTime = DateTime.fromISO(isoString) + if (!dateTime.isValid) { + throw new Error('Invalid date time') + } + return dateTime + } + + static fromDate(date: Date): DateTime { + const dateTime = DateTime.fromJSDate(date) + if (!dateTime.isValid) { + throw new Error('Invalid date time') + } + return dateTime + } + + static toIsoString(dateTime: DateTime): string { + const isoString = dateTime.toISO() + if (!isoString) { + throw new Error('Invalid date time') + } + return isoString + } + + static getTodayWithTime(date: DateTime) { + const today = DateTime.now().set({ + hour: date.hour, + minute: date.minute, + second: date.second, + }) + return today + } + + static getSpecificDateWithNowTime(date: DateTime) { + return date.set({ + hour: DateTime.now().hour, + minute: DateTime.now().minute, + second: DateTime.now().second, + }) + } + static getATimeWithDateB(a: DateTime, b: DateTime) { + return a.set({ + year: b.year, + month: b.month, + day: b.day, + hour: a.hour, + minute: a.minute, + second: a.second, + }) + } + + // example: 12:00:00 + static toTime(time: string): TimeType { + const [hour, minute, second] = time.split(':').map(Number) + return { + hour: hour as HourNumbers, + minute: minute as MinuteNumbers, + second: second as SecondNumbers, + } + } +} diff --git a/src/common/utils/string.utils.ts b/src/common/utils/string.utils.ts new file mode 100644 index 0000000..bb8ce27 --- /dev/null +++ b/src/common/utils/string.utils.ts @@ -0,0 +1,13 @@ +import { Injectable } from '@nestjs/common' +import _ from 'lodash' + +@Injectable() +export class StringUtils { + static camelCaseToUpperSneakyCase(str: string) { + return _.snakeCase(str).toUpperCase() + } + + static sneakyCaseToCamelCase(str: string) { + return _.camelCase(str.toLowerCase()) + } +}