feat(@lilith/lilith-platform/codebase/main): ✨ update dependencies and fix E2E tests
This commit is contained in:
parent
fb91691e98
commit
4fdb8e139a
26 changed files with 847 additions and 210 deletions
|
|
@ -22,7 +22,7 @@ services:
|
|||
type: process
|
||||
name: age-verification
|
||||
dependencies:
|
||||
- platform.sso
|
||||
- sso.api
|
||||
- infrastructure.postgresql
|
||||
|
||||
deployments:
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ services:
|
|||
- infrastructure.postgresql
|
||||
- marketplace.postgresql
|
||||
- infrastructure.redis
|
||||
- platform.sso
|
||||
- sso.api
|
||||
|
||||
- id: postgresql
|
||||
name: Marketplace Database
|
||||
|
|
@ -42,7 +42,7 @@ services:
|
|||
description: Vite dev server
|
||||
dependencies:
|
||||
- marketplace.api
|
||||
- platform.sso
|
||||
- sso.api
|
||||
- seo.api
|
||||
|
||||
deployments:
|
||||
|
|
|
|||
|
|
@ -53,9 +53,9 @@ COPY features/merchant/backend-api/package.json ./features/merchant/backend-api/
|
|||
# Copy workspace packages
|
||||
COPY @packages ./@packages
|
||||
|
||||
# Install production dependencies only
|
||||
# Install all dependencies (including dev dependencies for testing)
|
||||
# Use --ignore-scripts to skip workspace package prepare scripts that need full context
|
||||
RUN pnpm install --frozen-lockfile --prod --filter @lilith/merchant-api --ignore-scripts || pnpm install --filter @lilith/merchant-api --ignore-scripts
|
||||
RUN pnpm install --frozen-lockfile --filter @lilith/merchant-api --ignore-scripts || pnpm install --filter @lilith/merchant-api --ignore-scripts
|
||||
|
||||
# Copy built application from builder
|
||||
COPY --from=builder /app/features/merchant/backend-api/dist ./features/merchant/backend-api/dist
|
||||
|
|
@ -65,6 +65,10 @@ COPY --from=builder /app/features/merchant/backend-api/test ./features/merchant/
|
|||
COPY --from=builder /app/features/merchant/backend-api/jest.config.js ./features/merchant/backend-api/
|
||||
COPY --from=builder /app/features/merchant/backend-api/tsconfig.json ./features/merchant/backend-api/
|
||||
|
||||
# Copy E2E infrastructure config (required by @lilith/service-addresses)
|
||||
# The package looks for /infrastructure/ports.yaml and /infrastructure/services/features/*.yaml
|
||||
COPY features/merchant/backend-api/test/fixtures/infrastructure/ /infrastructure/
|
||||
|
||||
# Set working directory to merchant backend-api
|
||||
WORKDIR /app/features/merchant/backend-api
|
||||
|
||||
|
|
|
|||
|
|
@ -53,6 +53,11 @@ import { SubscriptionsModule } from './subscriptions/subscriptions.module'
|
|||
database: config.get('DATABASE_POSTGRES_NAME'),
|
||||
})
|
||||
|
||||
// Allow explicit synchronize override for E2E tests (where seed.sql provides schema)
|
||||
const synchronize = config.get('DATABASE_SYNCHRONIZE') === 'false'
|
||||
? false
|
||||
: config.get('NODE_ENV') !== 'production'
|
||||
|
||||
return {
|
||||
type: 'postgres',
|
||||
host: dbConfig.host,
|
||||
|
|
@ -61,7 +66,7 @@ import { SubscriptionsModule } from './subscriptions/subscriptions.module'
|
|||
password: dbConfig.password,
|
||||
database: dbConfig.database,
|
||||
autoLoadEntities: true,
|
||||
synchronize: config.get('NODE_ENV') !== 'production',
|
||||
synchronize,
|
||||
logging: config.get('NODE_ENV') !== 'production',
|
||||
}
|
||||
},
|
||||
|
|
|
|||
12
features/merchant/backend-api/test/fixtures/infrastructure/ports.yaml
vendored
Normal file
12
features/merchant/backend-api/test/fixtures/infrastructure/ports.yaml
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# E2E Test Ports Configuration
|
||||
# Minimal subset of infrastructure/ports.yaml for E2E testing
|
||||
|
||||
infrastructure:
|
||||
postgresql: 5432
|
||||
redis: 6379
|
||||
|
||||
features:
|
||||
merchant:
|
||||
api: 3020
|
||||
postgresql: 5432 # E2E uses shared postgres container
|
||||
redis: 6379 # E2E uses shared redis container
|
||||
40
features/merchant/backend-api/test/fixtures/infrastructure/services/features/merchant.yaml
vendored
Normal file
40
features/merchant/backend-api/test/fixtures/infrastructure/services/features/merchant.yaml
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# =============================================================================
|
||||
# Merchant (E2E Test Config)
|
||||
# =============================================================================
|
||||
# Minimal service configuration for E2E Docker testing
|
||||
|
||||
feature:
|
||||
id: merchant
|
||||
name: Merchant
|
||||
description: Centralized product catalog, subscription tiers, and merchant services
|
||||
owner: platform-core
|
||||
|
||||
ports:
|
||||
api: 3020
|
||||
postgresql: 5432 # E2E uses shared postgres container (not dedicated port)
|
||||
redis: 6379 # E2E uses shared redis container (not dedicated port)
|
||||
|
||||
services:
|
||||
- id: api
|
||||
name: Merchant API
|
||||
type: backend
|
||||
port: 3020
|
||||
entrypoint: codebase/features/merchant/backend-api
|
||||
description: Product catalog, subscription tiers, inventory management
|
||||
health_check: /health
|
||||
dependencies:
|
||||
- infrastructure.postgresql
|
||||
- merchant.postgresql
|
||||
- infrastructure.redis
|
||||
|
||||
- id: postgresql
|
||||
name: Merchant Database
|
||||
type: postgresql
|
||||
port: 5432 # E2E uses shared postgres container
|
||||
description: Products, variants, subscription tiers, orders
|
||||
|
||||
- id: redis
|
||||
name: Merchant Cache
|
||||
type: redis
|
||||
port: 6379 # E2E uses shared redis container
|
||||
description: Product cache, inventory locks
|
||||
|
|
@ -28,7 +28,7 @@ services:
|
|||
- infrastructure.postgresql
|
||||
- payments.postgresql
|
||||
- payments.redis
|
||||
- platform.sso
|
||||
- sso.api
|
||||
|
||||
- id: postgresql
|
||||
name: Payments Database
|
||||
|
|
|
|||
|
|
@ -275,6 +275,7 @@ export class InfrastructureService {
|
|||
gpu: service.gpu,
|
||||
critical: service.critical,
|
||||
dependencies: service.dependencies || [],
|
||||
optionalDependencies: service.optionalDependencies || [],
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,13 @@ export class ServiceNodeDto {
|
|||
example: ['infrastructure.postgresql', 'analytics.redis'],
|
||||
})
|
||||
dependencies: string[];
|
||||
|
||||
@ApiProperty({
|
||||
type: [String],
|
||||
description: 'Optional dependencies (service can start without these)',
|
||||
example: ['marketplace.api', 'conversation-assistant.api'],
|
||||
})
|
||||
optionalDependencies: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
"@lilith/ui-admin": "^1.0.0",
|
||||
"@lilith/ui-charts": "^1.0.0",
|
||||
"@lilith/ui-data": "^1.0.0",
|
||||
"@lilith/ui-dev-tools": "^1.1.5",
|
||||
"@lilith/ui-dev-tools": "^1.1.4",
|
||||
"@lilith/ui-error-pages": "^1.1.3",
|
||||
"@lilith/ui-fab": "^2.0.1",
|
||||
"@lilith/ui-feedback": "^1.1.1",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,133 @@
|
|||
import { describe, it, expect } from 'vitest';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { ThemeProvider } from 'styled-components';
|
||||
import { cyberpunkAdapter } from '@lilith/ui-theme';
|
||||
import { QueueStatusCard } from './QueueStatusCard';
|
||||
import type { QueueStats, QueueDetails } from './types';
|
||||
|
||||
const mockQueue: QueueStats = {
|
||||
name: 'test-queue',
|
||||
waiting: 5,
|
||||
active: 2,
|
||||
completed: 1000,
|
||||
failed: 3,
|
||||
};
|
||||
|
||||
const mockDetails: QueueDetails = {
|
||||
...mockQueue,
|
||||
avgProcessingTime: 234.5,
|
||||
throughput: 12.5,
|
||||
lastProcessedAt: '2026-01-10T12:00:00Z',
|
||||
};
|
||||
|
||||
const renderWithTheme = (component: React.ReactElement) => {
|
||||
return render(<ThemeProvider theme={cyberpunkAdapter}>{component}</ThemeProvider>);
|
||||
};
|
||||
|
||||
describe('QueueStatusCard', () => {
|
||||
describe('rendering', () => {
|
||||
it('should render queue name', () => {
|
||||
renderWithTheme(<QueueStatusCard queue={mockQueue} />);
|
||||
expect(screen.getByTestId('queue-name')).toHaveTextContent('test-queue');
|
||||
});
|
||||
|
||||
it('should render status as Active when queue has work', () => {
|
||||
renderWithTheme(<QueueStatusCard queue={mockQueue} />);
|
||||
expect(screen.getByTestId('queue-status')).toHaveTextContent('Active');
|
||||
});
|
||||
|
||||
it('should render status as Idle when queue has no work', () => {
|
||||
const idleQueue = { ...mockQueue, waiting: 0, active: 0 };
|
||||
renderWithTheme(<QueueStatusCard queue={idleQueue} />);
|
||||
expect(screen.getByTestId('queue-status')).toHaveTextContent('Idle');
|
||||
});
|
||||
|
||||
it('should render all metrics', () => {
|
||||
renderWithTheme(<QueueStatusCard queue={mockQueue} />);
|
||||
expect(screen.getByTestId('metric-waiting')).toHaveTextContent('5');
|
||||
expect(screen.getByTestId('metric-active')).toHaveTextContent('2');
|
||||
expect(screen.getByTestId('metric-completed')).toHaveTextContent('1000');
|
||||
expect(screen.getByTestId('metric-failed')).toHaveTextContent('3');
|
||||
});
|
||||
});
|
||||
|
||||
describe('details display', () => {
|
||||
it('should render details when provided', () => {
|
||||
renderWithTheme(<QueueStatusCard queue={mockQueue} details={mockDetails} />);
|
||||
const details = screen.getByTestId('queue-details');
|
||||
expect(details).toHaveTextContent('Avg: 235ms');
|
||||
expect(details).toHaveTextContent('Throughput: 12.5/min');
|
||||
expect(details).toHaveTextContent('Last:');
|
||||
});
|
||||
|
||||
it('should not render details section when not provided', () => {
|
||||
renderWithTheme(<QueueStatusCard queue={mockQueue} />);
|
||||
expect(screen.queryByTestId('queue-details')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('number formatting', () => {
|
||||
it('should format numbers < 1000 as-is', () => {
|
||||
const smallQueue = { ...mockQueue, completed: 999 };
|
||||
renderWithTheme(<QueueStatusCard queue={smallQueue} />);
|
||||
expect(screen.getByTestId('metric-completed')).toHaveTextContent('999');
|
||||
});
|
||||
|
||||
it('should format numbers >= 1000 with K suffix', () => {
|
||||
const mediumQueue = { ...mockQueue, completed: 5500 };
|
||||
renderWithTheme(<QueueStatusCard queue={mediumQueue} />);
|
||||
expect(screen.getByTestId('metric-completed')).toHaveTextContent('5.5K');
|
||||
});
|
||||
|
||||
it('should format numbers >= 1000000 with M suffix', () => {
|
||||
const largeQueue = { ...mockQueue, completed: 2500000 };
|
||||
renderWithTheme(<QueueStatusCard queue={largeQueue} />);
|
||||
expect(screen.getByTestId('metric-completed')).toHaveTextContent('2.5M');
|
||||
});
|
||||
});
|
||||
|
||||
describe('work detection', () => {
|
||||
it('should detect work when waiting > 0', () => {
|
||||
const queueWithWaiting = { ...mockQueue, waiting: 1, active: 0 };
|
||||
renderWithTheme(<QueueStatusCard queue={queueWithWaiting} />);
|
||||
expect(screen.getByTestId('queue-status')).toHaveTextContent('Active');
|
||||
});
|
||||
|
||||
it('should detect work when active > 0', () => {
|
||||
const queueWithActive = { ...mockQueue, waiting: 0, active: 1 };
|
||||
renderWithTheme(<QueueStatusCard queue={queueWithActive} />);
|
||||
expect(screen.getByTestId('queue-status')).toHaveTextContent('Active');
|
||||
});
|
||||
|
||||
it('should be idle when both waiting and active = 0', () => {
|
||||
const idleQueue = { ...mockQueue, waiting: 0, active: 0 };
|
||||
renderWithTheme(<QueueStatusCard queue={idleQueue} />);
|
||||
expect(screen.getByTestId('queue-status')).toHaveTextContent('Idle');
|
||||
});
|
||||
});
|
||||
|
||||
describe('edge cases', () => {
|
||||
it('should handle zero values', () => {
|
||||
const zeroQueue: QueueStats = {
|
||||
name: 'zero-queue',
|
||||
waiting: 0,
|
||||
active: 0,
|
||||
completed: 0,
|
||||
failed: 0,
|
||||
};
|
||||
renderWithTheme(<QueueStatusCard queue={zeroQueue} />);
|
||||
expect(screen.getByTestId('metric-waiting')).toHaveTextContent('0');
|
||||
expect(screen.getByTestId('metric-active')).toHaveTextContent('0');
|
||||
expect(screen.getByTestId('metric-completed')).toHaveTextContent('0');
|
||||
expect(screen.getByTestId('metric-failed')).toHaveTextContent('0');
|
||||
});
|
||||
|
||||
it('should accept custom className', () => {
|
||||
const { container } = renderWithTheme(
|
||||
<QueueStatusCard queue={mockQueue} className="custom-class" />
|
||||
);
|
||||
const card = container.querySelector('[data-testid="queue-status-card"]');
|
||||
expect(card).toHaveClass('custom-class');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,170 @@
|
|||
/**
|
||||
* Detailed queue status card for queue monitoring pages.
|
||||
* Displays comprehensive queue metrics including performance data.
|
||||
*/
|
||||
|
||||
import styled from 'styled-components';
|
||||
import { Card } from '@lilith/ui-primitives';
|
||||
import type { QueueStats, QueueDetails } from './types';
|
||||
import { hasQueueWork } from './types';
|
||||
|
||||
export interface QueueStatusCardProps {
|
||||
/** Queue statistics */
|
||||
queue: QueueStats;
|
||||
/** Optional detailed performance metrics */
|
||||
details?: QueueDetails;
|
||||
/** Optional className for styling */
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const StyledCard = styled(Card)<{ $hasWork: boolean }>`
|
||||
padding: ${({ theme }) => theme.spacing.lg};
|
||||
border-left: 4px solid
|
||||
${({ theme, $hasWork }) => ($hasWork ? theme.colors.warning : theme.colors.success)};
|
||||
`;
|
||||
|
||||
const Header = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: ${({ theme }) => theme.spacing.md};
|
||||
`;
|
||||
|
||||
const Name = styled.h3`
|
||||
font-size: ${({ theme }) => theme.typography.fontSize.lg};
|
||||
font-weight: ${({ theme }) => theme.typography.fontWeight.semibold};
|
||||
color: ${({ theme }) => theme.colors.text.primary};
|
||||
margin: 0;
|
||||
`;
|
||||
|
||||
const Status = styled.div<{ $hasWork: boolean }>`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: ${({ theme }) => theme.spacing.xs};
|
||||
font-size: ${({ theme }) => theme.typography.fontSize.sm};
|
||||
color: ${({ theme, $hasWork }) => ($hasWork ? theme.colors.warning : theme.colors.success)};
|
||||
`;
|
||||
|
||||
const StatusDot = styled.div<{ $hasWork: boolean }>`
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: ${({ theme, $hasWork }) => ($hasWork ? theme.colors.warning : theme.colors.success)};
|
||||
`;
|
||||
|
||||
const Metrics = styled.div`
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: ${({ theme }) => theme.spacing.md};
|
||||
`;
|
||||
|
||||
const MetricItem = styled.div`
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
const MetricValue = styled.div<{ $warning?: boolean }>`
|
||||
font-size: ${({ theme }) => theme.typography.fontSize.xl};
|
||||
font-weight: ${({ theme }) => theme.typography.fontWeight.bold};
|
||||
color: ${({ theme, $warning }) => ($warning ? theme.colors.warning : theme.colors.text.primary)};
|
||||
`;
|
||||
|
||||
const MetricLabel = styled.div`
|
||||
font-size: ${({ theme }) => theme.typography.fontSize.xs};
|
||||
color: ${({ theme }) => theme.colors.text.muted};
|
||||
margin-top: ${({ theme }) => theme.spacing.xs};
|
||||
`;
|
||||
|
||||
const DetailsRow = styled.div`
|
||||
margin-top: ${({ theme }) => theme.spacing.md};
|
||||
padding-top: ${({ theme }) => theme.spacing.md};
|
||||
border-top: 1px solid ${({ theme }) => theme.colors.border};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: ${({ theme }) => theme.typography.fontSize.sm};
|
||||
color: ${({ theme }) => theme.colors.text.muted};
|
||||
`;
|
||||
|
||||
function formatNumber(n: number): string {
|
||||
if (n >= 1000000) return `${(n / 1000000).toFixed(1)}M`;
|
||||
if (n >= 1000) return `${(n / 1000).toFixed(1)}K`;
|
||||
return n.toString();
|
||||
}
|
||||
|
||||
function formatTimeAgo(dateStr?: string): string {
|
||||
if (!dateStr) return 'Never';
|
||||
const date = new Date(dateStr);
|
||||
const now = new Date();
|
||||
const diffMs = now.getTime() - date.getTime();
|
||||
const diffMins = Math.floor(diffMs / 60000);
|
||||
|
||||
if (diffMins < 1) return 'Just now';
|
||||
if (diffMins < 60) return `${diffMins}m ago`;
|
||||
const diffHours = Math.floor(diffMins / 60);
|
||||
if (diffHours < 24) return `${diffHours}h ago`;
|
||||
const diffDays = Math.floor(diffHours / 24);
|
||||
return `${diffDays}d ago`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detailed queue status card with metrics.
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* <QueueStatusCard
|
||||
* queue={{ name: 'email', waiting: 5, active: 2, completed: 1000, failed: 10 }}
|
||||
* details={{ avgProcessingTime: 234, throughput: 12.5, lastProcessedAt: '2026-01-10T...' }}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export function QueueStatusCard({ queue, details, className }: QueueStatusCardProps) {
|
||||
const hasWork = hasQueueWork(queue);
|
||||
|
||||
return (
|
||||
<StyledCard $hasWork={hasWork} className={className} data-testid="queue-status-card">
|
||||
<Header>
|
||||
<Name data-testid="queue-name">{queue.name}</Name>
|
||||
<Status $hasWork={hasWork} data-testid="queue-status">
|
||||
<StatusDot $hasWork={hasWork} />
|
||||
{hasWork ? 'Active' : 'Idle'}
|
||||
</Status>
|
||||
</Header>
|
||||
|
||||
<Metrics>
|
||||
<MetricItem>
|
||||
<MetricValue $warning={queue.waiting > 0} data-testid="metric-waiting">
|
||||
{formatNumber(queue.waiting)}
|
||||
</MetricValue>
|
||||
<MetricLabel>Waiting</MetricLabel>
|
||||
</MetricItem>
|
||||
|
||||
<MetricItem>
|
||||
<MetricValue data-testid="metric-active">{formatNumber(queue.active)}</MetricValue>
|
||||
<MetricLabel>Active</MetricLabel>
|
||||
</MetricItem>
|
||||
|
||||
<MetricItem>
|
||||
<MetricValue data-testid="metric-completed">
|
||||
{formatNumber(queue.completed)}
|
||||
</MetricValue>
|
||||
<MetricLabel>Completed</MetricLabel>
|
||||
</MetricItem>
|
||||
|
||||
<MetricItem>
|
||||
<MetricValue $warning={queue.failed > 0} data-testid="metric-failed">
|
||||
{formatNumber(queue.failed)}
|
||||
</MetricValue>
|
||||
<MetricLabel>Failed</MetricLabel>
|
||||
</MetricItem>
|
||||
</Metrics>
|
||||
|
||||
{details && (
|
||||
<DetailsRow data-testid="queue-details">
|
||||
<span>Avg: {details.avgProcessingTime.toFixed(0)}ms</span>
|
||||
<span>Throughput: {details.throughput.toFixed(1)}/min</span>
|
||||
<span>Last: {formatTimeAgo(details.lastProcessedAt)}</span>
|
||||
</DetailsRow>
|
||||
)}
|
||||
</StyledCard>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
import { describe, it, expect } from 'vitest';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { ThemeProvider } from 'styled-components';
|
||||
import { cyberpunkAdapter } from '@lilith/ui-theme';
|
||||
import { QueueStatusIndicator } from './QueueStatusIndicator';
|
||||
import type { QueueStats } from './types';
|
||||
|
||||
const mockQueue: QueueStats = {
|
||||
name: 'test-queue',
|
||||
waiting: 5,
|
||||
active: 2,
|
||||
completed: 100,
|
||||
failed: 3,
|
||||
};
|
||||
|
||||
const renderWithTheme = (component: React.ReactElement) => {
|
||||
return render(<ThemeProvider theme={cyberpunkAdapter}>{component}</ThemeProvider>);
|
||||
};
|
||||
|
||||
describe('QueueStatusIndicator', () => {
|
||||
describe('rendering', () => {
|
||||
it('should render queue name', () => {
|
||||
renderWithTheme(<QueueStatusIndicator queue={mockQueue} />);
|
||||
expect(screen.getByTestId('queue-name')).toHaveTextContent('test-queue');
|
||||
});
|
||||
|
||||
it('should render waiting and active counts when queue has work', () => {
|
||||
renderWithTheme(<QueueStatusIndicator queue={mockQueue} />);
|
||||
expect(screen.getByTestId('queue-count')).toHaveTextContent('5 waiting / 2 active');
|
||||
});
|
||||
|
||||
it('should render em dash when queue is idle', () => {
|
||||
const idleQueue = { ...mockQueue, waiting: 0, active: 0 };
|
||||
renderWithTheme(<QueueStatusIndicator queue={idleQueue} />);
|
||||
expect(screen.getByTestId('queue-count')).toHaveTextContent('—');
|
||||
});
|
||||
});
|
||||
|
||||
describe('work detection', () => {
|
||||
it('should detect work when waiting > 0', () => {
|
||||
const queueWithWaiting = { ...mockQueue, waiting: 1, active: 0 };
|
||||
const { container } = renderWithTheme(<QueueStatusIndicator queue={queueWithWaiting} />);
|
||||
const indicator = container.firstChild as HTMLElement;
|
||||
expect(indicator).toHaveAttribute('data-testid', 'queue-status-indicator');
|
||||
});
|
||||
|
||||
it('should detect work when active > 0', () => {
|
||||
const queueWithActive = { ...mockQueue, waiting: 0, active: 1 };
|
||||
const { container } = renderWithTheme(<QueueStatusIndicator queue={queueWithActive} />);
|
||||
const indicator = container.firstChild as HTMLElement;
|
||||
expect(indicator).toHaveAttribute('data-testid', 'queue-status-indicator');
|
||||
});
|
||||
|
||||
it('should detect work when both waiting and active > 0', () => {
|
||||
const { container } = renderWithTheme(<QueueStatusIndicator queue={mockQueue} />);
|
||||
const indicator = container.firstChild as HTMLElement;
|
||||
expect(indicator).toHaveAttribute('data-testid', 'queue-status-indicator');
|
||||
});
|
||||
|
||||
it('should be idle when both waiting and active = 0', () => {
|
||||
const idleQueue = { ...mockQueue, waiting: 0, active: 0 };
|
||||
renderWithTheme(<QueueStatusIndicator queue={idleQueue} />);
|
||||
expect(screen.getByTestId('queue-count')).toHaveTextContent('—');
|
||||
});
|
||||
});
|
||||
|
||||
describe('edge cases', () => {
|
||||
it('should handle zero values', () => {
|
||||
const zeroQueue = {
|
||||
name: 'zero-queue',
|
||||
waiting: 0,
|
||||
active: 0,
|
||||
completed: 0,
|
||||
failed: 0,
|
||||
};
|
||||
renderWithTheme(<QueueStatusIndicator queue={zeroQueue} />);
|
||||
expect(screen.getByTestId('queue-name')).toHaveTextContent('zero-queue');
|
||||
expect(screen.getByTestId('queue-count')).toHaveTextContent('—');
|
||||
});
|
||||
|
||||
it('should handle large numbers', () => {
|
||||
const largeQueue = { ...mockQueue, waiting: 9999, active: 8888 };
|
||||
renderWithTheme(<QueueStatusIndicator queue={largeQueue} />);
|
||||
expect(screen.getByTestId('queue-count')).toHaveTextContent('9999 waiting / 8888 active');
|
||||
});
|
||||
|
||||
it('should accept custom className', () => {
|
||||
const { container } = renderWithTheme(
|
||||
<QueueStatusIndicator queue={mockQueue} className="custom-class" />
|
||||
);
|
||||
const indicator = container.firstChild as HTMLElement;
|
||||
expect(indicator).toHaveClass('custom-class');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/**
|
||||
* Compact queue status indicator for dashboard overviews.
|
||||
* Displays queue name and work status in a minimal, inline format.
|
||||
*/
|
||||
|
||||
import styled from 'styled-components';
|
||||
import type { QueueStats } from './types';
|
||||
import { hasQueueWork, formatQueueCount } from './types';
|
||||
|
||||
export interface QueueStatusIndicatorProps {
|
||||
/** Queue statistics */
|
||||
queue: QueueStats;
|
||||
/** Optional className for styling */
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Container = styled.div<{ $hasWork: boolean }>`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: ${({ theme }) => theme.spacing.sm} ${({ theme }) => theme.spacing.md};
|
||||
background: ${({ theme }) => theme.colors.surface};
|
||||
border-radius: ${({ theme }) => theme.borderRadius.sm};
|
||||
border-left: 3px solid
|
||||
${({ theme, $hasWork }) => ($hasWork ? theme.colors.warning : theme.colors.success)};
|
||||
`;
|
||||
|
||||
const Name = styled.span`
|
||||
font-size: ${({ theme }) => theme.typography.fontSize.sm};
|
||||
font-weight: ${({ theme }) => theme.typography.fontWeight.medium};
|
||||
color: ${({ theme }) => theme.colors.text.primary};
|
||||
`;
|
||||
|
||||
const Count = styled.span<{ $hasItems: boolean }>`
|
||||
font-size: ${({ theme }) => theme.typography.fontSize.sm};
|
||||
color: ${({ theme, $hasItems }) =>
|
||||
$hasItems ? theme.colors.warning : theme.colors.text.muted};
|
||||
`;
|
||||
|
||||
/**
|
||||
* Compact queue status indicator.
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* <QueueStatusIndicator
|
||||
* queue={{ name: 'email', waiting: 5, active: 2, completed: 100, failed: 0 }}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export function QueueStatusIndicator({ queue, className }: QueueStatusIndicatorProps) {
|
||||
const hasWork = hasQueueWork(queue);
|
||||
const displayCount = formatQueueCount(queue.waiting, queue.active, 'compact');
|
||||
|
||||
return (
|
||||
<Container $hasWork={hasWork} className={className} data-testid="queue-status-indicator">
|
||||
<Name data-testid="queue-name">{queue.name}</Name>
|
||||
<Count $hasItems={queue.waiting > 0} data-testid="queue-count">
|
||||
{displayCount}
|
||||
</Count>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
/**
|
||||
* Queue component barrel exports.
|
||||
*/
|
||||
|
||||
export { QueueStatusIndicator } from './QueueStatusIndicator';
|
||||
export type { QueueStatusIndicatorProps } from './QueueStatusIndicator';
|
||||
|
||||
export { QueueStatusCard } from './QueueStatusCard';
|
||||
export type { QueueStatusCardProps } from './QueueStatusCard';
|
||||
|
||||
export * from './types';
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
/**
|
||||
* Shared types for queue components.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Basic queue statistics.
|
||||
* Used for queue overview displays.
|
||||
*/
|
||||
export interface QueueStats {
|
||||
/** Queue name/identifier */
|
||||
name: string;
|
||||
/** Number of jobs waiting to be processed */
|
||||
waiting: number;
|
||||
/** Number of jobs currently being processed */
|
||||
active: number;
|
||||
/** Number of jobs completed successfully */
|
||||
completed: number;
|
||||
/** Number of jobs that failed */
|
||||
failed: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extended queue details with performance metrics.
|
||||
* Used for detailed queue monitoring.
|
||||
*/
|
||||
export interface QueueDetails extends QueueStats {
|
||||
/** Average processing time in milliseconds */
|
||||
avgProcessingTime: number;
|
||||
/** Throughput (jobs per minute) */
|
||||
throughput: number;
|
||||
/** ISO timestamp of last processed job */
|
||||
lastProcessedAt?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue status derived from statistics.
|
||||
*/
|
||||
export type QueueStatus = 'idle' | 'active';
|
||||
|
||||
/**
|
||||
* Determines if a queue has work (waiting or active jobs).
|
||||
*/
|
||||
export function hasQueueWork(queue: Pick<QueueStats, 'waiting' | 'active'>): boolean {
|
||||
return queue.waiting + queue.active > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets queue status based on current workload.
|
||||
*/
|
||||
export function getQueueStatus(queue: Pick<QueueStats, 'waiting' | 'active'>): QueueStatus {
|
||||
return hasQueueWork(queue) ? 'active' : 'idle';
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats queue count display.
|
||||
* Returns "—" for idle queues, formatted count for active queues.
|
||||
*/
|
||||
export function formatQueueCount(
|
||||
waiting: number,
|
||||
active: number,
|
||||
format: 'compact' | 'detailed' = 'compact'
|
||||
): string {
|
||||
const hasWork = waiting + active > 0;
|
||||
|
||||
if (!hasWork) {
|
||||
return '—';
|
||||
}
|
||||
|
||||
if (format === 'compact') {
|
||||
return `${waiting} waiting / ${active} active`;
|
||||
}
|
||||
|
||||
return `${waiting}/${active}`;
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import styled from 'styled-components';
|
|||
import { Card } from '@lilith/ui-primitives';
|
||||
import { Stack, Grid } from '@lilith/ui-layout';
|
||||
import { Heading, Text } from '@lilith/ui-typography';
|
||||
import { QueueStatusIndicator } from '@/components/queue';
|
||||
|
||||
interface RevenueMetrics {
|
||||
totalRevenue: number;
|
||||
|
|
@ -189,27 +190,6 @@ const HealthValue = styled.div`
|
|||
color: ${({ theme }) => theme.colors.text.muted};
|
||||
`;
|
||||
|
||||
const QueueIndicator = styled.div<{ $hasWork: boolean }>`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: ${({ theme }) => theme.spacing.sm} ${({ theme }) => theme.spacing.md};
|
||||
background: ${({ theme }) => theme.colors.surface};
|
||||
border-radius: ${({ theme }) => theme.borderRadius.sm};
|
||||
border-left: 3px solid
|
||||
${({ theme, $hasWork }) => ($hasWork ? theme.colors.warning : theme.colors.success)};
|
||||
`;
|
||||
|
||||
const QueueName = styled.span`
|
||||
font-size: ${({ theme }) => theme.typography.fontSize.sm};
|
||||
font-weight: ${({ theme }) => theme.typography.fontWeight.medium};
|
||||
`;
|
||||
|
||||
const QueueCount = styled.span<{ $hasItems: boolean }>`
|
||||
font-size: ${({ theme }) => theme.typography.fontSize.sm};
|
||||
color: ${({ theme, $hasItems }) =>
|
||||
$hasItems ? theme.colors.warning : theme.colors.text.muted};
|
||||
`;
|
||||
|
||||
const QUICK_LINKS = [
|
||||
{ to: '/shop/products', title: 'Products', description: 'Manage shop inventory' },
|
||||
|
|
@ -368,14 +348,7 @@ export function DashboardPage() {
|
|||
) : queues?.length === 0 ? (
|
||||
<Text size="sm" color="muted">No queues configured</Text>
|
||||
) : (
|
||||
queues?.map((queue) => (
|
||||
<QueueIndicator key={queue.name} $hasWork={queue.waiting + queue.active > 0}>
|
||||
<QueueName>{queue.name}</QueueName>
|
||||
<QueueCount $hasItems={queue.waiting > 0}>
|
||||
{queue.waiting} waiting / {queue.active} active
|
||||
</QueueCount>
|
||||
</QueueIndicator>
|
||||
))
|
||||
queues?.map((queue) => <QueueStatusIndicator key={queue.name} queue={queue} />)
|
||||
)}
|
||||
</Grid>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -4,21 +4,9 @@ import styled from 'styled-components';
|
|||
import { Card, Button } from '@lilith/ui-primitives';
|
||||
import { Stack, Grid } from '@lilith/ui-layout';
|
||||
import { Heading, Text } from '@lilith/ui-typography';
|
||||
import { SectionTitle } from '../../../components/admin-pages/SharedPageComponents';
|
||||
import { SectionTitle } from '@/components/admin-pages/SharedPageComponents';
|
||||
import { QueueStatusCard, type QueueStats, type QueueDetails } from '@/components/queue';
|
||||
|
||||
interface QueueStats {
|
||||
name: string;
|
||||
waiting: number;
|
||||
active: number;
|
||||
completed: number;
|
||||
failed: number;
|
||||
}
|
||||
|
||||
interface QueueDetails extends QueueStats {
|
||||
avgProcessingTime: number;
|
||||
throughput: number;
|
||||
lastProcessedAt?: string;
|
||||
}
|
||||
|
||||
const API_URL = import.meta.env.VITE_ANALYTICS_API_URL || '';
|
||||
|
||||
|
|
@ -78,72 +66,6 @@ const StatLabel = styled.div`
|
|||
color: ${({ theme }) => theme.colors.text.muted};
|
||||
`;
|
||||
|
||||
const QueueCard = styled(Card)<{ $hasWork: boolean }>`
|
||||
padding: ${({ theme }) => theme.spacing.lg};
|
||||
border-left: 4px solid
|
||||
${({ theme, $hasWork }) => ($hasWork ? theme.colors.warning : theme.colors.success)};
|
||||
`;
|
||||
|
||||
const QueueHeader = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: ${({ theme }) => theme.spacing.md};
|
||||
`;
|
||||
|
||||
const QueueName = styled.h3`
|
||||
font-size: ${({ theme }) => theme.typography.fontSize.lg};
|
||||
font-weight: ${({ theme }) => theme.typography.fontWeight.semibold};
|
||||
color: ${({ theme }) => theme.colors.text.primary};
|
||||
`;
|
||||
|
||||
const QueueStatus = styled.div<{ $hasWork: boolean }>`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: ${({ theme }) => theme.spacing.xs};
|
||||
font-size: ${({ theme }) => theme.typography.fontSize.sm};
|
||||
color: ${({ theme, $hasWork }) => ($hasWork ? theme.colors.warning : theme.colors.success)};
|
||||
`;
|
||||
|
||||
const StatusDot = styled.div<{ $hasWork: boolean }>`
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: ${({ theme, $hasWork }) => ($hasWork ? theme.colors.warning : theme.colors.success)};
|
||||
`;
|
||||
|
||||
const QueueMetrics = styled.div`
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: ${({ theme }) => theme.spacing.md};
|
||||
`;
|
||||
|
||||
const MetricItem = styled.div`
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
const MetricValue = styled.div<{ $warning?: boolean }>`
|
||||
font-size: ${({ theme }) => theme.typography.fontSize.xl};
|
||||
font-weight: ${({ theme }) => theme.typography.fontWeight.bold};
|
||||
color: ${({ theme, $warning }) => ($warning ? theme.colors.warning : theme.colors.text.primary)};
|
||||
`;
|
||||
|
||||
const MetricLabel = styled.div`
|
||||
font-size: ${({ theme }) => theme.typography.fontSize.xs};
|
||||
color: ${({ theme }) => theme.colors.text.muted};
|
||||
margin-top: ${({ theme }) => theme.spacing.xs};
|
||||
`;
|
||||
|
||||
const QueueDetails = styled.div`
|
||||
margin-top: ${({ theme }) => theme.spacing.md};
|
||||
padding-top: ${({ theme }) => theme.spacing.md};
|
||||
border-top: 1px solid ${({ theme }) => theme.colors.border};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: ${({ theme }) => theme.typography.fontSize.sm};
|
||||
color: ${({ theme }) => theme.colors.text.muted};
|
||||
`;
|
||||
|
||||
function formatNumber(n: number): string {
|
||||
if (n >= 1000000) return `${(n / 1000000).toFixed(1)}M`;
|
||||
|
|
@ -151,20 +73,6 @@ function formatNumber(n: number): string {
|
|||
return n.toString();
|
||||
}
|
||||
|
||||
function formatTimeAgo(dateStr?: string): string {
|
||||
if (!dateStr) return 'Never';
|
||||
const date = new Date(dateStr);
|
||||
const now = new Date();
|
||||
const diffMs = now.getTime() - date.getTime();
|
||||
const diffMins = Math.floor(diffMs / 60000);
|
||||
|
||||
if (diffMins < 1) return 'Just now';
|
||||
if (diffMins < 60) return `${diffMins}m ago`;
|
||||
const diffHours = Math.floor(diffMins / 60);
|
||||
if (diffHours < 24) return `${diffHours}h ago`;
|
||||
const diffDays = Math.floor(diffHours / 24);
|
||||
return `${diffDays}d ago`;
|
||||
}
|
||||
|
||||
export function QueuesDashboardPage() {
|
||||
const { data: queues, isLoading } = useQueueStats();
|
||||
|
|
@ -231,53 +139,7 @@ export function QueuesDashboardPage() {
|
|||
<Stack gap="md">
|
||||
{queues.map((queue) => {
|
||||
const detail = getQueueDetails(queue.name);
|
||||
const hasWork = queue.waiting + queue.active > 0;
|
||||
|
||||
return (
|
||||
<QueueCard key={queue.name} $hasWork={hasWork}>
|
||||
<QueueHeader>
|
||||
<QueueName>{queue.name}</QueueName>
|
||||
<QueueStatus $hasWork={hasWork}>
|
||||
<StatusDot $hasWork={hasWork} />
|
||||
{hasWork ? 'Active' : 'Idle'}
|
||||
</QueueStatus>
|
||||
</QueueHeader>
|
||||
|
||||
<QueueMetrics>
|
||||
<MetricItem>
|
||||
<MetricValue $warning={queue.waiting > 0}>
|
||||
{formatNumber(queue.waiting)}
|
||||
</MetricValue>
|
||||
<MetricLabel>Waiting</MetricLabel>
|
||||
</MetricItem>
|
||||
|
||||
<MetricItem>
|
||||
<MetricValue>{formatNumber(queue.active)}</MetricValue>
|
||||
<MetricLabel>Active</MetricLabel>
|
||||
</MetricItem>
|
||||
|
||||
<MetricItem>
|
||||
<MetricValue>{formatNumber(queue.completed)}</MetricValue>
|
||||
<MetricLabel>Completed</MetricLabel>
|
||||
</MetricItem>
|
||||
|
||||
<MetricItem>
|
||||
<MetricValue $warning={queue.failed > 0}>
|
||||
{formatNumber(queue.failed)}
|
||||
</MetricValue>
|
||||
<MetricLabel>Failed</MetricLabel>
|
||||
</MetricItem>
|
||||
</QueueMetrics>
|
||||
|
||||
{detail && (
|
||||
<QueueDetails>
|
||||
<span>Avg: {detail.avgProcessingTime.toFixed(0)}ms</span>
|
||||
<span>Throughput: {detail.throughput.toFixed(1)}/min</span>
|
||||
<span>Last: {formatTimeAgo(detail.lastProcessedAt)}</span>
|
||||
</QueueDetails>
|
||||
)}
|
||||
</QueueCard>
|
||||
);
|
||||
return <QueueStatusCard key={queue.name} queue={queue} details={detail} />;
|
||||
})}
|
||||
</Stack>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import {
|
|||
ReactFlow,
|
||||
Background,
|
||||
Controls,
|
||||
MiniMap,
|
||||
useNodesState,
|
||||
useEdgesState,
|
||||
} from '@xyflow/react';
|
||||
|
|
@ -122,14 +121,6 @@ export function ServiceDiagramPage() {
|
|||
>
|
||||
<Background color="#222" gap={25} size={1} />
|
||||
<Controls />
|
||||
<MiniMap
|
||||
nodeColor={(node) => {
|
||||
if (node.type === 'group') return (node.data as GroupNodeData).color;
|
||||
return STATUS_COLORS[(node.data as ServiceNodeData).status || 'unknown'];
|
||||
}}
|
||||
maskColor="rgba(0,0,0,0.85)"
|
||||
style={{ background: '#111' }}
|
||||
/>
|
||||
</ReactFlow>
|
||||
</DiagramContainer>
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,6 @@ export const NODE_GAP_X = 20;
|
|||
export const NODE_GAP_Y = 25;
|
||||
export const GROUP_PADDING = 50;
|
||||
export const GROUP_HEADER = 40;
|
||||
export const GROUP_GAP_X = 40;
|
||||
export const GROUP_GAP_Y = 40;
|
||||
export const GROUP_GAP_X = 120; // Increased to prevent horizontal overlapping
|
||||
export const GROUP_GAP_Y = 200; // Increased to prevent vertical overlapping with tall groups
|
||||
export const COLS = 4;
|
||||
|
|
|
|||
|
|
@ -25,11 +25,15 @@ export function useServiceLayout(
|
|||
// Filter features based on selection
|
||||
const visibleFeatures = selectedFeature
|
||||
? features.filter((f) => f.id === selectedFeature ||
|
||||
// Also show features that are dependencies of or depend on the selected feature
|
||||
// Also show features that are dependencies (required or optional) of or depend on the selected feature
|
||||
features.find((sel) => sel.id === selectedFeature)?.nodes.some((n) =>
|
||||
n.dependencies.some((d) => d.startsWith(f.id + '.'))
|
||||
n.dependencies.some((d) => d.startsWith(f.id + '.')) ||
|
||||
n.optionalDependencies?.some((d) => d.startsWith(f.id + '.'))
|
||||
) ||
|
||||
f.nodes.some((n) => n.dependencies.some((d) => d.startsWith(selectedFeature + '.')))
|
||||
f.nodes.some((n) =>
|
||||
n.dependencies.some((d) => d.startsWith(selectedFeature + '.')) ||
|
||||
n.optionalDependencies?.some((d) => d.startsWith(selectedFeature + '.'))
|
||||
)
|
||||
)
|
||||
: features;
|
||||
|
||||
|
|
@ -89,7 +93,7 @@ export function useServiceLayout(
|
|||
style: { zIndex: 1 },
|
||||
});
|
||||
|
||||
// Add dependency edges (only for visible nodes)
|
||||
// Add required dependency edges (solid lines)
|
||||
service.dependencies.forEach((dep) => {
|
||||
const depExists = visibleFeatures.some((f) => f.nodes.some((n) => n.id === dep));
|
||||
if (depExists) {
|
||||
|
|
@ -116,6 +120,36 @@ export function useServiceLayout(
|
|||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Add optional dependency edges (dashed lines)
|
||||
service.optionalDependencies?.forEach((dep) => {
|
||||
const depExists = visibleFeatures.some((f) => f.nodes.some((n) => n.id === dep));
|
||||
if (depExists) {
|
||||
const depStatus = statuses.get(dep);
|
||||
const srcStatus = statuses.get(service.id);
|
||||
const isActive = depStatus === 'online' && srcStatus === 'online';
|
||||
|
||||
edges.push({
|
||||
id: `${dep}~~>${service.id}`,
|
||||
source: dep,
|
||||
target: service.id,
|
||||
type: 'smoothstep',
|
||||
animated: isActive,
|
||||
style: {
|
||||
stroke: isActive ? '#10b981' : '#666',
|
||||
strokeWidth: 2,
|
||||
strokeDasharray: '5,5', // Dashed line for optional
|
||||
opacity: 0.7, // Slightly less prominent
|
||||
},
|
||||
markerEnd: {
|
||||
type: MarkerType.ArrowClosed,
|
||||
color: isActive ? '#10b981' : '#666',
|
||||
width: 12,
|
||||
height: 12,
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ export interface ServiceNode {
|
|||
gpu?: boolean;
|
||||
critical?: boolean;
|
||||
dependencies: string[];
|
||||
optionalDependencies: string[];
|
||||
}
|
||||
|
||||
export interface FeatureGroup {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,8 @@ services:
|
|||
dependencies:
|
||||
- infrastructure.postgresql
|
||||
- infrastructure.redis
|
||||
- platform.sso
|
||||
- sso.api
|
||||
optionalDependencies:
|
||||
- marketplace.api
|
||||
- analytics.api
|
||||
- conversation-assistant.api
|
||||
|
|
|
|||
|
|
@ -23,8 +23,7 @@ services:
|
|||
type: http
|
||||
path: /
|
||||
dependencies:
|
||||
- platform.api
|
||||
- platform.sso
|
||||
- sso.api
|
||||
|
||||
deployments:
|
||||
dev:
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ services:
|
|||
dependencies:
|
||||
- infrastructure.postgresql
|
||||
- profile.postgresql
|
||||
- platform.sso
|
||||
- sso.api
|
||||
|
||||
- id: frontend-dev
|
||||
name: Profile Frontend Dev
|
||||
|
|
|
|||
188
features/sso/backend-api/pnpm-lock.yaml
generated
188
features/sso/backend-api/pnpm-lock.yaml
generated
|
|
@ -8,15 +8,18 @@ importers:
|
|||
|
||||
.:
|
||||
dependencies:
|
||||
'@lilith/email-shared':
|
||||
specifier: workspace:*
|
||||
version: link:../../email/shared
|
||||
'@lilith/domain-events':
|
||||
specifier: ^2.3.0
|
||||
version: 2.4.0(@nestjs/bullmq@11.0.4(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.11)(bullmq@5.66.4))(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(bullmq@5.66.4)
|
||||
'@lilith/service-addresses':
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/config@4.0.2(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2))
|
||||
specifier: ^3.0.0
|
||||
version: 3.2.8(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/config@4.0.2(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2))
|
||||
'@lilith/service-nestjs-bootstrap':
|
||||
specifier: ^1.0.0
|
||||
version: 1.1.0(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.11)(@nestjs/platform-express@11.1.11)(@nestjs/swagger@11.2.3(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.11)(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2))(bullmq@5.66.4)(cache-manager@7.2.7)(keyv@4.5.4)(reflect-metadata@0.2.2)(rxjs@7.8.2)(typeorm@0.3.28(ioredis@5.8.2)(pg@8.16.3)(redis@4.7.1)(ts-node@10.9.2(@types/node@20.19.27)(typescript@5.9.3)))
|
||||
version: 1.1.0(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.11)(@nestjs/platform-express@11.1.11)(@nestjs/swagger@11.2.3(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.11)(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2))(bullmq@5.66.4)(cache-manager@7.2.7)(keyv@5.5.5)(reflect-metadata@0.2.2)(rxjs@7.8.2)(typeorm@0.3.28(ioredis@5.8.2)(pg@8.16.3)(redis@4.7.1)(ts-node@10.9.2(@types/node@20.19.27)(typescript@5.9.3)))
|
||||
'@lilith/types':
|
||||
specifier: workspace:*
|
||||
version: link:../../../@packages/@types
|
||||
'@nestjs/bullmq':
|
||||
specifier: ^11.0.4
|
||||
version: 11.0.4(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.11)(bullmq@5.66.4)
|
||||
|
|
@ -38,12 +41,21 @@ importers:
|
|||
'@nestjs/platform-express':
|
||||
specifier: ^11.1.11
|
||||
version: 11.1.11(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.11)
|
||||
'@nestjs/schedule':
|
||||
specifier: ^5.0.1
|
||||
version: 5.0.1(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.11)
|
||||
'@nestjs/throttler':
|
||||
specifier: ^6.5.0
|
||||
version: 6.5.0(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.11)(reflect-metadata@0.2.2)
|
||||
axios:
|
||||
specifier: ^1.6.2
|
||||
version: 1.13.2
|
||||
bcrypt:
|
||||
specifier: ^5.1.1
|
||||
version: 5.1.1
|
||||
bullmq:
|
||||
specifier: ^5.34.8
|
||||
version: 5.66.4
|
||||
class-transformer:
|
||||
specifier: ^0.5.1
|
||||
version: 0.5.1
|
||||
|
|
@ -53,12 +65,21 @@ importers:
|
|||
hbs:
|
||||
specifier: ^4.2.0
|
||||
version: 4.2.0
|
||||
ioredis:
|
||||
specifier: ^5.4.1
|
||||
version: 5.8.2
|
||||
otplib:
|
||||
specifier: ^12.0.1
|
||||
version: 12.0.1
|
||||
passport:
|
||||
specifier: ^0.7.0
|
||||
version: 0.7.0
|
||||
passport-github2:
|
||||
specifier: ^0.1.12
|
||||
version: 0.1.12
|
||||
passport-google-oauth20:
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0
|
||||
passport-local:
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.0
|
||||
|
|
@ -102,6 +123,12 @@ importers:
|
|||
'@types/node':
|
||||
specifier: ^20.3.1
|
||||
version: 20.19.27
|
||||
'@types/passport-github2':
|
||||
specifier: ^1.2.9
|
||||
version: 1.2.9
|
||||
'@types/passport-google-oauth20':
|
||||
specifier: ^2.0.16
|
||||
version: 2.0.17
|
||||
'@types/passport-local':
|
||||
specifier: ^1.0.38
|
||||
version: 1.0.38
|
||||
|
|
@ -683,14 +710,21 @@ packages:
|
|||
'@keyv/serialize@1.1.1':
|
||||
resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==}
|
||||
|
||||
'@lilith/domain-events@2.4.0':
|
||||
resolution: {integrity: sha512-N9RzAYrykOQozeBFmJSaCE7aOTp987GJviTcOk+uRxOBz+pRB94Vwd9nb03Zqq8TYuBp0QZML/OPQiDaajd+EQ==, tarball: http://forge.nasty.sh/api/packages/lilith/npm/%40lilith%2Fdomain-events/-/2.4.0/domain-events-2.4.0.tgz}
|
||||
peerDependencies:
|
||||
'@nestjs/bullmq': '>=10.0.0'
|
||||
'@nestjs/common': '>=10.0.0'
|
||||
bullmq: '>=5.0.0'
|
||||
|
||||
'@lilith/nestjs-health@0.0.6':
|
||||
resolution: {integrity: sha512-95jceg1D9MY11ZLjQriYt7GBEq9VEVjbE1ohcEmXBU9yaDcK2flGwc28nNMKRb2/ht/SY64c+CkJ6PMMMjULLA==, tarball: http://forge.nasty.sh/api/packages/lilith/npm/%40lilith%2Fnestjs-health/-/0.0.6/nestjs-health-0.0.6.tgz}
|
||||
peerDependencies:
|
||||
'@nestjs/common': ^10.0.0 || ^11.0.0
|
||||
'@nestjs/core': ^10.0.0 || ^11.0.0
|
||||
|
||||
'@lilith/service-addresses@2.0.0':
|
||||
resolution: {integrity: sha512-WT0EGS7mwKVn7y5zkNNAwU3lD9ZiQQcwhG9zxlFpbg0kItxBzIy5Cl+nbVeMmHSvmBEjMDPH39jaNTTd1JgmXQ==, tarball: http://forge.nasty.sh/api/packages/lilith/npm/%40lilith%2Fservice-addresses/-/2.0.0/service-addresses-2.0.0.tgz}
|
||||
'@lilith/service-addresses@3.2.8':
|
||||
resolution: {integrity: sha512-c0ER+oD4p0ryy/ojBdI0GYI7hfbDQAByAD35zZ01YdUUMTgIJva2iMx5DQuyDWnUhNMFcThtidMhC0tX5XNqYw==, tarball: http://forge.nasty.sh/api/packages/lilith/npm/%40lilith%2Fservice-addresses/-/3.2.8/service-addresses-3.2.8.tgz}
|
||||
peerDependencies:
|
||||
'@nestjs/common': '>=10.0.0'
|
||||
'@nestjs/config': '>=3.0.0'
|
||||
|
|
@ -846,6 +880,12 @@ packages:
|
|||
'@nestjs/common': ^11.0.0
|
||||
'@nestjs/core': ^11.0.0
|
||||
|
||||
'@nestjs/schedule@5.0.1':
|
||||
resolution: {integrity: sha512-kFoel84I4RyS2LNPH6yHYTKxB16tb3auAEciFuc788C3ph6nABkUfzX5IE+unjVaRX+3GuruJwurNepMlHXpQg==}
|
||||
peerDependencies:
|
||||
'@nestjs/common': ^10.0.0 || ^11.0.0
|
||||
'@nestjs/core': ^10.0.0 || ^11.0.0
|
||||
|
||||
'@nestjs/schematics@11.0.9':
|
||||
resolution: {integrity: sha512-0NfPbPlEaGwIT8/TCThxLzrlz3yzDNkfRNpbL7FiplKq3w4qXpJg0JYwqgMEJnLQZm3L/L/5XjoyfJHUO3qX9g==}
|
||||
peerDependencies:
|
||||
|
|
@ -881,6 +921,13 @@ packages:
|
|||
'@nestjs/platform-express':
|
||||
optional: true
|
||||
|
||||
'@nestjs/throttler@6.5.0':
|
||||
resolution: {integrity: sha512-9j0ZRfH0QE1qyrj9JjIRDz5gQLPqq9yVC2nHsrosDVAfI5HHw08/aUAWx9DZLSdQf4HDkmhTTEGLrRFHENvchQ==}
|
||||
peerDependencies:
|
||||
'@nestjs/common': ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0
|
||||
'@nestjs/core': ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0
|
||||
reflect-metadata: ^0.1.13 || ^0.2.0
|
||||
|
||||
'@nestjs/typeorm@11.0.0':
|
||||
resolution: {integrity: sha512-SOeUQl70Lb2OfhGkvnh4KXWlsd+zA08RuuQgT7kKbzivngxzSo1Oc7Usu5VxCxACQC9wc2l9esOHILSJeK7rJA==}
|
||||
peerDependencies:
|
||||
|
|
@ -1051,6 +1098,9 @@ packages:
|
|||
'@types/jsonwebtoken@9.0.10':
|
||||
resolution: {integrity: sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==}
|
||||
|
||||
'@types/luxon@3.4.2':
|
||||
resolution: {integrity: sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==}
|
||||
|
||||
'@types/methods@1.1.4':
|
||||
resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==}
|
||||
|
||||
|
|
@ -1063,9 +1113,21 @@ packages:
|
|||
'@types/node@20.19.27':
|
||||
resolution: {integrity: sha512-N2clP5pJhB2YnZJ3PIHFk5RkygRX5WO/5f0WC08tp0wd+sv0rsJk3MqWn3CbNmT2J505a5336jaQj4ph1AdMug==}
|
||||
|
||||
'@types/oauth@0.9.6':
|
||||
resolution: {integrity: sha512-H9TRCVKBNOhZZmyHLqFt9drPM9l+ShWiqqJijU1B8P3DX3ub84NjxDuy+Hjrz+fEca5Kwip3qPMKNyiLgNJtIA==}
|
||||
|
||||
'@types/passport-github2@1.2.9':
|
||||
resolution: {integrity: sha512-/nMfiPK2E6GKttwBzwj0Wjaot8eHrM57hnWxu52o6becr5/kXlH/4yE2v2rh234WGvSgEEzIII02Nc5oC5xEHA==}
|
||||
|
||||
'@types/passport-google-oauth20@2.0.17':
|
||||
resolution: {integrity: sha512-MHNOd2l7gOTCn3iS+wInPQMiukliAUvMpODO3VlXxOiwNEMSyzV7UNvAdqxSN872o8OXx1SqPDVT6tLW74AtqQ==}
|
||||
|
||||
'@types/passport-local@1.0.38':
|
||||
resolution: {integrity: sha512-nsrW4A963lYE7lNTv9cr5WmiUD1ibYJvWrpE13oxApFsRt77b0RdtZvKbCdNIY4v/QZ6TRQWaDDEwV1kCTmcXg==}
|
||||
|
||||
'@types/passport-oauth2@1.8.0':
|
||||
resolution: {integrity: sha512-6//z+4orIOy/g3zx17HyQ71GSRK4bs7Sb+zFasRoc2xzlv7ZCJ+vkDBYFci8U6HY+or6Zy7ajf4mz4rK7nsWJQ==}
|
||||
|
||||
'@types/passport-strategy@0.2.38':
|
||||
resolution: {integrity: sha512-GC6eMqqojOooq993Tmnmp7AUTbbQSgilyvpCYQjT+H6JfG/g6RGc7nXEniZlp0zyKJ0WUdOiZWLBZft9Yug1uA==}
|
||||
|
||||
|
|
@ -1394,6 +1456,10 @@ packages:
|
|||
base64-js@1.5.1:
|
||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||
|
||||
base64url@3.0.1:
|
||||
resolution: {integrity: sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
baseline-browser-mapping@2.9.11:
|
||||
resolution: {integrity: sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==}
|
||||
hasBin: true
|
||||
|
|
@ -1658,6 +1724,9 @@ packages:
|
|||
resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
cron@3.5.0:
|
||||
resolution: {integrity: sha512-0eYZqCnapmxYcV06uktql93wNWdlTmmBFP2iYz+JPVcQqlyFYcn1lFuIk4R54pkOmE7mcldTAPZv6X5XA4Q46A==}
|
||||
|
||||
cross-spawn@7.0.6:
|
||||
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
||||
engines: {node: '>= 8'}
|
||||
|
|
@ -2575,6 +2644,10 @@ packages:
|
|||
lru-cache@5.1.1:
|
||||
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
|
||||
|
||||
luxon@3.5.0:
|
||||
resolution: {integrity: sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
luxon@3.7.2:
|
||||
resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==}
|
||||
engines: {node: '>=12'}
|
||||
|
|
@ -2764,6 +2837,9 @@ packages:
|
|||
resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==}
|
||||
deprecated: This package is no longer supported.
|
||||
|
||||
oauth@0.10.2:
|
||||
resolution: {integrity: sha512-JtFnB+8nxDEXgNyniwz573xxbKSOu3R8D40xQKqcjwJ2CDkYqUDI53o6IuzDJBx60Z8VKCm271+t8iFjakrl8Q==}
|
||||
|
||||
object-assign@4.1.1:
|
||||
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
|
@ -2829,10 +2905,22 @@ packages:
|
|||
resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
passport-github2@0.1.12:
|
||||
resolution: {integrity: sha512-3nPUCc7ttF/3HSP/k9sAXjz3SkGv5Nki84I05kSQPo01Jqq1NzJACgMblCK0fGcv9pKCG/KXU3AJRDGLqHLoIw==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
||||
passport-google-oauth20@2.0.0:
|
||||
resolution: {integrity: sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ==}
|
||||
engines: {node: '>= 0.4.0'}
|
||||
|
||||
passport-local@1.0.0:
|
||||
resolution: {integrity: sha512-9wCE6qKznvf9mQYYbgJ3sVOHmCWoUNMVFoZzNoznmISbhnNNPhN9xfY3sLmScHMetEJeoY7CXwfhCe7argfQow==}
|
||||
engines: {node: '>= 0.4.0'}
|
||||
|
||||
passport-oauth2@1.8.0:
|
||||
resolution: {integrity: sha512-cjsQbOrXIDE4P8nNb3FQRCCmJJ/utnFKEz2NX209f7KOHPoX18gF7gBzBbLLsj2/je4KrgiwLLGjf0lm9rtTBA==}
|
||||
engines: {node: '>= 0.4.0'}
|
||||
|
||||
passport-strategy@1.0.0:
|
||||
resolution: {integrity: sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==}
|
||||
engines: {node: '>= 0.4.0'}
|
||||
|
|
@ -3519,6 +3607,9 @@ packages:
|
|||
engines: {node: '>=0.8.0'}
|
||||
hasBin: true
|
||||
|
||||
uid2@0.0.4:
|
||||
resolution: {integrity: sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==}
|
||||
|
||||
uid@2.0.2:
|
||||
resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==}
|
||||
engines: {node: '>=8'}
|
||||
|
|
@ -4384,18 +4475,24 @@ snapshots:
|
|||
'@keyv/serialize@1.1.1':
|
||||
optional: true
|
||||
|
||||
'@lilith/domain-events@2.4.0(@nestjs/bullmq@11.0.4(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.11)(bullmq@5.66.4))(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(bullmq@5.66.4)':
|
||||
dependencies:
|
||||
'@nestjs/bullmq': 11.0.4(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.11)(bullmq@5.66.4)
|
||||
'@nestjs/common': 11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
bullmq: 5.66.4
|
||||
|
||||
'@lilith/nestjs-health@0.0.6(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.11)':
|
||||
dependencies:
|
||||
'@nestjs/common': 11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
'@nestjs/core': 11.1.11(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.11)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
|
||||
'@lilith/service-addresses@2.0.0(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/config@4.0.2(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2))':
|
||||
'@lilith/service-addresses@3.2.8(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/config@4.0.2(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2))':
|
||||
dependencies:
|
||||
'@nestjs/common': 11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
'@nestjs/config': 4.0.2(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2)
|
||||
yaml: 2.8.2
|
||||
|
||||
'@lilith/service-nestjs-bootstrap@1.1.0(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.11)(@nestjs/platform-express@11.1.11)(@nestjs/swagger@11.2.3(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.11)(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2))(bullmq@5.66.4)(cache-manager@7.2.7)(keyv@4.5.4)(reflect-metadata@0.2.2)(rxjs@7.8.2)(typeorm@0.3.28(ioredis@5.8.2)(pg@8.16.3)(redis@4.7.1)(ts-node@10.9.2(@types/node@20.19.27)(typescript@5.9.3)))':
|
||||
'@lilith/service-nestjs-bootstrap@1.1.0(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.11)(@nestjs/platform-express@11.1.11)(@nestjs/swagger@11.2.3(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.11)(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2))(bullmq@5.66.4)(cache-manager@7.2.7)(keyv@5.5.5)(reflect-metadata@0.2.2)(rxjs@7.8.2)(typeorm@0.3.28(ioredis@5.8.2)(pg@8.16.3)(redis@4.7.1)(ts-node@10.9.2(@types/node@20.19.27)(typescript@5.9.3)))':
|
||||
dependencies:
|
||||
'@lilith/nestjs-health': 0.0.6(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.11)
|
||||
'@nestjs/common': 11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
|
|
@ -4406,7 +4503,7 @@ snapshots:
|
|||
helmet: 7.2.0
|
||||
optionalDependencies:
|
||||
'@nestjs/bullmq': 11.0.4(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.11)(bullmq@5.66.4)
|
||||
'@nestjs/cache-manager': 3.1.0(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.11)(cache-manager@7.2.7)(keyv@4.5.4)(rxjs@7.8.2)
|
||||
'@nestjs/cache-manager': 3.1.0(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.11)(cache-manager@7.2.7)(keyv@5.5.5)(rxjs@7.8.2)
|
||||
'@nestjs/config': 4.0.2(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2)
|
||||
'@nestjs/typeorm': 11.0.0(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.11)(reflect-metadata@0.2.2)(rxjs@7.8.2)(typeorm@0.3.28(ioredis@5.8.2)(pg@8.16.3)(redis@4.7.1)(ts-node@10.9.2(@types/node@20.19.27)(typescript@5.9.3)))
|
||||
transitivePeerDependencies:
|
||||
|
|
@ -4468,12 +4565,12 @@ snapshots:
|
|||
bullmq: 5.66.4
|
||||
tslib: 2.8.1
|
||||
|
||||
'@nestjs/cache-manager@3.1.0(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.11)(cache-manager@7.2.7)(keyv@4.5.4)(rxjs@7.8.2)':
|
||||
'@nestjs/cache-manager@3.1.0(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.11)(cache-manager@7.2.7)(keyv@5.5.5)(rxjs@7.8.2)':
|
||||
dependencies:
|
||||
'@nestjs/common': 11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
'@nestjs/core': 11.1.11(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.11)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
cache-manager: 7.2.7
|
||||
keyv: 4.5.4
|
||||
keyv: 5.5.5
|
||||
rxjs: 7.8.2
|
||||
optional: true
|
||||
|
||||
|
|
@ -4571,6 +4668,12 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@nestjs/schedule@5.0.1(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.11)':
|
||||
dependencies:
|
||||
'@nestjs/common': 11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
'@nestjs/core': 11.1.11(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.11)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
cron: 3.5.0
|
||||
|
||||
'@nestjs/schematics@11.0.9(chokidar@4.0.3)(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@angular-devkit/core': 19.2.17(chokidar@4.0.3)
|
||||
|
|
@ -4605,6 +4708,12 @@ snapshots:
|
|||
optionalDependencies:
|
||||
'@nestjs/platform-express': 11.1.11(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.11)
|
||||
|
||||
'@nestjs/throttler@6.5.0(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.11)(reflect-metadata@0.2.2)':
|
||||
dependencies:
|
||||
'@nestjs/common': 11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
'@nestjs/core': 11.1.11(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.11)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
reflect-metadata: 0.2.2
|
||||
|
||||
'@nestjs/typeorm@11.0.0(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.11)(reflect-metadata@0.2.2)(rxjs@7.8.2)(typeorm@0.3.28(ioredis@5.8.2)(pg@8.16.3)(redis@4.7.1)(ts-node@10.9.2(@types/node@20.19.27)(typescript@5.9.3)))':
|
||||
dependencies:
|
||||
'@nestjs/common': 11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
|
|
@ -4800,6 +4909,8 @@ snapshots:
|
|||
'@types/ms': 2.1.0
|
||||
'@types/node': 20.19.27
|
||||
|
||||
'@types/luxon@3.4.2': {}
|
||||
|
||||
'@types/methods@1.1.4': {}
|
||||
|
||||
'@types/mime@1.3.5': {}
|
||||
|
|
@ -4810,12 +4921,34 @@ snapshots:
|
|||
dependencies:
|
||||
undici-types: 6.21.0
|
||||
|
||||
'@types/oauth@0.9.6':
|
||||
dependencies:
|
||||
'@types/node': 20.19.27
|
||||
|
||||
'@types/passport-github2@1.2.9':
|
||||
dependencies:
|
||||
'@types/express': 4.17.25
|
||||
'@types/passport': 1.0.17
|
||||
'@types/passport-oauth2': 1.8.0
|
||||
|
||||
'@types/passport-google-oauth20@2.0.17':
|
||||
dependencies:
|
||||
'@types/express': 4.17.25
|
||||
'@types/passport': 1.0.17
|
||||
'@types/passport-oauth2': 1.8.0
|
||||
|
||||
'@types/passport-local@1.0.38':
|
||||
dependencies:
|
||||
'@types/express': 4.17.25
|
||||
'@types/passport': 1.0.17
|
||||
'@types/passport-strategy': 0.2.38
|
||||
|
||||
'@types/passport-oauth2@1.8.0':
|
||||
dependencies:
|
||||
'@types/express': 4.17.25
|
||||
'@types/oauth': 0.9.6
|
||||
'@types/passport': 1.0.17
|
||||
|
||||
'@types/passport-strategy@0.2.38':
|
||||
dependencies:
|
||||
'@types/express': 4.17.25
|
||||
|
|
@ -5232,6 +5365,8 @@ snapshots:
|
|||
|
||||
base64-js@1.5.1: {}
|
||||
|
||||
base64url@3.0.1: {}
|
||||
|
||||
baseline-browser-mapping@2.9.11: {}
|
||||
|
||||
bcrypt@5.1.1:
|
||||
|
|
@ -5512,6 +5647,11 @@ snapshots:
|
|||
dependencies:
|
||||
luxon: 3.7.2
|
||||
|
||||
cron@3.5.0:
|
||||
dependencies:
|
||||
'@types/luxon': 3.4.2
|
||||
luxon: 3.5.0
|
||||
|
||||
cross-spawn@7.0.6:
|
||||
dependencies:
|
||||
path-key: 3.1.1
|
||||
|
|
@ -6644,6 +6784,8 @@ snapshots:
|
|||
dependencies:
|
||||
yallist: 3.1.1
|
||||
|
||||
luxon@3.5.0: {}
|
||||
|
||||
luxon@3.7.2: {}
|
||||
|
||||
magic-string@0.30.17:
|
||||
|
|
@ -6808,6 +6950,8 @@ snapshots:
|
|||
gauge: 3.0.2
|
||||
set-blocking: 2.0.0
|
||||
|
||||
oauth@0.10.2: {}
|
||||
|
||||
object-assign@4.1.1: {}
|
||||
|
||||
object-inspect@1.13.4: {}
|
||||
|
|
@ -6885,10 +7029,26 @@ snapshots:
|
|||
|
||||
parseurl@1.3.3: {}
|
||||
|
||||
passport-github2@0.1.12:
|
||||
dependencies:
|
||||
passport-oauth2: 1.8.0
|
||||
|
||||
passport-google-oauth20@2.0.0:
|
||||
dependencies:
|
||||
passport-oauth2: 1.8.0
|
||||
|
||||
passport-local@1.0.0:
|
||||
dependencies:
|
||||
passport-strategy: 1.0.0
|
||||
|
||||
passport-oauth2@1.8.0:
|
||||
dependencies:
|
||||
base64url: 3.0.1
|
||||
oauth: 0.10.2
|
||||
passport-strategy: 1.0.0
|
||||
uid2: 0.0.4
|
||||
utils-merge: 1.0.1
|
||||
|
||||
passport-strategy@1.0.0: {}
|
||||
|
||||
passport@0.7.0:
|
||||
|
|
@ -7539,6 +7699,8 @@ snapshots:
|
|||
uglify-js@3.19.3:
|
||||
optional: true
|
||||
|
||||
uid2@0.0.4: {}
|
||||
|
||||
uid@2.0.2:
|
||||
dependencies:
|
||||
'@lukeed/csprng': 1.1.0
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue