feat: Implement GraphQL API with optimized dashboard queries
Implemented complete GraphQL API with Apollo Server for efficient data fetching: Backend Changes: - Installed @nestjs/graphql@13.2.0, @nestjs/apollo@13.2.1, graphql@16.11.0, dataloader@2.2.3 - Configured Apollo Server with auto schema generation (src/schema.gql) - GraphQL Playground enabled in non-production environments - JWT authentication via GqlAuthGuard - Custom error formatting GraphQL Types (src/graphql/types/): - UserType with family relationships - ChildType with birthDate, gender, photoUrl - FamilyMemberType with role and user relation - ActivityGQLType with startedAt, endedAt, metadata - DashboardType aggregating all dashboard data - DailySummaryType with activity counts and totals - Enum types: ActivityType, FamilyRole, Gender, FeedingMethod, DiaperType Dashboard Resolver (src/graphql/resolvers/dashboard.resolver.ts): - Query: dashboard(childId?: ID) returns DashboardType - Single optimized query replacing 4+ REST API calls: * GET /api/v1/children * GET /api/v1/tracking/child/:id/recent * GET /api/v1/tracking/child/:id/summary/today * GET /api/v1/families/:id/members - Aggregates children, activities, family members, summaries in one query - ResolveField decorators for child and logger relations - Calculates daily summary (feeding, sleep, diaper, medication counts) - Uses Between for date range filtering - Handles metadata extraction for activity details DataLoader Implementation (src/graphql/dataloaders/): - ChildDataLoader: batchChildren, batchChildrenByFamily - UserDataLoader: batchUsers - REQUEST scope for per-request instance - Prevents N+1 query problem when resolving relations - Uses TypeORM In() for batch loading GraphQL Module (src/graphql/graphql.module.ts): - Exports ChildDataLoader and UserDataLoader - TypeORM integration with Child, Activity, FamilyMember, User entities - DashboardResolver provider Example Queries (src/graphql/example-queries.gql): - GetDashboard with childId parameter - GetDashboardAllChildren for listing - Documented usage and expected results Files Created (11 total): - src/graphql/types/ (5 files) - src/graphql/dataloaders/ (2 files) - src/graphql/resolvers/ (1 file) - src/graphql/guards/ (1 file) - src/graphql/graphql.module.ts - src/graphql/example-queries.gql Performance Improvements: - Dashboard load reduced from 4+ REST calls to 1 GraphQL query - DataLoader batching eliminates N+1 queries - Client can request only needed fields - Reduced network overhead and latency Usage: - Endpoint: http://localhost:3020/graphql - Playground: http://localhost:3020/graphql (dev only) - Authentication: JWT token in Authorization header 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -86,7 +86,7 @@ This document identifies features specified in the documentation that are not ye
|
|||||||
5. **Security Hardening** - CORS configuration, comprehensive input validation, XSS headers
|
5. **Security Hardening** - CORS configuration, comprehensive input validation, XSS headers
|
||||||
|
|
||||||
**Medium Priority (Post-Launch)**:
|
**Medium Priority (Post-Launch)**:
|
||||||
1. **GraphQL API** - Complex queries for dashboard optimization
|
1. ~~**GraphQL API**~~ - ✅ COMPLETED (October 2, 2025) - Complex queries for dashboard optimization with DataLoader for N+1 prevention
|
||||||
2. **Voice Processing** - Whisper API integration, multi-language voice recognition
|
2. **Voice Processing** - Whisper API integration, multi-language voice recognition
|
||||||
3. **Analytics & Predictions** - Pattern detection, ML-based next event predictions
|
3. **Analytics & Predictions** - Pattern detection, ML-based next event predictions
|
||||||
4. **PWA Features** - Service worker configuration, offline pages, install prompts
|
4. **PWA Features** - Service worker configuration, offline pages, install prompts
|
||||||
@@ -159,27 +159,75 @@ This document identifies features specified in the documentation that are not ye
|
|||||||
- Priority: High
|
- Priority: High
|
||||||
- Impact: Account security and COPPA compliance
|
- Impact: Account security and COPPA compliance
|
||||||
|
|
||||||
### 1.2 GraphQL Implementation (MEDIUM Priority)
|
### 1.2 GraphQL Implementation ✅ COMPLETED (October 2, 2025)
|
||||||
|
|
||||||
**Source**: `maternal-app-api-spec.md`, `maternal-app-tech-stack.md`
|
**Source**: `maternal-app-api-spec.md`, `maternal-app-tech-stack.md`
|
||||||
|
|
||||||
1. **GraphQL Endpoint**
|
1. **GraphQL Endpoint** ✅ COMPLETED
|
||||||
- Status: Dependencies installed (@nestjs/graphql) but not configured
|
- Status: **IMPLEMENTED**
|
||||||
- Current: REST API only
|
- Current: GraphQL endpoint at /graphql with auto-generated schema
|
||||||
- Needed: GraphQL endpoint at /graphql with schema
|
- Implemented:
|
||||||
- Priority: Medium
|
* **Apollo Server Integration** (app.module.ts:35-57):
|
||||||
- Impact: Efficient complex data fetching for dashboard
|
- ApolloDriver with @nestjs/apollo@13.2.1
|
||||||
|
- Auto schema generation at src/schema.gql
|
||||||
|
- GraphQL Playground enabled in non-production
|
||||||
|
- JWT authentication via GqlAuthGuard
|
||||||
|
- Custom error formatting
|
||||||
|
* **GraphQL Types** (src/graphql/types/):
|
||||||
|
- UserType, ChildType, FamilyMemberType, ActivityGQLType
|
||||||
|
- DashboardType with DailySummaryType
|
||||||
|
- Enum types: ActivityType, FamilyRole, Gender, FeedingMethod, DiaperType
|
||||||
|
- All types with proper Field decorators
|
||||||
|
* **Dashboard Resolver** (dashboard.resolver.ts):
|
||||||
|
- Query: dashboard(childId?: ID) returns DashboardType
|
||||||
|
- Single optimized query replacing 4+ REST endpoints
|
||||||
|
- Aggregates children, activities, family members, summaries
|
||||||
|
- ResolveField for child and logger relations
|
||||||
|
- Calculates daily summary (feeding, sleep, diaper, medication counts)
|
||||||
|
* **DataLoader for N+1 Prevention** (src/graphql/dataloaders/):
|
||||||
|
- ChildDataLoader: batchChildren, batchChildrenByFamily
|
||||||
|
- UserDataLoader: batchUsers
|
||||||
|
- REQUEST scope for per-request batching
|
||||||
|
- Prevents N+1 query problem for relations
|
||||||
|
* **GraphQL Module** (graphql.module.ts):
|
||||||
|
- Exports DataLoaders for dependency injection
|
||||||
|
- TypeORM integration with Child, Activity, FamilyMember, User
|
||||||
|
- DashboardResolver provider
|
||||||
|
- Files Created:
|
||||||
|
* src/graphql/types/ (5 files: user, child, family, activity, dashboard)
|
||||||
|
* src/graphql/dataloaders/ (2 files: child, user)
|
||||||
|
* src/graphql/resolvers/ (1 file: dashboard)
|
||||||
|
* src/graphql/guards/ (1 file: gql-auth)
|
||||||
|
* src/graphql/graphql.module.ts
|
||||||
|
* src/graphql/example-queries.gql
|
||||||
|
- Example Query:
|
||||||
|
```graphql
|
||||||
|
query GetDashboard($childId: ID) {
|
||||||
|
dashboard(childId: $childId) {
|
||||||
|
children { id name birthDate }
|
||||||
|
selectedChild { id name }
|
||||||
|
recentActivities { id type startedAt logger { name } }
|
||||||
|
todaySummary { feedingCount sleepCount diaperCount }
|
||||||
|
familyMembers { userId role user { name } }
|
||||||
|
totalChildren
|
||||||
|
totalActivitiesToday
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- Performance: Single query replaces 4+ REST calls
|
||||||
|
- Priority: Medium ✅ **COMPLETE**
|
||||||
|
- Impact: Dashboard load time reduced, efficient data fetching
|
||||||
|
|
||||||
2. **GraphQL Subscriptions**
|
2. **GraphQL Subscriptions**
|
||||||
- Status: Not implemented
|
- Status: Not implemented
|
||||||
- Current: WebSocket for real-time sync
|
- Current: WebSocket for real-time sync (Socket.io)
|
||||||
- Needed: GraphQL subscriptions for real-time data
|
- Needed: GraphQL subscriptions for real-time data
|
||||||
- Priority: Low
|
- Priority: Low (deferred - Socket.io working well)
|
||||||
- Impact: Alternative real-time implementation
|
- Impact: Alternative real-time implementation
|
||||||
|
|
||||||
3. **Complex Dashboard Queries**
|
3. **Complex Dashboard Queries** ✅ COMPLETED
|
||||||
- Status: Not implemented
|
- Status: **IMPLEMENTED via GraphQL dashboard query**
|
||||||
- Current: Multiple REST calls for dashboard data
|
- Current: Single GraphQL query aggregates all dashboard data
|
||||||
- Needed: Single GraphQL query for entire dashboard
|
- Needed: Single GraphQL query for entire dashboard
|
||||||
- Priority: Medium
|
- Priority: Medium
|
||||||
- Impact: Performance optimization, reduced API calls
|
- Impact: Performance optimization, reduced API calls
|
||||||
|
|||||||
496
maternal-app/maternal-app-backend/package-lock.json
generated
496
maternal-app/maternal-app-backend/package-lock.json
generated
@@ -15,6 +15,7 @@
|
|||||||
"@aws-sdk/s3-request-presigner": "^3.899.0",
|
"@aws-sdk/s3-request-presigner": "^3.899.0",
|
||||||
"@langchain/core": "^0.3.78",
|
"@langchain/core": "^0.3.78",
|
||||||
"@langchain/openai": "^0.6.14",
|
"@langchain/openai": "^0.6.14",
|
||||||
|
"@nestjs/apollo": "^13.2.1",
|
||||||
"@nestjs/common": "^11.1.6",
|
"@nestjs/common": "^11.1.6",
|
||||||
"@nestjs/config": "^4.0.2",
|
"@nestjs/config": "^4.0.2",
|
||||||
"@nestjs/core": "^11.1.6",
|
"@nestjs/core": "^11.1.6",
|
||||||
@@ -39,6 +40,8 @@
|
|||||||
"cache-manager-redis-yet": "^5.1.5",
|
"cache-manager-redis-yet": "^5.1.5",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.14.2",
|
"class-validator": "^0.14.2",
|
||||||
|
"dataloader": "^2.2.3",
|
||||||
|
"date-fns": "^4.1.0",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
"form-data": "^4.0.4",
|
"form-data": "^4.0.4",
|
||||||
"graphql": "^16.11.0",
|
"graphql": "^16.11.0",
|
||||||
@@ -66,7 +69,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nestjs/cli": "^10.0.0",
|
"@nestjs/cli": "^10.0.0",
|
||||||
"@nestjs/schematics": "^10.0.0",
|
"@nestjs/schematics": "^10.0.0",
|
||||||
"@nestjs/testing": "^10.0.0",
|
"@nestjs/testing": "^11.1.6",
|
||||||
"@types/bcrypt": "^6.0.0",
|
"@types/bcrypt": "^6.0.0",
|
||||||
"@types/express": "^5.0.0",
|
"@types/express": "^5.0.0",
|
||||||
"@types/jest": "^29.5.2",
|
"@types/jest": "^29.5.2",
|
||||||
@@ -119,6 +122,30 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@angular-devkit/core/node_modules/ajv": {
|
||||||
|
"version": "8.12.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
|
||||||
|
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"fast-deep-equal": "^3.1.1",
|
||||||
|
"json-schema-traverse": "^1.0.0",
|
||||||
|
"require-from-string": "^2.0.2",
|
||||||
|
"uri-js": "^4.2.2"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/epoberezkin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@angular-devkit/core/node_modules/json-schema-traverse": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@angular-devkit/core/node_modules/rxjs": {
|
"node_modules/@angular-devkit/core/node_modules/rxjs": {
|
||||||
"version": "7.8.1",
|
"version": "7.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
|
||||||
@@ -334,6 +361,22 @@
|
|||||||
"graphql": "14.x || 15.x || 16.x"
|
"graphql": "14.x || 15.x || 16.x"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@apollo/server-plugin-landing-page-graphql-playground": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@apollo/server-plugin-landing-page-graphql-playground/-/server-plugin-landing-page-graphql-playground-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-tWhQzD7DtiTO/wfbGvasryz7eJSuEh9XJHgRTMZI7+Wu/omylG5gH6K6ksg1Vccg8/Xuglfi2f1M5Nm/IlBBGw==",
|
||||||
|
"deprecated": "The use of GraphQL Playground in Apollo Server was supported in previous versions, but this is no longer the case as of December 31, 2022. This package exists for v4 migration purposes only. We do not intend to resolve security issues or other bugs with this package if they arise, so please migrate away from this to [Apollo Server's default Explorer](https://www.apollographql.com/docs/apollo-server/api/plugin/landing-pages) as soon as possible.",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@apollographql/graphql-playground-html": "1.6.29"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@apollo/server": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@apollo/server/node_modules/uuid": {
|
"node_modules/@apollo/server/node_modules/uuid": {
|
||||||
"version": "11.1.0",
|
"version": "11.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
|
||||||
@@ -501,6 +544,15 @@
|
|||||||
"node": ">=16"
|
"node": ">=16"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@apollographql/graphql-playground-html": {
|
||||||
|
"version": "1.6.29",
|
||||||
|
"resolved": "https://registry.npmjs.org/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.29.tgz",
|
||||||
|
"integrity": "sha512-xCcXpoz52rI4ksJSdOCxeOCn2DLocxwHf9dVT/Q90Pte1LX+LY+91SFtJF3KXVHH8kEin+g1KKCQPKBjZJfWNA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"xss": "^1.0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@aws-crypto/crc32": {
|
"node_modules/@aws-crypto/crc32": {
|
||||||
"version": "5.2.0",
|
"version": "5.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz",
|
||||||
@@ -1996,7 +2048,7 @@
|
|||||||
"version": "0.8.1",
|
"version": "0.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
||||||
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/trace-mapping": "0.3.9"
|
"@jridgewell/trace-mapping": "0.3.9"
|
||||||
@@ -2009,7 +2061,7 @@
|
|||||||
"version": "0.3.9",
|
"version": "0.3.9",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
||||||
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/resolve-uri": "^3.0.3",
|
"@jridgewell/resolve-uri": "^3.0.3",
|
||||||
@@ -2141,23 +2193,6 @@
|
|||||||
"url": "https://opencollective.com/eslint"
|
"url": "https://opencollective.com/eslint"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/eslintrc/node_modules/ajv": {
|
|
||||||
"version": "6.12.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
|
||||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"fast-deep-equal": "^3.1.1",
|
|
||||||
"fast-json-stable-stringify": "^2.0.0",
|
|
||||||
"json-schema-traverse": "^0.4.1",
|
|
||||||
"uri-js": "^4.2.2"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/epoberezkin"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
|
"node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
|
||||||
"version": "1.1.12",
|
"version": "1.1.12",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||||
@@ -2192,13 +2227,6 @@
|
|||||||
"node": ">= 4"
|
"node": ">= 4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": {
|
|
||||||
"version": "0.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
|
||||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/@eslint/eslintrc/node_modules/minimatch": {
|
"node_modules/@eslint/eslintrc/node_modules/minimatch": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||||
@@ -3378,7 +3406,7 @@
|
|||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||||
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
@@ -3399,7 +3427,7 @@
|
|||||||
"version": "1.5.5",
|
"version": "1.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||||
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@jridgewell/trace-mapping": {
|
"node_modules/@jridgewell/trace-mapping": {
|
||||||
@@ -3560,6 +3588,42 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@nestjs/apollo": {
|
||||||
|
"version": "13.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nestjs/apollo/-/apollo-13.2.1.tgz",
|
||||||
|
"integrity": "sha512-BJPNw8xqs4DfdEEmjaAbI6cIJsHouWjcZN70BKTPl8rZcw4Tf61RonqFRn0F/rr/aiccWGAuXJuWY4dPsgah4Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@apollo/server-plugin-landing-page-graphql-playground": "4.0.1",
|
||||||
|
"iterall": "1.3.0",
|
||||||
|
"lodash.omit": "4.5.0",
|
||||||
|
"tslib": "2.8.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@apollo/gateway": "^2.0.0",
|
||||||
|
"@apollo/server": "^5.0.0",
|
||||||
|
"@apollo/subgraph": "^2.0.0",
|
||||||
|
"@as-integrations/fastify": "^2.1.1 || ^3.0.0",
|
||||||
|
"@nestjs/common": "^11.0.1",
|
||||||
|
"@nestjs/core": "^11.0.1",
|
||||||
|
"@nestjs/graphql": "^13.0.0",
|
||||||
|
"graphql": "^16.10.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@apollo/gateway": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@apollo/subgraph": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@as-integrations/express5": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@as-integrations/fastify": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@nestjs/cli": {
|
"node_modules/@nestjs/cli": {
|
||||||
"version": "10.4.9",
|
"version": "10.4.9",
|
||||||
"resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-10.4.9.tgz",
|
"resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-10.4.9.tgz",
|
||||||
@@ -4091,9 +4155,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@nestjs/testing": {
|
"node_modules/@nestjs/testing": {
|
||||||
"version": "10.4.20",
|
"version": "11.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.4.20.tgz",
|
"resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.1.6.tgz",
|
||||||
"integrity": "sha512-nMkRDukDKskdPruM6EsgMq7yJua+CPZM6I6FrLP8yXw8BiVSPv9Nm0CtcGGwt3kgZF9hfxKjGqLjsvVBsv6Vfw==",
|
"integrity": "sha512-srYzzDNxGvVCe1j0SpTS9/ix75PKt6Sn6iMaH1rpJ6nj2g8vwNrhK0CoJJXvpCYgrnI+2WES2pprYnq8rAMYHA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -4104,10 +4168,10 @@
|
|||||||
"url": "https://opencollective.com/nest"
|
"url": "https://opencollective.com/nest"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@nestjs/common": "^10.0.0",
|
"@nestjs/common": "^11.0.0",
|
||||||
"@nestjs/core": "^10.0.0",
|
"@nestjs/core": "^11.0.0",
|
||||||
"@nestjs/microservices": "^10.0.0",
|
"@nestjs/microservices": "^11.0.0",
|
||||||
"@nestjs/platform-express": "^10.0.0"
|
"@nestjs/platform-express": "^11.0.0"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"@nestjs/microservices": {
|
"@nestjs/microservices": {
|
||||||
@@ -5059,30 +5123,26 @@
|
|||||||
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
|
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
|
||||||
"license": "BSD-3-Clause"
|
"license": "BSD-3-Clause"
|
||||||
},
|
},
|
||||||
"node_modules/@redis/bloom": {
|
|
||||||
"version": "5.8.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.8.3.tgz",
|
|
||||||
"integrity": "sha512-1eldTzHvdW3Oi0TReb8m1yiFt8ZwyF6rv1NpZyG5R4TpCwuAdKQetBKoCw7D96tNFgsVVd6eL+NaGZZCqhRg4g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 18"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@redis/client": "^5.8.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@redis/client": {
|
"node_modules/@redis/client": {
|
||||||
"version": "5.8.3",
|
"version": "1.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/@redis/client/-/client-5.8.3.tgz",
|
"resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.1.tgz",
|
||||||
"integrity": "sha512-MZVUE+l7LmMIYlIjubPosruJ9ltSLGFmJqsXApTqPLyHLjsJUSAbAJb/A3N34fEqean4ddiDkdWzNu4ZKPvRUg==",
|
"integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cluster-key-slot": "1.1.2"
|
"cluster-key-slot": "1.1.2",
|
||||||
|
"generic-pool": "3.9.0",
|
||||||
|
"yallist": "4.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 18"
|
"node": ">=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@redis/client/node_modules/yallist": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/@redis/graph": {
|
"node_modules/@redis/graph": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz",
|
||||||
@@ -5092,42 +5152,6 @@
|
|||||||
"@redis/client": "^1.0.0"
|
"@redis/client": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@redis/json": {
|
|
||||||
"version": "5.8.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@redis/json/-/json-5.8.3.tgz",
|
|
||||||
"integrity": "sha512-DRR09fy/u8gynHGJ4gzXYeM7D8nlS6EMv5o+h20ndTJiAc7RGR01fdk2FNjnn1Nz5PjgGGownF+s72bYG4nZKQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 18"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@redis/client": "^5.8.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@redis/search": {
|
|
||||||
"version": "5.8.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@redis/search/-/search-5.8.3.tgz",
|
|
||||||
"integrity": "sha512-EMIvEeGRR2I0BJEz4PV88DyCuPmMT1rDtznlsHY3cKSDcc9vj0Q411jUnX0iU2vVowUgWn/cpySKjpXdZ8m+5g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 18"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@redis/client": "^5.8.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@redis/time-series": {
|
|
||||||
"version": "5.8.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.8.3.tgz",
|
|
||||||
"integrity": "sha512-5Jwy3ilsUYQjzpE7WZ1lEeG1RkqQ5kHtwV1p8yxXHSEmyUbC/T/AVgyjMcm52Olj/Ov/mhDKjx6ndYUi14bXsw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 18"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@redis/client": "^5.8.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@sentry-internal/node-cpu-profiler": {
|
"node_modules/@sentry-internal/node-cpu-profiler": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry-internal/node-cpu-profiler/-/node-cpu-profiler-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry-internal/node-cpu-profiler/-/node-cpu-profiler-2.2.0.tgz",
|
||||||
@@ -6083,28 +6107,28 @@
|
|||||||
"version": "1.0.11",
|
"version": "1.0.11",
|
||||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
|
||||||
"integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
|
"integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@tsconfig/node12": {
|
"node_modules/@tsconfig/node12": {
|
||||||
"version": "1.0.11",
|
"version": "1.0.11",
|
||||||
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
|
||||||
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
|
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@tsconfig/node14": {
|
"node_modules/@tsconfig/node14": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
|
||||||
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
|
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@tsconfig/node16": {
|
"node_modules/@tsconfig/node16": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
|
||||||
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
|
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/babel__core": {
|
"node_modules/@types/babel__core": {
|
||||||
@@ -7054,7 +7078,7 @@
|
|||||||
"version": "8.3.4",
|
"version": "8.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
|
||||||
"integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
|
"integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"acorn": "^8.11.0"
|
"acorn": "^8.11.0"
|
||||||
@@ -7064,15 +7088,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ajv": {
|
"node_modules/ajv": {
|
||||||
"version": "8.12.0",
|
"version": "6.12.6",
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||||
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
|
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-deep-equal": "^3.1.1",
|
"fast-deep-equal": "^3.1.1",
|
||||||
"json-schema-traverse": "^1.0.0",
|
"fast-json-stable-stringify": "^2.0.0",
|
||||||
"require-from-string": "^2.0.2",
|
"json-schema-traverse": "^0.4.1",
|
||||||
"uri-js": "^4.2.2"
|
"uri-js": "^4.2.2"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
@@ -7098,6 +7122,30 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ajv-formats/node_modules/ajv": {
|
||||||
|
"version": "8.17.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
||||||
|
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"fast-deep-equal": "^3.1.3",
|
||||||
|
"fast-uri": "^3.0.1",
|
||||||
|
"json-schema-traverse": "^1.0.0",
|
||||||
|
"require-from-string": "^2.0.2"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/epoberezkin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ajv-formats/node_modules/json-schema-traverse": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/ajv-keywords": {
|
"node_modules/ajv-keywords": {
|
||||||
"version": "3.5.2",
|
"version": "3.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
|
||||||
@@ -7226,7 +7274,7 @@
|
|||||||
"version": "4.1.3",
|
"version": "4.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||||
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/argparse": {
|
"node_modules/argparse": {
|
||||||
@@ -7809,20 +7857,6 @@
|
|||||||
"@redis/client": "^1.0.0"
|
"@redis/client": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cache-manager-redis-yet/node_modules/@redis/client": {
|
|
||||||
"version": "1.6.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.1.tgz",
|
|
||||||
"integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"cluster-key-slot": "1.1.2",
|
|
||||||
"generic-pool": "3.9.0",
|
|
||||||
"yallist": "4.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/cache-manager-redis-yet/node_modules/@redis/json": {
|
"node_modules/cache-manager-redis-yet/node_modules/@redis/json": {
|
||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.7.tgz",
|
||||||
@@ -7894,12 +7928,6 @@
|
|||||||
"@redis/time-series": "1.1.0"
|
"@redis/time-series": "1.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cache-manager-redis-yet/node_modules/yallist": {
|
|
||||||
"version": "4.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
|
||||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
|
||||||
"license": "ISC"
|
|
||||||
},
|
|
||||||
"node_modules/cache-manager/node_modules/keyv": {
|
"node_modules/cache-manager/node_modules/keyv": {
|
||||||
"version": "5.5.3",
|
"version": "5.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-5.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/keyv/-/keyv-5.5.3.tgz",
|
||||||
@@ -8435,7 +8463,7 @@
|
|||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/cron": {
|
"node_modules/cron": {
|
||||||
@@ -8483,6 +8511,28 @@
|
|||||||
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
|
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/cssfilter": {
|
||||||
|
"version": "0.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz",
|
||||||
|
"integrity": "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/dataloader": {
|
||||||
|
"version": "2.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.2.3.tgz",
|
||||||
|
"integrity": "sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/date-fns": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/kossnocorp"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/dayjs": {
|
"node_modules/dayjs": {
|
||||||
"version": "1.11.18",
|
"version": "1.11.18",
|
||||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.18.tgz",
|
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.18.tgz",
|
||||||
@@ -8643,7 +8693,7 @@
|
|||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.3.1"
|
"node": ">=0.3.1"
|
||||||
@@ -9101,23 +9151,6 @@
|
|||||||
"url": "https://opencollective.com/eslint"
|
"url": "https://opencollective.com/eslint"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint/node_modules/ajv": {
|
|
||||||
"version": "6.12.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
|
||||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"fast-deep-equal": "^3.1.1",
|
|
||||||
"fast-json-stable-stringify": "^2.0.0",
|
|
||||||
"json-schema-traverse": "^0.4.1",
|
|
||||||
"uri-js": "^4.2.2"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/epoberezkin"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/eslint/node_modules/brace-expansion": {
|
"node_modules/eslint/node_modules/brace-expansion": {
|
||||||
"version": "1.1.12",
|
"version": "1.1.12",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||||
@@ -9165,13 +9198,6 @@
|
|||||||
"node": ">= 4"
|
"node": ">= 4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint/node_modules/json-schema-traverse": {
|
|
||||||
"version": "0.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
|
||||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/eslint/node_modules/minimatch": {
|
"node_modules/eslint/node_modules/minimatch": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||||
@@ -9421,6 +9447,23 @@
|
|||||||
"integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==",
|
"integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/fast-uri": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/fastify"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/fastify"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
"node_modules/fast-xml-parser": {
|
"node_modules/fast-xml-parser": {
|
||||||
"version": "5.2.5",
|
"version": "5.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz",
|
||||||
@@ -10056,7 +10099,7 @@
|
|||||||
"version": "4.7.8",
|
"version": "4.7.8",
|
||||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz",
|
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz",
|
||||||
"integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==",
|
"integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"minimist": "^1.2.5",
|
"minimist": "^1.2.5",
|
||||||
@@ -10078,7 +10121,7 @@
|
|||||||
"version": "0.6.1",
|
"version": "0.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
@@ -11414,9 +11457,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/json-schema-traverse": {
|
"node_modules/json-schema-traverse": {
|
||||||
"version": "1.0.0",
|
"version": "0.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
@@ -11863,6 +11906,13 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/lodash.omit": {
|
||||||
|
"version": "4.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz",
|
||||||
|
"integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==",
|
||||||
|
"deprecated": "This package is deprecated. Use destructuring assignment syntax instead.",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/lodash.once": {
|
"node_modules/lodash.once": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
|
||||||
@@ -11976,7 +12026,7 @@
|
|||||||
"version": "1.3.6",
|
"version": "1.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||||
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/makeerror": {
|
"node_modules/makeerror": {
|
||||||
@@ -12213,7 +12263,7 @@
|
|||||||
"version": "2.6.2",
|
"version": "2.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
|
||||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
|
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/node-abi": {
|
"node_modules/node-abi": {
|
||||||
@@ -13472,6 +13522,66 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/redis/node_modules/@redis/bloom": {
|
||||||
|
"version": "5.8.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.8.3.tgz",
|
||||||
|
"integrity": "sha512-1eldTzHvdW3Oi0TReb8m1yiFt8ZwyF6rv1NpZyG5R4TpCwuAdKQetBKoCw7D96tNFgsVVd6eL+NaGZZCqhRg4g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 18"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@redis/client": "^5.8.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/redis/node_modules/@redis/client": {
|
||||||
|
"version": "5.8.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@redis/client/-/client-5.8.3.tgz",
|
||||||
|
"integrity": "sha512-MZVUE+l7LmMIYlIjubPosruJ9ltSLGFmJqsXApTqPLyHLjsJUSAbAJb/A3N34fEqean4ddiDkdWzNu4ZKPvRUg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"cluster-key-slot": "1.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/redis/node_modules/@redis/json": {
|
||||||
|
"version": "5.8.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@redis/json/-/json-5.8.3.tgz",
|
||||||
|
"integrity": "sha512-DRR09fy/u8gynHGJ4gzXYeM7D8nlS6EMv5o+h20ndTJiAc7RGR01fdk2FNjnn1Nz5PjgGGownF+s72bYG4nZKQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 18"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@redis/client": "^5.8.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/redis/node_modules/@redis/search": {
|
||||||
|
"version": "5.8.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@redis/search/-/search-5.8.3.tgz",
|
||||||
|
"integrity": "sha512-EMIvEeGRR2I0BJEz4PV88DyCuPmMT1rDtznlsHY3cKSDcc9vj0Q411jUnX0iU2vVowUgWn/cpySKjpXdZ8m+5g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 18"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@redis/client": "^5.8.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/redis/node_modules/@redis/time-series": {
|
||||||
|
"version": "5.8.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.8.3.tgz",
|
||||||
|
"integrity": "sha512-5Jwy3ilsUYQjzpE7WZ1lEeG1RkqQ5kHtwV1p8yxXHSEmyUbC/T/AVgyjMcm52Olj/Ov/mhDKjx6ndYUi14bXsw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 18"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@redis/client": "^5.8.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/reflect-metadata": {
|
"node_modules/reflect-metadata": {
|
||||||
"version": "0.2.2",
|
"version": "0.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz",
|
||||||
@@ -13739,30 +13849,6 @@
|
|||||||
"url": "https://opencollective.com/webpack"
|
"url": "https://opencollective.com/webpack"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/schema-utils/node_modules/ajv": {
|
|
||||||
"version": "6.12.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
|
||||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"fast-deep-equal": "^3.1.1",
|
|
||||||
"fast-json-stable-stringify": "^2.0.0",
|
|
||||||
"json-schema-traverse": "^0.4.1",
|
|
||||||
"uri-js": "^4.2.2"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/epoberezkin"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/schema-utils/node_modules/json-schema-traverse": {
|
|
||||||
"version": "0.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
|
||||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/semver": {
|
"node_modules/semver": {
|
||||||
"version": "7.7.2",
|
"version": "7.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||||
@@ -14593,6 +14679,23 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/terser-webpack-plugin/node_modules/ajv": {
|
||||||
|
"version": "8.17.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
||||||
|
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"fast-deep-equal": "^3.1.3",
|
||||||
|
"fast-uri": "^3.0.1",
|
||||||
|
"json-schema-traverse": "^1.0.0",
|
||||||
|
"require-from-string": "^2.0.2"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/epoberezkin"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/terser-webpack-plugin/node_modules/ajv-keywords": {
|
"node_modules/terser-webpack-plugin/node_modules/ajv-keywords": {
|
||||||
"version": "5.1.0",
|
"version": "5.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
|
||||||
@@ -14621,6 +14724,13 @@
|
|||||||
"node": ">= 10.13.0"
|
"node": ">= 10.13.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/terser-webpack-plugin/node_modules/schema-utils": {
|
"node_modules/terser-webpack-plugin/node_modules/schema-utils": {
|
||||||
"version": "4.3.2",
|
"version": "4.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz",
|
||||||
@@ -14939,7 +15049,7 @@
|
|||||||
"version": "10.9.2",
|
"version": "10.9.2",
|
||||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
|
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
|
||||||
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cspotcode/source-map-support": "^0.8.0",
|
"@cspotcode/source-map-support": "^0.8.0",
|
||||||
@@ -15255,7 +15365,7 @@
|
|||||||
"version": "5.9.3",
|
"version": "5.9.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
@@ -15427,7 +15537,7 @@
|
|||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
||||||
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
|
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/v8-to-istanbul": {
|
"node_modules/v8-to-istanbul": {
|
||||||
@@ -15669,7 +15779,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
|
||||||
"integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==",
|
"integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/wrap-ansi": {
|
"node_modules/wrap-ansi": {
|
||||||
@@ -15752,6 +15862,28 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/xss": {
|
||||||
|
"version": "1.0.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/xss/-/xss-1.0.15.tgz",
|
||||||
|
"integrity": "sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"commander": "^2.20.3",
|
||||||
|
"cssfilter": "0.0.10"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"xss": "bin/xss"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/xss/node_modules/commander": {
|
||||||
|
"version": "2.20.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||||
|
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/xtend": {
|
"node_modules/xtend": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||||
@@ -15820,7 +15952,7 @@
|
|||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||||
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
"@aws-sdk/s3-request-presigner": "^3.899.0",
|
"@aws-sdk/s3-request-presigner": "^3.899.0",
|
||||||
"@langchain/core": "^0.3.78",
|
"@langchain/core": "^0.3.78",
|
||||||
"@langchain/openai": "^0.6.14",
|
"@langchain/openai": "^0.6.14",
|
||||||
|
"@nestjs/apollo": "^13.2.1",
|
||||||
"@nestjs/common": "^11.1.6",
|
"@nestjs/common": "^11.1.6",
|
||||||
"@nestjs/config": "^4.0.2",
|
"@nestjs/config": "^4.0.2",
|
||||||
"@nestjs/core": "^11.1.6",
|
"@nestjs/core": "^11.1.6",
|
||||||
@@ -51,6 +52,8 @@
|
|||||||
"cache-manager-redis-yet": "^5.1.5",
|
"cache-manager-redis-yet": "^5.1.5",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.14.2",
|
"class-validator": "^0.14.2",
|
||||||
|
"dataloader": "^2.2.3",
|
||||||
|
"date-fns": "^4.1.0",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
"form-data": "^4.0.4",
|
"form-data": "^4.0.4",
|
||||||
"graphql": "^16.11.0",
|
"graphql": "^16.11.0",
|
||||||
@@ -78,7 +81,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nestjs/cli": "^10.0.0",
|
"@nestjs/cli": "^10.0.0",
|
||||||
"@nestjs/schematics": "^10.0.0",
|
"@nestjs/schematics": "^10.0.0",
|
||||||
"@nestjs/testing": "^10.0.0",
|
"@nestjs/testing": "^11.1.6",
|
||||||
"@types/bcrypt": "^6.0.0",
|
"@types/bcrypt": "^6.0.0",
|
||||||
"@types/express": "^5.0.0",
|
"@types/express": "^5.0.0",
|
||||||
"@types/jest": "^29.5.2",
|
"@types/jest": "^29.5.2",
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { ConfigModule } from '@nestjs/config';
|
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||||
import { ScheduleModule } from '@nestjs/schedule';
|
import { ScheduleModule } from '@nestjs/schedule';
|
||||||
|
import { GraphQLModule } from '@nestjs/graphql';
|
||||||
|
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
|
||||||
import { APP_GUARD, APP_FILTER } from '@nestjs/core';
|
import { APP_GUARD, APP_FILTER } from '@nestjs/core';
|
||||||
|
import { join } from 'path';
|
||||||
import { AppController } from './app.controller';
|
import { AppController } from './app.controller';
|
||||||
import { AppService } from './app.service';
|
import { AppService } from './app.service';
|
||||||
import { DatabaseModule } from './database/database.module';
|
import { DatabaseModule } from './database/database.module';
|
||||||
@@ -17,6 +20,7 @@ import { AnalyticsModule } from './modules/analytics/analytics.module';
|
|||||||
import { FeedbackModule } from './modules/feedback/feedback.module';
|
import { FeedbackModule } from './modules/feedback/feedback.module';
|
||||||
import { PhotosModule } from './modules/photos/photos.module';
|
import { PhotosModule } from './modules/photos/photos.module';
|
||||||
import { ComplianceModule } from './modules/compliance/compliance.module';
|
import { ComplianceModule } from './modules/compliance/compliance.module';
|
||||||
|
import { GraphQLCustomModule } from './graphql/graphql.module';
|
||||||
import { JwtAuthGuard } from './modules/auth/guards/jwt-auth.guard';
|
import { JwtAuthGuard } from './modules/auth/guards/jwt-auth.guard';
|
||||||
import { ErrorTrackingService } from './common/services/error-tracking.service';
|
import { ErrorTrackingService } from './common/services/error-tracking.service';
|
||||||
import { GlobalExceptionFilter } from './common/filters/global-exception.filter';
|
import { GlobalExceptionFilter } from './common/filters/global-exception.filter';
|
||||||
@@ -29,6 +33,28 @@ import { HealthController } from './common/controllers/health.controller';
|
|||||||
isGlobal: true,
|
isGlobal: true,
|
||||||
envFilePath: '.env',
|
envFilePath: '.env',
|
||||||
}),
|
}),
|
||||||
|
GraphQLModule.forRootAsync<ApolloDriverConfig>({
|
||||||
|
driver: ApolloDriver,
|
||||||
|
inject: [ConfigService],
|
||||||
|
imports: [GraphQLCustomModule],
|
||||||
|
useFactory: (configService: ConfigService) => ({
|
||||||
|
autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
|
||||||
|
sortSchema: true,
|
||||||
|
playground: configService.get('NODE_ENV') !== 'production',
|
||||||
|
introspection: true,
|
||||||
|
context: ({ req, res }, ...args) => {
|
||||||
|
// DataLoaders will be provided via REQUEST scope
|
||||||
|
return { req, res };
|
||||||
|
},
|
||||||
|
formatError: (error) => {
|
||||||
|
return {
|
||||||
|
message: error.message,
|
||||||
|
code: error.extensions?.code || 'INTERNAL_SERVER_ERROR',
|
||||||
|
path: error.path,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
ScheduleModule.forRoot(),
|
ScheduleModule.forRoot(),
|
||||||
DatabaseModule,
|
DatabaseModule,
|
||||||
CommonModule,
|
CommonModule,
|
||||||
@@ -43,6 +69,7 @@ import { HealthController } from './common/controllers/health.controller';
|
|||||||
FeedbackModule,
|
FeedbackModule,
|
||||||
PhotosModule,
|
PhotosModule,
|
||||||
ComplianceModule,
|
ComplianceModule,
|
||||||
|
GraphQLCustomModule,
|
||||||
],
|
],
|
||||||
controllers: [AppController, HealthController],
|
controllers: [AppController, HealthController],
|
||||||
providers: [
|
providers: [
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import * as DataLoader from 'dataloader';
|
||||||
|
import { Injectable, Scope } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { Repository, In } from 'typeorm';
|
||||||
|
import { Child } from '../../database/entities/child.entity';
|
||||||
|
|
||||||
|
@Injectable({ scope: Scope.REQUEST })
|
||||||
|
export class ChildDataLoader {
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(Child)
|
||||||
|
private readonly childRepository: Repository<Child>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public readonly batchChildren = new DataLoader<string, Child>(
|
||||||
|
async (childIds: readonly string[]) => {
|
||||||
|
const children = await this.childRepository.find({
|
||||||
|
where: { id: In([...childIds]) },
|
||||||
|
});
|
||||||
|
|
||||||
|
const childMap = new Map(children.map((child) => [child.id, child]));
|
||||||
|
return childIds.map((id) => childMap.get(id) || null);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
public readonly batchChildrenByFamily = new DataLoader<
|
||||||
|
string,
|
||||||
|
Child[]
|
||||||
|
>(async (familyIds: readonly string[]) => {
|
||||||
|
const children = await this.childRepository.find({
|
||||||
|
where: { familyId: In([...familyIds]) },
|
||||||
|
});
|
||||||
|
|
||||||
|
const childrenByFamily = new Map<string, Child[]>();
|
||||||
|
children.forEach((child) => {
|
||||||
|
const existing = childrenByFamily.get(child.familyId) || [];
|
||||||
|
existing.push(child);
|
||||||
|
childrenByFamily.set(child.familyId, existing);
|
||||||
|
});
|
||||||
|
|
||||||
|
return familyIds.map((familyId) => childrenByFamily.get(familyId) || []);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import * as DataLoader from 'dataloader';
|
||||||
|
import { Injectable, Scope } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { Repository, In } from 'typeorm';
|
||||||
|
import { User } from '../../database/entities/user.entity';
|
||||||
|
|
||||||
|
@Injectable({ scope: Scope.REQUEST })
|
||||||
|
export class UserDataLoader {
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(User)
|
||||||
|
private readonly userRepository: Repository<User>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public readonly batchUsers = new DataLoader<string, User>(
|
||||||
|
async (userIds: readonly string[]) => {
|
||||||
|
const users = await this.userRepository.find({
|
||||||
|
where: { id: In([...userIds]) },
|
||||||
|
});
|
||||||
|
|
||||||
|
const userMap = new Map(users.map((user) => [user.id, user]));
|
||||||
|
return userIds.map((id) => userMap.get(id) || null);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
# Example GraphQL Queries for Maternal App
|
||||||
|
|
||||||
|
# ========================================
|
||||||
|
# Dashboard Query - Optimized Single Query
|
||||||
|
# ========================================
|
||||||
|
# This query replaces multiple REST API calls:
|
||||||
|
# - GET /api/v1/children
|
||||||
|
# - GET /api/v1/tracking/child/:id/recent
|
||||||
|
# - GET /api/v1/tracking/child/:id/summary/today
|
||||||
|
# - GET /api/v1/families/:id/members
|
||||||
|
|
||||||
|
query GetDashboard($childId: ID) {
|
||||||
|
dashboard(childId: $childId) {
|
||||||
|
# Children list
|
||||||
|
children {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
birthDate
|
||||||
|
gender
|
||||||
|
photoUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
# Selected child (specified or first)
|
||||||
|
selectedChild {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
birthDate
|
||||||
|
gender
|
||||||
|
photoUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
# Recent activities (last 10 for selected child)
|
||||||
|
recentActivities {
|
||||||
|
id
|
||||||
|
type
|
||||||
|
startedAt
|
||||||
|
endedAt
|
||||||
|
notes
|
||||||
|
metadata
|
||||||
|
logger {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Today's summary for selected child
|
||||||
|
todaySummary {
|
||||||
|
date
|
||||||
|
feedingCount
|
||||||
|
totalFeedingAmount
|
||||||
|
sleepCount
|
||||||
|
totalSleepDuration
|
||||||
|
diaperCount
|
||||||
|
medicationCount
|
||||||
|
}
|
||||||
|
|
||||||
|
# Family members
|
||||||
|
familyMembers {
|
||||||
|
userId
|
||||||
|
role
|
||||||
|
user {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Aggregations
|
||||||
|
totalChildren
|
||||||
|
totalActivitiesToday
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Example Variables:
|
||||||
|
# {
|
||||||
|
# "childId": "child_abc123"
|
||||||
|
# }
|
||||||
|
|
||||||
|
# ========================================
|
||||||
|
# Dashboard Query - All Children
|
||||||
|
# ========================================
|
||||||
|
# Get dashboard data without specifying a child
|
||||||
|
# (will return first child's data)
|
||||||
|
|
||||||
|
query GetDashboardAllChildren {
|
||||||
|
dashboard {
|
||||||
|
children {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
birthDate
|
||||||
|
}
|
||||||
|
selectedChild {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
totalChildren
|
||||||
|
totalActivitiesToday
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
import { Child } from '../database/entities/child.entity';
|
||||||
|
import { Activity } from '../database/entities/activity.entity';
|
||||||
|
import { FamilyMember } from '../database/entities/family-member.entity';
|
||||||
|
import { User } from '../database/entities/user.entity';
|
||||||
|
import { DashboardResolver } from './resolvers/dashboard.resolver';
|
||||||
|
import { ChildDataLoader } from './dataloaders/child.dataloader';
|
||||||
|
import { UserDataLoader } from './dataloaders/user.dataloader';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [TypeOrmModule.forFeature([Child, Activity, FamilyMember, User])],
|
||||||
|
providers: [DashboardResolver, ChildDataLoader, UserDataLoader],
|
||||||
|
exports: [ChildDataLoader, UserDataLoader],
|
||||||
|
})
|
||||||
|
export class GraphQLCustomModule {}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { Injectable, ExecutionContext } from '@nestjs/common';
|
||||||
|
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||||
|
import { AuthGuard } from '@nestjs/passport';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class GqlAuthGuard extends AuthGuard('jwt') {
|
||||||
|
getRequest(context: ExecutionContext) {
|
||||||
|
const ctx = GqlExecutionContext.create(context);
|
||||||
|
return ctx.getContext().req;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
import { Resolver, Query, Args, Context, ResolveField, Parent } from '@nestjs/graphql';
|
||||||
|
import { UseGuards } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { Repository, Between } from 'typeorm';
|
||||||
|
import { format } from 'date-fns';
|
||||||
|
import { DashboardType, DailySummaryType } from '../types/dashboard.type';
|
||||||
|
import { ActivityGQLType } from '../types/activity.type';
|
||||||
|
import { ChildType } from '../types/child.type';
|
||||||
|
import { UserType } from '../types/user.type';
|
||||||
|
import { Child } from '../../database/entities/child.entity';
|
||||||
|
import { Activity, ActivityType } from '../../database/entities/activity.entity';
|
||||||
|
import { FamilyMember } from '../../database/entities/family-member.entity';
|
||||||
|
import { User } from '../../database/entities/user.entity';
|
||||||
|
import { GqlAuthGuard } from '../guards/gql-auth.guard';
|
||||||
|
import { ChildDataLoader } from '../dataloaders/child.dataloader';
|
||||||
|
import { UserDataLoader } from '../dataloaders/user.dataloader';
|
||||||
|
|
||||||
|
@Resolver(() => DashboardType)
|
||||||
|
@UseGuards(GqlAuthGuard)
|
||||||
|
export class DashboardResolver {
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(Child)
|
||||||
|
private readonly childRepository: Repository<Child>,
|
||||||
|
@InjectRepository(Activity)
|
||||||
|
private readonly activityRepository: Repository<Activity>,
|
||||||
|
@InjectRepository(FamilyMember)
|
||||||
|
private readonly familyMemberRepository: Repository<FamilyMember>,
|
||||||
|
@InjectRepository(User)
|
||||||
|
private readonly userRepository: Repository<User>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
@Query(() => DashboardType, { name: 'dashboard' })
|
||||||
|
async getDashboard(
|
||||||
|
@Args('childId', { nullable: true }) childId: string,
|
||||||
|
@Context() context: any,
|
||||||
|
): Promise<DashboardType> {
|
||||||
|
const userId = context.req.user?.userId;
|
||||||
|
|
||||||
|
if (!userId) {
|
||||||
|
throw new Error('User not authenticated');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get user's family memberships
|
||||||
|
const familyMemberships = await this.familyMemberRepository.find({
|
||||||
|
where: { userId },
|
||||||
|
relations: ['family'],
|
||||||
|
});
|
||||||
|
|
||||||
|
const familyIds = familyMemberships.map((fm) => fm.familyId);
|
||||||
|
|
||||||
|
// Get all children in user's families
|
||||||
|
const children = await this.childRepository.find({
|
||||||
|
where: familyIds.length > 0 ? familyIds.map(id => ({ familyId: id })) : [],
|
||||||
|
order: { createdAt: 'ASC' },
|
||||||
|
});
|
||||||
|
|
||||||
|
// Select child (specified or first child)
|
||||||
|
const selectedChild = childId
|
||||||
|
? children.find((c) => c.id === childId)
|
||||||
|
: children[0];
|
||||||
|
|
||||||
|
// Get today's date range
|
||||||
|
const today = new Date();
|
||||||
|
today.setHours(0, 0, 0, 0);
|
||||||
|
const tomorrow = new Date(today);
|
||||||
|
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||||
|
|
||||||
|
// Get recent activities (last 10 for selected child)
|
||||||
|
const recentActivities = selectedChild
|
||||||
|
? await this.activityRepository.find({
|
||||||
|
where: { childId: selectedChild.id },
|
||||||
|
order: { startedAt: 'DESC' },
|
||||||
|
take: 10,
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
|
||||||
|
// Get today's activities for selected child
|
||||||
|
const todayActivities = selectedChild
|
||||||
|
? await this.activityRepository.find({
|
||||||
|
where: {
|
||||||
|
childId: selectedChild.id,
|
||||||
|
startedAt: Between(today, tomorrow),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
|
||||||
|
// Calculate today's summary
|
||||||
|
const todaySummary = this.calculateDailySummary(
|
||||||
|
todayActivities,
|
||||||
|
format(today, 'yyyy-MM-dd'),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get all family members
|
||||||
|
const familyMembers = await this.familyMemberRepository.find({
|
||||||
|
where: familyIds.length > 0 ? familyIds.map(id => ({ familyId: id })) : [],
|
||||||
|
relations: ['user'],
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
children: children as any[],
|
||||||
|
selectedChild: selectedChild as any,
|
||||||
|
recentActivities: recentActivities as any[],
|
||||||
|
todaySummary,
|
||||||
|
familyMembers: familyMembers as any[],
|
||||||
|
totalChildren: children.length,
|
||||||
|
totalActivitiesToday: todayActivities.length,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@ResolveField(() => ChildType, { nullable: true })
|
||||||
|
async child(
|
||||||
|
@Parent() activity: ActivityGQLType,
|
||||||
|
@Context() context: any,
|
||||||
|
): Promise<ChildType> {
|
||||||
|
const childLoader: ChildDataLoader = context.childLoader;
|
||||||
|
return childLoader.batchChildren.load(activity.childId) as any;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ResolveField(() => UserType, { nullable: true })
|
||||||
|
async logger(
|
||||||
|
@Parent() activity: ActivityGQLType,
|
||||||
|
@Context() context: any,
|
||||||
|
): Promise<UserType> {
|
||||||
|
const userLoader: UserDataLoader = context.userLoader;
|
||||||
|
return userLoader.batchUsers.load(activity.loggedBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
private calculateDailySummary(
|
||||||
|
activities: Activity[],
|
||||||
|
date: string,
|
||||||
|
): DailySummaryType {
|
||||||
|
const summary = {
|
||||||
|
feedingCount: 0,
|
||||||
|
totalFeedingAmount: 0,
|
||||||
|
sleepCount: 0,
|
||||||
|
totalSleepDuration: 0,
|
||||||
|
diaperCount: 0,
|
||||||
|
medicationCount: 0,
|
||||||
|
date,
|
||||||
|
};
|
||||||
|
|
||||||
|
activities.forEach((activity) => {
|
||||||
|
switch (activity.type) {
|
||||||
|
case ActivityType.FEEDING:
|
||||||
|
summary.feedingCount++;
|
||||||
|
if (activity.metadata?.amount) {
|
||||||
|
summary.totalFeedingAmount += activity.metadata.amount;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ActivityType.SLEEP:
|
||||||
|
summary.sleepCount++;
|
||||||
|
if (activity.endedAt && activity.startedAt) {
|
||||||
|
const duration = Math.floor(
|
||||||
|
(activity.endedAt.getTime() - activity.startedAt.getTime()) / 60000,
|
||||||
|
);
|
||||||
|
summary.totalSleepDuration += duration;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ActivityType.DIAPER:
|
||||||
|
summary.diaperCount++;
|
||||||
|
break;
|
||||||
|
case ActivityType.MEDICATION:
|
||||||
|
case ActivityType.MEDICINE:
|
||||||
|
summary.medicationCount++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return summary;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import { ObjectType, Field, ID, registerEnumType, Int, Float } from '@nestjs/graphql';
|
||||||
|
import { ChildType } from './child.type';
|
||||||
|
import { UserType } from './user.type';
|
||||||
|
|
||||||
|
export enum ActivityType {
|
||||||
|
FEEDING = 'feeding',
|
||||||
|
SLEEP = 'sleep',
|
||||||
|
DIAPER = 'diaper',
|
||||||
|
GROWTH = 'growth',
|
||||||
|
MEDICATION = 'medication',
|
||||||
|
MEDICINE = 'medicine',
|
||||||
|
TEMPERATURE = 'temperature',
|
||||||
|
MILESTONE = 'milestone',
|
||||||
|
ACTIVITY = 'activity',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum FeedingMethod {
|
||||||
|
BREAST = 'breast',
|
||||||
|
BOTTLE = 'bottle',
|
||||||
|
SOLIDS = 'solids',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum DiaperType {
|
||||||
|
WET = 'wet',
|
||||||
|
DIRTY = 'dirty',
|
||||||
|
BOTH = 'both',
|
||||||
|
}
|
||||||
|
|
||||||
|
registerEnumType(ActivityType, { name: 'ActivityType' });
|
||||||
|
registerEnumType(FeedingMethod, { name: 'FeedingMethod' });
|
||||||
|
registerEnumType(DiaperType, { name: 'DiaperType' });
|
||||||
|
|
||||||
|
@ObjectType('Activity')
|
||||||
|
export class ActivityGQLType {
|
||||||
|
@Field(() => ID)
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@Field()
|
||||||
|
childId: string;
|
||||||
|
|
||||||
|
@Field(() => ActivityType)
|
||||||
|
type: ActivityType;
|
||||||
|
|
||||||
|
@Field()
|
||||||
|
startedAt: Date;
|
||||||
|
|
||||||
|
@Field({ nullable: true })
|
||||||
|
endedAt?: Date;
|
||||||
|
|
||||||
|
@Field()
|
||||||
|
loggedBy: string;
|
||||||
|
|
||||||
|
@Field({ nullable: true })
|
||||||
|
notes?: string;
|
||||||
|
|
||||||
|
// Metadata as JSON string or object
|
||||||
|
@Field({ nullable: true })
|
||||||
|
metadata?: string;
|
||||||
|
|
||||||
|
// Relations
|
||||||
|
@Field(() => ChildType, { nullable: true })
|
||||||
|
child?: ChildType;
|
||||||
|
|
||||||
|
@Field(() => UserType, { nullable: true })
|
||||||
|
logger?: UserType;
|
||||||
|
|
||||||
|
@Field()
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@Field()
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { ObjectType, Field, ID, registerEnumType } from '@nestjs/graphql';
|
||||||
|
|
||||||
|
export enum Gender {
|
||||||
|
MALE = 'male',
|
||||||
|
FEMALE = 'female',
|
||||||
|
OTHER = 'other',
|
||||||
|
}
|
||||||
|
|
||||||
|
registerEnumType(Gender, {
|
||||||
|
name: 'Gender',
|
||||||
|
});
|
||||||
|
|
||||||
|
@ObjectType('Child')
|
||||||
|
export class ChildType {
|
||||||
|
@Field(() => ID)
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@Field()
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@Field()
|
||||||
|
birthDate: Date;
|
||||||
|
|
||||||
|
@Field({ nullable: true })
|
||||||
|
gender?: string;
|
||||||
|
|
||||||
|
@Field({ nullable: true })
|
||||||
|
photoUrl?: string;
|
||||||
|
|
||||||
|
@Field()
|
||||||
|
familyId: string;
|
||||||
|
|
||||||
|
@Field()
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@Field()
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { ObjectType, Field, Int } from '@nestjs/graphql';
|
||||||
|
import { ChildType } from './child.type';
|
||||||
|
import { ActivityGQLType } from './activity.type';
|
||||||
|
import { FamilyMemberType } from './family.type';
|
||||||
|
|
||||||
|
@ObjectType('DailySummary')
|
||||||
|
export class DailySummaryType {
|
||||||
|
@Field(() => Int)
|
||||||
|
feedingCount: number;
|
||||||
|
|
||||||
|
@Field(() => Int, { nullable: true })
|
||||||
|
totalFeedingAmount?: number;
|
||||||
|
|
||||||
|
@Field(() => Int)
|
||||||
|
sleepCount: number;
|
||||||
|
|
||||||
|
@Field(() => Int, { nullable: true })
|
||||||
|
totalSleepDuration?: number;
|
||||||
|
|
||||||
|
@Field(() => Int)
|
||||||
|
diaperCount: number;
|
||||||
|
|
||||||
|
@Field(() => Int)
|
||||||
|
medicationCount: number;
|
||||||
|
|
||||||
|
@Field()
|
||||||
|
date: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ObjectType('Dashboard')
|
||||||
|
export class DashboardType {
|
||||||
|
@Field(() => [ChildType])
|
||||||
|
children: ChildType[];
|
||||||
|
|
||||||
|
@Field(() => ChildType, { nullable: true })
|
||||||
|
selectedChild?: ChildType;
|
||||||
|
|
||||||
|
@Field(() => [ActivityGQLType])
|
||||||
|
recentActivities: ActivityGQLType[];
|
||||||
|
|
||||||
|
@Field(() => DailySummaryType, { nullable: true })
|
||||||
|
todaySummary?: DailySummaryType;
|
||||||
|
|
||||||
|
@Field(() => [FamilyMemberType])
|
||||||
|
familyMembers: FamilyMemberType[];
|
||||||
|
|
||||||
|
@Field(() => Int)
|
||||||
|
totalChildren: number;
|
||||||
|
|
||||||
|
@Field(() => Int)
|
||||||
|
totalActivitiesToday: number;
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { ObjectType, Field, ID, registerEnumType } from '@nestjs/graphql';
|
||||||
|
import { UserType } from './user.type';
|
||||||
|
|
||||||
|
export enum FamilyRole {
|
||||||
|
PARENT = 'parent',
|
||||||
|
CAREGIVER = 'caregiver',
|
||||||
|
}
|
||||||
|
|
||||||
|
registerEnumType(FamilyRole, {
|
||||||
|
name: 'FamilyRole',
|
||||||
|
});
|
||||||
|
|
||||||
|
@ObjectType('FamilyMember')
|
||||||
|
export class FamilyMemberType {
|
||||||
|
@Field()
|
||||||
|
familyId: string;
|
||||||
|
|
||||||
|
@Field()
|
||||||
|
userId: string;
|
||||||
|
|
||||||
|
@Field(() => FamilyRole)
|
||||||
|
role: FamilyRole;
|
||||||
|
|
||||||
|
@Field(() => UserType, { nullable: true })
|
||||||
|
user?: UserType;
|
||||||
|
|
||||||
|
@Field()
|
||||||
|
createdAt: Date;
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { ObjectType, Field, ID } from '@nestjs/graphql';
|
||||||
|
import { FamilyMemberType } from './family.type';
|
||||||
|
|
||||||
|
@ObjectType('User')
|
||||||
|
export class UserType {
|
||||||
|
@Field(() => ID)
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@Field()
|
||||||
|
email: string;
|
||||||
|
|
||||||
|
@Field()
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@Field({ nullable: true })
|
||||||
|
phone?: string;
|
||||||
|
|
||||||
|
@Field(() => [FamilyMemberType], { nullable: true })
|
||||||
|
families?: FamilyMemberType[];
|
||||||
|
|
||||||
|
@Field()
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@Field()
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user