diff --git a/.env.example b/.env.example index 9faf0fba420a7ae3aa5d184553b8442f77f82781..46f3dfcd89f8be34b9a66b545141ea3865a5db8e 100644 --- a/.env.example +++ b/.env.example @@ -19,3 +19,13 @@ AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= AWS_REGION=default S3_BUCKET=mecredteste + +ELASTICSEARCH_USER=elastic +ELASTICSEARCH_PASSWORD=123 +ELASTICSEARCH_HOST=localhost +ELASTICSEARCH_PORT=9200 +ELASTICSEARCH_URL=http://${ELASTICSEARCH_USER}:${ELASTICSEARCH_PASSWORD}@${ELASTICSEARCH_HOST}:${ELASTICSEARCH_PORT} +ELASTIC_INDEX_PREFIX= +ELASTIC_INDEX_RESOURCES=${ELASTIC_INDEX_PREFIX}_resources +ELASTIC_INDEX_COLLECTIONS=${ELASTIC_INDEX_PREFIX}_collections +ELASTIC_INDEX_USERS=${ELASTIC_INDEX_PREFIX}_users diff --git a/bun.lockb b/bun.lockb index 21895ee2a75f1cca3c0d0a06287fadd4e7396b1e..07d65b227b112081b8a5c9b3bf38892687972d65 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000000000000000000000000000000000000..3544e82cac5eac0f8985ff33d220cbff88a6d222 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2250 @@ +{ + "name": "hono-backend", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "hono-backend", + "dependencies": { + "@elastic/elasticsearch": "^8.17.0", + "@hono/zod-validator": "^0.2.2", + "dotenv": "^16.4.5", + "dotenv-expand": "^11.0.6", + "drizzle-orm": "^0.31.2", + "drizzle-zod": "^0.5.1", + "hono": "^4.4.8", + "postgres": "^3.4.4", + "reflect-metadata": "^0.2.2", + "typedi": "^0.10.0", + "zod": "^3.23.8" + }, + "devDependencies": { + "@eslint/js": "^9.5.0", + "@types/bun": "latest", + "cross-env": "^7.0.3", + "drizzle-kit": "^0.22.7", + "eslint": "^8.56.0", + "eslint-config-prettier": "^9.1.0", + "globals": "^15.6.0", + "prettier": "^3.3.2", + "typescript-eslint": "^7.14.1" + } + }, + "node_modules/@elastic/elasticsearch": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/@elastic/elasticsearch/-/elasticsearch-8.17.0.tgz", + "integrity": "sha512-FZ+gQUrPsMpQ2RRIXwTmCoUeFCEausMhp4eQOyxT9j1cwGXHJrhelR6jffM1SC95kQUkB7+TcTq7oQ+bG2BQ9g==", + "dependencies": { + "@elastic/transport": "^8.9.1", + "apache-arrow": "^18.0.0", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@elastic/transport": { + "version": "8.9.4", + "resolved": "https://registry.npmjs.org/@elastic/transport/-/transport-8.9.4.tgz", + "integrity": "sha512-y6kjy5s0MQE3MQx9ItmvQ8th7GlGcZfzZ7ZDvI8bUhaKua2dJk01k9ia/bdJ4dnPpWpOyFTRgkgBZS31ZTLpcg==", + "dependencies": { + "@opentelemetry/api": "1.x", + "debug": "^4.3.7", + "hpagent": "^1.2.0", + "ms": "^2.1.3", + "secure-json-parse": "^3.0.1", + "tslib": "^2.8.1", + "undici": "^6.21.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild-kit/core-utils": { + "version": "3.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.18.20", + "source-map-support": "^0.5.21" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/esbuild": { + "version": "0.18.20", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/esbuild/node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/esm-loader": { + "version": "2.6.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@esbuild-kit/core-utils": "^3.3.2", + "get-tsconfig": "^4.7.0" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.19.12", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.5.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@hono/zod-validator": { + "version": "0.2.2", + "license": "MIT", + "peerDependencies": { + "hono": ">=3.9.0", + "zod": "^3.19.1" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@types/bun": { + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/@types/bun/-/bun-1.1.18.tgz", + "integrity": "sha512-gtw6cIv/8Q530D0BmoYnjEzR65SjVq2SaUE0NeU6tbm7QBMsTZ61/NBNERtK/FUJaoi7PiteUohK7JcrXBCkvw==", + "dev": true, + "dependencies": { + "bun-types": "1.1.44" + } + }, + "node_modules/@types/command-line-args": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/command-line-args/-/command-line-args-5.2.3.tgz", + "integrity": "sha512-uv0aG6R0Y8WHZLTamZwtfsDLVRnOa+n+n5rEvFWL5Na5gZ8V2Teab/duDPFzIIIhs9qizDpcavCusCLJZu62Kw==" + }, + "node_modules/@types/command-line-usage": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/command-line-usage/-/command-line-usage-5.0.4.tgz", + "integrity": "sha512-BwR5KP3Es/CSht0xqBcUXS3qCAUVXwpRKsV2+arxeb65atasuXG9LykC9Ab10Cw3s2raH92ZqOeILaQbsB2ACg==" + }, + "node_modules/@types/node": { + "version": "20.12.14", + "devOptional": true, + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/ws": { + "version": "8.5.10", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "7.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.14.1", + "@typescript-eslint/type-utils": "7.14.1", + "@typescript-eslint/utils": "7.14.1", + "@typescript-eslint/visitor-keys": "7.14.1", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "7.14.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "7.14.1", + "@typescript-eslint/types": "7.14.1", + "@typescript-eslint/typescript-estree": "7.14.1", + "@typescript-eslint/visitor-keys": "7.14.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "7.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.14.1", + "@typescript-eslint/visitor-keys": "7.14.1" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "7.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "7.14.1", + "@typescript-eslint/utils": "7.14.1", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "7.14.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "7.14.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "7.14.1", + "@typescript-eslint/visitor-keys": "7.14.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "7.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.14.1", + "@typescript-eslint/types": "7.14.1", + "@typescript-eslint/typescript-estree": "7.14.1" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.14.1", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.1.tgz", + "integrity": "sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/apache-arrow": { + "version": "18.1.0", + "resolved": "https://registry.npmjs.org/apache-arrow/-/apache-arrow-18.1.0.tgz", + "integrity": "sha512-v/ShMp57iBnBp4lDgV8Jx3d3Q5/Hac25FWmQ98eMahUiHPXcvwIMKJD0hBIgclm/FCG+LwPkAKtkRO1O/W0YGg==", + "dependencies": { + "@swc/helpers": "^0.5.11", + "@types/command-line-args": "^5.2.3", + "@types/command-line-usage": "^5.0.4", + "@types/node": "^20.13.0", + "command-line-args": "^5.2.1", + "command-line-usage": "^7.0.1", + "flatbuffers": "^24.3.25", + "json-bignum": "^0.0.3", + "tslib": "^2.6.2" + }, + "bin": { + "arrow2csv": "bin/arrow2csv.js" + } + }, + "node_modules/apache-arrow/node_modules/@types/node": { + "version": "20.17.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.14.tgz", + "integrity": "sha512-w6qdYetNL5KRBiSClK/KWai+2IMEJuAj+EujKCumalFOwXtvOXaEan9AuwcRID2IcOIAWSIfR495hBtgKlx2zg==", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/apache-arrow/node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-back": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", + "engines": { + "node": ">=6" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/bun-types": { + "version": "1.1.44", + "resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.1.44.tgz", + "integrity": "sha512-jtcekoZeSINgEcHSISzhR13w/cyE+Fankw2Cpl4c0fN3lRmKVAX0i9ay4FyK4lOxUK1HG4HkuIlrPvXKz4Y7sw==", + "devOptional": true, + "dependencies": { + "@types/node": "~20.12.8", + "@types/ws": "~8.5.10" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk-template": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz", + "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==", + "dependencies": { + "chalk": "^4.1.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/chalk-template?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "license": "MIT" + }, + "node_modules/command-line-args": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz", + "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", + "dependencies": { + "array-back": "^3.1.0", + "find-replace": "^3.0.0", + "lodash.camelcase": "^4.3.0", + "typical": "^4.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/command-line-usage": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-7.0.3.tgz", + "integrity": "sha512-PqMLy5+YGwhMh1wS04mVG44oqDsgyLRSKJBdOo1bnYhMKBW65gZF1dRp2OZRhiTjgUHljy99qkO7bsctLaw35Q==", + "dependencies": { + "array-back": "^6.2.2", + "chalk-template": "^0.4.0", + "table-layout": "^4.1.0", + "typical": "^7.1.1" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/command-line-usage/node_modules/array-back": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", + "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", + "engines": { + "node": ">=12.17" + } + }, + "node_modules/command-line-usage/node_modules/typical": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-7.3.0.tgz", + "integrity": "sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==", + "engines": { + "node": ">=12.17" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/cross-env": { + "version": "7.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dotenv": { + "version": "16.4.5", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand": { + "version": "11.0.6", + "license": "BSD-2-Clause", + "dependencies": { + "dotenv": "^16.4.4" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/drizzle-kit": { + "version": "0.22.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@esbuild-kit/esm-loader": "^2.5.5", + "esbuild": "^0.19.7", + "esbuild-register": "^3.5.0" + }, + "bin": { + "drizzle-kit": "bin.cjs" + } + }, + "node_modules/drizzle-orm": { + "version": "0.31.2", + "license": "Apache-2.0", + "peerDependencies": { + "@aws-sdk/client-rds-data": ">=3", + "@cloudflare/workers-types": ">=3", + "@electric-sql/pglite": ">=0.1.1", + "@libsql/client": "*", + "@neondatabase/serverless": ">=0.1", + "@op-engineering/op-sqlite": ">=2", + "@opentelemetry/api": "^1.4.1", + "@planetscale/database": ">=1", + "@tidbcloud/serverless": "*", + "@types/better-sqlite3": "*", + "@types/pg": "*", + "@types/react": ">=18", + "@types/sql.js": "*", + "@vercel/postgres": ">=0.8.0", + "@xata.io/client": "*", + "better-sqlite3": ">=7", + "bun-types": "*", + "expo-sqlite": ">=13.2.0", + "knex": "*", + "kysely": "*", + "mysql2": ">=2", + "pg": ">=8", + "postgres": ">=3", + "react": ">=18", + "sql.js": ">=1", + "sqlite3": ">=5" + }, + "peerDependenciesMeta": { + "@aws-sdk/client-rds-data": { + "optional": true + }, + "@cloudflare/workers-types": { + "optional": true + }, + "@electric-sql/pglite": { + "optional": true + }, + "@libsql/client": { + "optional": true + }, + "@neondatabase/serverless": { + "optional": true + }, + "@op-engineering/op-sqlite": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@tidbcloud/serverless": { + "optional": true + }, + "@types/better-sqlite3": { + "optional": true + }, + "@types/pg": { + "optional": true + }, + "@types/react": { + "optional": true + }, + "@types/sql.js": { + "optional": true + }, + "@vercel/postgres": { + "optional": true + }, + "@xata.io/client": { + "optional": true + }, + "better-sqlite3": { + "optional": true + }, + "bun-types": { + "optional": true + }, + "expo-sqlite": { + "optional": true + }, + "knex": { + "optional": true + }, + "kysely": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "pg": { + "optional": true + }, + "postgres": { + "optional": true + }, + "react": { + "optional": true + }, + "sql.js": { + "optional": true + }, + "sqlite3": { + "optional": true + } + } + }, + "node_modules/drizzle-zod": { + "version": "0.5.1", + "license": "Apache-2.0", + "peerDependencies": { + "drizzle-orm": ">=0.23.13", + "zod": "*" + } + }, + "node_modules/esbuild": { + "version": "0.19.12", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" + } + }, + "node_modules/esbuild-register": { + "version": "3.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "peerDependencies": { + "esbuild": ">=0.12 <1" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.56.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/@eslint/js": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.17.1", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-replace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", + "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", + "dependencies": { + "array-back": "^3.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatbuffers": { + "version": "24.12.23", + "resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-24.12.23.tgz", + "integrity": "sha512-dLVCAISd5mhls514keQzmEG6QHmUUsNuWsb4tFafIUwvvgDjXhtfAYSKOzt5SWOy+qByV5pbsDZ+Vb7HUOBEdA==" + }, + "node_modules/flatted": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/get-tsconfig": { + "version": "4.7.5", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "15.6.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hono": { + "version": "4.4.8", + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/hpagent": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hpagent/-/hpagent-1.2.0.tgz", + "integrity": "sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/ignore": { + "version": "5.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-bignum": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/json-bignum/-/json-bignum-0.0.3.tgz", + "integrity": "sha512-2WHyXj3OfHSgNyuzDbSxI1w2jgw5gkWSWhS7Qg4bWXx1nLk3jnbwfUeS0PSba3IzpTUWdHxBieELUzXRjQB2zg==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postgres": { + "version": "3.4.4", + "license": "Unlicense", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/porsager" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.3.2", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "license": "Apache-2.0" + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/secure-json-parse": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-3.0.2.tgz", + "integrity": "sha512-H6nS2o8bWfpFEV6U38sOSjS7bTbdgbCGU9wEM6W14P5H0QOsz94KCusifV44GpHDTu2nqZbuDNhTzu+mjDSw1w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, + "node_modules/semver": { + "version": "7.6.2", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/table-layout": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-4.1.1.tgz", + "integrity": "sha512-iK5/YhZxq5GO5z8wb0bY1317uDF3Zjpha0QFFLA8/trAoiLbQD0HUbMesEaxyzUgDxi2QlcbM8IvqOlEjgoXBA==", + "dependencies": { + "array-back": "^6.2.2", + "wordwrapjs": "^5.1.0" + }, + "engines": { + "node": ">=12.17" + } + }, + "node_modules/table-layout/node_modules/array-back": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", + "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", + "engines": { + "node": ">=12.17" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/type-check": { + "version": "0.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typedi": { + "version": "0.10.0", + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.5.2", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "7.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "7.14.1", + "@typescript-eslint/parser": "7.14.1", + "@typescript-eslint/utils": "7.14.1" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typical": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", + "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/undici": { + "version": "6.21.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.1.tgz", + "integrity": "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ==", + "engines": { + "node": ">=18.17" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "devOptional": true, + "license": "MIT" + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrapjs": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-5.1.0.tgz", + "integrity": "sha512-JNjcULU2e4KJwUNv6CHgI46UvDGitb6dGryHajXTDiLgg1/RiGoPSDw4kZfYnwGtEXf2ZMeIewDQgFGzkCB2Sg==", + "engines": { + "node": ">=12.17" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.23.8", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/package.json b/package.json index ccb1549d89711056b5513a72a183914fc8fc835a..2cd1559215768cbf6c8d6f3664f09971050d9854 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "@aws-sdk/client-s3": "^3.750.0", "@hono/swagger-ui": "^0.5.0", "@hono/zod-openapi": "^0.18.3", + "@elastic/elasticsearch": "^8.17.0", "@hono/zod-validator": "^0.2.2", "@scalar/hono-api-reference": "^0.8.0", "archiver": "^7.0.1", @@ -35,7 +36,7 @@ "@types/bun": "latest", "cross-env": "^7.0.3", "drizzle-kit": "^0.22.7", - "eslint": "9.x", + "eslint": "^9.x", "eslint-config-prettier": "^9.1.0", "globals": "^15.6.0", "prettier": "^3.3.2", diff --git a/src/db/repo/search.repo.ts b/src/db/repo/search.repo.ts new file mode 100644 index 0000000000000000000000000000000000000000..210de3505446d2a9ff8e9e67ca51e9d4829e59e3 --- /dev/null +++ b/src/db/repo/search.repo.ts @@ -0,0 +1,157 @@ +import { Service } from "typedi"; +import db from ".."; +import { eq, sql } from "drizzle-orm"; +import { resourceIndexSchema, type ResourceIndex } from "../schema/search.schema"; +import { userIndexSchema, type UserIndex } from "../schema/search.schema"; +import { collectionIndexSchema, type CollectionIndex } from "../schema/search.schema"; + +@Service() +export class SearchRepo { + async findResourcesForIndexing(id: number): Promise<ResourceIndex[]> { + const query = db + .select({ + id: sql`re.id`, + name: sql`re.name`, + author: sql`re.author`, + description: sql`re.description`, + link: sql`re.link`, + created_at: sql`re.published_at`, // "created" ao invés de "published" pra ficar consistente com users e collections + educational_stages: sql`string_agg(distinct es.name, ',')`, + languages: sql`string_agg(distinct lg.name, ',')`, + subjects: sql`string_agg(distinct sj.name, ',')`, + license: sql`lc.name`, + object_type: sql`ot.name`, + state: sql`re.state`, + user: sql`us.name`, + views: sql`rest.views`, + downloads: sql`rest.downloads`, + likes: sql`rest.likes`, + shares: sql`rest.shares`, + score: sql`rest.score`, + comments: sql`rest.comments`, + }) + .from(sql`resources re`) + .leftJoin(sql`resource_educational_stage res`, sql`res.resource_id = re.id`) + .leftJoin(sql`educational_stages es`, sql`res.educational_stage_id = es.id`) + .leftJoin(sql`resource_languages rel`, sql`rel.resource_id = re.id`) + .leftJoin(sql`languages lg`, sql`rel.language_id = lg.id`) + .leftJoin(sql`resource_subjects res`, sql`res.resource_id = re.id`) + .leftJoin(sql`subjects sj`, sql`res.subject_id = sj.id`) + .leftJoin(sql`licenses lc`, sql`re.license_id = lc.id`) + .leftJoin(sql`object_types ot`, sql`re.object_type_id = ot.id`) + .leftJoin(sql`users us`, sql`us.id = re.user_id`) + .leftJoin(sql`resource_stats rest`, sql`rest.id = re.resource_stats_id`) + .where(eq(sql`re.id`, id)) + .groupBy( + sql`re.id`, + sql`re.name`, + sql`re.author`, + sql`re.description`, + sql`re.link`, + sql`re.published_at`, + sql`re.state`, + sql`re.views_count`, + sql`re.downloads_count`, + sql`re.likes_count`, + sql`re.shares_count`, + sql`ot.name`, + sql`lc.name`, + sql`us.name`, + sql`rest.views`, + sql`rest.downloads`, + sql`rest.likes`, + sql`rest.shares`, + sql`rest.score`, + sql`rest.comments` + ); + + const results = await query.execute(); + + // Parse and validate each result using Zod schema + return results.map((result) => resourceIndexSchema.parse(result)); + } + + async findUsersForIndexing(id: number): Promise<UserIndex[]> { + const query = db + .select({ + id: sql`users.id`, + name: sql`users.name`, + username: sql`users.username`, + description: sql`users.description`, + roles: sql`string_agg(distinct roles.name, ',')`, + institutions: sql`string_agg(distinct inst.name, ',')`, + created_at: sql`users.created_at`, + is_active: sql`users.is_active`, + score: sql`usst.score`, + likes_received: sql`usst.likes_received`, + followers: sql`usst.followers`, + approved_resources: sql`usst.approved_resources`, + }) + .from(sql`users`) + .leftJoin(sql`user_roles ur`, sql`users.id = ur.user_id`) + .leftJoin(sql`roles`, sql`roles.id = ur.role_id`) + .leftJoin(sql`user_institutions ui`, sql`ui.user_id = users.id`) + .leftJoin(sql`institutions inst`, sql`inst.id = ui.institution_id`) + .leftJoin(sql`user_stats usst`, sql`usst.id = users.user_stats_id`) + .where(eq(sql`users.id`, id)) + .groupBy( + sql`users.id`, + sql`users.name`, + sql`users.username`, + sql`users.description`, + sql`users.created_at`, + sql`users.is_active`, + sql`usst.score`, + sql`usst.likes_received`, + sql`usst.followers`, + sql`usst.approved_resources` + ); + + + const results = await query.execute(); + + // Parse and validate each result using Zod schema + return results.map((result) => userIndexSchema.parse(result)); + } + + // TODO fix the collections query + async findCollectionsForIndexing(id: number): Promise<CollectionIndex[]> { + const query = db + .select({ + id: sql`col.id`, + name: sql`col.name`, + author: sql`users.name`, + description: sql`col.description`, + created_at: sql`col.created_at`, + score: sql`colst.score`, + views: sql`colst.views`, + downloads: sql`colst.downloads`, + likes: sql`colst.likes`, + shares: sql`colst.shares`, + follows: sql`colst.follows`, + }) + .from(sql`collections col`) + .leftJoin(sql`users`, sql`users.id = col.user_id`) + .leftJoin(sql`collection_stats as colst`, sql`colst.id = col.id`) + .where(eq(sql`col.id`, id)) + .groupBy( + sql`col.id`, + sql`col.name`, + sql`users.name`, + sql`col.description`, + sql`col.created_at`, + sql`colst.score`, + sql`colst.views`, + sql`colst.downloads`, + sql`colst.likes`, + sql`colst.shares`, + sql`colst.follows`, + ); + + const results = await query.execute(); + + // Parse and validate each result using Zod schema + return results.map((result) => collectionIndexSchema.parse(result)); + } + +} diff --git a/src/db/schema/resource-enum.schema.ts b/src/db/schema/resource-enum.schema.ts index 78a1d61fc5896d20feab7bfac26a663e421ee68a..cb59688c272c6aa8f8fa05484a8a2ded490f62c7 100644 --- a/src/db/schema/resource-enum.schema.ts +++ b/src/db/schema/resource-enum.schema.ts @@ -6,7 +6,7 @@ import { z } from "zod"; const resourceEnum = pgEnum('resource_state', ['draft', 'submitted', 'accepted', 'reported', 'deleted'] ) -const resourceEnumSchema = z.enum(resourceEnum.enumValues) +export const resourceEnumSchema = z.enum(resourceEnum.enumValues) export default resourceEnum export type ResourceEnum = z.infer<typeof resourceEnumSchema> \ No newline at end of file diff --git a/src/db/schema/search.schema.ts b/src/db/schema/search.schema.ts new file mode 100644 index 0000000000000000000000000000000000000000..d385eeea9161c4aceab60d486b3fcb5346c7b915 --- /dev/null +++ b/src/db/schema/search.schema.ts @@ -0,0 +1,64 @@ +import { z } from "zod"; +import { resourceEnumSchema } from '../schema/resource-enum.schema' + +export const resourceIndexSchema = z.object({ + id: z.number(), + name: z.string(), + author: z.string().nullable(), + description: z.string().nullable(), + link: z.string().nullable(), + created_at: z.string(), + educational_stages: z.string().nullable(), + languages: z.string(), + subjects: z.string().nullable(), + license: z.string(), + object_type: z.string(), + state: resourceEnumSchema, + user: z.string(), + views: z.number(), + downloads: z.number(), + likes: z.number(), + shares: z.number(), + score: z.number(), + comments: z.number() +}); + +export const userIndexSchema = z.object({ + id: z.number(), + name: z.string(), + username: z.string(), + description: z.string().nullable(), + roles: z.string().nullable(), + institutions: z.string().nullable(), + created_at: z.string(), + is_active: z.boolean(), + score: z.number(), + likes_received: z.number(), + followers: z.number(), + approved_resources: z.number() +}); + +export const collectionIndexSchema = z.object({ + id: z.number(), + name: z.string(), + author: z.string().nullable(), + description: z.string().nullable(), + created_at: z.string(), + score: z.number(), + views: z.number(), + downloads: z.number(), + likes: z.number(), + shares: z.number(), + follows: z.number() +}); + +// export type LearningObjectIndex = z.infer<typeof learningObjectIndexSchema>; +// export type UserIndex = z.infer<typeof userIndexSchema>; +// export type CollectionIndex = z.infer<typeof collectionIndexSchema>; + + +// export default { learningObjectIndexSchema, collectionIndexSchema, userIndexSchema }; + +export type ResourceIndex = z.infer<typeof resourceIndexSchema>; +export type UserIndex = z.infer<typeof userIndexSchema>; +export type CollectionIndex = z.infer<typeof collectionIndexSchema>; \ No newline at end of file diff --git a/src/env.ts b/src/env.ts index 4b3b2aa73f5337e7eb2f9563eb4d22259e78d19e..a8f1e670144fd6b95fd9bcab9c6fdf73691e856e 100644 --- a/src/env.ts +++ b/src/env.ts @@ -26,6 +26,16 @@ const EnvSchema = z.object({ AWS_REGION: z.string(), S3_BUCKET: z.string(), URL: z.string(), + + ELASTICSEARCH_USER: z.string(), + ELASTICSEARCH_PASSWORD: z.string(), + ELASTICSEARCH_HOST: z.string(), + ELASTICSEARCH_PORT: z.string(), + ELASTICSEARCH_URL: z.string(), + ELASTIC_INDEX_PREFIX: z.string(), + ELASTIC_INDEX_RESOURCES: z.string(), + ELASTIC_INDEX_COLLECTIONS: z.string(), + ELASTIC_INDEX_USERS: z.string(), }) export type EnvSchema = z.infer<typeof EnvSchema> diff --git a/src/index.ts b/src/index.ts index 14b251f368bca425ac20dadf279f8d2e975d0a9e..6ec066b181764a21a0c153943efe18f3712bf147 100644 --- a/src/index.ts +++ b/src/index.ts @@ -46,6 +46,7 @@ import { publicUserCollectionsRoutes, userCollectionsRoute } from './routes/user import { publicS3, s3Routes } from './routes/s3.route' import { homologationRouter } from './routes/homologation.route' import { contactRoute } from './routes/contact.route' +import { searchRouter } from './routes/search.route' @@ -140,6 +141,7 @@ app .route('/userCollections', publicUserCollectionsRoutes) .route('/s3', publicS3) .route('/contact', contactRoute) + .route('/elastic', searchRouter) //rotas que precisam de token app .basePath('/api') diff --git a/src/routes/auth.route.ts b/src/routes/auth.route.ts index 11171e5ef93ce307e4fd75c2d7e5c51a489a09be..7a90b10077543860337198acbdb6d33728f8cbda 100644 --- a/src/routes/auth.route.ts +++ b/src/routes/auth.route.ts @@ -7,6 +7,7 @@ import { UserStatsService } from '@/services/user-stats.service' import { UserService } from '@/services/user.service' import { zValidator } from '@hono/zod-validator' import { Hono } from 'hono' +import { SearchService } from "@/services/search.service"; import Container from 'typedi' import jwt from 'jwt-simple'; import { @@ -21,6 +22,8 @@ import { const authService = Container.get(AuthService) const userService = Container.get(UserService) const userStatsService = Container.get(UserStatsService) +// const passwordRecoveryService = Container.get(PasswordRecoveryService) +const searchService = Container.get(SearchService); export const authRouter = new Hono().post( '/signin', signinRoute, @@ -62,6 +65,8 @@ export const authRouter = new Hono().post( const user = userSchemas.userDtoSchema.parse(await userService.create(input)); + await searchService.indexUser(user.id) + await authService.sendConfirmationEmail(user.email, "Confirmação de email", user.name, user.email); return c.json({ user, userStats }); diff --git a/src/routes/collections.route.ts b/src/routes/collections.route.ts index fcca066cbcb90c2e62b25913abf41f2c42c6635c..9de54735a1f5aa5d01db7e2c5a00b85b21d5c457 100644 --- a/src/routes/collections.route.ts +++ b/src/routes/collections.route.ts @@ -10,8 +10,8 @@ import { CollectionStatsService } from "@/services/collection-stats.service"; import { getFile } from "@/services/s3.service"; import archiver from "archiver"; // Biblioteca para criar ZIPs import { CollectionResourcesService } from "@/services/collection-resources.service"; -import { ResourceService } from "@/services/resource.service"; import { addObjectTypeNameArray } from "./resource.route"; +import { SearchService } from "@/services/search.service"; import { createCollectionRoute, @@ -31,7 +31,7 @@ import { const service = Container.get(CollectionsService); const serviceStats = Container.get(CollectionStatsService); const serviceResourceCollection = Container.get(CollectionResourcesService) ; -const resourceService = Container.get(ResourceService) +const searchService = Container.get(SearchService); export const collectionsRouter = honoWithJwt() .post( @@ -46,10 +46,13 @@ export const collectionsRouter = honoWithJwt() ) input.collection_stats_id = collectionStats.id + const collection = collectionSchemas.collectionDtoSchema.parse( await service.create(input) ) + await searchService.indexCollection(collection.id) + return c.json({ collection, collectionStats }) } catch (e) { return c.json( @@ -76,6 +79,8 @@ export const collectionsRouter = honoWithJwt() await service.update(input) ) + await searchService.indexCollection(collection.id) + return c.json({ collection }) } catch (e) { console.log(e) @@ -101,6 +106,8 @@ export const collectionsRouter = honoWithJwt() await service.delete(id) ) + await searchService.deindexDocument(id, 'collections') + return c.json({ collection }) } catch (e) { return c.json( @@ -123,6 +130,8 @@ export const collectionsRouter = honoWithJwt() await service.deletePermanently(id) ) + await searchService.deindexDocument(id, 'collections') + return c.json({ collection }) } catch (e) { return c.json( @@ -145,6 +154,8 @@ export const collectionsRouter = honoWithJwt() await service.restore(id) ) + await searchService.indexCollection(id) + return c.json({ collection }) } catch (e) { return c.json( diff --git a/src/routes/homologation.route.ts b/src/routes/homologation.route.ts index d6f4b8fb9a6788baeab72747b33c8ea2630b6577..eb50889d94cf24ce92f581749677a46daffa3f59 100644 --- a/src/routes/homologation.route.ts +++ b/src/routes/homologation.route.ts @@ -7,6 +7,8 @@ import { z } from "zod"; import { SubmissionService } from "@/services/submission.service"; import { HomologationService } from "@/services/homologation.service"; import { ResourceService } from "@/services/resource.service"; +import { SearchService } from "@/services/search.service"; + import { getAllHomologationRoute, @@ -16,6 +18,7 @@ import { const serviceHomologation = Container.get(HomologationService) const service = Container.get(SubmissionService); const serviceResource = Container.get(ResourceService); +const searchService = Container.get(SearchService); export const inputSchemaHomologation = z.object({ resource_id: z.number(), @@ -105,6 +108,7 @@ export const homologationRouter = honoWithJwt() // Apenas altera o estado se houver pelo menos 3 submissões aceitas if (acceptedSubmissionsCount >= 3) { await serviceResource.setResourceState(validInput.resource_id); + await searchService.indexResource(validInput.resource_id); } return c.json({ message: "Homologação feita com sucesso!", data: homologation }) diff --git a/src/routes/resource.route.ts b/src/routes/resource.route.ts index 0da397c9e4615614470e8de6c04567025c397334..78b30b5be50be13581bb8a945850ec00b282905e 100644 --- a/src/routes/resource.route.ts +++ b/src/routes/resource.route.ts @@ -13,6 +13,7 @@ import { ObjectTypeService } from "@/services/object-type.service"; import { ResourceEducationalStagesService } from "@/services/resource-educational-stages.service"; import { ResourceSubjectsService } from "@/services/resource-subjects.service"; import { UserService } from "@/services/user.service"; +import { SearchService } from "@/services/search.service"; import { createResourceRoute, @@ -35,6 +36,7 @@ const resourceLanguagesService = Container.get(resourceLanguagesRepo); const resourceEducationalStagesService = Container.get(ResourceEducationalStagesService); const resourceSubjectsService = Container.get(ResourceSubjectsService); const userService = Container.get(UserService); +const searchService = Container.get(SearchService); //define um novo Model type ResourceWithObjectTypeName = ResourceModel & { @@ -89,10 +91,13 @@ export const resourceRouter = honoWithJwt() ) input.resource_stats_id = stats.id + const resource = resourceSchema.dto.parse( await service.create(input) ) + await searchService.indexResource(resource.id) + return c.json({ resource, stats }) } catch (e) { return c.json( @@ -118,6 +123,8 @@ export const resourceRouter = honoWithJwt() const input = c.req.valid('json') const resource = resourceSchema.dto.parse(await service.update(input)) + await searchService.indexResource(resource.id) + return c.json({ resource }) } catch (e) { return c.json( @@ -191,6 +198,8 @@ export const resourceRouter = honoWithJwt() const id = +c.req.param('id') const resource = resourceSchema.dto.parse(await service.delete(id)) + await searchService.deindexDocument(resource.id, 'resources') + return c.json(resource) } catch (e) { return c.json( diff --git a/src/routes/search.route.ts b/src/routes/search.route.ts new file mode 100644 index 0000000000000000000000000000000000000000..a30fe330f02d96f45c945232fe5d32cb3d67fca0 --- /dev/null +++ b/src/routes/search.route.ts @@ -0,0 +1,111 @@ +import { SearchService } from "@/services/search.service"; +import Container from "typedi"; +// import { honoWithJwt } from ".."; +import { Hono } from "hono"; +import { z } from "zod"; +import { zValidator } from "@hono/zod-validator"; +import { createApexError, HttpStatus } from "@/services/error.service"; + +const searchService = Container.get(SearchService); + +export const searchRouter = new Hono() + .get( + '/search', + zValidator( + 'query', + z.object({ + query: z.string().min(1, 'Query is required'), + }) + ), + async (c) => { + try { + const { query } = c.req.valid('query'); + + const results = await searchService.searchIndex(query, {}); + + return c.json({ results }); + } catch (e) { + console.log(e) + return c.json( + createApexError({ + status: 'error', + message: 'Search failed', + code: HttpStatus.BAD_REQUEST, + path: c.req.routePath, + suggestion: 'Try a different query', + }), + HttpStatus.BAD_REQUEST + ); + } + } + ) + + .post( + '/index/resource/:id', + async (c) => { + try { + const id: number = +c.req.param('id') + + await searchService.indexResource(id); + return c.json({ message: `resource indexed successfully`}); + } catch (e) { + return c.json( + createApexError({ + status: 'error', + message: 'Failed to index resource', + code: HttpStatus.INTERNAL_SERVER_ERROR, + path: c.req.routePath, + suggestion: 'Check resource format and try again', + }), + HttpStatus.INTERNAL_SERVER_ERROR + ); + } + } + ) + + .post( + '/index/collection/:id', + async (c) => { + try { + const id: number = +c.req.param('id') + + await searchService.indexCollection(id); + return c.json({ message: `collection indexed successfully`}); + } catch (e) { + return c.json( + createApexError({ + status: 'error', + message: 'Failed to index collection', + code: HttpStatus.INTERNAL_SERVER_ERROR, + path: c.req.routePath, + suggestion: 'Check collection format and try again', + }), + HttpStatus.INTERNAL_SERVER_ERROR + ); + } + } + ) + + .post( + '/index/user/:id', + async (c) => { + try { + const id: number = +c.req.param('id') + + await searchService.indexUser(id); + + return c.json({ message: `user indexed successfully`}); + } catch (e) { + return c.json( + createApexError({ + status: 'error', + message: 'Failed to index user', + code: HttpStatus.INTERNAL_SERVER_ERROR, + path: c.req.routePath, + suggestion: 'Check user format and try again', + }), + HttpStatus.INTERNAL_SERVER_ERROR + ); + } + } + ); diff --git a/src/routes/user-institution.route.ts b/src/routes/user-institution.route.ts index a053519ccda16b890faf0f9671d7aec52a47c6b3..097681ceca1dd8104f809eadad7447b9220610f0 100644 --- a/src/routes/user-institution.route.ts +++ b/src/routes/user-institution.route.ts @@ -33,7 +33,6 @@ export const userInstitutionRouter = honoWithJwt() if(user == null || institution == null || alreadyInstitution){ throw new Error() } - console.log('oiiii') const user_institution = userInstitutionRelationSchemas.dto.parse( await service.create(input) diff --git a/src/routes/user.route.ts b/src/routes/user.route.ts index af4790d29824fa329970fac58e50f15eefed2a22..51bad6245337226ef9088727496ad6391245d10f 100644 --- a/src/routes/user.route.ts +++ b/src/routes/user.route.ts @@ -8,6 +8,7 @@ import { createApexError, HttpStatus } from '@/services/error.service' import { followRelationSchemas } from '@/db/relations/followers.relation' import { UserStatsService } from '@/services/user-stats.service' import { FollowRelationService } from '@/services/follow.relation.service' +import { SearchService } from "@/services/search.service"; import { followUserRoute, @@ -27,6 +28,7 @@ import { const service = Container.get(UserService) const followService = Container.get(FollowRelationService) const userStatsService = Container.get(UserStatsService) +const searchService = Container.get(SearchService); export const userRouter = honoWithJwt() .get('/me', meUserRoute, @@ -289,6 +291,8 @@ export const userRouter = honoWithJwt() const deleted_user = userSchemas.userDtoSchema.parse( await service.update(user) ) + + await searchService.deindexDocument(id, 'users') return c.json(deleted_user) } catch (e) { @@ -312,6 +316,8 @@ export const userRouter = honoWithJwt() await service.systemDelete(id) ) + await searchService.deindexDocument(id, 'users') + return c.json(user) } catch (e) { return c.json( diff --git a/src/search/index.ts b/src/search/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..3269b06dd5f6f0d5be213f3c87eefc2eef590fb1 --- /dev/null +++ b/src/search/index.ts @@ -0,0 +1,29 @@ +import { Client, HttpConnection } from '@elastic/elasticsearch' +import env from '@/env' +import { Container } from 'typedi' + +// Initialize Elasticsearch client +export const es = new Client({ + node: env.ELASTICSEARCH_URL, + Connection: HttpConnection, +}) + +// Register the Elasticsearch client in the DI container +console.log("Registering Elasticsearch client..."); +Container.set('ElasticSearchClient', es) + +export const checkElasticsearchConnection = async () => { + try { + const health = await es.cluster.health() + console.log(`Elasticsearch cluster status: ${health.status}`) + } catch (error) { + console.error('Error connecting to Elasticsearch:', error) + process.exit(1) + } +} + +checkElasticsearchConnection() + +export type es = typeof es + +export default es \ No newline at end of file diff --git a/src/services/search.service.ts b/src/services/search.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..5991e1fc8a32b8ff446097a45d78d7d95d2ea417 --- /dev/null +++ b/src/services/search.service.ts @@ -0,0 +1,237 @@ +import { Inject, Service } from "typedi" +import es from '@/search' +import env from '@/env' +import { SearchRepo } from "@/db/repo/search.repo"; + +type ExactFilterFields = { + language?: string; + objectType?: string; + institution?: string; +}; + +@Service() +export class SearchService { + @Inject('ElasticSearchClient') + private readonly esClient!: es + + @Inject() + private readonly repo: SearchRepo + + async indexResource(id: number): Promise<void> { + try { + const documents = await this.repo.findResourcesForIndexing(id); + + if (!documents.length) { + console.warn(`No document found for ID ${id}.`); + return; + } + + const document = documents[0]; + await this.esClient.index({ + index: env.ELASTIC_INDEX_RESOURCES, + id: document.id.toString(), + body: { + name: document.name, + author: document.author ?? "", + description: document.description ?? "", + link: document.link ?? "", + created_at: document.created_at, + educational_stages: document.educational_stages?.split(','), // Converte para array + languages: document.languages?.split(',') ?? [], // Converte para array + subjects: document.subjects?.split(',') ?? [], // Converte para array + license: document.license, + objectType: document.object_type, + state: document.state, + user: document.user, + views: document.views, + downloads: document.downloads, + likes: document.likes, + shares: document.shares, + score: document.score, + comments: document.comments + }, + }); + + console.log(`Document ${document.id} indexed successfully`); + } catch (error) { + console.error(`Error indexing document with ID ${id}:`, error); + } + } + + async indexCollection(id: number): Promise<void> { + try { + const documents = await this.repo.findCollectionsForIndexing(id); + + if (!documents.length) { + console.warn(`No document found for ID ${id}.`); + return; + } + + const document = documents[0]; + await this.esClient.index({ + index: env.ELASTIC_INDEX_COLLECTIONS, + id: document.id.toString(), + body: { + name: document.name, + author: document.author ?? "", + description: document.description ?? "", + created_at: document.created_at, + score: document.score, + views: document.views, + downloads: document.downloads, + likes: document.likes, + shares: document.shares, + follows: document.follows + }, + }); + + console.log(`Document ${document.id} indexed successfully`); + } catch (error) { + console.error(`Error indexing document with ID ${id}:`, error); + } + } + + async indexUser(id: number): Promise<void> { + try { + const documents = await this.repo.findUsersForIndexing(id); + + if (!documents.length) { + console.warn(`No document found for ID ${id}.`); + return; + } + + const document = documents[0]; + await this.esClient.index({ + index: env.ELASTIC_INDEX_USERS, + id: document.id.toString(), + body: { + name: document.name, + username: document.username, + description: document.description ?? "", + roles: document.roles?.split(',') ?? [], + institutions: document.institutions?.split(',') ?? [], // Converte para array + created_at: document.created_at, + is_active: document.is_active, + score: document.score, + likes_received: document.likes_received, + followers: document.followers, + approved_resources: document.approved_resources + }, + }); + + console.log(`Document ${document.id} indexed successfully`); + } catch (error) { + console.error(`Error indexing document with ID ${id}:`, error); + } + } + + // filters: filtros para valores exatos (ex: language: "Português") + // sortBy: filtros para ordenação. No futuro: views, downloads... + // sortOrder: ordenação ascendente ou descendente + // index: lista de indexes + // offset: offset da paginação, número da página + // page_size: tamanho da página + async searchIndex( + query: string, + filters: Partial<ExactFilterFields>, + sortBy?: 'created_at', + sortOrder: 'asc' | 'desc' = 'desc', + index: string[] = [env.ELASTIC_INDEX_USERS, env.ELASTIC_INDEX_COLLECTIONS, env.ELASTIC_INDEX_RESOURCES], + offset: number = 0, + page_size: number = 1000 + ) { + + const filterClauses = Object.entries(filters) + .filter(([, value]) => value !== undefined) + .map(([key, value]) => ({ + term: { [key]: value }, + })); + + let sort: Array<{ [key: string]: { order: 'asc' | 'desc' } }> = []; + if (sortBy) { + sort = [ + { + [sortBy]: { + order: sortOrder, + }, + }, + ]; + } + + const result = await this.esClient.search({ + index: index, + body: { + from: offset, + size: page_size, + query: { + bool: { + must: [ + { + multi_match: { + query, + fuzziness: "AUTO", + fields: ['name', + 'author', + 'description', + 'link', + 'fileFormats', + 'educational_stages', + 'languages', + 'subjects', + 'license', + 'object_type', + 'state', + 'user', + 'roles', + 'institution', + 'city', + 'views', + 'downloads', + 'likes', + 'shares', + 'score', + 'comments', + 'created_at'], + }, + }, + ], + filter: filterClauses, + }, + }, + sort: sort.length > 0 ? sort : undefined, + }, + }) + + return result.hits.hits + } + + async deindexDocument(id: number, index: string): Promise<void> { + try { + await this.esClient.delete({ + index, + id: id.toString(), + }); + + console.log(`Document ${id} deindexed successfully`); + } catch (error) { + console.error(`Error indexing document with ID ${id}:`, error); + } + } + + async updateDocumentField(id: number, index: string, partialDoc: Record<string, unknown>): Promise<void> { + try { + await this.esClient.update({ + index, + id: id.toString(), + body: { + doc: partialDoc, + }, + }); + + console.log(`Document ${id} updated successfully`); + } catch (error) { + console.error(`Error indexing document with ID ${id}:`, error); + } + } + +}