diff --git a/.docker/.manifests/apps__client__package.json b/.docker/.manifests/apps__client__package.json new file mode 100644 index 0000000..2229dae --- /dev/null +++ b/.docker/.manifests/apps__client__package.json @@ -0,0 +1,56 @@ +{ + "name": "@cv/client", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "preview": "vite preview", + "lint": "biome check .", + "lint:fix": "biome check --write .", + "start": "vite", + "typecheck": "tsc -b --noEmit", + "codegen": "graphql-codegen --config codegen.ts", + "codegen:client": "graphql-codegen --config codegen.ts" + }, + "dependencies": { + "@cv/routing": "*", + "@cv/ui": "*", + "@cv/utils": "*", + "@hookform/resolvers": "^5.2.2", + "@tanstack/react-query": "^5.59.0", + "@tanstack/react-query-devtools": "^5.59.0", + "@tanstack/react-virtual": "^3.13.12", + "@types/react-router-dom": "^5.3.3", + "class-variance-authority": "^0.7.1", + "clsx": "^2.0.0", + "graphql": "^16.12.0", + "graphql-request": "^6.1.0", + "graphql-type-json": "^0.3.2", + "react": "^19.2.3", + "react-dom": "^19.2.3", + "react-dropzone": "^14.3.8", + "react-hook-form": "^7.70.0", + "react-router-dom": "^7.9.4", + "tailwind-merge": "^2.0.0", + "zod": "^4.3.6" + }, + "devDependencies": { + "@biomejs/biome": "^2.2.6", + "@cv/biome-config": "*", + "@cv/tsconfig": "*", + "@graphql-codegen/cli": "^6.1.0", + "@graphql-codegen/typescript": "^5.0.6", + "@graphql-codegen/typescript-operations": "^5.0.6", + "@graphql-codegen/typescript-react-query": "^6.1.0", + "@tailwindcss/postcss": "^4.1.15", + "@tailwindcss/vite": "^4.0.0", + "@types/react": "^19.0.6", + "@types/react-dom": "^19.0.3", + "@vitejs/plugin-react": "^4.3.4", + "tailwindcss": "^4.0.0", + "typescript": "^5.6.3", + "vite": "^7.1.10" + } +} diff --git a/.docker/.manifests/apps__docs__package.json b/.docker/.manifests/apps__docs__package.json new file mode 100644 index 0000000..8514c44 --- /dev/null +++ b/.docker/.manifests/apps__docs__package.json @@ -0,0 +1,44 @@ +{ + "name": "@cv/docs", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", + "lint": "biome check .", + "lint:fix": "biome check --write .", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@catppuccin/palette": "^1.4.0", + "@cv/routing": "*", + "@cv/ui": "*", + "@cv/utils": "*", + "@mdx-js/react": "^3.1.1", + "@mdx-js/rollup": "^3.1.1", + "@types/mdx": "^2.0.13", + "highlight.js": "^11.10.0", + "react": "^19.2.3", + "react-dom": "^19.2.3", + "react-markdown": "^9.0.1", + "react-router-dom": "^6.26.1", + "rehype-highlight": "^7.0.0", + "remark-gfm": "^4.0.0" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4.0.0", + "@tailwindcss/vite": "^4.0.0", + "@types/mdast": "^4.0.4", + "@types/react": "^19.0.6", + "@types/react-dom": "^19.0.3", + "@types/unist": "^3.0.3", + "@vitejs/plugin-react": "^4.3.1", + "autoprefixer": "^10.4.20", + "postcss": "^8.4.47", + "tailwindcss": "^4.0.0", + "typescript": "^5.5.3", + "vite": "^5.4.3" + } +} diff --git a/.docker/.manifests/apps__server__package.json b/.docker/.manifests/apps__server__package.json new file mode 100644 index 0000000..9d72006 --- /dev/null +++ b/.docker/.manifests/apps__server__package.json @@ -0,0 +1,118 @@ +{ + "name": "@cv/server", + "version": "0.0.0", + "private": true, + "type": "commonjs", + "scripts": { + "build": "tsup", + "start": "node dist/main.js", + "dev": "nodemon --watch src -e ts --exec \"ts-node -r tsconfig-paths/register src/main.ts\"", + "lint": "biome check .", + "lint:fix": "biome check --write .", + "typecheck": "tsc -p tsconfig.build.json --noEmit", + "test": "jest --config ./test/jest-unit.json", + "test:unit": "jest --config ./test/jest-unit.json", + "test:e2e": "jest --config ./test/jest-e2e.json", + "e2e:coverage:c8:start": "npm run prisma:deploy && npm run build && npx c8 -r text -r lcov node dist/main.js", + "e2e:coverage:c8:report": "npx c8 report -r text -r lcov", + "e2e:coverage:c8": "E2E_EXTERNAL_URL=http://localhost:3000 npm run test:e2e && npm run e2e:coverage:c8:report", + "e2e:coverage:c8:stop": "pkill -f 'node dist/main.js' || true", + "e2e:coverage:all": "node src/scripts/e2e-coverage-all.cjs", + "prepare": "prisma generate || true", + "prisma:generate": "prisma generate", + "prisma:migrate": "prisma migrate dev", + "prisma:deploy": "prisma migrate deploy", + "prisma:studio": "prisma studio", + "seed": "ts-node -r tsconfig-paths/register src/scripts/seed.ts", + "seed:test": "ts-node -r tsconfig-paths/register src/scripts/seed-test.ts", + "test:integration": "vitest run --config vitest.config.ts", + "test:integration:watch": "vitest --config vitest.config.ts", + "test:integration:ui": "vitest --ui --config vitest.config.ts" + }, + "dependencies": { + "@apollo/server": "^4.11.0", + "@cv/ai-parser": "*", + "@cv/ai-provider": "*", + "@cv/auth": "*", + "@cv/cv-renderer": "*", + "@cv/file-storage": "*", + "@cv/file-upload": "*", + "@cv/system": "*", + "@cv/utils": "*", + "@nestjs/apollo": "^12.2.2", + "@nestjs/common": "^10.4.7", + "@nestjs/config": "^3.2.0", + "@nestjs/core": "^10.4.7", + "@nestjs/event-emitter": "^3.0.1", + "@nestjs/graphql": "^12.2.2", + "@nestjs/jwt": "^10.2.0", + "@nestjs/mapped-types": "^2.1.0", + "@nestjs/passport": "^10.0.3", + "@nestjs/platform-express": "^10.4.7", + "@prisma/adapter-pg": "^7.1.0", + "@prisma/client": "^7.1.0", + "@riotbyte-com/nest-service-locator": "0.3.0-rc.4", + "@riotbyte-com/project-q-core": "1.0.19-rc.3", + "@riotbyte-com/project-q-nestjs": "1.0.19-rc.3", + "@riotbyte-com/project-q-prisma": "1.0.19-rc.3", + "@types/cookie-parser": "^1.4.10", + "@types/handlebars": "^4.0.40", + "@types/multer": "^2.0.0", + "apollo-server-express": "^3.13.0", + "bcryptjs": "^2.4.3", + "canvas": "^3.1.0", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.0", + "cookie-parser": "^1.4.7", + "dataloader": "^2.2.3", + "express": "^4.21.2", + "graphql": "^16.12.0", + "graphql-scalars": "^1.23.0", + "graphql-type-json": "^0.3.2", + "handlebars": "^4.7.8", + "mammoth": "^1.11.0", + "multer": "^2.0.2", + "nest-commander": "^3.16.0", + "nestjs-zod": "^5.1.1", + "passport": "^0.7.0", + "passport-jwt": "^4.0.1", + "passport-local": "^1.0.0", + "pdf-parse": "^1.1.4", + "pdf2json": "^3.1.4", + "pdfjs-dist": "^3.11.174", + "pg": "^8.16.3", + "reflect-metadata": "^0.2.2", + "resend": "^6.5.2", + "rxjs": "^7.8.1", + "tesseract.js": "^5.1.1", + "zod": "^4.3.6" + }, + "devDependencies": { + "@biomejs/biome": "^2.2.6", + "@cv/biome-config": "*", + "@cv/tsconfig": "*", + "@faker-js/faker": "^9.9.0", + "@nestjs/testing": "^10.4.7", + "@swc/core": "^1.15.13", + "@types/bcryptjs": "^2.4.6", + "@types/express": "^5.0.0", + "@types/jest": "^29.5.12", + "@types/node": "^22.7.5", + "@types/passport-jwt": "^4.0.1", + "@types/passport-local": "^1.0.38", + "@types/pdf-parse": "^1.1.5", + "@types/pg": "^8.15.6", + "@vitest/ui": "^4.0.16", + "jest": "^29.7.0", + "nodemon": "^3.1.7", + "prisma": "^7.1.0", + "supertest": "^6.3.4", + "ts-jest": "^29.1.2", + "ts-node": "^10.9.2", + "tsconfig-paths": "^4.2.0", + "tslib": "^2.8.0", + "tsup": "^8.5.1", + "typescript": "^5.6.3", + "vitest": "^4.0.16" + } +} diff --git a/.docker/.manifests/apps__worker__package.json b/.docker/.manifests/apps__worker__package.json new file mode 100644 index 0000000..063c5cd --- /dev/null +++ b/.docker/.manifests/apps__worker__package.json @@ -0,0 +1,42 @@ +{ + "name": "@cv/worker", + "version": "0.0.0", + "private": true, + "type": "commonjs", + "scripts": { + "build": "tsup", + "start": "node dist/main.js project-q:work async", + "dev": "nodemon --watch src -e ts --exec \"ts-node src/main.ts project-q:work async\"", + "typecheck": "tsc -p tsconfig.build.json --noEmit", + "lint": "biome check .", + "lint:fix": "biome check --write ." + }, + "dependencies": { + "@cv/file-storage": "*", + "@cv/system": "*", + "@nestjs/common": "^10.4.7", + "@nestjs/config": "^3.2.0", + "@nestjs/core": "^10.4.7", + "@nestjs/event-emitter": "^3.0.1", + "@riotbyte-com/nest-service-locator": "0.3.0-rc.4", + "@riotbyte-com/project-q-core": "1.0.19-rc.3", + "@riotbyte-com/project-q-nestjs": "1.0.19-rc.3", + "@riotbyte-com/project-q-prisma": "1.0.19-rc.3", + "eventemitter2": "^6.4.9", + "nest-commander": "^3.16.0", + "playwright": "^1.52.0", + "playwright-core": "^1.52.0", + "reflect-metadata": "^0.2.2", + "rxjs": "^7.8.1", + "zod": "^4.3.6" + }, + "devDependencies": { + "@cv/tsconfig": "*", + "@swc/core": "^1.15.13", + "@types/node": "^22.7.5", + "nodemon": "^3.1.7", + "ts-node": "^10.9.2", + "tsup": "^8.5.1", + "typescript": "^5.6.3" + } +} diff --git a/.docker/.manifests/packages__ai-parser__package.json b/.docker/.manifests/packages__ai-parser__package.json new file mode 100644 index 0000000..d7ef9fe --- /dev/null +++ b/.docker/.manifests/packages__ai-parser__package.json @@ -0,0 +1,29 @@ +{ + "name": "@cv/ai-parser", + "version": "0.0.0", + "private": true, + "main": "./src/index.ts", + "types": "./src/index.ts", + "scripts": { + "typecheck": "tsc --noEmit", + "test": "jest", + "test:watch": "jest --watch", + "test:coverage": "jest --coverage" + }, + "dependencies": { + "@cv/ai-provider": "*", + "@nestjs/common": "^11.1.3", + "zod": "^4.3.6" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0" + }, + "devDependencies": { + "@cv/tsconfig": "*", + "@types/jest": "^29.5.0", + "@types/node": "^22.7.5", + "jest": "^29.7.0", + "ts-jest": "^29.1.1", + "typescript": "^5.3.3" + } +} diff --git a/.docker/.manifests/packages__ai-provider__package.json b/.docker/.manifests/packages__ai-provider__package.json new file mode 100644 index 0000000..4854bb0 --- /dev/null +++ b/.docker/.manifests/packages__ai-provider__package.json @@ -0,0 +1,31 @@ +{ + "name": "@cv/ai-provider", + "version": "0.0.0", + "private": true, + "main": "./src/index.ts", + "types": "./src/index.ts", + "scripts": { + "typecheck": "tsc --noEmit", + "test": "jest", + "test:watch": "jest --watch", + "test:integration": "jest --testPathPattern='.integration.test.ts$' --runInBand", + "test:all": "jest --testPathIgnorePatterns=[]" + }, + "dependencies": { + "@nestjs/common": "^11.1.3", + "@nestjs/config": "^4.0.2", + "zod": "^4.3.6" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/config": "^4.0.0" + }, + "devDependencies": { + "@cv/tsconfig": "*", + "@types/jest": "^29.5.12", + "@types/node": "^22.7.5", + "jest": "^29.7.0", + "ts-jest": "^29.1.2", + "typescript": "^5.3.3" + } +} diff --git a/.docker/.manifests/packages__auth__package.json b/.docker/.manifests/packages__auth__package.json new file mode 100644 index 0000000..a8677e6 --- /dev/null +++ b/.docker/.manifests/packages__auth__package.json @@ -0,0 +1,54 @@ +{ + "name": "@cv/auth", + "version": "0.0.0", + "private": true, + "main": "./src/index.ts", + "types": "./src/index.ts", + "exports": { + ".": { + "require": "./src/index.ts", + "import": "./src/index.ts", + "types": "./src/index.ts" + } + }, + "files": [ + "src/" + ], + "scripts": { + "lint": "biome check .", + "lint:fix": "biome check --write .", + "typecheck": "tsc -b" + }, + "dependencies": { + "@cv/system": "*", + "@nestjs/common": "^10.4.7", + "@nestjs/config": "^3.2.0", + "@nestjs/core": "^10.4.7", + "@nestjs/event-emitter": "^3.0.1", + "@nestjs/graphql": "^12.2.2", + "@nestjs/jwt": "^10.2.0", + "bcryptjs": "^2.4.3", + "graphql": "^16.12.0", + "reflect-metadata": "^0.2.2", + "zod": "^4.3.6" + }, + "devDependencies": { + "@biomejs/biome": "^2.2.6", + "@cv/biome-config": "*", + "@cv/tsconfig": "*", + "@types/bcryptjs": "^2.4.0", + "@types/express": "^5.0.0", + "typescript": "^5.6.3" + }, + "peerDependencies": { + "@cv/system": "*", + "@nestjs/common": "^10.4.7", + "@nestjs/config": "^3.2.0", + "@nestjs/core": "^10.4.7", + "@nestjs/event-emitter": "^3.0.1", + "@nestjs/graphql": "^12.2.2", + "@nestjs/jwt": "^10.2.0", + "bcryptjs": "^2.4.3", + "graphql": "^16.12.0" + } +} diff --git a/.docker/.manifests/packages__biome-config__package.json b/.docker/.manifests/packages__biome-config__package.json new file mode 100644 index 0000000..fee8c54 --- /dev/null +++ b/.docker/.manifests/packages__biome-config__package.json @@ -0,0 +1,8 @@ +{ + "name": "@cv/biome-config", + "version": "0.0.0", + "private": true, + "files": [ + "biome.json" + ] +} diff --git a/.docker/.manifests/packages__cv-renderer__package.json b/.docker/.manifests/packages__cv-renderer__package.json new file mode 100644 index 0000000..c8084c4 --- /dev/null +++ b/.docker/.manifests/packages__cv-renderer__package.json @@ -0,0 +1,15 @@ +{ + "name": "@cv/cv-renderer", + "version": "0.0.0", + "private": true, + "main": "./src/index.ts", + "types": "./src/index.ts", + "dependencies": { + "handlebars": "^4.7.8" + }, + "devDependencies": { + "@cv/tsconfig": "*", + "@types/node": "^22.7.5", + "typescript": "^5.3.3" + } +} diff --git a/.docker/.manifests/packages__file-storage__package.json b/.docker/.manifests/packages__file-storage__package.json new file mode 100644 index 0000000..e95932c --- /dev/null +++ b/.docker/.manifests/packages__file-storage__package.json @@ -0,0 +1,39 @@ +{ + "name": "@cv/file-storage", + "version": "0.0.0", + "private": true, + "main": "./src/index.ts", + "types": "./src/index.ts", + "exports": { + ".": { + "require": "./src/index.ts", + "import": "./src/index.ts", + "types": "./src/index.ts" + } + }, + "files": [ + "src/" + ], + "scripts": { + "lint": "biome check .", + "lint:fix": "biome check --write .", + "typecheck": "tsc -b" + }, + "dependencies": { + "@aws-sdk/client-s3": "^3.800.0", + "@nestjs/common": "^10.4.7", + "@nestjs/config": "^3.2.0", + "@nestjs/core": "^10.4.7" + }, + "devDependencies": { + "@biomejs/biome": "^2.2.6", + "@cv/biome-config": "*", + "@cv/tsconfig": "*", + "typescript": "^5.6.3" + }, + "peerDependencies": { + "@nestjs/common": "^10.4.7", + "@nestjs/config": "^3.2.0", + "@nestjs/core": "^10.4.7" + } +} diff --git a/.docker/.manifests/packages__file-upload__package.json b/.docker/.manifests/packages__file-upload__package.json new file mode 100644 index 0000000..d709aaa --- /dev/null +++ b/.docker/.manifests/packages__file-upload__package.json @@ -0,0 +1,33 @@ +{ + "name": "@cv/file-upload", + "version": "0.0.0", + "private": true, + "main": "./src/index.ts", + "types": "./src/index.ts", + "scripts": { + "typecheck": "tsc --noEmit", + "test": "jest" + }, + "dependencies": { + "@nestjs/common": "^11.1.3", + "canvas": "^3.1.0", + "mammoth": "^1.6.0", + "pdf-parse": "^1.1.1", + "pdf2json": "^3.1.4", + "pdfjs-dist": "^3.11.174", + "tesseract.js": "^5.1.1", + "zod": "^4.3.6" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0" + }, + "devDependencies": { + "@cv/tsconfig": "*", + "@types/jest": "^29.5.0", + "@types/node": "^22.7.5", + "@types/pdf-parse": "^1.1.5", + "jest": "^29.7.0", + "ts-jest": "^29.1.1", + "typescript": "^5.3.3" + } +} diff --git a/.docker/.manifests/packages__routing__package.json b/.docker/.manifests/packages__routing__package.json new file mode 100644 index 0000000..8be746c --- /dev/null +++ b/.docker/.manifests/packages__routing__package.json @@ -0,0 +1,33 @@ +{ + "name": "@cv/routing", + "version": "0.0.0", + "private": true, + "type": "module", + "main": "./src/index.ts", + "types": "./src/index.ts", + "exports": { + ".": { + "import": "./src/index.ts", + "types": "./src/index.ts" + } + }, + "files": [ + "src/" + ], + "scripts": { + "lint": "biome check .", + "lint:fix": "biome check --write .", + "typecheck": "tsc --noEmit" + }, + "peerDependencies": { + "react": "^19.2.3", + "react-dom": "^19.2.3", + "react-router-dom": "^7.9.4" + }, + "devDependencies": { + "@cv/tsconfig": "workspace:^", + "@types/react": "^19.0.6", + "@types/react-dom": "^19.0.3", + "typescript": "^5.5.3" + } +} diff --git a/.docker/.manifests/packages__system__package.json b/.docker/.manifests/packages__system__package.json new file mode 100644 index 0000000..0870ae2 --- /dev/null +++ b/.docker/.manifests/packages__system__package.json @@ -0,0 +1,57 @@ +{ + "name": "@cv/system", + "version": "0.0.0", + "private": true, + "main": "./src/index.ts", + "types": "./src/index.ts", + "exports": { + ".": { + "require": "./src/index.ts", + "import": "./src/index.ts", + "types": "./src/index.ts" + } + }, + "files": [ + "src/" + ], + "scripts": { + "lint": "biome check .", + "lint:fix": "biome check --write .", + "typecheck": "tsc -b" + }, + "dependencies": { + "@nestjs/common": "^10.4.7", + "@nestjs/config": "^3.2.0", + "@nestjs/core": "^10.4.7", + "@nestjs/event-emitter": "^3.0.1", + "@nestjs/graphql": "^12.2.2", + "@prisma/adapter-pg": "^7.1.0", + "@prisma/client": "^7.1.0", + "dataloader": "^2.2.3", + "graphql": "^16.12.0", + "handlebars": "^4.7.8", + "pg": "^8.16.3", + "reflect-metadata": "^0.2.2", + "resend": "^6.5.2" + }, + "devDependencies": { + "@biomejs/biome": "^2.2.6", + "@cv/biome-config": "*", + "@cv/tsconfig": "*", + "@types/pg": "^8.15.0", + "typescript": "^5.6.3" + }, + "peerDependencies": { + "@nestjs/common": "^10.4.7", + "@nestjs/config": "^3.2.0", + "@nestjs/core": "^10.4.7", + "@nestjs/event-emitter": "^3.0.1", + "@nestjs/graphql": "^12.2.2", + "@prisma/adapter-pg": "^7.1.0", + "@prisma/client": "^7.1.0", + "graphql": "^16.12.0", + "handlebars": "^4.7.8", + "pg": "^8.16.3", + "resend": "^6.5.2" + } +} diff --git a/.docker/.manifests/packages__tsconfig__package.json b/.docker/.manifests/packages__tsconfig__package.json new file mode 100644 index 0000000..a136956 --- /dev/null +++ b/.docker/.manifests/packages__tsconfig__package.json @@ -0,0 +1,10 @@ +{ + "name": "@cv/tsconfig", + "version": "0.0.0", + "private": true, + "files": [ + "tsconfig.base.json", + "tsconfig.node.json", + "tsconfig.library.json" + ] +} diff --git a/.docker/.manifests/packages__ui__package.json b/.docker/.manifests/packages__ui__package.json new file mode 100644 index 0000000..d22584f --- /dev/null +++ b/.docker/.manifests/packages__ui__package.json @@ -0,0 +1,45 @@ +{ + "name": "@cv/ui", + "version": "0.0.0", + "private": true, + "type": "module", + "main": "./src/index.ts", + "types": "./src/index.ts", + "exports": { + ".": { + "import": "./src/index.ts", + "types": "./src/index.ts" + }, + "./styles": "./src/index.css" + }, + "files": [ + "src/", + "*.css" + ], + "scripts": { + "lint": "biome check .", + "lint:fix": "biome check --write .", + "typecheck": "tsc --noEmit" + }, + "peerDependencies": { + "react": "^19.2.3", + "react-dom": "^19.2.3" + }, + "dependencies": { + "@cv/utils": "*", + "@catppuccin/tailwindcss": "^1.0.0", + "@tanstack/react-virtual": "^3.11.2", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", + "tailwind-merge": "^2.5.3", + "zod": "^4.3.6" + }, + "devDependencies": { + "@tailwindcss/cli": "^4.1.16", + "@tailwindcss/postcss": "^4.0.0", + "@types/react": "^19.0.6", + "@types/react-dom": "^19.0.3", + "tailwindcss": "^4.0.0", + "typescript": "^5.5.3" + } +} diff --git a/.docker/.manifests/packages__utils__package.json b/.docker/.manifests/packages__utils__package.json new file mode 100644 index 0000000..45d4939 --- /dev/null +++ b/.docker/.manifests/packages__utils__package.json @@ -0,0 +1,13 @@ +{ + "name": "@cv/utils", + "version": "0.0.0", + "private": true, + "main": "index.ts", + "types": "index.ts", + "scripts": { + "typecheck": "tsc -b" + }, + "devDependencies": { + "@cv/tsconfig": "workspace:*" + } +} diff --git a/.docker/client.Dockerfile b/.docker/client.Dockerfile deleted file mode 100644 index 3a97ca0..0000000 --- a/.docker/client.Dockerfile +++ /dev/null @@ -1,60 +0,0 @@ -FROM node:24-bookworm-slim AS development - -RUN apt-get update && apt-get install -y --no-install-recommends curl && rm -rf /var/lib/apt/lists/* -RUN corepack enable && corepack prepare pnpm@latest --activate - -WORKDIR /app - -# Layer 1: workspace config + lockfile -COPY package.json pnpm-lock.yaml pnpm-workspace.yaml lerna.json ./ - -# Layer 2: package.json manifests only -COPY .docker/.manifests/ /tmp/manifests/ -COPY .docker/restore-manifests.sh /tmp/ -RUN sh /tmp/restore-manifests.sh /tmp/manifests - -# Layer 3: install (skip native addon builds — canvas is server-only) -RUN pnpm install --frozen-lockfile --ignore-scripts - -# Layer 4: source code -COPY packages/ ./packages/ -COPY apps/client/ ./apps/client/ - -EXPOSE 5173 - -WORKDIR /app/apps/client -CMD ["pnpm", "dev"] - -# ---- Production build ---- -FROM node:24-bookworm-slim AS builder - -RUN corepack enable && corepack prepare pnpm@latest --activate - -WORKDIR /app - -COPY package.json pnpm-lock.yaml pnpm-workspace.yaml lerna.json ./ -COPY .docker/.manifests/ /tmp/manifests/ -COPY .docker/restore-manifests.sh /tmp/ -RUN sh /tmp/restore-manifests.sh /tmp/manifests -RUN pnpm install --frozen-lockfile --ignore-scripts - -COPY packages/ ./packages/ -COPY apps/client/ ./apps/client/ - -ARG VITE_SERVER_URL -ARG VITE_DOCS_URL -ENV VITE_SERVER_URL=${VITE_SERVER_URL} -ENV VITE_DOCS_URL=${VITE_DOCS_URL} - -WORKDIR /app/apps/client -RUN pnpm exec vite build - -# ---- Production serve ---- -FROM nginx:alpine AS production - -COPY --from=builder /app/apps/client/dist /usr/share/nginx/html -COPY apps/client/nginx.conf /etc/nginx/conf.d/default.conf - -EXPOSE 80 - -CMD ["nginx", "-g", "daemon off;"] diff --git a/.docker/patch-vendor-links.sh b/.docker/patch-vendor-links.sh deleted file mode 100644 index 6bb641a..0000000 --- a/.docker/patch-vendor-links.sh +++ /dev/null @@ -1,64 +0,0 @@ -#!/bin/sh -# Patches link: paths in package.json to point to vendored copies at -# $ROOT/vendor/ so pnpm install can resolve them with --frozen-lockfile. -# -# Also creates a /riotbyte/project-q/packages/ directory tree that -# mirrors the layout pnpm's lockfile expects (relative link: targets -# resolve to /riotbyte/... inside Docker because the workspace root -# is at /app which is only 1 level deep). -# -# Usage: patch-vendor-links.sh [workspace-root] - -set -e - -ROOT="${1:-.}" - -# Step 1: Patch package.json specifiers (link:host-path → link:$ROOT/vendor/*) -# pnpm validates specifiers match package.json during --frozen-lockfile -find "$ROOT" -name 'package.json' -not -path '*/node_modules/*' | while read -r f; do - if grep -q '"link:.*project-q\|"link:.*nest-service-locator' "$f" 2>/dev/null; then - sed -i \ - -e "s|\"link:[^\"]*project-q/packages/core\"|\"link:$ROOT/vendor/project-q-core\"|g" \ - -e "s|\"link:[^\"]*project-q/packages/framework/nest\"|\"link:$ROOT/vendor/project-q-nestjs\"|g" \ - -e "s|\"link:[^\"]*project-q/packages/transport/prisma\"|\"link:$ROOT/vendor/project-q-prisma\"|g" \ - -e "s|\"link:[^\"]*nest-service-locator\"|\"link:$ROOT/vendor/nest-service-locator\"|g" \ - "$f" - echo "Patched $f" - fi -done - -# Step 2: Patch lockfile specifiers to match the package.json changes -# Only patch absolute specifier lines (link:/...), leave relative -# version lines untouched — they resolve via mount points below. -LOCKFILE="$ROOT/pnpm-lock.yaml" -if [ -f "$LOCKFILE" ] && grep -q 'project-q\|nest-service-locator' "$LOCKFILE" 2>/dev/null; then - sed -i \ - -e "s|link:/[^ ]*project-q/packages/core|link:$ROOT/vendor/project-q-core|g" \ - -e "s|link:/[^ ]*project-q/packages/framework/nest|link:$ROOT/vendor/project-q-nestjs|g" \ - -e "s|link:/[^ ]*project-q/packages/transport/prisma|link:$ROOT/vendor/project-q-prisma|g" \ - -e "s|link:/[^ ]*nest-service-locator|link:$ROOT/vendor/nest-service-locator|g" \ - "$LOCKFILE" - echo "Patched $LOCKFILE" -fi - -# Step 3: Create mount points so pnpm's relative symlinks resolve correctly. -# Inside Docker, relative link: paths from the lockfile resolve to -# /riotbyte/project-q/packages/* (because the workspace is at /app). -# We create symlinks there pointing to the vendored copies inside the -# workspace tree (at $ROOT/vendor/). -mkdir -p /riotbyte/project-q/packages/framework /riotbyte/project-q/packages/transport /riotbyte -ln -sfn "$ROOT/vendor/project-q-core" /riotbyte/project-q/packages/core -ln -sfn "$ROOT/vendor/project-q-nestjs" /riotbyte/project-q/packages/framework/nest -ln -sfn "$ROOT/vendor/project-q-prisma" /riotbyte/project-q/packages/transport/prisma -ln -sfn "$ROOT/vendor/nest-service-locator" /riotbyte/nest-service-locator -echo "Created mount points at /riotbyte/" - -# Step 4: Create root-level node_modules symlinks so vendored packages -# can resolve each other (e.g. transport-postgres depends on core). -# pnpm only creates per-workspace symlinks for link: deps, not at root. -mkdir -p "$ROOT/node_modules/@riotbyte" -ln -sfn "$ROOT/vendor/project-q-core" "$ROOT/node_modules/@riotbyte/project-q-core" -ln -sfn "$ROOT/vendor/project-q-nestjs" "$ROOT/node_modules/@riotbyte/project-q-nestjs" -ln -sfn "$ROOT/vendor/project-q-prisma" "$ROOT/node_modules/@riotbyte/project-q-prisma" -ln -sfn "$ROOT/vendor/nest-service-locator" "$ROOT/node_modules/@riotbyte/nest-service-locator" -echo "Created root node_modules symlinks for vendored packages" diff --git a/.docker/server.Dockerfile b/.docker/server.Dockerfile index 0cec5ae..f438caf 100644 --- a/.docker/server.Dockerfile +++ b/.docker/server.Dockerfile @@ -19,30 +19,18 @@ COPY .docker/.manifests/ /tmp/manifests/ COPY .docker/restore-manifests.sh /tmp/ RUN sh /tmp/restore-manifests.sh /tmp/manifests -# Layer 2b: vendor project-q packages (from additional build context) -COPY --from=project-q packages/core/package.json ./vendor/project-q-core/package.json -COPY --from=project-q packages/core/dist/ ./vendor/project-q-core/dist/ -COPY --from=project-q packages/framework/nest/package.json ./vendor/project-q-nestjs/package.json -COPY --from=project-q packages/framework/nest/dist/ ./vendor/project-q-nestjs/dist/ -COPY --from=project-q packages/transport/prisma/package.json ./vendor/project-q-prisma/package.json -COPY --from=project-q packages/transport/prisma/dist/ ./vendor/project-q-prisma/dist/ -COPY --from=nest-service-locator package.json ./vendor/nest-service-locator/package.json -COPY --from=nest-service-locator dist/src/ ./vendor/nest-service-locator/dist/src/ - -# Patch link: → file: for project-q packages -COPY .docker/patch-vendor-links.sh /tmp/ -RUN sh /tmp/patch-vendor-links.sh /app - -# Layer 3: install (lockfile was patched above to match vendored paths) -RUN NPM_CONFIG_SHAMEFULLY_HOIST=true pnpm install --frozen-lockfile +# GHCR npm auth for @riotbyte-com packages +ARG GITHUB_TOKEN +COPY .npmrc .npmrc +RUN echo "//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}" >> .npmrc + +# Layer 3: install +RUN pnpm install --frozen-lockfile # Layer 4: source code (changes frequently) COPY packages/ ./packages/ COPY apps/server/ ./apps/server/ -# Patch server package.json too (COPY overwrote the patched version) -RUN sh /tmp/patch-vendor-links.sh /app - # Generate Prisma client WORKDIR /app/apps/server RUN pnpm exec prisma generate @@ -68,21 +56,14 @@ COPY .docker/.manifests/ /tmp/manifests/ COPY .docker/restore-manifests.sh /tmp/ RUN sh /tmp/restore-manifests.sh /tmp/manifests -COPY --from=project-q packages/core/package.json ./vendor/project-q-core/package.json -COPY --from=project-q packages/core/dist/ ./vendor/project-q-core/dist/ -COPY --from=project-q packages/framework/nest/package.json ./vendor/project-q-nestjs/package.json -COPY --from=project-q packages/framework/nest/dist/ ./vendor/project-q-nestjs/dist/ -COPY --from=project-q packages/transport/prisma/package.json ./vendor/project-q-prisma/package.json -COPY --from=project-q packages/transport/prisma/dist/ ./vendor/project-q-prisma/dist/ - -COPY .docker/patch-vendor-links.sh /tmp/ -RUN sh /tmp/patch-vendor-links.sh /app +ARG GITHUB_TOKEN +COPY .npmrc .npmrc +RUN echo "//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}" >> .npmrc -RUN NPM_CONFIG_SHAMEFULLY_HOIST=true pnpm install --frozen-lockfile +RUN pnpm install --frozen-lockfile COPY packages/ ./packages/ COPY apps/server/ ./apps/server/ -RUN sh /tmp/patch-vendor-links.sh /app WORKDIR /app/apps/server RUN pnpm exec prisma generate @@ -106,19 +87,11 @@ COPY .docker/.manifests/ /tmp/manifests/ COPY .docker/restore-manifests.sh /tmp/ RUN sh /tmp/restore-manifests.sh /tmp/manifests -COPY --from=project-q packages/core/package.json ./vendor/project-q-core/package.json -COPY --from=project-q packages/core/dist/ ./vendor/project-q-core/dist/ -COPY --from=project-q packages/framework/nest/package.json ./vendor/project-q-nestjs/package.json -COPY --from=project-q packages/framework/nest/dist/ ./vendor/project-q-nestjs/dist/ -COPY --from=project-q packages/transport/prisma/package.json ./vendor/project-q-prisma/package.json -COPY --from=project-q packages/transport/prisma/dist/ ./vendor/project-q-prisma/dist/ -COPY --from=nest-service-locator package.json ./vendor/nest-service-locator/package.json -COPY --from=nest-service-locator dist/src/ ./vendor/nest-service-locator/dist/src/ +ARG GITHUB_TOKEN +COPY .npmrc .npmrc +RUN echo "//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}" >> .npmrc -COPY .docker/patch-vendor-links.sh /tmp/ -RUN sh /tmp/patch-vendor-links.sh /app - -RUN NPM_CONFIG_SHAMEFULLY_HOIST=true pnpm install --frozen-lockfile --prod +RUN pnpm install --frozen-lockfile --prod # Copy Prisma schema + generated client COPY apps/server/prisma/ ./apps/server/prisma/ @@ -128,6 +101,9 @@ COPY --from=builder /app/node_modules/.prisma/ ./node_modules/.prisma/ # Copy bundled output (single file, @cv/* packages inlined) COPY --from=builder /app/apps/server/dist/ ./apps/server/dist/ +# Remove auth token from final image +RUN sed -i '/_authToken/d' .npmrc + ENV NODE_ENV=production EXPOSE 3000 diff --git a/.docker/worker.Dockerfile b/.docker/worker.Dockerfile index 01147e9..35a6b2b 100644 --- a/.docker/worker.Dockerfile +++ b/.docker/worker.Dockerfile @@ -13,33 +13,20 @@ COPY .docker/.manifests/ /tmp/manifests/ COPY .docker/restore-manifests.sh /tmp/ RUN sh /tmp/restore-manifests.sh /tmp/manifests -# Layer 2b: vendor project-q packages (inside workspace tree so Node -# module resolution walks up to /app/node_modules) -COPY --from=project-q packages/core/package.json ./vendor/project-q-core/package.json -COPY --from=project-q packages/core/dist/ ./vendor/project-q-core/dist/ -COPY --from=project-q packages/framework/nest/package.json ./vendor/project-q-nestjs/package.json -COPY --from=project-q packages/framework/nest/dist/ ./vendor/project-q-nestjs/dist/ -COPY --from=project-q packages/transport/prisma/package.json ./vendor/project-q-prisma/package.json -COPY --from=project-q packages/transport/prisma/dist/ ./vendor/project-q-prisma/dist/ -COPY --from=nest-service-locator package.json ./vendor/nest-service-locator/package.json -COPY --from=nest-service-locator dist/src/ ./vendor/nest-service-locator/dist/src/ - -# Patch link: specifiers + create mount points for lockfile relative paths -COPY .docker/patch-vendor-links.sh /tmp/ -RUN sh /tmp/patch-vendor-links.sh /app - -# Layer 3: install (lockfile was patched above to match vendored paths) -# shamefully-hoist makes all deps available at /app/node_modules/ so -# vendored project-q packages (linked via link: protocol) can resolve -# their transitive deps (uuid, zod, pg) through Node's upward walk. -RUN NPM_CONFIG_SHAMEFULLY_HOIST=true pnpm install --frozen-lockfile +# GHCR npm auth for @riotbyte-com packages +ARG GITHUB_TOKEN +COPY .npmrc .npmrc +RUN echo "//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}" >> .npmrc + +# Layer 3: install +RUN pnpm install --frozen-lockfile # Layer 4: source code COPY packages/tsconfig/ ./packages/tsconfig/ COPY packages/system/ ./packages/system/ +COPY packages/file-storage/ ./packages/file-storage/ COPY apps/server/prisma/ ./apps/server/prisma/ COPY apps/worker/ ./apps/worker/ -RUN sh /tmp/patch-vendor-links.sh /app # Generate Prisma client (@cv/system depends on @prisma/client) RUN cd apps/server && pnpm exec prisma generate @@ -63,26 +50,17 @@ COPY .docker/.manifests/ /tmp/manifests/ COPY .docker/restore-manifests.sh /tmp/ RUN sh /tmp/restore-manifests.sh /tmp/manifests -COPY --from=project-q packages/core/package.json ./vendor/project-q-core/package.json -COPY --from=project-q packages/core/dist/ ./vendor/project-q-core/dist/ -COPY --from=project-q packages/framework/nest/package.json ./vendor/project-q-nestjs/package.json -COPY --from=project-q packages/framework/nest/dist/ ./vendor/project-q-nestjs/dist/ -COPY --from=project-q packages/transport/prisma/package.json ./vendor/project-q-prisma/package.json -COPY --from=project-q packages/transport/prisma/dist/ ./vendor/project-q-prisma/dist/ - -COPY .docker/patch-vendor-links.sh /tmp/ -RUN sh /tmp/patch-vendor-links.sh /app +ARG GITHUB_TOKEN +COPY .npmrc .npmrc +RUN echo "//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}" >> .npmrc -# shamefully-hoist makes all deps available at /app/node_modules/ so -# vendored project-q packages (linked via link: protocol) can resolve -# their transitive deps (uuid, zod, pg) through Node's upward walk. -RUN NPM_CONFIG_SHAMEFULLY_HOIST=true pnpm install --frozen-lockfile +RUN pnpm install --frozen-lockfile COPY packages/tsconfig/ ./packages/tsconfig/ COPY packages/system/ ./packages/system/ +COPY packages/file-storage/ ./packages/file-storage/ COPY apps/server/prisma/ ./apps/server/prisma/ COPY apps/worker/ ./apps/worker/ -RUN sh /tmp/patch-vendor-links.sh /app RUN cd apps/server && pnpm exec prisma generate @@ -101,19 +79,11 @@ COPY .docker/.manifests/ /tmp/manifests/ COPY .docker/restore-manifests.sh /tmp/ RUN sh /tmp/restore-manifests.sh /tmp/manifests -COPY --from=project-q packages/core/package.json ./vendor/project-q-core/package.json -COPY --from=project-q packages/core/dist/ ./vendor/project-q-core/dist/ -COPY --from=project-q packages/framework/nest/package.json ./vendor/project-q-nestjs/package.json -COPY --from=project-q packages/framework/nest/dist/ ./vendor/project-q-nestjs/dist/ -COPY --from=project-q packages/transport/prisma/package.json ./vendor/project-q-prisma/package.json -COPY --from=project-q packages/transport/prisma/dist/ ./vendor/project-q-prisma/dist/ -COPY --from=nest-service-locator package.json ./vendor/nest-service-locator/package.json -COPY --from=nest-service-locator dist/src/ ./vendor/nest-service-locator/dist/src/ +ARG GITHUB_TOKEN +COPY .npmrc .npmrc +RUN echo "//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}" >> .npmrc -COPY .docker/patch-vendor-links.sh /tmp/ -RUN sh /tmp/patch-vendor-links.sh /app - -RUN NPM_CONFIG_SHAMEFULLY_HOIST=true pnpm install --frozen-lockfile --prod +RUN pnpm install --frozen-lockfile --prod # Install Chromium + system deps via Playwright RUN cd apps/worker && pnpm exec playwright install --with-deps chromium @@ -121,6 +91,9 @@ RUN cd apps/worker && pnpm exec playwright install --with-deps chromium # Copy compiled output COPY --from=builder /app/apps/worker/dist/ ./apps/worker/dist/ +# Remove auth token from final image +RUN sed -i '/_authToken/d' .npmrc + ENV NODE_ENV=production WORKDIR /app/apps/worker diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml new file mode 100644 index 0000000..1b6f99c --- /dev/null +++ b/.github/workflows/create-release.yml @@ -0,0 +1,63 @@ +name: Create release + +on: + workflow_dispatch: + inputs: + bump: + description: "Version bump type" + required: true + type: choice + options: + - patch + - minor + - major + +permissions: + contents: write + +jobs: + release: + name: Create tag and release + runs-on: ubuntu-latest + steps: + - uses: actions/create-github-app-token@v1 + id: app-token + with: + app-id: ${{ vars.RIOTBYTE_BOT_APP_ID }} + private-key: ${{ secrets.RIOTBYTE_BOT_PRIVATE_KEY }} + + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Determine next version + id: version + run: | + LATEST=$(git tag --list --sort=-v:refname '[0-9]*.[0-9]*.[0-9]*' --merged HEAD | head -n1) + + if [ -z "$LATEST" ]; then + LATEST="0.0.0" + fi + + IFS='.' read -r MAJOR MINOR PATCH <<< "$LATEST" + + case "${{ inputs.bump }}" in + major) MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 ;; + minor) MINOR=$((MINOR + 1)); PATCH=0 ;; + patch) PATCH=$((PATCH + 1)) ;; + esac + + NEXT="${MAJOR}.${MINOR}.${PATCH}" + echo "previous=${LATEST}" >> "$GITHUB_OUTPUT" + echo "next=${NEXT}" >> "$GITHUB_OUTPUT" + echo "Bumping ${LATEST} → ${NEXT} (${{ inputs.bump }})" + + - name: Create tag and GitHub release + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + run: | + gh release create "${{ steps.version.outputs.next }}" \ + --target "${{ github.sha }}" \ + --title "${{ steps.version.outputs.next }}" \ + --generate-notes \ + --notes-start-tag "${{ steps.version.outputs.previous }}" diff --git a/.github/workflows/deploy-client.yml b/.github/workflows/deploy-client.yml new file mode 100644 index 0000000..19b70e4 --- /dev/null +++ b/.github/workflows/deploy-client.yml @@ -0,0 +1,34 @@ +name: Deploy client to Cloudflare Pages + +on: + push: + tags: + - "*.*.*" + +jobs: + deploy: + name: Build and deploy SPA + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + with: + version: latest + + - uses: actions/setup-node@v4 + with: + node-version: 24 + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build and deploy + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + run: ./ci/deploy-client.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..5947d8e --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,22 @@ +name: Build and publish Docker images + +on: + push: + tags: + - "*.*.*" + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - uses: actions/checkout@v4 + + - name: Build and push images + env: + VERSION: ${{ github.ref_name }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: ./ci/build-images.sh diff --git a/.npmrc b/.npmrc index 6c2b9be..96ab8ea 100644 --- a/.npmrc +++ b/.npmrc @@ -1,2 +1,3 @@ link-workspace-packages=true prefer-workspace-packages=true +@riotbyte-com:registry=https://npm.pkg.github.com diff --git a/apps/server/package.json b/apps/server/package.json index 4e9f153..9d72006 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -35,6 +35,7 @@ "@cv/ai-provider": "*", "@cv/auth": "*", "@cv/cv-renderer": "*", + "@cv/file-storage": "*", "@cv/file-upload": "*", "@cv/system": "*", "@cv/utils": "*", @@ -50,10 +51,10 @@ "@nestjs/platform-express": "^10.4.7", "@prisma/adapter-pg": "^7.1.0", "@prisma/client": "^7.1.0", - "@riotbyte/nest-service-locator": "link:/Users/niels/Developer/riotbyte/nest-service-locator", - "@riotbyte/project-q-core": "link:/Users/niels/Developer/riotbyte/project-q/packages/core", - "@riotbyte/project-q-nestjs": "link:/Users/niels/Developer/riotbyte/project-q/packages/framework/nest", - "@riotbyte/project-q-prisma": "link:/Users/niels/Developer/riotbyte/project-q/packages/transport/prisma", + "@riotbyte-com/nest-service-locator": "0.3.0-rc.4", + "@riotbyte-com/project-q-core": "1.0.19-rc.3", + "@riotbyte-com/project-q-nestjs": "1.0.19-rc.3", + "@riotbyte-com/project-q-prisma": "1.0.19-rc.3", "@types/cookie-parser": "^1.4.10", "@types/handlebars": "^4.0.40", "@types/multer": "^2.0.0", diff --git a/apps/server/src/config/env.validation.ts b/apps/server/src/config/env.validation.ts index cfd7d99..74e7480 100644 --- a/apps/server/src/config/env.validation.ts +++ b/apps/server/src/config/env.validation.ts @@ -92,8 +92,13 @@ export const envSchema = z ANTHROPIC_BASE_URL: z.string().url().default("https://api.anthropic.com"), ANTHROPIC_MODEL: z.string().default("claude-sonnet-4-5-20250929"), - // PDF Output + // File Storage + FILE_STORAGE_DRIVER: z.enum(["disk", "r2"]).default("disk"), PDF_OUTPUT_DIR: z.string().default("./pdf-output"), + R2_ENDPOINT: z.string().optional().default(""), + R2_ACCESS_KEY_ID: z.string().optional().default(""), + R2_SECRET_ACCESS_KEY: z.string().optional().default(""), + R2_BUCKET: z.string().optional().default(""), }) .superRefine((data, ctx) => { if (data.AI_PROVIDER === "openai" && !data.OPENAI_API_KEY) { @@ -111,6 +116,18 @@ export const envSchema = z path: ["ANTHROPIC_API_KEY"], }); } + + if (data.FILE_STORAGE_DRIVER === "r2") { + for (const key of ["R2_ENDPOINT", "R2_ACCESS_KEY_ID", "R2_SECRET_ACCESS_KEY", "R2_BUCKET"] as const) { + if (!data[key]) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: `${key} is required when FILE_STORAGE_DRIVER=r2`, + path: [key], + }); + } + } + } }); export type Env = z.infer; diff --git a/apps/server/src/config/file-storage.config.ts b/apps/server/src/config/file-storage.config.ts new file mode 100644 index 0000000..a4b2b6c --- /dev/null +++ b/apps/server/src/config/file-storage.config.ts @@ -0,0 +1,31 @@ +import { Injectable } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import type { + FileStorageConfigProvider, + FileStorageModuleConfig, +} from "@cv/file-storage"; +import type { Env } from "./env.validation"; + +@Injectable() +export class FileStorageConfig implements FileStorageConfigProvider { + constructor(private readonly configService: ConfigService) {} + + createFileStorageConfig(): FileStorageModuleConfig { + const driver = this.configService.get("FILE_STORAGE_DRIVER"); + + if (driver === "r2") { + return { + driver: "r2", + endpoint: this.configService.get("R2_ENDPOINT"), + accessKeyId: this.configService.get("R2_ACCESS_KEY_ID"), + secretAccessKey: this.configService.get("R2_SECRET_ACCESS_KEY"), + bucket: this.configService.get("R2_BUCKET"), + }; + } + + return { + driver: "disk", + baseDir: this.configService.get("PDF_OUTPUT_DIR"), + }; + } +} diff --git a/apps/server/src/modules/app.module.ts b/apps/server/src/modules/app.module.ts index 4a08a57..8e6f1e2 100644 --- a/apps/server/src/modules/app.module.ts +++ b/apps/server/src/modules/app.module.ts @@ -1,4 +1,5 @@ import { AuthModule, AuthorizationModule, UserModule } from "@cv/auth"; +import { FileStorageModule } from "@cv/file-storage"; import { BaseModule, DatabaseModule, @@ -13,6 +14,7 @@ import { JwtModule } from "@nestjs/jwt"; import type { Request, Response } from "express"; import { AppConfigModule } from "@/config/config.module"; import { validate } from "@/config/env.validation"; +import { FileStorageConfig } from "@/config/file-storage.config"; import { AdminModule } from "./admin/admin.module"; import { AiCallLogModule } from "./admin/ai-call-log.module"; import { AppModule as AppModuleComponent } from "./app/app.module"; @@ -65,6 +67,9 @@ import { VacancyModule } from "./vacancies/vacancy.module"; res, }), }), + FileStorageModule.forRootAsync({ + useClass: FileStorageConfig, + }), AiCallLogModule, AppConfigModule, BaseModule, diff --git a/apps/server/src/modules/cv-template/graphql/cv-template.resolver.ts b/apps/server/src/modules/cv-template/graphql/cv-template.resolver.ts index 334cf67..1465487 100644 --- a/apps/server/src/modules/cv-template/graphql/cv-template.resolver.ts +++ b/apps/server/src/modules/cv-template/graphql/cv-template.resolver.ts @@ -7,8 +7,8 @@ import { import { PaginationService, UuidFactoryService } from "@cv/system"; import { UseGuards } from "@nestjs/common"; import { Args, Mutation, Query, Resolver } from "@nestjs/graphql"; -import type { MessageBus } from "@riotbyte/project-q-core"; -import { InjectMessageBus } from "@riotbyte/project-q-nestjs"; +import type { MessageBus } from "@riotbyte-com/project-q-core"; +import { InjectMessageBus } from "@riotbyte-com/project-q-nestjs"; import { CurrentUser } from "@/modules/current-user/current-user.decorator"; import { RenderPdfMessage } from "@/modules/messenger/messages/render-pdf.message"; import { CVRendererService } from "../cv-renderer.service"; diff --git a/apps/server/src/modules/cv-template/pdf-download.controller.ts b/apps/server/src/modules/cv-template/pdf-download.controller.ts index 02736d0..3860fca 100644 --- a/apps/server/src/modules/cv-template/pdf-download.controller.ts +++ b/apps/server/src/modules/cv-template/pdf-download.controller.ts @@ -1,34 +1,28 @@ -import { existsSync } from "node:fs"; -import { join } from "node:path"; import type { User as DomainUser } from "@cv/auth"; import { AuthorizationService, JwtAuthGuard } from "@cv/auth"; +import { FILE_STORAGE, type FileStorage } from "@cv/file-storage"; import { Controller, Get, + Inject, NotFoundException, Param, Req, Res, UseGuards, } from "@nestjs/common"; -import { ConfigService } from "@nestjs/config"; import type { Response } from "express"; -import type { Env } from "@/config/env.validation"; import { CVService } from "./cv.service"; import { CV } from "./graphql/cv.type"; @Controller("api/cv") @UseGuards(JwtAuthGuard) export class PdfDownloadController { - private readonly pdfOutputDir: string; - constructor( private readonly cvService: CVService, private readonly authorizationService: AuthorizationService, - configService: ConfigService, - ) { - this.pdfOutputDir = configService.get("PDF_OUTPUT_DIR"); - } + @Inject(FILE_STORAGE) private readonly storage: FileStorage, + ) {} @Get(":id/pdf") async downloadPdf( @@ -39,13 +33,20 @@ export class PdfDownloadController { const cv = await this.cvService.findByIdOrFail(id); await this.authorizationService.canView(req.user, cv, CV); - const filePath = join(this.pdfOutputDir, `${id}.pdf`); + const key = `${id}.pdf`; - if (!existsSync(filePath)) { + if (!(await this.storage.exists(key))) { throw new NotFoundException("PDF not yet available"); } + const pdf = await this.storage.read(key); const safeTitle = cv.title.replace(/[^a-zA-Z0-9_\- ]/g, ""); - res.download(filePath, `${safeTitle}.pdf`); + + res.set({ + "Content-Type": "application/pdf", + "Content-Disposition": `attachment; filename="${safeTitle}.pdf"`, + "Content-Length": pdf.length.toString(), + }); + res.send(pdf); } } diff --git a/apps/server/src/modules/messenger/logger.provider.ts b/apps/server/src/modules/messenger/logger.provider.ts index 58a7e1d..ad00320 100644 --- a/apps/server/src/modules/messenger/logger.provider.ts +++ b/apps/server/src/modules/messenger/logger.provider.ts @@ -1,5 +1,5 @@ import { Logger as NestLogger } from "@nestjs/common"; -import type { Logger } from "@riotbyte/project-q-core"; +import type { Logger } from "@riotbyte-com/project-q-core"; export class NestProjectQLogger implements Logger { private readonly logger = new NestLogger("ProjectQ"); diff --git a/apps/server/src/modules/messenger/messages/render-pdf.message.ts b/apps/server/src/modules/messenger/messages/render-pdf.message.ts index 479c5ab..e39cf5d 100644 --- a/apps/server/src/modules/messenger/messages/render-pdf.message.ts +++ b/apps/server/src/modules/messenger/messages/render-pdf.message.ts @@ -1,4 +1,4 @@ -import { defineZodMessage } from "@riotbyte/project-q-core"; +import { defineZodMessage } from "@riotbyte-com/project-q-core"; import { z } from "zod/v4"; export const RenderPdfMessage = defineZodMessage( diff --git a/apps/server/src/modules/messenger/messenger.module.ts b/apps/server/src/modules/messenger/messenger.module.ts index 280e908..11851a7 100644 --- a/apps/server/src/modules/messenger/messenger.module.ts +++ b/apps/server/src/modules/messenger/messenger.module.ts @@ -1,5 +1,5 @@ import { Module } from "@nestjs/common"; -import { LoggerProvider, MessengerModule } from "@riotbyte/project-q-nestjs"; +import { LoggerProvider, MessengerModule } from "@riotbyte-com/project-q-nestjs"; import { NestProjectQLogger } from "./logger.provider"; import { PrismaTransportModule } from "./prisma-transport.module"; diff --git a/apps/server/src/modules/messenger/prisma-transport.module.ts b/apps/server/src/modules/messenger/prisma-transport.module.ts index bb015ab..24aa4b2 100644 --- a/apps/server/src/modules/messenger/prisma-transport.module.ts +++ b/apps/server/src/modules/messenger/prisma-transport.module.ts @@ -1,6 +1,6 @@ import { Module } from "@nestjs/common"; import { DatabaseModule, PrismaService } from "@cv/system"; -import { PrismaClientToken, PrismaTransportFactory } from "@riotbyte/project-q-prisma"; +import { PrismaClientToken, PrismaTransportFactory } from "@riotbyte-com/project-q-prisma"; @Module({ imports: [DatabaseModule], diff --git a/apps/worker/package.json b/apps/worker/package.json index 35b638f..063c5cd 100644 --- a/apps/worker/package.json +++ b/apps/worker/package.json @@ -12,15 +12,16 @@ "lint:fix": "biome check --write ." }, "dependencies": { + "@cv/file-storage": "*", "@cv/system": "*", "@nestjs/common": "^10.4.7", "@nestjs/config": "^3.2.0", "@nestjs/core": "^10.4.7", "@nestjs/event-emitter": "^3.0.1", - "@riotbyte/nest-service-locator": "link:/Users/niels/Developer/riotbyte/nest-service-locator", - "@riotbyte/project-q-core": "link:/Users/niels/Developer/riotbyte/project-q/packages/core", - "@riotbyte/project-q-nestjs": "link:/Users/niels/Developer/riotbyte/project-q/packages/framework/nest", - "@riotbyte/project-q-prisma": "link:/Users/niels/Developer/riotbyte/project-q/packages/transport/prisma", + "@riotbyte-com/nest-service-locator": "0.3.0-rc.4", + "@riotbyte-com/project-q-core": "1.0.19-rc.3", + "@riotbyte-com/project-q-nestjs": "1.0.19-rc.3", + "@riotbyte-com/project-q-prisma": "1.0.19-rc.3", "eventemitter2": "^6.4.9", "nest-commander": "^3.16.0", "playwright": "^1.52.0", diff --git a/apps/worker/src/config.ts b/apps/worker/src/config.ts index 1556250..05000da 100644 --- a/apps/worker/src/config.ts +++ b/apps/worker/src/config.ts @@ -24,4 +24,9 @@ export const config = { .split(",") .map((s) => s.trim()) .filter(Boolean), + fileStorageDriver: (process.env["FILE_STORAGE_DRIVER"] ?? "disk") as "disk" | "r2", + r2Endpoint: process.env["R2_ENDPOINT"] ?? "", + r2AccessKeyId: process.env["R2_ACCESS_KEY_ID"] ?? "", + r2SecretAccessKey: process.env["R2_SECRET_ACCESS_KEY"] ?? "", + r2Bucket: process.env["R2_BUCKET"] ?? "", } as const; diff --git a/apps/worker/src/handlers/render-pdf.handler.ts b/apps/worker/src/handlers/render-pdf.handler.ts index 4257acc..776df1e 100644 --- a/apps/worker/src/handlers/render-pdf.handler.ts +++ b/apps/worker/src/handlers/render-pdf.handler.ts @@ -1,8 +1,7 @@ -import * as fs from "node:fs/promises"; -import * as path from "node:path"; import { Inject, Injectable, Logger } from "@nestjs/common"; -import type { Envelope, Handler } from "@riotbyte/project-q-core"; -import { HandlerTag } from "@riotbyte/project-q-nestjs"; +import type { Envelope, Handler } from "@riotbyte-com/project-q-core"; +import { HandlerTag } from "@riotbyte-com/project-q-nestjs"; +import { FILE_STORAGE, type FileStorage } from "@cv/file-storage"; import type { WorkerConfig } from "../config"; import { HtmlToPdfService } from "../pdf/html-to-pdf.service"; import { WORKER_CONFIG } from "../worker.module"; @@ -21,6 +20,7 @@ export class RenderPdfHandler implements Handler { constructor( private readonly pdfService: HtmlToPdfService, @Inject(WORKER_CONFIG) private readonly config: WorkerConfig, + @Inject(FILE_STORAGE) private readonly storage: FileStorage, ) {} async handle(envelope: Envelope): Promise { @@ -32,10 +32,9 @@ export class RenderPdfHandler implements Handler { const pdf = await this.pdfService.convert(html, this.config.pdfTimeoutMs); - await fs.mkdir(this.config.pdfOutputDir, { recursive: true }); - const outputPath = path.join(this.config.pdfOutputDir, `${cvId}.pdf`); - await fs.writeFile(outputPath, pdf); + const key = `${cvId}.pdf`; + await this.storage.write(key, pdf); - this.logger.log(`PDF written to ${outputPath} (${pdf.length} bytes)`); + this.logger.log(`PDF stored as ${key} (${pdf.length} bytes)`); } } diff --git a/apps/worker/src/heartbeat/heartbeat.listener.ts b/apps/worker/src/heartbeat/heartbeat.listener.ts index 9764105..d8d669c 100644 --- a/apps/worker/src/heartbeat/heartbeat.listener.ts +++ b/apps/worker/src/heartbeat/heartbeat.listener.ts @@ -3,7 +3,7 @@ import { WorkerRunningEvent, WorkerStartedEvent, WorkerStoppedEvent, -} from "@riotbyte/project-q-core"; +} from "@riotbyte-com/project-q-core"; import { EventEmitter2 } from "eventemitter2"; import type { HeartbeatStrategy } from "./heartbeat.strategy"; import { HEARTBEAT_STRATEGIES } from "./heartbeat.strategy"; diff --git a/apps/worker/src/logger.provider.ts b/apps/worker/src/logger.provider.ts index 58a7e1d..ad00320 100644 --- a/apps/worker/src/logger.provider.ts +++ b/apps/worker/src/logger.provider.ts @@ -1,5 +1,5 @@ import { Logger as NestLogger } from "@nestjs/common"; -import type { Logger } from "@riotbyte/project-q-core"; +import type { Logger } from "@riotbyte-com/project-q-core"; export class NestProjectQLogger implements Logger { private readonly logger = new NestLogger("ProjectQ"); diff --git a/apps/worker/src/worker.module.ts b/apps/worker/src/worker.module.ts index a306b93..8d9845c 100644 --- a/apps/worker/src/worker.module.ts +++ b/apps/worker/src/worker.module.ts @@ -2,8 +2,9 @@ import { Module } from "@nestjs/common"; import { ConfigModule } from "@nestjs/config"; import { EventEmitterModule } from "@nestjs/event-emitter"; import { DatabaseModule, PrismaService } from "@cv/system"; -import { LoggerProvider, MessengerModule } from "@riotbyte/project-q-nestjs"; -import { PrismaClientToken, PrismaTransportFactory } from "@riotbyte/project-q-prisma"; +import { FileStorageModule } from "@cv/file-storage"; +import { LoggerProvider, MessengerModule } from "@riotbyte-com/project-q-nestjs"; +import { PrismaClientToken, PrismaTransportFactory } from "@riotbyte-com/project-q-prisma"; import type { WorkerConfig } from "./config"; import { config } from "./config"; import { RenderPdfHandler } from "./handlers/render-pdf.handler"; @@ -22,6 +23,17 @@ export const WORKER_CONFIG = Symbol("WORKER_CONFIG"); DatabaseModule, EventEmitterModule.forRoot(), HeartbeatModule.register(config), + FileStorageModule.forRoot( + config.fileStorageDriver === "r2" + ? { + driver: "r2", + endpoint: config.r2Endpoint, + accessKeyId: config.r2AccessKeyId, + secretAccessKey: config.r2SecretAccessKey, + bucket: config.r2Bucket, + } + : { driver: "disk", baseDir: config.pdfOutputDir }, + ), MessengerModule.forRoot({ transports: { async: { dsn: `prisma://?queue=${config.queueName}`, retry: true }, diff --git a/ci/build-images.sh b/ci/build-images.sh new file mode 100755 index 0000000..e86f19f --- /dev/null +++ b/ci/build-images.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Builds and pushes server + worker Docker images to GHCR. +# +# Required env vars: +# VERSION — semver tag (e.g. "1.2.3") +# GITHUB_TOKEN — GHCR auth token (also used as build arg for npm auth) +# REGISTRY — container registry (default: ghcr.io) +# IMAGE_BASE — image name prefix (default: ghcr.io/riotbyte-com/cv-generator) + +REGISTRY="${REGISTRY:-ghcr.io}" +IMAGE_BASE="${IMAGE_BASE:-${REGISTRY}/riotbyte-com/cv-generator}" + +: "${VERSION:?VERSION is required}" +: "${GITHUB_TOKEN:?GITHUB_TOKEN is required}" + +echo "${GITHUB_TOKEN}" | docker login "${REGISTRY}" -u _ --password-stdin + +IFS='.' read -r MAJOR MINOR PATCH <<< "${VERSION}" + +build_and_push() { + local name="$1" + local dockerfile="$2" + local image="${IMAGE_BASE}-${name}" + + local tags=( + "${image}:${VERSION}" + "${image}:${MAJOR}.${MINOR}" + "${image}:latest" + ) + + local tag_args=() + for t in "${tags[@]}"; do + tag_args+=("-t" "$t") + done + + echo "Building ${image}:${VERSION} from ${dockerfile}..." + docker build \ + -f "${dockerfile}" \ + --target production \ + --build-arg "GITHUB_TOKEN=${GITHUB_TOKEN}" \ + "${tag_args[@]}" \ + . + + for t in "${tags[@]}"; do + echo "Pushing ${t}..." + docker push "$t" + done +} + +build_and_push server .docker/server.Dockerfile +build_and_push worker .docker/worker.Dockerfile diff --git a/ci/deploy-client.sh b/ci/deploy-client.sh new file mode 100755 index 0000000..52d0ad6 --- /dev/null +++ b/ci/deploy-client.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Builds the client SPA and deploys to Cloudflare Pages. +# +# Required env vars: +# CLOUDFLARE_API_TOKEN — Cloudflare API token with Pages permissions +# CLOUDFLARE_ACCOUNT_ID — Cloudflare account ID +# +# Optional env vars: +# VITE_SERVER_URL — API server URL (default: https://cv-generator-api.riotbyte.com) +# PAGES_PROJECT — Cloudflare Pages project name (default: cv-generator) + +export VITE_SERVER_URL="${VITE_SERVER_URL:-https://cv-generator-api.riotbyte.com}" +PAGES_PROJECT="${PAGES_PROJECT:-cv-generator}" + +: "${CLOUDFLARE_API_TOKEN:?CLOUDFLARE_API_TOKEN is required}" +: "${CLOUDFLARE_ACCOUNT_ID:?CLOUDFLARE_ACCOUNT_ID is required}" + +echo "Building client with VITE_SERVER_URL=${VITE_SERVER_URL}..." +pnpm --filter @cv/client build + +echo "Deploying to Cloudflare Pages project '${PAGES_PROJECT}'..." +npx wrangler pages deploy apps/client/dist \ + --project-name="${PAGES_PROJECT}" diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml deleted file mode 100644 index 0384ab5..0000000 --- a/docker-compose.prod.yml +++ /dev/null @@ -1,153 +0,0 @@ -services: - caddy: - image: caddy:2-alpine - environment: - DOMAIN: ${DOMAIN} - BASIC_AUTH_USER: ${BASIC_AUTH_USER:-admin} - BASIC_AUTH_HASH: ${BASIC_AUTH_HASH} - ports: - - "80:80" - - "443:443" - volumes: - - ./ci/Caddyfile:/etc/caddy/Caddyfile:ro - - caddy-data:/data - - caddy-config:/config - depends_on: - server: - condition: service_healthy - client: - condition: service_healthy - restart: unless-stopped - - db: - image: postgres:16-alpine - environment: - POSTGRES_USER: ${POSTGRES_USER} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} - POSTGRES_DB: ${POSTGRES_DB} - volumes: - - db-data:/var/lib/postgresql/data - healthcheck: - test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"] - interval: 10s - timeout: 5s - retries: 3 - restart: unless-stopped - - server: - build: - context: . - dockerfile: .docker/server.Dockerfile - target: production - additional_contexts: - project-q: ${PROJECT_Q_PATH} - nest-service-locator: ${NEST_SERVICE_LOCATOR_PATH} - environment: - PORT: "3000" - NODE_ENV: production - DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB} - JWT_SECRET: ${JWT_SECRET} - JWT_ACCESS_TOKEN_EXPIRY: ${JWT_ACCESS_TOKEN_EXPIRY:-15m} - JWT_REFRESH_TOKEN_EXPIRY: ${JWT_REFRESH_TOKEN_EXPIRY:-7d} - ENCRYPTION_KEY: ${ENCRYPTION_KEY} - RESEND_API_KEY: ${RESEND_API_KEY:-} - EMAIL_FROM_ADDRESS: ${EMAIL_FROM_ADDRESS:-} - EMAIL_FROM_NAME: ${EMAIL_FROM_NAME:-CV Generator} - CLIENT_URL: ${PUBLIC_URL} - ALLOWED_ORIGINS: ${PUBLIC_URL} - AI_PROVIDER: ${AI_PROVIDER:-anthropic} - AI_TEMPERATURE: ${AI_TEMPERATURE:-0.1} - AI_MAX_TOKENS: ${AI_MAX_TOKENS:-8192} - AI_TIMEOUT: ${AI_TIMEOUT:-60000} - ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-} - ANTHROPIC_MODEL: ${ANTHROPIC_MODEL:-claude-sonnet-4-5-20250929} - OPENAI_API_KEY: ${OPENAI_API_KEY:-} - OPENAI_MODEL: ${OPENAI_MODEL:-gpt-4o-mini} - PDF_OUTPUT_DIR: /app/pdf-output - depends_on: - db: - condition: service_healthy - volumes: - - worker-output:/app/pdf-output:ro - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:3000/health"] - interval: 15s - timeout: 5s - retries: 3 - start_period: 30s - restart: unless-stopped - - client: - build: - context: . - dockerfile: .docker/client.Dockerfile - target: production - args: - VITE_SERVER_URL: ${PUBLIC_URL} - VITE_DOCS_URL: https://docs.${DOMAIN} - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:80"] - interval: 15s - timeout: 5s - retries: 3 - start_period: 5s - restart: unless-stopped - - worker: - build: - context: . - dockerfile: .docker/worker.Dockerfile - target: production - additional_contexts: - project-q: ${PROJECT_Q_PATH} - nest-service-locator: ${NEST_SERVICE_LOCATOR_PATH} - environment: - DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB} - QUEUE_SCHEMA: ${QUEUE_SCHEMA:-queue} - QUEUE_NAME: ${QUEUE_NAME:-default} - POLL_INTERVAL_MS: ${POLL_INTERVAL_MS:-2000} - PDF_OUTPUT_DIR: /app/pdf-output - PDF_TIMEOUT_MS: ${PDF_TIMEOUT_MS:-30000} - HEARTBEAT_FILE_PATH: /tmp/worker-heartbeat - HEARTBEAT_DB_INTERVAL_MS: ${HEARTBEAT_DB_INTERVAL_MS:-30000} - CHROMIUM_ARGS: ${CHROMIUM_ARGS:---no-sandbox,--disable-dev-shm-usage,--disable-gpu,--single-process,--disable-extensions} - depends_on: - db: - condition: service_healthy - volumes: - - worker-output:/app/pdf-output - deploy: - resources: - limits: - memory: 512M - healthcheck: - test: ["CMD-SHELL", "find /tmp/worker-heartbeat -mmin -1 | grep -q ."] - interval: 30s - timeout: 5s - retries: 3 - start_period: 15s - restart: unless-stopped - - docs: - profiles: - - docs - build: - context: . - dockerfile: .docker/docs.Dockerfile - target: production - args: - VITE_CLIENT_URL: ${PUBLIC_URL} - VITE_SERVER_URL: ${PUBLIC_URL} - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:80"] - interval: 15s - timeout: 5s - retries: 3 - start_period: 5s - restart: unless-stopped - -volumes: - db-data: - worker-output: - caddy-data: - caddy-config: diff --git a/packages/file-storage/package.json b/packages/file-storage/package.json new file mode 100644 index 0000000..e95932c --- /dev/null +++ b/packages/file-storage/package.json @@ -0,0 +1,39 @@ +{ + "name": "@cv/file-storage", + "version": "0.0.0", + "private": true, + "main": "./src/index.ts", + "types": "./src/index.ts", + "exports": { + ".": { + "require": "./src/index.ts", + "import": "./src/index.ts", + "types": "./src/index.ts" + } + }, + "files": [ + "src/" + ], + "scripts": { + "lint": "biome check .", + "lint:fix": "biome check --write .", + "typecheck": "tsc -b" + }, + "dependencies": { + "@aws-sdk/client-s3": "^3.800.0", + "@nestjs/common": "^10.4.7", + "@nestjs/config": "^3.2.0", + "@nestjs/core": "^10.4.7" + }, + "devDependencies": { + "@biomejs/biome": "^2.2.6", + "@cv/biome-config": "*", + "@cv/tsconfig": "*", + "typescript": "^5.6.3" + }, + "peerDependencies": { + "@nestjs/common": "^10.4.7", + "@nestjs/config": "^3.2.0", + "@nestjs/core": "^10.4.7" + } +} diff --git a/packages/file-storage/src/disk-file-storage.ts b/packages/file-storage/src/disk-file-storage.ts new file mode 100644 index 0000000..31db67b --- /dev/null +++ b/packages/file-storage/src/disk-file-storage.ts @@ -0,0 +1,39 @@ +import * as fs from "node:fs/promises"; +import * as path from "node:path"; +import { Injectable, Logger } from "@nestjs/common"; +import type { FileStorage } from "./file-storage.interface"; + +@Injectable() +export class DiskFileStorage implements FileStorage { + private readonly logger = new Logger(DiskFileStorage.name); + + constructor(private readonly baseDir: string) {} + + async write(key: string, data: Buffer): Promise { + const filePath = this.resolve(key); + await fs.mkdir(path.dirname(filePath), { recursive: true }); + await fs.writeFile(filePath, data); + this.logger.debug(`Written ${data.length} bytes to ${filePath}`); + } + + async read(key: string): Promise { + return fs.readFile(this.resolve(key)); + } + + async delete(key: string): Promise { + await fs.unlink(this.resolve(key)); + } + + async exists(key: string): Promise { + try { + await fs.access(this.resolve(key)); + return true; + } catch { + return false; + } + } + + private resolve(key: string): string { + return path.join(this.baseDir, key); + } +} diff --git a/packages/file-storage/src/file-storage.interface.ts b/packages/file-storage/src/file-storage.interface.ts new file mode 100644 index 0000000..13653f1 --- /dev/null +++ b/packages/file-storage/src/file-storage.interface.ts @@ -0,0 +1,12 @@ +/** + * Storage backend abstraction for file persistence. + * Implementations: DiskFileStorage (local dev), R2FileStorage (prod). + */ +export interface FileStorage { + write(key: string, data: Buffer): Promise; + read(key: string): Promise; + delete(key: string): Promise; + exists(key: string): Promise; +} + +export const FILE_STORAGE = Symbol("FILE_STORAGE"); diff --git a/packages/file-storage/src/file-storage.module.ts b/packages/file-storage/src/file-storage.module.ts new file mode 100644 index 0000000..4d948ee --- /dev/null +++ b/packages/file-storage/src/file-storage.module.ts @@ -0,0 +1,72 @@ +import { type DynamicModule, Module, type Type } from "@nestjs/common"; +import { DiskFileStorage } from "./disk-file-storage"; +import { FILE_STORAGE } from "./file-storage.interface"; +import { R2FileStorage, type R2FileStorageConfig } from "./r2-file-storage"; + +type DiskConfig = { driver: "disk"; baseDir: string }; +type R2Config = { driver: "r2" } & R2FileStorageConfig; +export type FileStorageModuleConfig = DiskConfig | R2Config; + +/** + * Implement this interface to provide FileStorage configuration + * via NestJS DI. Register it with `FileStorageModule.forRootAsync()`. + */ +export interface FileStorageConfigProvider { + createFileStorageConfig(): FileStorageModuleConfig; +} + +export const FILE_STORAGE_CONFIG_PROVIDER = Symbol("FILE_STORAGE_CONFIG_PROVIDER"); + +@Module({}) +export class FileStorageModule { + /** + * Static configuration — pass the config directly. + */ + static forRoot(config: FileStorageModuleConfig): DynamicModule { + return { + module: FileStorageModule, + providers: [ + { + provide: FILE_STORAGE, + useFactory: () => createStorage(config), + }, + ], + exports: [FILE_STORAGE], + global: true, + }; + } + + /** + * Async configuration — provide a class that implements + * `FileStorageConfigProvider`. The class can inject ConfigService + * or any other NestJS provider. + */ + static forRootAsync(options: { + imports?: Type[]; + useClass: Type; + }): DynamicModule { + return { + module: FileStorageModule, + imports: options.imports ?? [], + providers: [ + { + provide: FILE_STORAGE_CONFIG_PROVIDER, + useClass: options.useClass, + }, + { + provide: FILE_STORAGE, + useFactory: (provider: FileStorageConfigProvider) => + createStorage(provider.createFileStorageConfig()), + inject: [FILE_STORAGE_CONFIG_PROVIDER], + }, + ], + exports: [FILE_STORAGE], + global: true, + }; + } +} + +const createStorage = (config: FileStorageModuleConfig) => + config.driver === "r2" + ? new R2FileStorage(config) + : new DiskFileStorage(config.baseDir); diff --git a/packages/file-storage/src/index.ts b/packages/file-storage/src/index.ts new file mode 100644 index 0000000..63cffe2 --- /dev/null +++ b/packages/file-storage/src/index.ts @@ -0,0 +1,8 @@ +export { FILE_STORAGE, type FileStorage } from "./file-storage.interface"; +export { DiskFileStorage } from "./disk-file-storage"; +export { R2FileStorage, type R2FileStorageConfig } from "./r2-file-storage"; +export { + FileStorageModule, + type FileStorageModuleConfig, + type FileStorageConfigProvider, +} from "./file-storage.module"; diff --git a/packages/file-storage/src/r2-file-storage.ts b/packages/file-storage/src/r2-file-storage.ts new file mode 100644 index 0000000..f6c1f7a --- /dev/null +++ b/packages/file-storage/src/r2-file-storage.ts @@ -0,0 +1,81 @@ +import { Injectable, Logger } from "@nestjs/common"; +import { + DeleteObjectCommand, + GetObjectCommand, + HeadObjectCommand, + PutObjectCommand, + S3Client, +} from "@aws-sdk/client-s3"; +import type { FileStorage } from "./file-storage.interface"; + +export type R2FileStorageConfig = { + endpoint: string; + accessKeyId: string; + secretAccessKey: string; + bucket: string; +}; + +@Injectable() +export class R2FileStorage implements FileStorage { + private readonly logger = new Logger(R2FileStorage.name); + private readonly client: S3Client; + private readonly bucket: string; + + constructor(config: R2FileStorageConfig) { + this.bucket = config.bucket; + this.client = new S3Client({ + region: "auto", + endpoint: config.endpoint, + credentials: { + accessKeyId: config.accessKeyId, + secretAccessKey: config.secretAccessKey, + }, + }); + } + + async write(key: string, data: Buffer): Promise { + await this.client.send( + new PutObjectCommand({ + Bucket: this.bucket, + Key: key, + Body: data, + }), + ); + this.logger.debug(`Uploaded ${data.length} bytes to r2://${this.bucket}/${key}`); + } + + async read(key: string): Promise { + const response = await this.client.send( + new GetObjectCommand({ + Bucket: this.bucket, + Key: key, + }), + ); + const bytes = await response.Body?.transformToByteArray(); + if (!bytes) throw new Error(`Empty response for key: ${key}`); + return Buffer.from(bytes); + } + + async delete(key: string): Promise { + await this.client.send( + new DeleteObjectCommand({ + Bucket: this.bucket, + Key: key, + }), + ); + } + + async exists(key: string): Promise { + try { + await this.client.send( + new HeadObjectCommand({ + Bucket: this.bucket, + Key: key, + }), + ); + return true; + } catch { + return false; + } + } +} diff --git a/packages/file-storage/tsconfig.json b/packages/file-storage/tsconfig.json new file mode 100644 index 0000000..09ae515 --- /dev/null +++ b/packages/file-storage/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "@cv/tsconfig/tsconfig.library.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src", + "baseUrl": ".", + "composite": true, + "declaration": true, + "emitDeclarationOnly": true + }, + "include": ["src/**/*"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cd8731e..92c1ede 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -236,6 +236,9 @@ importers: '@cv/cv-renderer': specifier: '*' version: link:../../packages/cv-renderer + '@cv/file-storage': + specifier: '*' + version: link:../../packages/file-storage '@cv/file-upload': specifier: '*' version: link:../../packages/file-upload @@ -281,18 +284,18 @@ importers: '@prisma/client': specifier: ^7.1.0 version: 7.2.0(prisma@7.2.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(typescript@5.9.3) - '@riotbyte/nest-service-locator': - specifier: link:/Users/niels/Developer/riotbyte/nest-service-locator - version: link:../../../../riotbyte/nest-service-locator - '@riotbyte/project-q-core': - specifier: link:/Users/niels/Developer/riotbyte/project-q/packages/core - version: link:../../../../riotbyte/project-q/packages/core - '@riotbyte/project-q-nestjs': - specifier: link:/Users/niels/Developer/riotbyte/project-q/packages/framework/nest - version: link:../../../../riotbyte/project-q/packages/framework/nest - '@riotbyte/project-q-prisma': - specifier: link:/Users/niels/Developer/riotbyte/project-q/packages/transport/prisma - version: link:../../../../riotbyte/project-q/packages/transport/prisma + '@riotbyte-com/nest-service-locator': + specifier: 0.3.0-rc.4 + version: 0.3.0-rc.4(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20) + '@riotbyte-com/project-q-core': + specifier: 1.0.19-rc.3 + version: 1.0.19-rc.3 + '@riotbyte-com/project-q-nestjs': + specifier: 1.0.19-rc.3 + version: 1.0.19-rc.3(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@nestjs/event-emitter@3.0.1(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20))(@riotbyte-com/nest-service-locator@0.3.0-rc.4(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20))(eventemitter2@6.4.9)(nest-commander@3.16.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@types/inquirer@8.2.12)(typescript@5.9.3)) + '@riotbyte-com/project-q-prisma': + specifier: 1.0.19-rc.3 + version: 1.0.19-rc.3(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@riotbyte-com/project-q-core@1.0.19-rc.3)(@riotbyte-com/project-q-nestjs@1.0.19-rc.3(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@nestjs/event-emitter@3.0.1(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20))(@riotbyte-com/nest-service-locator@0.3.0-rc.4(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20))(eventemitter2@6.4.9)(nest-commander@3.16.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@types/inquirer@8.2.12)(typescript@5.9.3))) '@types/cookie-parser': specifier: ^1.4.10 version: 1.4.10(@types/express@5.0.6) @@ -468,6 +471,9 @@ importers: apps/worker: dependencies: + '@cv/file-storage': + specifier: '*' + version: link:../../packages/file-storage '@cv/system': specifier: '*' version: link:../../packages/system @@ -483,18 +489,18 @@ importers: '@nestjs/event-emitter': specifier: ^3.0.1 version: 3.0.1(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20) - '@riotbyte/nest-service-locator': - specifier: link:/Users/niels/Developer/riotbyte/nest-service-locator - version: link:../../../../riotbyte/nest-service-locator - '@riotbyte/project-q-core': - specifier: link:/Users/niels/Developer/riotbyte/project-q/packages/core - version: link:../../../../riotbyte/project-q/packages/core - '@riotbyte/project-q-nestjs': - specifier: link:/Users/niels/Developer/riotbyte/project-q/packages/framework/nest - version: link:../../../../riotbyte/project-q/packages/framework/nest - '@riotbyte/project-q-prisma': - specifier: link:/Users/niels/Developer/riotbyte/project-q/packages/transport/prisma - version: link:../../../../riotbyte/project-q/packages/transport/prisma + '@riotbyte-com/nest-service-locator': + specifier: 0.3.0-rc.4 + version: 0.3.0-rc.4(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20) + '@riotbyte-com/project-q-core': + specifier: 1.0.19-rc.3 + version: 1.0.19-rc.3 + '@riotbyte-com/project-q-nestjs': + specifier: 1.0.19-rc.3 + version: 1.0.19-rc.3(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@nestjs/event-emitter@3.0.1(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20))(@riotbyte-com/nest-service-locator@0.3.0-rc.4(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20))(eventemitter2@6.4.9)(nest-commander@3.16.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@types/inquirer@8.2.12)(typescript@5.9.3)) + '@riotbyte-com/project-q-prisma': + specifier: 1.0.19-rc.3 + version: 1.0.19-rc.3(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@riotbyte-com/project-q-core@1.0.19-rc.3)(@riotbyte-com/project-q-nestjs@1.0.19-rc.3(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@nestjs/event-emitter@3.0.1(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20))(@riotbyte-com/nest-service-locator@0.3.0-rc.4(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20))(eventemitter2@6.4.9)(nest-commander@3.16.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@types/inquirer@8.2.12)(typescript@5.9.3))) eventemitter2: specifier: ^6.4.9 version: 6.4.9 @@ -674,6 +680,34 @@ importers: specifier: ^5.3.3 version: 5.9.3 + packages/file-storage: + dependencies: + '@aws-sdk/client-s3': + specifier: ^3.800.0 + version: 3.1013.0 + '@nestjs/common': + specifier: ^10.4.7 + version: 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/config': + specifier: ^3.2.0 + version: 3.3.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2) + '@nestjs/core': + specifier: ^10.4.7 + version: 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.20)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.2) + devDependencies: + '@biomejs/biome': + specifier: ^2.2.6 + version: 2.3.11 + '@cv/biome-config': + specifier: '*' + version: link:../biome-config + '@cv/tsconfig': + specifier: '*' + version: link:../tsconfig + typescript: + specifier: ^5.6.3 + version: 5.9.3 + packages/file-upload: dependencies: '@nestjs/common': @@ -1028,6 +1062,165 @@ packages: peerDependencies: graphql: '*' + '@aws-crypto/crc32@5.2.0': + resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/crc32c@5.2.0': + resolution: {integrity: sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==} + + '@aws-crypto/sha1-browser@5.2.0': + resolution: {integrity: sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==} + + '@aws-crypto/sha256-browser@5.2.0': + resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} + + '@aws-crypto/sha256-js@5.2.0': + resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/supports-web-crypto@5.2.0': + resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} + + '@aws-crypto/util@5.2.0': + resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} + + '@aws-sdk/client-s3@3.1013.0': + resolution: {integrity: sha512-vFdyRyRatF+xP9Fi+4alZkmzZadqOAM34Pm6SUZsYtumNrWkgMc/pFWITnsq6eltM8qcV/vcinQ1ZBXWm/PlKg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/core@3.973.22': + resolution: {integrity: sha512-lY6g5L95jBNgOUitUhfV2N/W+i08jHEl3xuLODYSQH5Sf50V+LkVYBSyZRLtv2RyuXZXiV7yQ+acpswK1tlrOA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/crc64-nvme@3.972.5': + resolution: {integrity: sha512-2VbTstbjKdT+yKi8m7b3a9CiVac+pL/IY2PHJwsaGkkHmuuqkJZIErPck1h6P3T9ghQMLSdMPyW6Qp7Di5swFg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-env@3.972.20': + resolution: {integrity: sha512-vI0QN96DFx3g9AunfOWF3CS4cMkqFiR/WM/FyP9QHr5rZ2dKPkYwP3tCgAOvGuu9CXI7dC1vU2FVUuZ+tfpNvQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-http@3.972.22': + resolution: {integrity: sha512-aS/81smalpe7XDnuQfOq4LIPuaV2PRKU2aMTrHcqO5BD4HwO5kESOHNcec2AYfBtLtIDqgF6RXisgBnfK/jt0w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-ini@3.972.22': + resolution: {integrity: sha512-rpF8fBT0LllMDp78s62aL2A/8MaccjyJ0ORzqu+ZADeECLSrrCWIeeXsuRam+pxiAMkI1uIyDZJmgLGdadkPXw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-login@3.972.22': + resolution: {integrity: sha512-u33CO9zeNznlVSg9tWTCRYxaGkqr1ufU6qeClpmzAabXZa8RZxQoVXxL5T53oZJFzQYj+FImORCSsi7H7B77gQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-node@3.972.23': + resolution: {integrity: sha512-U8tyLbLOZItuVWTH0ay9gWo4xMqZwqQbg1oMzdU4FQSkTpqXemm4X0uoKBR6llqAStgBp30ziKFJHTA43l4qMw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-process@3.972.20': + resolution: {integrity: sha512-QRfk7GbA4/HDRjhP3QYR6QBr/QKreVoOzvvlRHnOuGgYJkeoPgPY3LAI1kK1ZMgZ4hH9KiGp757/ntol+INAig==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-sso@3.972.22': + resolution: {integrity: sha512-4vqlSaUbBj4aNPVKfB6yXuIQ2Z2mvLfIGba2OzzF6zUkN437/PGWsxBU2F8QPSFHti6seckvyCXidU3H+R8NvQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.972.22': + resolution: {integrity: sha512-/wN1CYg2rVLhW8/jLxMWacQrkpaynnL+4j/Z+e6X1PfoE6NiC0BeOw3i0JmtZrKun85wNV5GmspvuWJihfeeUw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-bucket-endpoint@3.972.8': + resolution: {integrity: sha512-WR525Rr2QJSETa9a050isktyWi/4yIGcmY3BQ1kpHqb0LqUglQHCS8R27dTJxxWNZvQ0RVGtEZjTCbZJpyF3Aw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-expect-continue@3.972.8': + resolution: {integrity: sha512-5DTBTiotEES1e2jOHAq//zyzCjeMB78lEHd35u15qnrid4Nxm7diqIf9fQQ3Ov0ChH1V3Vvt13thOnrACmfGVQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-flexible-checksums@3.974.2': + resolution: {integrity: sha512-4soN/N4R6ptdnHw7hXPVDZMIIL+vhN8rwtLdDyS0uD7ExhadtJzolTBIM5eKSkbw5uBEbIwtJc8HCG2NM6tN/g==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-host-header@3.972.8': + resolution: {integrity: sha512-wAr2REfKsqoKQ+OkNqvOShnBoh+nkPurDKW7uAeVSu6kUECnWlSJiPvnoqxGlfousEY/v9LfS9sNc46hjSYDIQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-location-constraint@3.972.8': + resolution: {integrity: sha512-KaUoFuoFPziIa98DSQsTPeke1gvGXlc5ZGMhy+b+nLxZ4A7jmJgLzjEF95l8aOQN2T/qlPP3MrAyELm8ExXucw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-logger@3.972.8': + resolution: {integrity: sha512-CWl5UCM57WUFaFi5kB7IBY1UmOeLvNZAZ2/OZ5l20ldiJ3TiIz1pC65gYj8X0BCPWkeR1E32mpsCk1L1I4n+lA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-recursion-detection@3.972.8': + resolution: {integrity: sha512-BnnvYs2ZEpdlmZ2PNlV2ZyQ8j8AEkMTjN79y/YA475ER1ByFYrkVR85qmhni8oeTaJcDqbx364wDpitDAA/wCA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-sdk-s3@3.972.22': + resolution: {integrity: sha512-dkUcRxF4rVpPbyHpxjCApGK6b7JpnSeo7tDoNakpRKmiLMCqgy4tlGBgeEYJnZgLrA4xc5jVKuXgvgqKqU18Kw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-ssec@3.972.8': + resolution: {integrity: sha512-wqlK0yO/TxEC2UsY9wIlqeeutF6jjLe0f96Pbm40XscTo57nImUk9lBcw0dPgsm0sppFtAkSlDrfpK+pC30Wqw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-user-agent@3.972.23': + resolution: {integrity: sha512-HQu8QoqGZZTvg0Spl9H39QTsSMFwgu+8yz/QGKndXFLk9FZMiCiIgBCVlTVKMDvVbgqIzD9ig+/HmXsIL2Rb+g==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/nested-clients@3.996.12': + resolution: {integrity: sha512-KLdQGJPSm98uLINolQ0Tol8OAbk7g0Y7zplHJ1K83vbMIH13aoCvR6Tho66xueW4l4aZlEgVGLWBnD8ifUMsGQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/region-config-resolver@3.972.8': + resolution: {integrity: sha512-1eD4uhTDeambO/PNIDVG19A6+v4NdD7xzwLHDutHsUqz0B+i661MwQB2eYO4/crcCvCiQG4SRm1k81k54FEIvw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/signature-v4-multi-region@3.996.10': + resolution: {integrity: sha512-yJSbFTedh1McfqXa9wZzjchqQ2puq5PI/qRz5kUjg2UXS5mO4MBYBbeXaZ2rp/h+ZbkcYEdo4Qsiah9psyoxrA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/token-providers@3.1013.0': + resolution: {integrity: sha512-IL1c54UvbuERrs9oLm5rvkzMciwhhpn1FL0SlC3XUMoLlFhdBsWJgQKK8O5fsQLxbFVqjbjFx9OBkrn44X9PHw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/types@3.973.6': + resolution: {integrity: sha512-Atfcy4E++beKtwJHiDln2Nby8W/mam64opFPTiHEqgsthqeydFS1pY+OUlN1ouNOmf8ArPU/6cDS65anOP3KQw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-arn-parser@3.972.3': + resolution: {integrity: sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-endpoints@3.996.5': + resolution: {integrity: sha512-Uh93L5sXFNbyR5sEPMzUU8tJ++Ku97EY4udmC01nB8Zu+xfBPwpIwJ6F7snqQeq8h2pf+8SGN5/NoytfKgYPIw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-locate-window@3.965.5': + resolution: {integrity: sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-user-agent-browser@3.972.8': + resolution: {integrity: sha512-B3KGXJviV2u6Cdw2SDY2aDhoJkVfY/Q/Trwk2CMSkikE1Oi6gRzxhvhIfiRpHfmIsAhV4EA54TVEX8K6CbHbkA==} + + '@aws-sdk/util-user-agent-node@3.973.9': + resolution: {integrity: sha512-jeFqqp8KD/P5O+qeKxyGeu7WEVIZFNprnkaDjGmBOjwxYwafCBhpxTgV1TlW6L8e76Vh/siNylNmN/OmSIFBUQ==} + engines: {node: '>=20.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + + '@aws-sdk/xml-builder@3.972.14': + resolution: {integrity: sha512-G/Yd8Bnnyh8QrqLf8jWJbixEnScUFW24e/wOBGYdw1Cl4r80KX/DvHyM2GVZ2vTp7J4gTEr8IXJlTadA8+UfuQ==} + engines: {node: '>=20.0.0'} + + '@aws/lambda-invoke-store@0.2.4': + resolution: {integrity: sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==} + engines: {node: '>=18.0.0'} + '@babel/code-frame@7.27.1': resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} @@ -3010,6 +3203,34 @@ packages: '@repeaterjs/repeater@3.0.6': resolution: {integrity: sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA==} + '@riotbyte-com/nest-service-locator@0.3.0-rc.4': + resolution: {integrity: sha512-mQKzGAHikszzeILl2fns6jNK7qEWGV0UGHegLH5yF9/88UFyMm8lFRQDTA2oDQaass1T6JHPB+dVDSijceoWxA==, tarball: https://npm.pkg.github.com/download/@riotbyte-com/nest-service-locator/0.3.0-rc.4/6042137a64cf014f73995d30ce4035e4c33211b8} + engines: {node: '>= 20.0.0'} + peerDependencies: + '@nestjs/common': ^10.0.0 || ^11.0.0 + '@nestjs/core': ^10.0.0 || ^11.0.0 + + '@riotbyte-com/project-q-core@1.0.19-rc.3': + resolution: {integrity: sha512-/E+o3IMCWjIMS6O81RZFmpww5KJDxwiaj0LcTrs9gwi2hldJ+7ZR1nBXVlBxVB8b5mIshkp5AKgR26Re/NRM/Q==, tarball: https://npm.pkg.github.com/download/@riotbyte-com/project-q-core/1.0.19-rc.3/b78ac8b11c804150fdc1dcc0d635340e07143e8e} + + '@riotbyte-com/project-q-nestjs@1.0.19-rc.3': + resolution: {integrity: sha512-FwvF/6gHPTcSijzajcqNPeadhVUHMJf0el2Py4CBcnw8p/KihbF0LNu330UUVYJ6Cs/w4HsAUVrOkR+f2fNJmw==, tarball: https://npm.pkg.github.com/download/@riotbyte-com/project-q-nestjs/1.0.19-rc.3/85526283b3b35e70c0b7ec767f524cf624db8aec} + peerDependencies: + '@nestjs/common': ^10.0 || ^11.0 + '@nestjs/core': ^10.0 || ^11.0 + '@nestjs/event-emitter': ^3.0 + '@riotbyte-com/nest-service-locator': '>=0.3.0-rc.3' + eventemitter2: ^6.0 + nest-commander: ^3.0 + + '@riotbyte-com/project-q-prisma@1.0.19-rc.3': + resolution: {integrity: sha512-VKNL3J5Abb0DIgbNXDdUyz+Gxiy5bFhfPy1LhC5gpjBlMk09Ud0n0Op1+2Y4wv75FncRrQDyt2rfSN0HNBwXJQ==, tarball: https://npm.pkg.github.com/download/@riotbyte-com/project-q-prisma/1.0.19-rc.3/b729eca9b298d56612e24ee6a6f43bfdac105116} + engines: {node: '>=18'} + peerDependencies: + '@nestjs/common': ^9.0 || ^10.0 || ^11.0 + '@riotbyte-com/project-q-core': 1.0.19-rc.3 + '@riotbyte-com/project-q-nestjs': 1.0.19-rc.3 + '@rolldown/pluginutils@1.0.0-beta.27': resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} @@ -3176,6 +3397,222 @@ packages: '@sinonjs/fake-timers@10.3.0': resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + '@smithy/abort-controller@4.2.12': + resolution: {integrity: sha512-xolrFw6b+2iYGl6EcOL7IJY71vvyZ0DJ3mcKtpykqPe2uscwtzDZJa1uVQXyP7w9Dd+kGwYnPbMsJrGISKiY/Q==} + engines: {node: '>=18.0.0'} + + '@smithy/chunked-blob-reader-native@4.2.3': + resolution: {integrity: sha512-jA5k5Udn7Y5717L86h4EIv06wIr3xn8GM1qHRi/Nf31annXcXHJjBKvgztnbn2TxH3xWrPBfgwHsOwZf0UmQWw==} + engines: {node: '>=18.0.0'} + + '@smithy/chunked-blob-reader@5.2.2': + resolution: {integrity: sha512-St+kVicSyayWQca+I1rGitaOEH6uKgE8IUWoYnnEX26SWdWQcL6LvMSD19Lg+vYHKdT9B2Zuu7rd3i6Wnyb/iw==} + engines: {node: '>=18.0.0'} + + '@smithy/config-resolver@4.4.13': + resolution: {integrity: sha512-iIzMC5NmOUP6WL6o8iPBjFhUhBZ9pPjpUpQYWMUFQqKyXXzOftbfK8zcQCz/jFV1Psmf05BK5ypx4K2r4Tnwdg==} + engines: {node: '>=18.0.0'} + + '@smithy/core@3.23.12': + resolution: {integrity: sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w==} + engines: {node: '>=18.0.0'} + + '@smithy/credential-provider-imds@4.2.12': + resolution: {integrity: sha512-cr2lR792vNZcYMriSIj+Um3x9KWrjcu98kn234xA6reOAFMmbRpQMOv8KPgEmLLtx3eldU6c5wALKFqNOhugmg==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-codec@4.2.12': + resolution: {integrity: sha512-FE3bZdEl62ojmy8x4FHqxq2+BuOHlcxiH5vaZ6aqHJr3AIZzwF5jfx8dEiU/X0a8RboyNDjmXjlbr8AdEyLgiA==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-browser@4.2.12': + resolution: {integrity: sha512-XUSuMxlTxV5pp4VpqZf6Sa3vT/Q75FVkLSpSSE3KkWBvAQWeuWt1msTv8fJfgA4/jcJhrbrbMzN1AC/hvPmm5A==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-config-resolver@4.3.12': + resolution: {integrity: sha512-7epsAZ3QvfHkngz6RXQYseyZYHlmWXSTPOfPmXkiS+zA6TBNo1awUaMFL9vxyXlGdoELmCZyZe1nQE+imbmV+Q==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-node@4.2.12': + resolution: {integrity: sha512-D1pFuExo31854eAvg89KMn9Oab/wEeJR6Buy32B49A9Ogdtx5fwZPqBHUlDzaCDpycTFk2+fSQgX689Qsk7UGA==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-universal@4.2.12': + resolution: {integrity: sha512-+yNuTiyBACxOJUTvbsNsSOfH9G9oKbaJE1lNL3YHpGcuucl6rPZMi3nrpehpVOVR2E07YqFFmtwpImtpzlouHQ==} + engines: {node: '>=18.0.0'} + + '@smithy/fetch-http-handler@5.3.15': + resolution: {integrity: sha512-T4jFU5N/yiIfrtrsb9uOQn7RdELdM/7HbyLNr6uO/mpkj1ctiVs7CihVr51w4LyQlXWDpXFn4BElf1WmQvZu/A==} + engines: {node: '>=18.0.0'} + + '@smithy/hash-blob-browser@4.2.13': + resolution: {integrity: sha512-YrF4zWKh+ghLuquldj6e/RzE3xZYL8wIPfkt0MqCRphVICjyyjH8OwKD7LLlKpVEbk4FLizFfC1+gwK6XQdR3g==} + engines: {node: '>=18.0.0'} + + '@smithy/hash-node@4.2.12': + resolution: {integrity: sha512-QhBYbGrbxTkZ43QoTPrK72DoYviDeg6YKDrHTMJbbC+A0sml3kSjzFtXP7BtbyJnXojLfTQldGdUR0RGD8dA3w==} + engines: {node: '>=18.0.0'} + + '@smithy/hash-stream-node@4.2.12': + resolution: {integrity: sha512-O3YbmGExeafuM/kP7Y8r6+1y0hIh3/zn6GROx0uNlB54K9oihAL75Qtc+jFfLNliTi6pxOAYZrRKD9A7iA6UFw==} + engines: {node: '>=18.0.0'} + + '@smithy/invalid-dependency@4.2.12': + resolution: {integrity: sha512-/4F1zb7Z8LOu1PalTdESFHR0RbPwHd3FcaG1sI3UEIriQTWakysgJr65lc1jj6QY5ye7aFsisajotH6UhWfm/g==} + engines: {node: '>=18.0.0'} + + '@smithy/is-array-buffer@2.2.0': + resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} + engines: {node: '>=14.0.0'} + + '@smithy/is-array-buffer@4.2.2': + resolution: {integrity: sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==} + engines: {node: '>=18.0.0'} + + '@smithy/md5-js@4.2.12': + resolution: {integrity: sha512-W/oIpHCpWU2+iAkfZYyGWE+qkpuf3vEXHLxQQDx9FPNZTTdnul0dZ2d/gUFrtQ5je1G2kp4cjG0/24YueG2LbQ==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-content-length@4.2.12': + resolution: {integrity: sha512-YE58Yz+cvFInWI/wOTrB+DbvUVz/pLn5mC5MvOV4fdRUc6qGwygyngcucRQjAhiCEbmfLOXX0gntSIcgMvAjmA==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-endpoint@4.4.27': + resolution: {integrity: sha512-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-retry@4.4.44': + resolution: {integrity: sha512-Y1Rav7m5CFRPQyM4CI0koD/bXjyjJu3EQxZZhtLGD88WIrBrQ7kqXM96ncd6rYnojwOo/u9MXu57JrEvu/nLrA==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-serde@4.2.15': + resolution: {integrity: sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-stack@4.2.12': + resolution: {integrity: sha512-kruC5gRHwsCOuyCd4ouQxYjgRAym2uDlCvQ5acuMtRrcdfg7mFBg6blaxcJ09STpt3ziEkis6bhg1uwrWU7txw==} + engines: {node: '>=18.0.0'} + + '@smithy/node-config-provider@4.3.12': + resolution: {integrity: sha512-tr2oKX2xMcO+rBOjobSwVAkV05SIfUKz8iI53rzxEmgW3GOOPOv0UioSDk+J8OpRQnpnhsO3Af6IEBabQBVmiw==} + engines: {node: '>=18.0.0'} + + '@smithy/node-http-handler@4.5.0': + resolution: {integrity: sha512-Rnq9vQWiR1+/I6NZZMNzJHV6pZYyEHt2ZnuV3MG8z2NNenC4i/8Kzttz7CjZiHSmsN5frhXhg17z3Zqjjhmz1A==} + engines: {node: '>=18.0.0'} + + '@smithy/property-provider@4.2.12': + resolution: {integrity: sha512-jqve46eYU1v7pZ5BM+fmkbq3DerkSluPr5EhvOcHxygxzD05ByDRppRwRPPpFrsFo5yDtCYLKu+kreHKVrvc7A==} + engines: {node: '>=18.0.0'} + + '@smithy/protocol-http@5.3.12': + resolution: {integrity: sha512-fit0GZK9I1xoRlR4jXmbLhoN0OdEpa96ul8M65XdmXnxXkuMxM0Y8HDT0Fh0Xb4I85MBvBClOzgSrV1X2s1Hxw==} + engines: {node: '>=18.0.0'} + + '@smithy/querystring-builder@4.2.12': + resolution: {integrity: sha512-6wTZjGABQufekycfDGMEB84BgtdOE/rCVTov+EDXQ8NHKTUNIp/j27IliwP7tjIU9LR+sSzyGBOXjeEtVgzCHg==} + engines: {node: '>=18.0.0'} + + '@smithy/querystring-parser@4.2.12': + resolution: {integrity: sha512-P2OdvrgiAKpkPNKlKUtWbNZKB1XjPxM086NeVhK+W+wI46pIKdWBe5QyXvhUm3MEcyS/rkLvY8rZzyUdmyDZBw==} + engines: {node: '>=18.0.0'} + + '@smithy/service-error-classification@4.2.12': + resolution: {integrity: sha512-LlP29oSQN0Tw0b6D0Xo6BIikBswuIiGYbRACy5ujw/JgWSzTdYj46U83ssf6Ux0GyNJVivs2uReU8pt7Eu9okQ==} + engines: {node: '>=18.0.0'} + + '@smithy/shared-ini-file-loader@4.4.7': + resolution: {integrity: sha512-HrOKWsUb+otTeo1HxVWeEb99t5ER1XrBi/xka2Wv6NVmTbuCUC1dvlrksdvxFtODLBjsC+PHK+fuy2x/7Ynyiw==} + engines: {node: '>=18.0.0'} + + '@smithy/signature-v4@5.3.12': + resolution: {integrity: sha512-B/FBwO3MVOL00DaRSXfXfa/TRXRheagt/q5A2NM13u7q+sHS59EOVGQNfG7DkmVtdQm5m3vOosoKAXSqn/OEgw==} + engines: {node: '>=18.0.0'} + + '@smithy/smithy-client@4.12.7': + resolution: {integrity: sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ==} + engines: {node: '>=18.0.0'} + + '@smithy/types@4.13.1': + resolution: {integrity: sha512-787F3yzE2UiJIQ+wYW1CVg2odHjmaWLGksnKQHUrK/lYZSEcy1msuLVvxaR/sI2/aDe9U+TBuLsXnr3vod1g0g==} + engines: {node: '>=18.0.0'} + + '@smithy/url-parser@4.2.12': + resolution: {integrity: sha512-wOPKPEpso+doCZGIlr+e1lVI6+9VAKfL4kZWFgzVgGWY2hZxshNKod4l2LXS3PRC9otH/JRSjtEHqQ/7eLciRA==} + engines: {node: '>=18.0.0'} + + '@smithy/util-base64@4.3.2': + resolution: {integrity: sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-body-length-browser@4.2.2': + resolution: {integrity: sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-body-length-node@4.2.3': + resolution: {integrity: sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==} + engines: {node: '>=18.0.0'} + + '@smithy/util-buffer-from@2.2.0': + resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-buffer-from@4.2.2': + resolution: {integrity: sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==} + engines: {node: '>=18.0.0'} + + '@smithy/util-config-provider@4.2.2': + resolution: {integrity: sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-defaults-mode-browser@4.3.43': + resolution: {integrity: sha512-Qd/0wCKMaXxev/z00TvNzGCH2jlKKKxXP1aDxB6oKwSQthe3Og2dMhSayGCnsma1bK/kQX1+X7SMP99t6FgiiQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-defaults-mode-node@4.2.47': + resolution: {integrity: sha512-qSRbYp1EQ7th+sPFuVcVO05AE0QH635hycdEXlpzIahqHHf2Fyd/Zl+8v0XYMJ3cgDVPa0lkMefU7oNUjAP+DQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-endpoints@3.3.3': + resolution: {integrity: sha512-VACQVe50j0HZPjpwWcjyT51KUQ4AnsvEaQ2lKHOSL4mNLD0G9BjEniQ+yCt1qqfKfiAHRAts26ud7hBjamrwig==} + engines: {node: '>=18.0.0'} + + '@smithy/util-hex-encoding@4.2.2': + resolution: {integrity: sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-middleware@4.2.12': + resolution: {integrity: sha512-Er805uFUOvgc0l8nv0e0su0VFISoxhJ/AwOn3gL2NWNY2LUEldP5WtVcRYSQBcjg0y9NfG8JYrCJaYDpupBHJQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-retry@4.2.12': + resolution: {integrity: sha512-1zopLDUEOwumjcHdJ1mwBHddubYF8GMQvstVCLC54Y46rqoHwlIU+8ZzUeaBcD+WCJHyDGSeZ2ml9YSe9aqcoQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-stream@4.5.20': + resolution: {integrity: sha512-4yXLm5n/B5SRBR2p8cZ90Sbv4zL4NKsgxdzCzp/83cXw2KxLEumt5p+GAVyRNZgQOSrzXn9ARpO0lUe8XSlSDw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-uri-escape@4.2.2': + resolution: {integrity: sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-utf8@2.3.0': + resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} + engines: {node: '>=14.0.0'} + + '@smithy/util-utf8@4.2.2': + resolution: {integrity: sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-waiter@4.2.13': + resolution: {integrity: sha512-2zdZ9DTHngRtcYxJK1GUDxruNr53kv5W2Lupe0LMU+Imr6ohQg8M2T14MNkj1Y0wS3FFwpgpGQyvuaMF7CiTmQ==} + engines: {node: '>=18.0.0'} + + '@smithy/uuid@1.1.2': + resolution: {integrity: sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==} + engines: {node: '>=18.0.0'} + '@stablelib/base64@1.0.1': resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==} @@ -4009,6 +4446,9 @@ packages: resolution: {integrity: sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + bowser@2.14.1: + resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} + brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} @@ -4905,6 +5345,13 @@ packages: fast-sha256@1.3.0: resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==} + fast-xml-builder@1.1.4: + resolution: {integrity: sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==} + + fast-xml-parser@5.5.6: + resolution: {integrity: sha512-3+fdZyBRVg29n4rXP0joHthhcHdPUHaIC16cuyyd1iLsuaO6Vea36MPrxgAzbZna8lhvZeRL8Bc9GP56/J9xEw==} + hasBin: true + fastq@1.20.1: resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} @@ -6856,6 +7303,10 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} + path-expression-matcher@1.1.3: + resolution: {integrity: sha512-qdVgY8KXmVdJZRSS1JdEPOKPdTiEK/pi0RkcT2sw1RhXxohdujUlJFPuS1TSkevZ9vzd3ZlL7ULl1MHGTApKzQ==} + engines: {node: '>=14.0.0'} + path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} @@ -7746,6 +8197,9 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + strnum@2.2.1: + resolution: {integrity: sha512-BwRvNd5/QoAtyW1na1y1LsJGQNvRlkde6Q/ipqqEaivoMdV+B1OMOTVdwR+N/cwVUcIt9PYyHmV8HyexCZSupg==} + strtok3@10.3.4: resolution: {integrity: sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==} engines: {node: '>=18'} @@ -8777,37 +9231,481 @@ snapshots: transitivePeerDependencies: - encoding - '@babel/code-frame@7.27.1': + '@aws-crypto/crc32@5.2.0': dependencies: - '@babel/helper-validator-identifier': 7.28.5 - js-tokens: 4.0.0 - picocolors: 1.1.1 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.6 + tslib: 2.8.1 - '@babel/compat-data@7.28.5': {} + '@aws-crypto/crc32c@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.6 + tslib: 2.8.1 - '@babel/core@7.28.5': + '@aws-crypto/sha1-browser@5.2.0': dependencies: - '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.5 - '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) - '@babel/helpers': 7.28.4 - '@babel/parser': 7.28.5 - '@babel/template': 7.27.2 - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 - '@jridgewell/remapping': 2.3.5 - convert-source-map: 2.0.0 - debug: 4.4.3(supports-color@5.5.0) - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.6 + '@aws-sdk/util-locate-window': 3.965.5 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 - '@babel/generator@7.28.5': + '@aws-crypto/sha256-browser@5.2.0': dependencies: - '@babel/parser': 7.28.5 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.6 + '@aws-sdk/util-locate-window': 3.965.5 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-crypto/sha256-js@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.6 + tslib: 2.8.1 + + '@aws-crypto/supports-web-crypto@5.2.0': + dependencies: + tslib: 2.8.1 + + '@aws-crypto/util@5.2.0': + dependencies: + '@aws-sdk/types': 3.973.6 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-sdk/client-s3@3.1013.0': + dependencies: + '@aws-crypto/sha1-browser': 5.2.0 + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.973.22 + '@aws-sdk/credential-provider-node': 3.972.23 + '@aws-sdk/middleware-bucket-endpoint': 3.972.8 + '@aws-sdk/middleware-expect-continue': 3.972.8 + '@aws-sdk/middleware-flexible-checksums': 3.974.2 + '@aws-sdk/middleware-host-header': 3.972.8 + '@aws-sdk/middleware-location-constraint': 3.972.8 + '@aws-sdk/middleware-logger': 3.972.8 + '@aws-sdk/middleware-recursion-detection': 3.972.8 + '@aws-sdk/middleware-sdk-s3': 3.972.22 + '@aws-sdk/middleware-ssec': 3.972.8 + '@aws-sdk/middleware-user-agent': 3.972.23 + '@aws-sdk/region-config-resolver': 3.972.8 + '@aws-sdk/signature-v4-multi-region': 3.996.10 + '@aws-sdk/types': 3.973.6 + '@aws-sdk/util-endpoints': 3.996.5 + '@aws-sdk/util-user-agent-browser': 3.972.8 + '@aws-sdk/util-user-agent-node': 3.973.9 + '@smithy/config-resolver': 4.4.13 + '@smithy/core': 3.23.12 + '@smithy/eventstream-serde-browser': 4.2.12 + '@smithy/eventstream-serde-config-resolver': 4.3.12 + '@smithy/eventstream-serde-node': 4.2.12 + '@smithy/fetch-http-handler': 5.3.15 + '@smithy/hash-blob-browser': 4.2.13 + '@smithy/hash-node': 4.2.12 + '@smithy/hash-stream-node': 4.2.12 + '@smithy/invalid-dependency': 4.2.12 + '@smithy/md5-js': 4.2.12 + '@smithy/middleware-content-length': 4.2.12 + '@smithy/middleware-endpoint': 4.4.27 + '@smithy/middleware-retry': 4.4.44 + '@smithy/middleware-serde': 4.2.15 + '@smithy/middleware-stack': 4.2.12 + '@smithy/node-config-provider': 4.3.12 + '@smithy/node-http-handler': 4.5.0 + '@smithy/protocol-http': 5.3.12 + '@smithy/smithy-client': 4.12.7 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.43 + '@smithy/util-defaults-mode-node': 4.2.47 + '@smithy/util-endpoints': 3.3.3 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-retry': 4.2.12 + '@smithy/util-stream': 4.5.20 + '@smithy/util-utf8': 4.2.2 + '@smithy/util-waiter': 4.2.13 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/core@3.973.22': + dependencies: + '@aws-sdk/types': 3.973.6 + '@aws-sdk/xml-builder': 3.972.14 + '@smithy/core': 3.23.12 + '@smithy/node-config-provider': 4.3.12 + '@smithy/property-provider': 4.2.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/signature-v4': 5.3.12 + '@smithy/smithy-client': 4.12.7 + '@smithy/types': 4.13.1 + '@smithy/util-base64': 4.3.2 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@aws-sdk/crc64-nvme@3.972.5': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-env@3.972.20': + dependencies: + '@aws-sdk/core': 3.973.22 + '@aws-sdk/types': 3.973.6 + '@smithy/property-provider': 4.2.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-http@3.972.22': + dependencies: + '@aws-sdk/core': 3.973.22 + '@aws-sdk/types': 3.973.6 + '@smithy/fetch-http-handler': 5.3.15 + '@smithy/node-http-handler': 4.5.0 + '@smithy/property-provider': 4.2.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/smithy-client': 4.12.7 + '@smithy/types': 4.13.1 + '@smithy/util-stream': 4.5.20 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-ini@3.972.22': + dependencies: + '@aws-sdk/core': 3.973.22 + '@aws-sdk/credential-provider-env': 3.972.20 + '@aws-sdk/credential-provider-http': 3.972.22 + '@aws-sdk/credential-provider-login': 3.972.22 + '@aws-sdk/credential-provider-process': 3.972.20 + '@aws-sdk/credential-provider-sso': 3.972.22 + '@aws-sdk/credential-provider-web-identity': 3.972.22 + '@aws-sdk/nested-clients': 3.996.12 + '@aws-sdk/types': 3.973.6 + '@smithy/credential-provider-imds': 4.2.12 + '@smithy/property-provider': 4.2.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-login@3.972.22': + dependencies: + '@aws-sdk/core': 3.973.22 + '@aws-sdk/nested-clients': 3.996.12 + '@aws-sdk/types': 3.973.6 + '@smithy/property-provider': 4.2.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-node@3.972.23': + dependencies: + '@aws-sdk/credential-provider-env': 3.972.20 + '@aws-sdk/credential-provider-http': 3.972.22 + '@aws-sdk/credential-provider-ini': 3.972.22 + '@aws-sdk/credential-provider-process': 3.972.20 + '@aws-sdk/credential-provider-sso': 3.972.22 + '@aws-sdk/credential-provider-web-identity': 3.972.22 + '@aws-sdk/types': 3.973.6 + '@smithy/credential-provider-imds': 4.2.12 + '@smithy/property-provider': 4.2.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-process@3.972.20': + dependencies: + '@aws-sdk/core': 3.973.22 + '@aws-sdk/types': 3.973.6 + '@smithy/property-provider': 4.2.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-sso@3.972.22': + dependencies: + '@aws-sdk/core': 3.973.22 + '@aws-sdk/nested-clients': 3.996.12 + '@aws-sdk/token-providers': 3.1013.0 + '@aws-sdk/types': 3.973.6 + '@smithy/property-provider': 4.2.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-web-identity@3.972.22': + dependencies: + '@aws-sdk/core': 3.973.22 + '@aws-sdk/nested-clients': 3.996.12 + '@aws-sdk/types': 3.973.6 + '@smithy/property-provider': 4.2.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/middleware-bucket-endpoint@3.972.8': + dependencies: + '@aws-sdk/types': 3.973.6 + '@aws-sdk/util-arn-parser': 3.972.3 + '@smithy/node-config-provider': 4.3.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + '@smithy/util-config-provider': 4.2.2 + tslib: 2.8.1 + + '@aws-sdk/middleware-expect-continue@3.972.8': + dependencies: + '@aws-sdk/types': 3.973.6 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@aws-sdk/middleware-flexible-checksums@3.974.2': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@aws-crypto/crc32c': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/core': 3.973.22 + '@aws-sdk/crc64-nvme': 3.972.5 + '@aws-sdk/types': 3.973.6 + '@smithy/is-array-buffer': 4.2.2 + '@smithy/node-config-provider': 4.3.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-stream': 4.5.20 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@aws-sdk/middleware-host-header@3.972.8': + dependencies: + '@aws-sdk/types': 3.973.6 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@aws-sdk/middleware-location-constraint@3.972.8': + dependencies: + '@aws-sdk/types': 3.973.6 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@aws-sdk/middleware-logger@3.972.8': + dependencies: + '@aws-sdk/types': 3.973.6 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@aws-sdk/middleware-recursion-detection@3.972.8': + dependencies: + '@aws-sdk/types': 3.973.6 + '@aws/lambda-invoke-store': 0.2.4 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@aws-sdk/middleware-sdk-s3@3.972.22': + dependencies: + '@aws-sdk/core': 3.973.22 + '@aws-sdk/types': 3.973.6 + '@aws-sdk/util-arn-parser': 3.972.3 + '@smithy/core': 3.23.12 + '@smithy/node-config-provider': 4.3.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/signature-v4': 5.3.12 + '@smithy/smithy-client': 4.12.7 + '@smithy/types': 4.13.1 + '@smithy/util-config-provider': 4.2.2 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-stream': 4.5.20 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@aws-sdk/middleware-ssec@3.972.8': + dependencies: + '@aws-sdk/types': 3.973.6 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@aws-sdk/middleware-user-agent@3.972.23': + dependencies: + '@aws-sdk/core': 3.973.22 + '@aws-sdk/types': 3.973.6 + '@aws-sdk/util-endpoints': 3.996.5 + '@smithy/core': 3.23.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + '@smithy/util-retry': 4.2.12 + tslib: 2.8.1 + + '@aws-sdk/nested-clients@3.996.12': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.973.22 + '@aws-sdk/middleware-host-header': 3.972.8 + '@aws-sdk/middleware-logger': 3.972.8 + '@aws-sdk/middleware-recursion-detection': 3.972.8 + '@aws-sdk/middleware-user-agent': 3.972.23 + '@aws-sdk/region-config-resolver': 3.972.8 + '@aws-sdk/types': 3.973.6 + '@aws-sdk/util-endpoints': 3.996.5 + '@aws-sdk/util-user-agent-browser': 3.972.8 + '@aws-sdk/util-user-agent-node': 3.973.9 + '@smithy/config-resolver': 4.4.13 + '@smithy/core': 3.23.12 + '@smithy/fetch-http-handler': 5.3.15 + '@smithy/hash-node': 4.2.12 + '@smithy/invalid-dependency': 4.2.12 + '@smithy/middleware-content-length': 4.2.12 + '@smithy/middleware-endpoint': 4.4.27 + '@smithy/middleware-retry': 4.4.44 + '@smithy/middleware-serde': 4.2.15 + '@smithy/middleware-stack': 4.2.12 + '@smithy/node-config-provider': 4.3.12 + '@smithy/node-http-handler': 4.5.0 + '@smithy/protocol-http': 5.3.12 + '@smithy/smithy-client': 4.12.7 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.43 + '@smithy/util-defaults-mode-node': 4.2.47 + '@smithy/util-endpoints': 3.3.3 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-retry': 4.2.12 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/region-config-resolver@3.972.8': + dependencies: + '@aws-sdk/types': 3.973.6 + '@smithy/config-resolver': 4.4.13 + '@smithy/node-config-provider': 4.3.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@aws-sdk/signature-v4-multi-region@3.996.10': + dependencies: + '@aws-sdk/middleware-sdk-s3': 3.972.22 + '@aws-sdk/types': 3.973.6 + '@smithy/protocol-http': 5.3.12 + '@smithy/signature-v4': 5.3.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@aws-sdk/token-providers@3.1013.0': + dependencies: + '@aws-sdk/core': 3.973.22 + '@aws-sdk/nested-clients': 3.996.12 + '@aws-sdk/types': 3.973.6 + '@smithy/property-provider': 4.2.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/types@3.973.6': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@aws-sdk/util-arn-parser@3.972.3': + dependencies: + tslib: 2.8.1 + + '@aws-sdk/util-endpoints@3.996.5': + dependencies: + '@aws-sdk/types': 3.973.6 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 + '@smithy/util-endpoints': 3.3.3 + tslib: 2.8.1 + + '@aws-sdk/util-locate-window@3.965.5': + dependencies: + tslib: 2.8.1 + + '@aws-sdk/util-user-agent-browser@3.972.8': + dependencies: + '@aws-sdk/types': 3.973.6 + '@smithy/types': 4.13.1 + bowser: 2.14.1 + tslib: 2.8.1 + + '@aws-sdk/util-user-agent-node@3.973.9': + dependencies: + '@aws-sdk/middleware-user-agent': 3.972.23 + '@aws-sdk/types': 3.973.6 + '@smithy/node-config-provider': 4.3.12 + '@smithy/types': 4.13.1 + '@smithy/util-config-provider': 4.2.2 + tslib: 2.8.1 + + '@aws-sdk/xml-builder@3.972.14': + dependencies: + '@smithy/types': 4.13.1 + fast-xml-parser: 5.5.6 + tslib: 2.8.1 + + '@aws/lambda-invoke-store@0.2.4': {} + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.28.5': {} + + '@babel/core@7.28.5': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3(supports-color@5.5.0) + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.28.5': + dependencies: + '@babel/parser': 7.28.5 '@babel/types': 7.28.5 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 @@ -11164,6 +12062,36 @@ snapshots: '@repeaterjs/repeater@3.0.6': {} + '@riotbyte-com/nest-service-locator@0.3.0-rc.4(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20)': + dependencies: + '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.20)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.2) + + '@riotbyte-com/project-q-core@1.0.19-rc.3': + dependencies: + uuid: 11.0.3 + zod: 4.3.6 + + '@riotbyte-com/project-q-nestjs@1.0.19-rc.3(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@nestjs/event-emitter@3.0.1(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20))(@riotbyte-com/nest-service-locator@0.3.0-rc.4(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20))(eventemitter2@6.4.9)(nest-commander@3.16.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@types/inquirer@8.2.12)(typescript@5.9.3))': + dependencies: + '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.20)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/event-emitter': 3.0.1(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20) + '@riotbyte-com/nest-service-locator': 0.3.0-rc.4(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20) + '@riotbyte-com/project-q-core': 1.0.19-rc.3 + eventemitter2: 6.4.9 + nest-commander: 3.16.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@types/inquirer@8.2.12)(typescript@5.9.3) + uuid: 11.0.3 + zod: 4.3.6 + + '@riotbyte-com/project-q-prisma@1.0.19-rc.3(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@riotbyte-com/project-q-core@1.0.19-rc.3)(@riotbyte-com/project-q-nestjs@1.0.19-rc.3(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@nestjs/event-emitter@3.0.1(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20))(@riotbyte-com/nest-service-locator@0.3.0-rc.4(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20))(eventemitter2@6.4.9)(nest-commander@3.16.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@types/inquirer@8.2.12)(typescript@5.9.3)))': + dependencies: + '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@riotbyte-com/project-q-core': 1.0.19-rc.3 + '@riotbyte-com/project-q-nestjs': 1.0.19-rc.3(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@nestjs/event-emitter@3.0.1(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20))(@riotbyte-com/nest-service-locator@0.3.0-rc.4(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20))(eventemitter2@6.4.9)(nest-commander@3.16.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@types/inquirer@8.2.12)(typescript@5.9.3)) + uuid: 11.0.3 + zod: 4.3.6 + '@rolldown/pluginutils@1.0.0-beta.27': {} '@rollup/pluginutils@5.3.0(rollup@4.54.0)': @@ -11282,6 +12210,345 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.1 + '@smithy/abort-controller@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/chunked-blob-reader-native@4.2.3': + dependencies: + '@smithy/util-base64': 4.3.2 + tslib: 2.8.1 + + '@smithy/chunked-blob-reader@5.2.2': + dependencies: + tslib: 2.8.1 + + '@smithy/config-resolver@4.4.13': + dependencies: + '@smithy/node-config-provider': 4.3.12 + '@smithy/types': 4.13.1 + '@smithy/util-config-provider': 4.2.2 + '@smithy/util-endpoints': 3.3.3 + '@smithy/util-middleware': 4.2.12 + tslib: 2.8.1 + + '@smithy/core@3.23.12': + dependencies: + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-stream': 4.5.20 + '@smithy/util-utf8': 4.2.2 + '@smithy/uuid': 1.1.2 + tslib: 2.8.1 + + '@smithy/credential-provider-imds@4.2.12': + dependencies: + '@smithy/node-config-provider': 4.3.12 + '@smithy/property-provider': 4.2.12 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 + tslib: 2.8.1 + + '@smithy/eventstream-codec@4.2.12': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@smithy/types': 4.13.1 + '@smithy/util-hex-encoding': 4.2.2 + tslib: 2.8.1 + + '@smithy/eventstream-serde-browser@4.2.12': + dependencies: + '@smithy/eventstream-serde-universal': 4.2.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/eventstream-serde-config-resolver@4.3.12': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/eventstream-serde-node@4.2.12': + dependencies: + '@smithy/eventstream-serde-universal': 4.2.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/eventstream-serde-universal@4.2.12': + dependencies: + '@smithy/eventstream-codec': 4.2.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/fetch-http-handler@5.3.15': + dependencies: + '@smithy/protocol-http': 5.3.12 + '@smithy/querystring-builder': 4.2.12 + '@smithy/types': 4.13.1 + '@smithy/util-base64': 4.3.2 + tslib: 2.8.1 + + '@smithy/hash-blob-browser@4.2.13': + dependencies: + '@smithy/chunked-blob-reader': 5.2.2 + '@smithy/chunked-blob-reader-native': 4.2.3 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/hash-node@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@smithy/hash-stream-node@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@smithy/invalid-dependency@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/is-array-buffer@2.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/is-array-buffer@4.2.2': + dependencies: + tslib: 2.8.1 + + '@smithy/md5-js@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@smithy/middleware-content-length@4.2.12': + dependencies: + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/middleware-endpoint@4.4.27': + dependencies: + '@smithy/core': 3.23.12 + '@smithy/middleware-serde': 4.2.15 + '@smithy/node-config-provider': 4.3.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 + '@smithy/util-middleware': 4.2.12 + tslib: 2.8.1 + + '@smithy/middleware-retry@4.4.44': + dependencies: + '@smithy/node-config-provider': 4.3.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/service-error-classification': 4.2.12 + '@smithy/smithy-client': 4.12.7 + '@smithy/types': 4.13.1 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-retry': 4.2.12 + '@smithy/uuid': 1.1.2 + tslib: 2.8.1 + + '@smithy/middleware-serde@4.2.15': + dependencies: + '@smithy/core': 3.23.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/middleware-stack@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/node-config-provider@4.3.12': + dependencies: + '@smithy/property-provider': 4.2.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/node-http-handler@4.5.0': + dependencies: + '@smithy/abort-controller': 4.2.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/querystring-builder': 4.2.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/property-provider@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/protocol-http@5.3.12': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/querystring-builder@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + '@smithy/util-uri-escape': 4.2.2 + tslib: 2.8.1 + + '@smithy/querystring-parser@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/service-error-classification@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + + '@smithy/shared-ini-file-loader@4.4.7': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/signature-v4@5.3.12': + dependencies: + '@smithy/is-array-buffer': 4.2.2 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + '@smithy/util-hex-encoding': 4.2.2 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-uri-escape': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@smithy/smithy-client@4.12.7': + dependencies: + '@smithy/core': 3.23.12 + '@smithy/middleware-endpoint': 4.4.27 + '@smithy/middleware-stack': 4.2.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + '@smithy/util-stream': 4.5.20 + tslib: 2.8.1 + + '@smithy/types@4.13.1': + dependencies: + tslib: 2.8.1 + + '@smithy/url-parser@4.2.12': + dependencies: + '@smithy/querystring-parser': 4.2.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/util-base64@4.3.2': + dependencies: + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@smithy/util-body-length-browser@4.2.2': + dependencies: + tslib: 2.8.1 + + '@smithy/util-body-length-node@4.2.3': + dependencies: + tslib: 2.8.1 + + '@smithy/util-buffer-from@2.2.0': + dependencies: + '@smithy/is-array-buffer': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-buffer-from@4.2.2': + dependencies: + '@smithy/is-array-buffer': 4.2.2 + tslib: 2.8.1 + + '@smithy/util-config-provider@4.2.2': + dependencies: + tslib: 2.8.1 + + '@smithy/util-defaults-mode-browser@4.3.43': + dependencies: + '@smithy/property-provider': 4.2.12 + '@smithy/smithy-client': 4.12.7 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/util-defaults-mode-node@4.2.47': + dependencies: + '@smithy/config-resolver': 4.4.13 + '@smithy/credential-provider-imds': 4.2.12 + '@smithy/node-config-provider': 4.3.12 + '@smithy/property-provider': 4.2.12 + '@smithy/smithy-client': 4.12.7 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/util-endpoints@3.3.3': + dependencies: + '@smithy/node-config-provider': 4.3.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/util-hex-encoding@4.2.2': + dependencies: + tslib: 2.8.1 + + '@smithy/util-middleware@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/util-retry@4.2.12': + dependencies: + '@smithy/service-error-classification': 4.2.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/util-stream@4.5.20': + dependencies: + '@smithy/fetch-http-handler': 5.3.15 + '@smithy/node-http-handler': 4.5.0 + '@smithy/types': 4.13.1 + '@smithy/util-base64': 4.3.2 + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-hex-encoding': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@smithy/util-uri-escape@4.2.2': + dependencies: + tslib: 2.8.1 + + '@smithy/util-utf8@2.3.0': + dependencies: + '@smithy/util-buffer-from': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-utf8@4.2.2': + dependencies: + '@smithy/util-buffer-from': 4.2.2 + tslib: 2.8.1 + + '@smithy/util-waiter@4.2.13': + dependencies: + '@smithy/abort-controller': 4.2.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/uuid@1.1.2': + dependencies: + tslib: 2.8.1 + '@stablelib/base64@1.0.1': {} '@standard-schema/spec@1.1.0': {} @@ -12257,6 +13524,8 @@ snapshots: transitivePeerDependencies: - supports-color + bowser@2.14.1: {} + brace-expansion@1.1.12: dependencies: balanced-match: 1.0.2 @@ -13208,6 +14477,16 @@ snapshots: fast-sha256@1.3.0: {} + fast-xml-builder@1.1.4: + dependencies: + path-expression-matcher: 1.1.3 + + fast-xml-parser@5.5.6: + dependencies: + fast-xml-builder: 1.1.4 + path-expression-matcher: 1.1.3 + strnum: 2.2.1 + fastq@1.20.1: dependencies: reusify: 1.1.0 @@ -15858,6 +17137,8 @@ snapshots: path-exists@4.0.0: {} + path-expression-matcher@1.1.3: {} + path-is-absolute@1.0.1: {} path-key@3.1.1: {} @@ -16798,6 +18079,8 @@ snapshots: strip-json-comments@3.1.1: {} + strnum@2.2.1: {} + strtok3@10.3.4: dependencies: '@tokenizer/token': 0.3.0