diff --git a/apps/edr-freight-api/package.json b/apps/edr-freight-api/package.json
index 32237b507..3ddef331e 100644
--- a/apps/edr-freight-api/package.json
+++ b/apps/edr-freight-api/package.json
@@ -15,6 +15,7 @@
"test:e2e": "jest --config ./test/jest-e2e.json",
"type-check": "tsc --noEmit",
"seed:demo-scheduling": "ts-node -r tsconfig-paths/register src/scripts/seed-demo-scheduling.ts",
+ "seed:freight-demo": "ts-node -r tsconfig-paths/register src/scripts/seed-freight-demo.ts",
"seed:fleet-wagons": "bash ../../../docs/new/seeds/seed-fleet-wagons.sh"
},
"dependencies": {
diff --git a/apps/edr-freight-api/src/app.module.ts b/apps/edr-freight-api/src/app.module.ts
index 30b25e013..4d855e055 100644
--- a/apps/edr-freight-api/src/app.module.ts
+++ b/apps/edr-freight-api/src/app.module.ts
@@ -48,6 +48,7 @@ import { DemoBookingsSeeder } from "./seed/demo-bookings.seeder";
import { PricingDataSeeder } from "./seed/pricing-data.seeder";
import { FileUploadSettingsSeeder } from "./seed/file-upload-settings.seeder";
import { FreightPermissionKeyMigrationSeeder } from "./seed/freight-permission-key-migration.seeder";
+import { DemoFreightDataSeeder } from "./seed/demo-freight-data.seeder";
//New Trains, Wagons, Container and Cargo management modules
import { TrainsModule } from "./modules/trains/trains.module";
import { WagonsModule } from './modules/wagons/wagons.module';
@@ -121,6 +122,7 @@ import { OverviewModule } from './modules/overview/overview.module';
PricingDataSeeder,
FileUploadSettingsSeeder,
FreightPermissionKeyMigrationSeeder,
+ DemoFreightDataSeeder,
],
})
export class AppModule implements OnApplicationBootstrap {
@@ -133,6 +135,7 @@ export class AppModule implements OnApplicationBootstrap {
private readonly pricingDataSeeder: PricingDataSeeder,
private readonly fileUploadSettingsSeeder: FileUploadSettingsSeeder,
private readonly freightPermissionKeyMigrationSeeder: FreightPermissionKeyMigrationSeeder,
+ private readonly demoFreightDataSeeder: DemoFreightDataSeeder,
) { }
async onApplicationBootstrap() {
@@ -144,5 +147,8 @@ export class AppModule implements OnApplicationBootstrap {
await this.demoBookingsSeeder.run();
await this.pricingDataSeeder.run();
await this.fileUploadSettingsSeeder.run();
+ // Idempotent demo data: ≥100 wagons/type, approval chains, 4 staff users.
+ // Each block self-guards on an empty-table check, so this is safe every boot.
+ await this.demoFreightDataSeeder.run();
}
}
diff --git a/apps/edr-freight-api/src/modules/train-scheduling/train-scheduling.controller.ts b/apps/edr-freight-api/src/modules/train-scheduling/train-scheduling.controller.ts
index 3ef7693ab..2a4c3a357 100644
--- a/apps/edr-freight-api/src/modules/train-scheduling/train-scheduling.controller.ts
+++ b/apps/edr-freight-api/src/modules/train-scheduling/train-scheduling.controller.ts
@@ -96,7 +96,8 @@ export class TrainSchedulingController {
}
@Get("bookable-schedules")
- // @TrainSchedulingView()
+ // No staff guard: customers hit this while creating a booking to find OPEN
+ // same-route schedules. Do not attach train_scheduling permissions here.
@ApiOperation({
summary: "OPEN same-route schedules a new booking can target",
})
diff --git a/apps/edr-freight-api/src/scripts/seed-freight-demo.ts b/apps/edr-freight-api/src/scripts/seed-freight-demo.ts
new file mode 100644
index 000000000..8f4e8d0b2
--- /dev/null
+++ b/apps/edr-freight-api/src/scripts/seed-freight-demo.ts
@@ -0,0 +1,28 @@
+import 'reflect-metadata';
+import { config } from 'dotenv';
+import { resolve } from 'path';
+
+config({ path: resolve(__dirname, '../../.env') });
+
+import { NestFactory } from '@nestjs/core';
+import { AppModule } from '../app.module';
+import { DemoFreightDataSeeder } from '../seed/demo-freight-data.seeder';
+
+async function main() {
+ const app = await NestFactory.createApplicationContext(AppModule, {
+ logger: ['error', 'warn', 'log'],
+ });
+
+ try {
+ const seeder = app.get(DemoFreightDataSeeder);
+ await seeder.run();
+ console.log('Freight demo data seeded (wagons, approval rules, staff users).');
+ } finally {
+ await app.close();
+ }
+}
+
+main().catch((err) => {
+ console.error('Freight demo seed failed:', err);
+ process.exit(1);
+});
diff --git a/apps/edr-freight-api/src/seed/demo-freight-data.seeder.ts b/apps/edr-freight-api/src/seed/demo-freight-data.seeder.ts
new file mode 100644
index 000000000..f4931f77b
--- /dev/null
+++ b/apps/edr-freight-api/src/seed/demo-freight-data.seeder.ts
@@ -0,0 +1,180 @@
+import { Injectable, Logger } from '@nestjs/common';
+import { WagonStatus } from '@edr/types';
+import { hashPassword } from '@tria-plc/api-common/utils/argon';
+import { EUserStatus } from '@tria-plc/api-common/utils/enums/user.enum';
+import {
+ Employee,
+ Organization,
+ Role,
+ User,
+ UserCredential,
+ UserRole,
+} from '@tria-plc/iamapi-common';
+import { DataSource, EntityManager } from 'typeorm';
+
+import { Wagon } from '../modules/wagons/entities/wagon.entity';
+import { WagonType } from '../modules/wagon-types/entities/wagon-type.entity';
+import { ApprovalRule } from '../modules/rule-engine/entities/approval-rule.entity';
+import { DEFAULT_APPROVAL_RULE_ROWS } from '../modules/rule-engine/approval-rules.defaults';
+
+const EDR_ORG_KEY = 'edr_freight';
+const MIN_WAGONS_PER_TYPE = 100;
+
+/** The four demo staff users, each mapped to a seeded freight role. */
+const DEMO_STAFF_USERS = [
+ { email: 'marketing@edr.local', username: 'marketing', roleKey: 'edr_marketing' },
+ { email: 'operations@edr.local', username: 'operations', roleKey: 'edr_operations_officer' },
+ { email: 'director@edr.local', username: 'director', roleKey: 'edr_director' },
+ { email: 'ceo@edr.local', username: 'ceo', roleKey: 'edr_ceo' },
+] as const;
+
+/**
+ * One-shot demo data: at least 100 wagons per wagon type, the default approval
+ * chains, and four staff users with distinct permissions. Every block guards on
+ * an "is it already populated?" check, so this is safe to run on every boot and
+ * does nothing once the data exists.
+ */
+@Injectable()
+export class DemoFreightDataSeeder {
+ private readonly logger = new Logger(DemoFreightDataSeeder.name);
+
+ constructor(private readonly dataSource: DataSource) {}
+
+ async run() {
+ await this.dataSource.transaction(async (manager) => {
+ await this.seedWagons(manager);
+ await this.seedApprovalRules(manager);
+ await this.seedStaffUsers(manager);
+ });
+ }
+
+ /** Ensure every wagon type has at least MIN_WAGONS_PER_TYPE wagons. */
+ private async seedWagons(manager: EntityManager) {
+ const wagonTypeRepo = manager.getRepository(WagonType);
+ const wagonRepo = manager.getRepository(Wagon);
+
+ const wagonTypes = await wagonTypeRepo.find();
+ if (wagonTypes.length === 0) {
+ this.logger.warn('No wagon types found; skipping wagon seed');
+ return;
+ }
+
+ for (const type of wagonTypes) {
+ const existing = await wagonRepo.count({ where: { wagonTypeId: type.id } });
+ if (existing >= MIN_WAGONS_PER_TYPE) {
+ this.logger.log(
+ `Wagon type ${type.code} already has ${existing} wagons; skipping`,
+ );
+ continue;
+ }
+
+ const toCreate = MIN_WAGONS_PER_TYPE - existing;
+ const tare = Number(type.tareWeightTons ?? 20);
+ const maxPayload = Number(type.capacityTons ?? 60);
+ const rows = Array.from({ length: toCreate }, (_, i) => {
+ const seq = existing + i + 1;
+ return wagonRepo.create({
+ wagonNumber: `${type.code}-${String(seq).padStart(4, '0')}`,
+ wagonTypeId: type.id,
+ tareWeight: tare,
+ maxPayloadWeight: maxPayload,
+ status: WagonStatus.Available,
+ });
+ });
+ await wagonRepo.save(rows);
+ this.logger.log(`Seeded ${toCreate} wagons for type ${type.code}`);
+ }
+ }
+
+ /** Seed the default approval chains when the table is empty. */
+ private async seedApprovalRules(manager: EntityManager) {
+ const repo = manager.getRepository(ApprovalRule);
+ const count = await repo.count();
+ if (count > 0) {
+ this.logger.log(`Approval rules already populated (${count}); skipping`);
+ return;
+ }
+ await repo.save(DEFAULT_APPROVAL_RULE_ROWS.map((row) => repo.create(row)));
+ this.logger.log(`Seeded ${DEFAULT_APPROVAL_RULE_ROWS.length} approval rules`);
+ }
+
+ /** Create the four demo staff users with their roles (idempotent per email). */
+ private async seedStaffUsers(manager: EntityManager) {
+ const organization = await manager.getRepository(Organization).findOne({
+ where: { key: EDR_ORG_KEY },
+ select: { id: true, key: true },
+ });
+ if (!organization) {
+ this.logger.warn(`Missing organization ${EDR_ORG_KEY}; skipping staff users`);
+ return;
+ }
+
+ const roleRepo = manager.getRepository(Role);
+ const userRepo = manager.getRepository(User);
+ const credentialRepo = manager.getRepository(UserCredential);
+ const userRoleRepo = manager.getRepository(UserRole);
+ const employeeRepo = manager.getRepository(Employee);
+
+ const password = process.env.DEFAULT_PASSWORD?.trim() || '12345678';
+ const hashedPassword = await hashPassword(password);
+
+ for (const staff of DEMO_STAFF_USERS) {
+ const role = await roleRepo.findOne({
+ where: { key: staff.roleKey },
+ select: { id: true, key: true },
+ });
+ if (!role) {
+ this.logger.warn(`Missing role ${staff.roleKey}; skipping ${staff.email}`);
+ continue;
+ }
+
+ let user = await userRepo.findOne({
+ where: { email: staff.email },
+ select: { id: true, email: true },
+ });
+ if (!user) {
+ user = await userRepo.save(
+ userRepo.create({
+ email: staff.email,
+ username: staff.username,
+ name: { en: staff.username },
+ isActive: true,
+ hasSetPassword: true,
+ status: EUserStatus.ACCEPTED,
+ }),
+ );
+ this.logger.log(`Seeded staff user ${staff.email}`);
+ }
+
+ const hasCredential = await credentialRepo.exists({
+ where: { userId: user.id, isActive: true },
+ });
+ if (!hasCredential) {
+ await credentialRepo.insert({
+ userId: user.id,
+ password: hashedPassword,
+ isActive: true,
+ });
+ }
+
+ await userRoleRepo.upsert(
+ { userId: user.id, roleId: role.id, organizationId: organization.id },
+ { conflictPaths: { userId: true, roleId: true } },
+ );
+
+ const hasEmployee = await employeeRepo.exists({
+ where: { userId: user.id, organizationId: organization.id, isCurrent: true },
+ });
+ if (!hasEmployee) {
+ await employeeRepo.insert({
+ userId: user.id,
+ organizationId: organization.id,
+ isCurrent: true,
+ name: { en: staff.username },
+ });
+ }
+ }
+
+ this.logger.log('Ensured demo staff users (marketing@, operations@, director@, ceo@)');
+ }
+}
diff --git a/apps/edr-freight-api/src/seed/edr-freight.seed.ts b/apps/edr-freight-api/src/seed/edr-freight.seed.ts
index c2705673f..a88ee8f89 100644
--- a/apps/edr-freight-api/src/seed/edr-freight.seed.ts
+++ b/apps/edr-freight-api/src/seed/edr-freight.seed.ts
@@ -212,6 +212,11 @@ export const EDR_FREIGHT_ROLES: FreightSeedRole[] = [
name: { en: "EDR Line Staff" },
permissionKeys: [...ROLE_PERMISSION_PRESETS.lineStaff],
},
+ {
+ key: "edr_operations_officer",
+ name: { en: "EDR Operations Officer" },
+ permissionKeys: [...ROLE_PERMISSION_PRESETS.operationsOfficer],
+ },
{
key: "edr_director",
name: { en: "EDR Director" },
diff --git a/apps/edr-freight-api/src/seed/freight-permissions.registry.ts b/apps/edr-freight-api/src/seed/freight-permissions.registry.ts
index 0ee62fa7c..9d8cd382f 100644
--- a/apps/edr-freight-api/src/seed/freight-permissions.registry.ts
+++ b/apps/edr-freight-api/src/seed/freight-permissions.registry.ts
@@ -121,6 +121,9 @@ const allRuleEngineViewKeys = () =>
RULE_ENGINE_RESOURCE_SLUGS.map((s) => FREIGHT_PERMS.ruleEngine.view(s));
export const ROLE_PERMISSION_PRESETS = {
+ // Marketing / line staff: drives a booking from intake through line-staff
+ // approval and contract generation/signing — i.e. until the contract is ready
+ // and signed. No director/CEO approval, no scheduling, no operations.
lineStaff: [
FREIGHT_PERMS.bookings.view,
FREIGHT_PERMS.bookings.staffAccept,
@@ -129,6 +132,12 @@ export const ROLE_PERMISSION_PRESETS = {
FREIGHT_PERMS.bookings.approveLineStaff,
FREIGHT_PERMS.bookings.rejectApproval,
FREIGHT_PERMS.bookings.cancel,
+ ...allRuleEngineViewKeys(),
+ ],
+ // Operations Officer: train scheduling + wagon allocation + transit/complete.
+ operationsOfficer: [
+ FREIGHT_PERMS.bookings.view,
+ FREIGHT_PERMS.bookings.operations,
FREIGHT_PERMS.trainScheduling.view,
FREIGHT_PERMS.trainScheduling.manage,
...allRuleEngineViewKeys(),
@@ -147,8 +156,15 @@ export const ROLE_PERMISSION_PRESETS = {
...allRuleEngineViewKeys(),
],
finance: [FREIGHT_PERMS.bookings.view],
+ // Marketing handles intake through contract (same as line staff here).
marketing: [
FREIGHT_PERMS.bookings.view,
+ FREIGHT_PERMS.bookings.staffAccept,
+ FREIGHT_PERMS.bookings.requestChanges,
+ FREIGHT_PERMS.bookings.reject,
+ FREIGHT_PERMS.bookings.approveLineStaff,
+ FREIGHT_PERMS.bookings.rejectApproval,
+ FREIGHT_PERMS.bookings.cancel,
FREIGHT_PERMS.bookings.generateContract,
FREIGHT_PERMS.bookings.signStaff,
],
diff --git a/apps/edr-freight-web/backoffice/src/App.tsx b/apps/edr-freight-web/backoffice/src/App.tsx
index cfe22845d..9468fe568 100644
--- a/apps/edr-freight-web/backoffice/src/App.tsx
+++ b/apps/edr-freight-web/backoffice/src/App.tsx
@@ -47,6 +47,8 @@ import TrainScheduleTrackPage from "./pages/trainScheduling/TrainScheduleTrackPa
import TrainSchedulingGlobalRulesPage from "./pages/trainScheduling/TrainSchedulingGlobalRulesPage";
import FleetResourcePage from "./pages/fleet/FleetResourcePage";
import { getCategorySidebarChildren } from "./pages/ruleEngine/config/resources";
+import { FREIGHT_PERMS, hasPermission as hasFreightPermission } from "./lib/permissions";
+import { RequirePermission } from "./components/auth/RequirePermission";
import TrainDetailPage from "./pages/trains/TrainDetailPage";
import RoutesPage from "./pages/fleet/RoutesPage";
@@ -80,11 +82,13 @@ const buildSidebarSections = (demoItems: SidebarItem[]): SidebarSection[] => [
label: "Train Schedules",
href: "/dashboard/operations/train-scheduling-v2",
icon: ,
+ permission: FREIGHT_PERMS.trainScheduling.view,
},
{
label: "Batch Board",
href: "/dashboard/operations/batch-board",
icon: ,
+ permission: FREIGHT_PERMS.trainScheduling.view,
},
],
},
@@ -196,18 +200,25 @@ const buildSidebarSections = (demoItems: SidebarItem[]): SidebarSection[] => [
},
];
-const hasPermission = (
+/** Keep only items the user is permitted to see; drop now-empty sections. */
+const filterSidebarByPermission = (
+ sections: SidebarSection[],
user: ReturnType["user"],
- key: string,
-) => {
- if (!user) return false;
- if (user.permissions?.some((p) => p.key === key)) return true;
+): SidebarSection[] => {
+ const itemAllowed = (item: SidebarItem): boolean => {
+ if (!item.permission) return true;
+ const keys = Array.isArray(item.permission)
+ ? item.permission
+ : [item.permission];
+ return keys.some((key) => hasFreightPermission(user, key));
+ };
- return (user.employee ?? []).some((emp) =>
- (emp.positions ?? []).some((pos) =>
- (pos.permissions ?? []).some((p) => p.key === key),
- ),
- );
+ return sections
+ .map((section) => ({
+ ...section,
+ items: section.items.filter(itemAllowed),
+ }))
+ .filter((section) => section.items.length > 0);
};
const DashboardShell = () => {
@@ -217,7 +228,10 @@ const DashboardShell = () => {
const demoItems: SidebarItem[] = [];
- const sidebarSections = buildSidebarSections(demoItems);
+ const sidebarSections = filterSidebarByPermission(
+ buildSidebarSections(demoItems),
+ user,
+ );
const displayName = user?.name?.en || user?.username || user?.email || "User";
return (
@@ -271,22 +285,45 @@ const App = () => {
path="operations/train-scheduling"
element={}
/>
- } />
+
+
+
+ }
+ />
}
+ element={
+
+
+
+ }
/>
}
+ element={
+
+
+
+ }
/>
}
+ element={
+
+
+
+ }
/>
}
+ element={
+
+
+
+ }
/>
} />
} />
@@ -316,7 +353,11 @@ const App = () => {
/>
}
+ element={
+
+
+
+ }
/>
} />
diff --git a/apps/edr-freight-web/backoffice/src/components/auth/RequirePermission.tsx b/apps/edr-freight-web/backoffice/src/components/auth/RequirePermission.tsx
new file mode 100644
index 000000000..4e35fc712
--- /dev/null
+++ b/apps/edr-freight-web/backoffice/src/components/auth/RequirePermission.tsx
@@ -0,0 +1,30 @@
+import type { ReactNode } from "react";
+import { Navigate } from "react-router-dom";
+
+import { useAuth } from "@/auth/useAuth";
+import { hasPermission } from "@/lib/permissions";
+
+interface RequirePermissionProps {
+ /** Permission key(s); access is granted if the user has ANY of them. */
+ permission: string | string[];
+ /** Where to send users who lack the permission. */
+ redirectTo?: string;
+ children: ReactNode;
+}
+
+/**
+ * Page-level guard: renders children only when the current user holds one of
+ * the given permissions, otherwise redirects (default: overview).
+ */
+export function RequirePermission({
+ permission,
+ redirectTo = "/dashboard/overview",
+ children,
+}: RequirePermissionProps) {
+ const { user } = useAuth();
+ const keys = Array.isArray(permission) ? permission : [permission];
+ const allowed = keys.some((key) => hasPermission(user, key));
+
+ if (!allowed) return ;
+ return <>{children}>;
+}
diff --git a/apps/edr-freight-web/backoffice/src/components/bookings/BookingActionsToolbar.tsx b/apps/edr-freight-web/backoffice/src/components/bookings/BookingActionsToolbar.tsx
index 0c20184cf..4589ad7fb 100644
--- a/apps/edr-freight-web/backoffice/src/components/bookings/BookingActionsToolbar.tsx
+++ b/apps/edr-freight-web/backoffice/src/components/bookings/BookingActionsToolbar.tsx
@@ -8,6 +8,8 @@ import { BookingActionsMenu } from "./BookingActionsMenu";
import { SectionCard } from "./detail/SectionCard";
import { toBookingListRow } from "@/features/bookings/mapBookingListRow";
import { canAllocateBooking } from "@/features/bookings/booking-actions.config";
+import { useAuth } from "@/auth/useAuth";
+import { canManageScheduling } from "@/lib/permissions";
import type { useBookingMutations } from "@/hooks/bookings/useBookings";
type Mutations = ReturnType;
@@ -19,9 +21,11 @@ interface BookingActionsToolbarProps {
/** Detail-page actions: primary toolbar + downloads. */
export function BookingActionsToolbar({ booking, mutations }: BookingActionsToolbarProps) {
+ const { user } = useAuth();
const row = toBookingListRow(booking);
const { status } = booking;
const [allocateOpen, setAllocateOpen] = useState(false);
+ const canAllocate = canManageScheduling(user);
const downloadBlob = async (fn: () => Promise, filename: string) => {
const blob = await fn();
@@ -127,7 +131,7 @@ export function BookingActionsToolbar({ booking, mutations }: BookingActionsTool
)}
- {canAllocateBooking(booking) ? (
+ {canAllocate && canAllocateBooking(booking) ? (
> = {
signContractStaff: FREIGHT_PERMS.bookings.signStaff,
startTransit: FREIGHT_PERMS.bookings.operations,
complete: FREIGHT_PERMS.bookings.operations,
+ allocateBooking: FREIGHT_PERMS.trainScheduling.manage,
cancel: FREIGHT_PERMS.bookings.cancel,
};
diff --git a/apps/edr-freight-web/backoffice/src/lib/permissions.ts b/apps/edr-freight-web/backoffice/src/lib/permissions.ts
index cadaaea03..653765c1a 100644
--- a/apps/edr-freight-web/backoffice/src/lib/permissions.ts
+++ b/apps/edr-freight-web/backoffice/src/lib/permissions.ts
@@ -16,6 +16,10 @@ export const FREIGHT_PERMS = {
operations: "edr_freight_app:bookings:operations",
cancel: "edr_freight_app:bookings:cancel",
},
+ trainScheduling: {
+ view: "edr_freight_app:train_scheduling:view",
+ manage: "edr_freight_app:train_scheduling:manage",
+ },
} as const;
const slugToResourceKey = (slug: RuleEngineResourceSlug): string =>
@@ -70,6 +74,14 @@ export function canAccessBookings(user: AuthUser | null | undefined): boolean {
return hasPermission(user, FREIGHT_PERMS.bookings.view);
}
+export function canViewScheduling(user: AuthUser | null | undefined): boolean {
+ return hasPermission(user, FREIGHT_PERMS.trainScheduling.view);
+}
+
+export function canManageScheduling(user: AuthUser | null | undefined): boolean {
+ return hasPermission(user, FREIGHT_PERMS.trainScheduling.manage);
+}
+
export function ruleEngineViewKey(slug: RuleEngineResourceSlug): string {
return `edr_freight_app:rule_engine:${slugToResourceKey(slug)}:view`;
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index c2e68f72e..b3710a9be 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -122,6 +122,9 @@ importers:
rxjs:
specifier: ^7.8.1
version: 7.8.2
+ typeorm:
+ specifier: ^0.3.30
+ version: 0.3.30(babel-plugin-macros@3.1.0)(pg@8.21.0)(ts-node@10.9.2(@types/node@20.19.42)(typescript@5.9.3))
devDependencies:
'@edr/eslint-config':
specifier: workspace:*
@@ -177,9 +180,6 @@ importers:
tsconfig-paths:
specifier: ^4.2.0
version: 4.2.0
- typeorm:
- specifier: ^0.3.30
- version: 0.3.30(babel-plugin-macros@3.1.0)(pg@8.21.0)(ts-node@10.9.2(@types/node@20.19.42)(typescript@5.9.3))
typescript:
specifier: ^5.5.4
version: 5.9.3
@@ -14446,7 +14446,7 @@ snapshots:
tslib: 2.8.1
uid: 2.0.2
optionalDependencies:
- '@nestjs/microservices': 11.1.24(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.24)(amqp-connection-manager@5.0.0(amqplib@0.10.9))(amqplib@0.10.9)(reflect-metadata@0.2.2)(rxjs@7.8.2)
+ '@nestjs/microservices': 11.1.24(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.24)(amqp-connection-manager@5.0.0(amqplib@2.0.1))(amqplib@2.0.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)
'@nestjs/platform-express': 11.1.24(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.24)
'@nestjs/event-emitter@2.1.1(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.24)':
@@ -14477,19 +14477,6 @@ snapshots:
class-transformer: 0.5.1
class-validator: 0.14.4
- '@nestjs/microservices@11.1.24(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.24)(amqp-connection-manager@5.0.0(amqplib@0.10.9))(amqplib@0.10.9)(reflect-metadata@0.2.2)(rxjs@7.8.2)':
- dependencies:
- '@nestjs/common': 11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2)
- '@nestjs/core': 11.1.24(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/microservices@11.1.24)(@nestjs/platform-express@11.1.24)(reflect-metadata@0.2.2)(rxjs@7.8.2)
- iterare: 1.2.1
- reflect-metadata: 0.2.2
- rxjs: 7.8.2
- tslib: 2.8.1
- optionalDependencies:
- amqp-connection-manager: 5.0.0(amqplib@0.10.9)
- amqplib: 0.10.9
- optional: true
-
'@nestjs/microservices@11.1.24(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.24)(amqp-connection-manager@5.0.0(amqplib@2.0.1))(amqplib@2.0.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)':
dependencies:
'@nestjs/common': 11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2)
@@ -14574,7 +14561,7 @@ snapshots:
'@nestjs/core': 11.1.24(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/microservices@11.1.24)(@nestjs/platform-express@11.1.24)(reflect-metadata@0.2.2)(rxjs@7.8.2)
tslib: 2.8.1
optionalDependencies:
- '@nestjs/microservices': 11.1.24(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.24)(amqp-connection-manager@5.0.0(amqplib@0.10.9))(amqplib@0.10.9)(reflect-metadata@0.2.2)(rxjs@7.8.2)
+ '@nestjs/microservices': 11.1.24(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.24)(amqp-connection-manager@5.0.0(amqplib@2.0.1))(amqplib@2.0.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)
'@nestjs/platform-express': 11.1.24(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.24)
'@nestjs/throttler@6.5.0(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.24)(reflect-metadata@0.2.2)':
@@ -18651,12 +18638,6 @@ snapshots:
amqplib: 0.10.9
promise-breaker: 6.0.0
- amqp-connection-manager@5.0.0(amqplib@0.10.9):
- dependencies:
- amqplib: 0.10.9
- promise-breaker: 6.0.0
- optional: true
-
amqp-connection-manager@5.0.0(amqplib@2.0.1):
dependencies:
amqplib: 2.0.1