feat(05-03): calendar backend — model, crypto, source CRUD module
- Add CalendarSource Prisma model with encrypted credentials (AES-256-GCM) - Create CalendarCryptoService with encrypt/decrypt using CALENDAR_ENCRYPTION_KEY - Create CalendarController with source CRUD endpoints (GET/POST/PATCH/DELETE) - Create CalendarService with ownership checks and SSRF URL validation - Add DTOs with https-only URL validation and class-validator decorators - Register CalendarModule in AppModule - Install tsdav, node-ical, ews-javascript-api, @microsoft/microsoft-graph-client - Stub provider files for Task 2 compilation
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
"type-check": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/microsoft-graph-client": "3.0.7",
|
||||
"@nestjs-modules/mailer": "^2.3.7",
|
||||
"@nestjs/common": "^11.0.0",
|
||||
"@nestjs/config": "^4.0.0",
|
||||
@@ -24,13 +25,16 @@
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.15.1",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"ews-javascript-api": "0.15.3",
|
||||
"ldapts": "^8.1.8",
|
||||
"node-ical": "0.26.1",
|
||||
"nodemailer": "^9.0.1",
|
||||
"passport": "^0.7.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"passport-local": "^1.0.0",
|
||||
"reflect-metadata": "^0.2.0",
|
||||
"rxjs": "^7.0.0"
|
||||
"rxjs": "^7.0.0",
|
||||
"tsdav": "2.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^11.0.0",
|
||||
|
||||
@@ -146,3 +146,25 @@ model SearchProvider {
|
||||
|
||||
@@index([userId])
|
||||
}
|
||||
|
||||
model CalendarSource {
|
||||
id String @id @default(uuid())
|
||||
userId String
|
||||
tenantId String
|
||||
name String
|
||||
type String // 'caldav' | 'ics' | 'exchange'
|
||||
exchangeMode String? // 'ews' | 'graph' — only for exchange type
|
||||
url String
|
||||
username String?
|
||||
encryptedPassword String? // AES-256-GCM ciphertext (iv:authTag:ciphertext hex)
|
||||
color String? @default("#3B82F6")
|
||||
isVisible Boolean @default(true)
|
||||
syncIntervalMin Int @default(15)
|
||||
lastSyncAt DateTime?
|
||||
lastSyncError String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([userId])
|
||||
@@index([tenantId])
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { ForcePasswordChangeInterceptor } from './auth/interceptors/force-passwo
|
||||
import { HealthModule } from './health/health.module';
|
||||
import { LdapModule } from './ldap/ldap.module';
|
||||
import { MailModule } from './mail/mail.module';
|
||||
import { CalendarModule } from './calendar/calendar.module';
|
||||
import { DashboardModule } from './dashboard/dashboard.module';
|
||||
import { DomaincheckModule } from './domaincheck/domaincheck.module';
|
||||
import { ModuleRegistryModule } from './module-registry/module-registry.module';
|
||||
@@ -29,6 +30,7 @@ import { UserModule } from './user/user.module';
|
||||
ModuleRegistryModule,
|
||||
DomaincheckModule,
|
||||
DashboardModule,
|
||||
CalendarModule,
|
||||
],
|
||||
providers: [
|
||||
// Global JWT guard: all routes require auth unless @Public()
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
ForbiddenException,
|
||||
Get,
|
||||
Param,
|
||||
Patch,
|
||||
Post,
|
||||
Query,
|
||||
Req,
|
||||
} from '@nestjs/common';
|
||||
import { Request } from 'express';
|
||||
import { CalendarService } from './calendar.service';
|
||||
import { CreateCalendarSourceDto } from './dto/create-calendar-source.dto';
|
||||
import { UpdateCalendarSourceDto } from './dto/update-calendar-source.dto';
|
||||
import { CalendarEventsQueryDto } from './dto/calendar-events-query.dto';
|
||||
|
||||
/**
|
||||
* REST controller for calendar source management and event aggregation.
|
||||
*
|
||||
* All endpoints require JWT auth (global JwtAuthGuard).
|
||||
* Every handler extracts userId and tenantId from the request
|
||||
* and scopes all operations to the calling user (T-05-12).
|
||||
*
|
||||
* Routes:
|
||||
* - GET /calendar/sources — list user's calendar sources (no passwords)
|
||||
* - POST /calendar/sources — add a new calendar source
|
||||
* - PATCH /calendar/sources/:id — update a calendar source
|
||||
* - DELETE /calendar/sources/:id — remove a calendar source
|
||||
* - POST /calendar/sources/:id/test — test connection to a source
|
||||
* - GET /calendar/events — aggregated events from visible sources
|
||||
*/
|
||||
@Controller('calendar')
|
||||
export class CalendarController {
|
||||
constructor(private readonly calendarService: CalendarService) {}
|
||||
|
||||
private extractContext(req: Request) {
|
||||
const userId = (req as any).user?.id;
|
||||
const tenantId =
|
||||
(req as any).tenantId ?? (req as any).user?.tenantId;
|
||||
|
||||
if (!tenantId) {
|
||||
throw new ForbiddenException('No tenant context');
|
||||
}
|
||||
if (!userId) {
|
||||
throw new ForbiddenException('No user context');
|
||||
}
|
||||
|
||||
return { userId, tenantId };
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /calendar/sources
|
||||
* Returns all calendar sources for the user WITHOUT encryptedPassword.
|
||||
* T-05-09: Uses Prisma select to exclude credentials; returns hasCredentials boolean.
|
||||
*/
|
||||
@Get('sources')
|
||||
async getSources(@Req() req: Request) {
|
||||
const { userId } = this.extractContext(req);
|
||||
return this.calendarService.getSources(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /calendar/sources
|
||||
* Creates a new calendar source. Password is encrypted before storage.
|
||||
* T-05-11: URL validated as https-only + SSRF private IP check.
|
||||
*/
|
||||
@Post('sources')
|
||||
async addSource(
|
||||
@Req() req: Request,
|
||||
@Body() dto: CreateCalendarSourceDto,
|
||||
) {
|
||||
const { userId, tenantId } = this.extractContext(req);
|
||||
return this.calendarService.addSource(userId, tenantId, dto);
|
||||
}
|
||||
|
||||
/**
|
||||
* PATCH /calendar/sources/:id
|
||||
* Updates an existing calendar source. Ownership verified.
|
||||
* CAL-02: isVisible toggle is part of this DTO.
|
||||
*/
|
||||
@Patch('sources/:id')
|
||||
async updateSource(
|
||||
@Param('id') id: string,
|
||||
@Req() req: Request,
|
||||
@Body() dto: UpdateCalendarSourceDto,
|
||||
) {
|
||||
const { userId } = this.extractContext(req);
|
||||
return this.calendarService.updateSource(id, userId, dto);
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE /calendar/sources/:id
|
||||
* Removes a calendar source. Ownership verified (T-05-12).
|
||||
*/
|
||||
@Delete('sources/:id')
|
||||
async deleteSource(
|
||||
@Param('id') id: string,
|
||||
@Req() req: Request,
|
||||
) {
|
||||
const { userId } = this.extractContext(req);
|
||||
return this.calendarService.deleteSource(id, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /calendar/sources/:id/test
|
||||
* Tests connection to a calendar source. Implemented in Task 3.
|
||||
*/
|
||||
@Post('sources/:id/test')
|
||||
async testSource(
|
||||
@Param('id') id: string,
|
||||
@Req() req: Request,
|
||||
) {
|
||||
const { userId } = this.extractContext(req);
|
||||
return this.calendarService.testConnection(id, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /calendar/events
|
||||
* Returns aggregated events from all visible sources. Implemented in Task 3.
|
||||
*/
|
||||
@Get('events')
|
||||
async getEvents(
|
||||
@Req() req: Request,
|
||||
@Query() query: CalendarEventsQueryDto,
|
||||
) {
|
||||
const { userId } = this.extractContext(req);
|
||||
return this.calendarService.aggregateEvents(userId, query.from, query.to);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { CalendarController } from './calendar.controller';
|
||||
import { CalendarService } from './calendar.service';
|
||||
import { CalendarCryptoService } from './crypto.service';
|
||||
import { CalDAVProvider } from './providers/caldav.provider';
|
||||
import { ICSProvider } from './providers/ics.provider';
|
||||
import { ExchangeProvider } from './providers/exchange.provider';
|
||||
|
||||
/**
|
||||
* NestJS module for calendar source management and event aggregation.
|
||||
*
|
||||
* Provides:
|
||||
* - CalendarCryptoService: AES-256-GCM encryption for calendar credentials
|
||||
* - CalendarService: Source CRUD + event aggregation across providers
|
||||
* - CalDAVProvider: CalDAV protocol integration via tsdav
|
||||
* - ICSProvider: ICS file fetch + parse via node-ical
|
||||
* - ExchangeProvider: Exchange Online (Graph) + Exchange Server (EWS) integration
|
||||
* - CalendarController: REST API for sources and events
|
||||
*
|
||||
* Exports CalendarService for potential use by other modules.
|
||||
*/
|
||||
@Module({
|
||||
controllers: [CalendarController],
|
||||
providers: [
|
||||
CalendarService,
|
||||
CalendarCryptoService,
|
||||
CalDAVProvider,
|
||||
ICSProvider,
|
||||
ExchangeProvider,
|
||||
],
|
||||
exports: [CalendarService],
|
||||
})
|
||||
export class CalendarModule {}
|
||||
@@ -0,0 +1,281 @@
|
||||
import {
|
||||
ForbiddenException,
|
||||
Injectable,
|
||||
Logger,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { CalendarCryptoService } from './crypto.service';
|
||||
import { CreateCalendarSourceDto } from './dto/create-calendar-source.dto';
|
||||
import { UpdateCalendarSourceDto } from './dto/update-calendar-source.dto';
|
||||
|
||||
/**
|
||||
* Common interface for normalized calendar events across all provider types.
|
||||
*/
|
||||
export interface CalendarEvent {
|
||||
id: string;
|
||||
sourceId: string;
|
||||
title: string;
|
||||
start: Date;
|
||||
end: Date;
|
||||
allDay: boolean;
|
||||
location?: string;
|
||||
description?: string;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provider interface for calendar source integrations.
|
||||
* Each provider (ICS, CalDAV, Exchange) implements this contract.
|
||||
*/
|
||||
export interface CalendarProvider {
|
||||
fetchEvents(
|
||||
source: { url: string; username?: string; password?: string; exchangeMode?: string | null; id: string; color?: string | null },
|
||||
from: Date,
|
||||
to: Date,
|
||||
): Promise<CalendarEvent[]>;
|
||||
|
||||
testConnection(
|
||||
source: { url: string; username?: string; password?: string; exchangeMode?: string | null; id: string },
|
||||
): Promise<boolean>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prisma `select` for safe source responses — NEVER includes encryptedPassword.
|
||||
* T-05-09: Return hasCredentials boolean instead.
|
||||
*/
|
||||
const SOURCE_SAFE_SELECT = {
|
||||
id: true,
|
||||
userId: true,
|
||||
tenantId: true,
|
||||
name: true,
|
||||
type: true,
|
||||
exchangeMode: true,
|
||||
url: true,
|
||||
username: true,
|
||||
// encryptedPassword: NEVER included — T-05-09
|
||||
color: true,
|
||||
isVisible: true,
|
||||
syncIntervalMin: true,
|
||||
lastSyncAt: true,
|
||||
lastSyncError: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Private IP ranges for SSRF protection (T-05-11).
|
||||
*/
|
||||
const PRIVATE_IP_PATTERNS = [
|
||||
/^10\.\d{1,3}\.\d{1,3}\.\d{1,3}$/,
|
||||
/^192\.168\.\d{1,3}\.\d{1,3}$/,
|
||||
/^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/,
|
||||
/^169\.254\.\d{1,3}\.\d{1,3}$/,
|
||||
/^172\.(1[6-9]|2\d|3[0-1])\.\d{1,3}\.\d{1,3}$/,
|
||||
/^0\.0\.0\.0$/,
|
||||
/^\[::1\]$/,
|
||||
/^\[fc00:/,
|
||||
/^\[fd00:/,
|
||||
/^\[fe80:/,
|
||||
];
|
||||
|
||||
/**
|
||||
* Service for calendar source CRUD and event aggregation.
|
||||
*
|
||||
* Source config is per-user (D-09), not per-tenant.
|
||||
* Credentials encrypted at rest via CalendarCryptoService (T-05-10).
|
||||
*/
|
||||
@Injectable()
|
||||
export class CalendarService {
|
||||
private readonly logger = new Logger(CalendarService.name);
|
||||
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly crypto: CalendarCryptoService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Returns all calendar sources for a user WITHOUT encryptedPassword.
|
||||
* Adds a `hasCredentials` boolean so the UI knows if credentials are set.
|
||||
*/
|
||||
async getSources(userId: string) {
|
||||
const sources = await this.prisma.calendarSource.findMany({
|
||||
where: { userId },
|
||||
select: {
|
||||
...SOURCE_SAFE_SELECT,
|
||||
encryptedPassword: true, // Need it only to derive hasCredentials
|
||||
},
|
||||
orderBy: { createdAt: 'asc' },
|
||||
});
|
||||
|
||||
return sources.map(({ encryptedPassword, ...source }) => ({
|
||||
...source,
|
||||
hasCredentials: !!encryptedPassword,
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new calendar source. Encrypts password before storage.
|
||||
* T-05-11: Validates URL against private IP ranges (SSRF).
|
||||
*/
|
||||
async addSource(userId: string, tenantId: string, dto: CreateCalendarSourceDto) {
|
||||
await this.validateUrlNotPrivate(dto.url);
|
||||
|
||||
const data: Record<string, unknown> = {
|
||||
userId,
|
||||
tenantId,
|
||||
name: dto.name,
|
||||
type: dto.type,
|
||||
url: dto.url,
|
||||
username: dto.username ?? null,
|
||||
exchangeMode: dto.exchangeMode ?? null,
|
||||
color: dto.color ?? '#3B82F6',
|
||||
};
|
||||
|
||||
if (dto.password) {
|
||||
data.encryptedPassword = this.crypto.encrypt(dto.password);
|
||||
}
|
||||
|
||||
const created = await this.prisma.calendarSource.create({
|
||||
data: data as any,
|
||||
select: SOURCE_SAFE_SELECT,
|
||||
});
|
||||
|
||||
return { ...created, hasCredentials: !!dto.password };
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a calendar source. Ownership check ensures user can only modify their own sources.
|
||||
* Re-encrypts password if provided; T-05-12 ownership enforcement.
|
||||
*/
|
||||
async updateSource(id: string, userId: string, dto: UpdateCalendarSourceDto) {
|
||||
const existing = await this.prisma.calendarSource.findUnique({
|
||||
where: { id },
|
||||
select: { userId: true },
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
throw new NotFoundException('Calendar source not found');
|
||||
}
|
||||
if (existing.userId !== userId) {
|
||||
throw new ForbiddenException('Not your calendar source');
|
||||
}
|
||||
|
||||
if (dto.url) {
|
||||
await this.validateUrlNotPrivate(dto.url);
|
||||
}
|
||||
|
||||
const data: Record<string, unknown> = {};
|
||||
if (dto.name !== undefined) data.name = dto.name;
|
||||
if (dto.type !== undefined) data.type = dto.type;
|
||||
if (dto.url !== undefined) data.url = dto.url;
|
||||
if (dto.username !== undefined) data.username = dto.username;
|
||||
if (dto.exchangeMode !== undefined) data.exchangeMode = dto.exchangeMode;
|
||||
if (dto.color !== undefined) data.color = dto.color;
|
||||
if (dto.isVisible !== undefined) data.isVisible = dto.isVisible;
|
||||
|
||||
if (dto.password !== undefined) {
|
||||
data.encryptedPassword = dto.password
|
||||
? this.crypto.encrypt(dto.password)
|
||||
: null;
|
||||
}
|
||||
|
||||
const updated = await this.prisma.calendarSource.update({
|
||||
where: { id },
|
||||
data: data as any,
|
||||
select: {
|
||||
...SOURCE_SAFE_SELECT,
|
||||
encryptedPassword: true,
|
||||
},
|
||||
});
|
||||
|
||||
const { encryptedPassword, ...safe } = updated;
|
||||
return { ...safe, hasCredentials: !!encryptedPassword };
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a calendar source. Ownership check enforced (T-05-12).
|
||||
*/
|
||||
async deleteSource(id: string, userId: string) {
|
||||
const existing = await this.prisma.calendarSource.findUnique({
|
||||
where: { id },
|
||||
select: { userId: true },
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
throw new NotFoundException('Calendar source not found');
|
||||
}
|
||||
if (existing.userId !== userId) {
|
||||
throw new ForbiddenException('Not your calendar source');
|
||||
}
|
||||
|
||||
await this.prisma.calendarSource.delete({ where: { id } });
|
||||
return { deleted: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* Test connection to a calendar source via its provider.
|
||||
* Stub — full implementation in Task 3.
|
||||
*/
|
||||
async testConnection(id: string, userId: string): Promise<{ success: boolean; error?: string }> {
|
||||
const source = await this.prisma.calendarSource.findUnique({ where: { id } });
|
||||
if (!source) throw new NotFoundException('Calendar source not found');
|
||||
if (source.userId !== userId) throw new ForbiddenException('Not your calendar source');
|
||||
return { success: false, error: 'Not yet implemented' };
|
||||
}
|
||||
|
||||
/**
|
||||
* Aggregate events from all visible sources for a user.
|
||||
* Stub — full implementation in Task 3.
|
||||
*/
|
||||
async aggregateEvents(
|
||||
userId: string,
|
||||
from?: string,
|
||||
to?: string,
|
||||
): Promise<CalendarEvent[]> {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts stored credentials for a source (used internally by providers).
|
||||
* NEVER expose this in API responses.
|
||||
*/
|
||||
decryptSourcePassword(encryptedPassword: string): string {
|
||||
return this.crypto.decrypt(encryptedPassword);
|
||||
}
|
||||
|
||||
/**
|
||||
* SSRF protection: reject URLs that resolve to private IP ranges.
|
||||
* T-05-11: Combined with https-only DTO validation.
|
||||
*/
|
||||
private async validateUrlNotPrivate(url: string): Promise<void> {
|
||||
try {
|
||||
const parsed = new URL(url);
|
||||
const hostname = parsed.hostname;
|
||||
|
||||
// Check against private IP patterns
|
||||
for (const pattern of PRIVATE_IP_PATTERNS) {
|
||||
if (pattern.test(hostname)) {
|
||||
throw new ForbiddenException(
|
||||
'Calendar source URL must not point to private/internal networks',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Also block localhost variants
|
||||
if (
|
||||
hostname === 'localhost' ||
|
||||
hostname === 'ip6-localhost' ||
|
||||
hostname.endsWith('.local') ||
|
||||
hostname.endsWith('.internal')
|
||||
) {
|
||||
throw new ForbiddenException(
|
||||
'Calendar source URL must not point to localhost or local networks',
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof ForbiddenException) throw error;
|
||||
throw new ForbiddenException('Invalid calendar source URL');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import { Injectable, OnModuleInit } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { createCipheriv, createDecipheriv, randomBytes } from 'crypto';
|
||||
|
||||
/**
|
||||
* Encryption service for calendar source credentials.
|
||||
*
|
||||
* Uses AES-256-GCM with a 32-byte hex key from CALENDAR_ENCRYPTION_KEY env var.
|
||||
* Encrypted values are stored as `iv:authTag:ciphertext` (hex-joined).
|
||||
*
|
||||
* Security: T-05-10 — credentials encrypted at rest, never returned in GET responses.
|
||||
*/
|
||||
@Injectable()
|
||||
export class CalendarCryptoService implements OnModuleInit {
|
||||
private key!: Buffer;
|
||||
|
||||
constructor(private readonly configService: ConfigService) {}
|
||||
|
||||
onModuleInit() {
|
||||
const hexKey = this.configService.get<string>('CALENDAR_ENCRYPTION_KEY');
|
||||
|
||||
if (!hexKey) {
|
||||
throw new Error(
|
||||
'CALENDAR_ENCRYPTION_KEY is not set. Generate one with: openssl rand -hex 32',
|
||||
);
|
||||
}
|
||||
|
||||
if (hexKey.length !== 64) {
|
||||
throw new Error(
|
||||
`CALENDAR_ENCRYPTION_KEY must be a 64-character hex string (32 bytes). Got ${hexKey.length} characters.`,
|
||||
);
|
||||
}
|
||||
|
||||
this.key = Buffer.from(hexKey, 'hex');
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts plaintext using AES-256-GCM.
|
||||
* @returns `iv:authTag:ciphertext` (all hex-encoded, colon-separated)
|
||||
*/
|
||||
encrypt(plaintext: string): string {
|
||||
const iv = randomBytes(12); // 96-bit IV for GCM
|
||||
const cipher = createCipheriv('aes-256-gcm', this.key, iv);
|
||||
|
||||
let encrypted = cipher.update(plaintext, 'utf8', 'hex');
|
||||
encrypted += cipher.final('hex');
|
||||
|
||||
const authTag = cipher.getAuthTag().toString('hex');
|
||||
|
||||
return `${iv.toString('hex')}:${authTag}:${encrypted}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts a stored `iv:authTag:ciphertext` value.
|
||||
* @returns The original plaintext
|
||||
*/
|
||||
decrypt(stored: string): string {
|
||||
const parts = stored.split(':');
|
||||
if (parts.length !== 3) {
|
||||
throw new Error('Invalid encrypted value format. Expected iv:authTag:ciphertext');
|
||||
}
|
||||
|
||||
const [ivHex, authTagHex, ciphertext] = parts;
|
||||
const iv = Buffer.from(ivHex, 'hex');
|
||||
const authTag = Buffer.from(authTagHex, 'hex');
|
||||
|
||||
const decipher = createDecipheriv('aes-256-gcm', this.key, iv);
|
||||
decipher.setAuthTag(authTag);
|
||||
|
||||
let decrypted = decipher.update(ciphertext, 'hex', 'utf8');
|
||||
decrypted += decipher.final('utf8');
|
||||
|
||||
return decrypted;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { IsDateString, IsOptional } from 'class-validator';
|
||||
|
||||
/**
|
||||
* DTO for querying aggregated calendar events.
|
||||
*
|
||||
* Default window: now to now + 30 days (applied in CalendarService).
|
||||
*/
|
||||
export class CalendarEventsQueryDto {
|
||||
@IsOptional()
|
||||
@IsDateString()
|
||||
from?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsDateString()
|
||||
to?: string;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import {
|
||||
IsBoolean,
|
||||
IsHexColor,
|
||||
IsIn,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsString,
|
||||
IsUrl,
|
||||
} from 'class-validator';
|
||||
|
||||
/**
|
||||
* DTO for creating a new calendar source.
|
||||
*
|
||||
* Security:
|
||||
* - T-05-11: URL restricted to https only (SSRF mitigation)
|
||||
* - T-05-10: password is plaintext in transit (over TLS), encrypted at rest by CryptoService
|
||||
*/
|
||||
export class CreateCalendarSourceDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
name!: string;
|
||||
|
||||
@IsIn(['caldav', 'ics', 'exchange'])
|
||||
type!: string;
|
||||
|
||||
@IsUrl(
|
||||
{ protocols: ['https'], require_protocol: true },
|
||||
{ message: 'URL must use HTTPS protocol' },
|
||||
)
|
||||
url!: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
username?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
password?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsIn(['ews', 'graph'])
|
||||
exchangeMode?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsHexColor()
|
||||
color?: string;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import {
|
||||
IsBoolean,
|
||||
IsHexColor,
|
||||
IsIn,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsString,
|
||||
IsUrl,
|
||||
} from 'class-validator';
|
||||
|
||||
/**
|
||||
* DTO for updating an existing calendar source.
|
||||
* All fields are optional — only provided fields are updated.
|
||||
*
|
||||
* Security:
|
||||
* - T-05-11: URL restricted to https only if provided
|
||||
* - CAL-02: isVisible toggle for widget source visibility
|
||||
*/
|
||||
export class UpdateCalendarSourceDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
name?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsIn(['caldav', 'ics', 'exchange'])
|
||||
type?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsUrl(
|
||||
{ protocols: ['https'], require_protocol: true },
|
||||
{ message: 'URL must use HTTPS protocol' },
|
||||
)
|
||||
url?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
username?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
password?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsIn(['ews', 'graph'])
|
||||
exchangeMode?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsHexColor()
|
||||
color?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
isVisible?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { CalendarEvent, CalendarProvider } from '../calendar.service';
|
||||
|
||||
/**
|
||||
* CalDAV calendar provider — uses tsdav for protocol handling.
|
||||
* Full implementation in Task 2.
|
||||
*/
|
||||
@Injectable()
|
||||
export class CalDAVProvider implements CalendarProvider {
|
||||
private readonly logger = new Logger(CalDAVProvider.name);
|
||||
|
||||
async fetchEvents(
|
||||
source: { url: string; username?: string; password?: string; id: string; color?: string | null },
|
||||
from: Date,
|
||||
to: Date,
|
||||
): Promise<CalendarEvent[]> {
|
||||
// Stub — implemented in Task 2
|
||||
return [];
|
||||
}
|
||||
|
||||
async testConnection(
|
||||
source: { url: string; username?: string; password?: string; id: string },
|
||||
): Promise<boolean> {
|
||||
// Stub — implemented in Task 2
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { CalendarEvent, CalendarProvider } from '../calendar.service';
|
||||
|
||||
/**
|
||||
* Exchange calendar provider — dispatches on exchangeMode ('graph' vs 'ews').
|
||||
* Uses @microsoft/microsoft-graph-client for Graph and ews-javascript-api for EWS.
|
||||
* Full implementation in Task 2.
|
||||
*/
|
||||
@Injectable()
|
||||
export class ExchangeProvider implements CalendarProvider {
|
||||
private readonly logger = new Logger(ExchangeProvider.name);
|
||||
|
||||
async fetchEvents(
|
||||
source: { url: string; username?: string; password?: string; exchangeMode?: string | null; id: string; color?: string | null },
|
||||
from: Date,
|
||||
to: Date,
|
||||
): Promise<CalendarEvent[]> {
|
||||
// Stub — implemented in Task 2
|
||||
return [];
|
||||
}
|
||||
|
||||
async testConnection(
|
||||
source: { url: string; username?: string; password?: string; exchangeMode?: string | null; id: string },
|
||||
): Promise<boolean> {
|
||||
// Stub — implemented in Task 2
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { CalendarEvent, CalendarProvider } from '../calendar.service';
|
||||
|
||||
/**
|
||||
* ICS calendar provider — fetches and parses .ics files via HTTPS.
|
||||
* Uses node-ical for parsing. Full implementation in Task 2.
|
||||
*/
|
||||
@Injectable()
|
||||
export class ICSProvider implements CalendarProvider {
|
||||
private readonly logger = new Logger(ICSProvider.name);
|
||||
|
||||
async fetchEvents(
|
||||
source: { url: string; id: string; color?: string | null },
|
||||
from: Date,
|
||||
to: Date,
|
||||
): Promise<CalendarEvent[]> {
|
||||
// Stub — implemented in Task 2
|
||||
return [];
|
||||
}
|
||||
|
||||
async testConnection(
|
||||
source: { url: string; id: string },
|
||||
): Promise<boolean> {
|
||||
// Stub — implemented in Task 2
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Generated
+365
-5
@@ -20,6 +20,9 @@ importers:
|
||||
|
||||
apps/api:
|
||||
dependencies:
|
||||
'@microsoft/microsoft-graph-client':
|
||||
specifier: 3.0.7
|
||||
version: 3.0.7
|
||||
'@nestjs-modules/mailer':
|
||||
specifier: ^2.3.7
|
||||
version: 2.3.7(@nestjs/common@11.1.27(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.27)(chokidar@4.0.3)(nodemailer@9.0.1)(svgo@4.0.1)(terser@5.48.0)(typescript@5.9.3)
|
||||
@@ -65,9 +68,15 @@ importers:
|
||||
cookie-parser:
|
||||
specifier: ^1.4.7
|
||||
version: 1.4.7
|
||||
ews-javascript-api:
|
||||
specifier: 0.15.3
|
||||
version: 0.15.3
|
||||
ldapts:
|
||||
specifier: ^8.1.8
|
||||
version: 8.1.8
|
||||
node-ical:
|
||||
specifier: 0.26.1
|
||||
version: 0.26.1
|
||||
nodemailer:
|
||||
specifier: ^9.0.1
|
||||
version: 9.0.1
|
||||
@@ -86,6 +95,9 @@ importers:
|
||||
rxjs:
|
||||
specifier: ^7.0.0
|
||||
version: 7.8.2
|
||||
tsdav:
|
||||
specifier: 2.2.2
|
||||
version: 2.2.2
|
||||
devDependencies:
|
||||
'@nestjs/cli':
|
||||
specifier: ^11.0.0
|
||||
@@ -261,6 +273,14 @@ packages:
|
||||
'@asamuzakjp/nwsapi@2.3.9':
|
||||
resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==}
|
||||
|
||||
'@azure/msal-common@14.16.1':
|
||||
resolution: {integrity: sha512-nyxsA6NA4SVKh5YyRpbSXiMr7oQbwark7JU9LMeg6tJYTSPyAGkdx61wPT4gyxZfxlSxMMEyAsWaubBlNyIa1w==}
|
||||
engines: {node: '>=0.8.0'}
|
||||
|
||||
'@azure/msal-node@2.16.3':
|
||||
resolution: {integrity: sha512-CO+SE4weOsfJf+C5LM8argzvotrXw252/ZU6SM2Tz63fEblhH1uuVaaO4ISYFuN4Q6BhTo7I3qIdi8ydUQCqhw==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
'@babel/code-frame@7.29.7':
|
||||
resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@@ -474,6 +494,13 @@ packages:
|
||||
'@epic-web/invariant@1.0.0':
|
||||
resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==}
|
||||
|
||||
'@ewsjs/ntlm-client@3.0.1':
|
||||
resolution: {integrity: sha512-eJEyxiR0Eb1kRU0N96ISubBgyhZJ8PXhw+gj5m5My+M5CqDEn8Ae2xPHiSyo0fUWiSj1A2tQ+kk3PZmursR5kQ==}
|
||||
engines: {node: '>=4.0.0'}
|
||||
|
||||
'@ewsjs/xhr@3.1.3':
|
||||
resolution: {integrity: sha512-vUMv1XwUvoGmlhLl0b/zXDB7qIUz4WCQqy70vsdl71RhnWXxnlUA/hIL0VkOBJSLBPEBkhsmERkbhBpnGvpkng==}
|
||||
|
||||
'@exodus/bytes@1.15.1':
|
||||
resolution: {integrity: sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==}
|
||||
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
|
||||
@@ -802,10 +829,32 @@ packages:
|
||||
'@jridgewell/trace-mapping@0.3.31':
|
||||
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
|
||||
|
||||
'@js-temporal/polyfill@0.5.1':
|
||||
resolution: {integrity: sha512-hloP58zRVCRSpgDxmqCWJNlizAlUgJFqG2ypq79DCvyv9tHjRYMDOcPFjzfl/A1/YxDvRCZz8wvZvmapQnKwFQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
'@lukeed/csprng@1.1.0':
|
||||
resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
'@microsoft/microsoft-graph-client@3.0.7':
|
||||
resolution: {integrity: sha512-/AazAV/F+HK4LIywF9C+NYHcJo038zEnWkteilcxC1FM/uK/4NVGDKGrxx7nNq1ybspAroRKT4I1FHfxQzxkUw==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
'@azure/identity': '*'
|
||||
'@azure/msal-browser': '*'
|
||||
buffer: '*'
|
||||
stream-browserify: '*'
|
||||
peerDependenciesMeta:
|
||||
'@azure/identity':
|
||||
optional: true
|
||||
'@azure/msal-browser':
|
||||
optional: true
|
||||
buffer:
|
||||
optional: true
|
||||
stream-browserify:
|
||||
optional: true
|
||||
|
||||
'@napi-rs/wasm-runtime@1.1.5':
|
||||
resolution: {integrity: sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==}
|
||||
peerDependencies:
|
||||
@@ -1687,6 +1736,10 @@ packages:
|
||||
'@webassemblyjs/wast-printer@1.14.1':
|
||||
resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==}
|
||||
|
||||
'@xmldom/xmldom@0.8.13':
|
||||
resolution: {integrity: sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
|
||||
'@xtuc/ieee754@1.2.0':
|
||||
resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==}
|
||||
|
||||
@@ -1723,6 +1776,14 @@ packages:
|
||||
engines: {node: '>=0.4.0'}
|
||||
hasBin: true
|
||||
|
||||
agent-base@6.0.2:
|
||||
resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
|
||||
engines: {node: '>= 6.0.0'}
|
||||
|
||||
agent-base@7.1.4:
|
||||
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
|
||||
engines: {node: '>= 14'}
|
||||
|
||||
ajv-formats@2.1.1:
|
||||
resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==}
|
||||
peerDependencies:
|
||||
@@ -1820,6 +1881,12 @@ packages:
|
||||
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
asynckit@0.4.0:
|
||||
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
|
||||
|
||||
axios@1.18.1:
|
||||
resolution: {integrity: sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==}
|
||||
|
||||
babel-walk@3.0.0-canary-5:
|
||||
resolution: {integrity: sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
@@ -1834,6 +1901,9 @@ packages:
|
||||
resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==}
|
||||
engines: {node: 18 || 20 || >=22}
|
||||
|
||||
base-64@1.0.0:
|
||||
resolution: {integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==}
|
||||
|
||||
base64-js@1.5.1:
|
||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||
|
||||
@@ -2018,6 +2088,10 @@ packages:
|
||||
color-name@1.1.4:
|
||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||
|
||||
combined-stream@1.0.8:
|
||||
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
comma-separated-tokens@2.0.3:
|
||||
resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==}
|
||||
|
||||
@@ -2237,6 +2311,10 @@ packages:
|
||||
defu@6.1.7:
|
||||
resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==}
|
||||
|
||||
delayed-stream@1.0.0:
|
||||
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
|
||||
depd@2.0.0:
|
||||
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
|
||||
engines: {node: '>= 0.8'}
|
||||
@@ -2245,6 +2323,9 @@ packages:
|
||||
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
des.js@1.1.0:
|
||||
resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==}
|
||||
|
||||
destr@2.0.5:
|
||||
resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==}
|
||||
|
||||
@@ -2421,6 +2502,10 @@ packages:
|
||||
resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
es-set-tostringtag@2.1.0:
|
||||
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
escalade@3.2.0:
|
||||
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -2484,6 +2569,9 @@ packages:
|
||||
resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
|
||||
engines: {node: '>=0.8.x'}
|
||||
|
||||
ews-javascript-api@0.15.3:
|
||||
resolution: {integrity: sha512-7re0Q/FSHyM9MgcPxjcNZlQuyzHmU7wHOdODRmQYMgxlGU8HB73cZdysyia4rE+CQCFyqhZqV8vMddYJXDwA9Q==}
|
||||
|
||||
execa@5.1.1:
|
||||
resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -2545,6 +2633,15 @@ packages:
|
||||
resolution: {integrity: sha512-5SM1+H2CcuJ3gGEwTiVo/+nd/hYpNj9Ch3iMDOQ58ndY+VGQ2QdvaUTkd3otjZvYnd/8LF/HkJ5cx7PBq0orCQ==}
|
||||
hasBin: true
|
||||
|
||||
follow-redirects@1.16.0:
|
||||
resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==}
|
||||
engines: {node: '>=4.0'}
|
||||
peerDependencies:
|
||||
debug: '*'
|
||||
peerDependenciesMeta:
|
||||
debug:
|
||||
optional: true
|
||||
|
||||
foreground-child@3.3.1:
|
||||
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
|
||||
engines: {node: '>=14'}
|
||||
@@ -2556,6 +2653,10 @@ packages:
|
||||
typescript: '>3.6.0'
|
||||
webpack: ^5.11.0
|
||||
|
||||
form-data@4.0.6:
|
||||
resolution: {integrity: sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
forwarded@0.2.0:
|
||||
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
|
||||
engines: {node: '>= 0.6'}
|
||||
@@ -2754,10 +2855,27 @@ packages:
|
||||
htmlparser2@9.1.0:
|
||||
resolution: {integrity: sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==}
|
||||
|
||||
http-cookie-agent@5.0.4:
|
||||
resolution: {integrity: sha512-OtvikW69RvfyP6Lsequ0fN5R49S+8QcS9zwd58k6VSr6r57T8G29BkPdyrBcSwLq6ExLs9V+rBlfxu7gDstJag==}
|
||||
engines: {node: '>=14.18.0 <15.0.0 || >=16.0.0'}
|
||||
peerDependencies:
|
||||
deasync: ^0.1.26
|
||||
tough-cookie: ^4.0.0
|
||||
undici: ^5.11.0
|
||||
peerDependenciesMeta:
|
||||
deasync:
|
||||
optional: true
|
||||
undici:
|
||||
optional: true
|
||||
|
||||
http-errors@2.0.1:
|
||||
resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
https-proxy-agent@5.0.1:
|
||||
resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
human-signals@2.1.0:
|
||||
resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
|
||||
engines: {node: '>=10.17.0'}
|
||||
@@ -2908,6 +3026,9 @@ packages:
|
||||
js-cookie@3.0.8:
|
||||
resolution: {integrity: sha512-yeJd4aNAdYZQjaon2bpD/Gb0B/omw7HQOsynXXcOiWVCacbBcPlgn8S/d1X6blFSaHao7ozqtW7NZW19xpCtIw==}
|
||||
|
||||
js-md4@0.3.2:
|
||||
resolution: {integrity: sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==}
|
||||
|
||||
js-stringify@1.0.2:
|
||||
resolution: {integrity: sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==}
|
||||
|
||||
@@ -2918,6 +3039,9 @@ packages:
|
||||
resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==}
|
||||
hasBin: true
|
||||
|
||||
jsbi@4.3.2:
|
||||
resolution: {integrity: sha512-9fqMSQbhJykSeii05nxKl4m6Eqn2P6rOlYiS+C5Dr/HPIU/7yZxu5qzbs40tgaFORiw2Amd0mirjxatXYMkIew==}
|
||||
|
||||
jsdom@29.1.1:
|
||||
resolution: {integrity: sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==}
|
||||
engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0}
|
||||
@@ -3335,6 +3459,9 @@ packages:
|
||||
resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
minimalistic-assert@1.0.1:
|
||||
resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==}
|
||||
|
||||
minimatch@10.2.5:
|
||||
resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==}
|
||||
engines: {node: 18 || 20 || >=22}
|
||||
@@ -3448,6 +3575,12 @@ packages:
|
||||
resolution: {integrity: sha512-cOrV1zC8SekUpZ2YGT174iWADRnuttYMtrAypQ1UvYXOUCOUc1DvwB8Dx8DH9+51+WyA0fNVOmUDx1Bc8qztqQ==}
|
||||
hasBin: true
|
||||
|
||||
moment-timezone@0.5.48:
|
||||
resolution: {integrity: sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw==}
|
||||
|
||||
moment@2.30.1:
|
||||
resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==}
|
||||
|
||||
ms@2.1.3:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
|
||||
@@ -3531,6 +3664,10 @@ packages:
|
||||
resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==}
|
||||
hasBin: true
|
||||
|
||||
node-ical@0.26.1:
|
||||
resolution: {integrity: sha512-KoYLpsz7Ga9lPDpt9vy0iKcgcb/9Ix7ICRZd0csLXMl2lZOSONGj7HrcktJFR7Jid1l44Zu1H4k/1nB04rWPgQ==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
node-releases@2.0.48:
|
||||
resolution: {integrity: sha512-1uz8041X6LoI6ZSdZacM9lVY28vuzDlSKitnpbSNK0RfKoIJkX29NBPVEFXhnuSuEOA9Ww0xnPJ+ILWbGAv8DA==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -3949,6 +4086,13 @@ packages:
|
||||
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
|
||||
engines: {node: '>= 0.10'}
|
||||
|
||||
proxy-from-env@2.1.0:
|
||||
resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
psl@1.15.0:
|
||||
resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==}
|
||||
|
||||
pug-attrs@3.0.0:
|
||||
resolution: {integrity: sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==}
|
||||
|
||||
@@ -4000,6 +4144,9 @@ packages:
|
||||
resolution: {integrity: sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==}
|
||||
engines: {node: '>=0.6'}
|
||||
|
||||
querystringify@2.2.0:
|
||||
resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
|
||||
|
||||
range-parser@1.2.1:
|
||||
resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
@@ -4138,6 +4285,9 @@ packages:
|
||||
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
requires-port@1.0.0:
|
||||
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
|
||||
|
||||
resize-observer-polyfill@1.5.1:
|
||||
resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==}
|
||||
|
||||
@@ -4163,6 +4313,9 @@ packages:
|
||||
resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
rrule-temporal@1.5.3:
|
||||
resolution: {integrity: sha512-qALnXyu4MKNUeykkkO0r6Xxl5or3rM8Cf6ibKIe/29sgmq3tGm1oNq4G1Ddp8Ku3mnKmvC3+3yFAJ3OgOu6OJw==}
|
||||
|
||||
run-applescript@5.0.0:
|
||||
resolution: {integrity: sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -4391,6 +4544,12 @@ packages:
|
||||
resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
temporal-polyfill@0.3.2:
|
||||
resolution: {integrity: sha512-TzHthD/heRK947GNiSu3Y5gSPpeUDH34+LESnfsq8bqpFhsB79HFBX8+Z834IVX68P3EUyRPZK5bL/1fh437Eg==}
|
||||
|
||||
temporal-spec@0.3.1:
|
||||
resolution: {integrity: sha512-B4TUhezh9knfSIMwt7RVggApDRJZo73uZdj8AacL2mZ8RP5KtLianh2MXxL06GN9ESYiIsiuoLQhgVfwe55Yhw==}
|
||||
|
||||
terser-webpack-plugin@5.6.1:
|
||||
resolution: {integrity: sha512-201R5j+sJpK8nFWwKVyNfZot8FaJbLZDq5evriVzbV1wDtSXDjRUDRfJzHpAaxFDMEhsZL1QkeqM61wgsS3KaQ==}
|
||||
engines: {node: '>= 10.13.0'}
|
||||
@@ -4476,6 +4635,10 @@ packages:
|
||||
resolution: {integrity: sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==}
|
||||
engines: {node: '>=14.16'}
|
||||
|
||||
tough-cookie@4.1.4:
|
||||
resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
tough-cookie@6.0.1:
|
||||
resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==}
|
||||
engines: {node: '>=16'}
|
||||
@@ -4498,6 +4661,10 @@ packages:
|
||||
resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
tsdav@2.2.2:
|
||||
resolution: {integrity: sha512-/uC/RItcdYVxUj8b2gT4CorAkUmDFUuzhgS4XVT+OVzGRE80hH5ldubcWH13dHa2T9rNaXu6UIEWHlQ1UL7H1Q==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
tslib@2.8.1:
|
||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||
|
||||
@@ -4572,6 +4739,10 @@ packages:
|
||||
unist-util-visit@5.1.0:
|
||||
resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==}
|
||||
|
||||
universalify@0.2.0:
|
||||
resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==}
|
||||
engines: {node: '>= 4.0.0'}
|
||||
|
||||
universalify@2.0.1:
|
||||
resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
@@ -4589,6 +4760,9 @@ packages:
|
||||
uri-js@4.4.1:
|
||||
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
|
||||
|
||||
url-parse@1.5.10:
|
||||
resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
|
||||
|
||||
use-intl@4.13.0:
|
||||
resolution: {integrity: sha512-fAFDrWaASxlhXOipcOyb5VDD+YONqj6+8O8EcG/J7RBoOUF3A8YahRWLN+mBxYMrlMQB8N6Voqk5X+YC+HSL0A==}
|
||||
peerDependencies:
|
||||
@@ -4601,6 +4775,11 @@ packages:
|
||||
resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
|
||||
engines: {node: '>= 0.4.0'}
|
||||
|
||||
uuid@8.3.2:
|
||||
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
|
||||
deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).
|
||||
hasBin: true
|
||||
|
||||
uuid@9.0.1:
|
||||
resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
|
||||
deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).
|
||||
@@ -4804,6 +4983,10 @@ packages:
|
||||
wrappy@1.0.2:
|
||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||
|
||||
xml-js@1.6.11:
|
||||
resolution: {integrity: sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==}
|
||||
hasBin: true
|
||||
|
||||
xml-name-validator@5.0.0:
|
||||
resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -4931,6 +5114,14 @@ snapshots:
|
||||
|
||||
'@asamuzakjp/nwsapi@2.3.9': {}
|
||||
|
||||
'@azure/msal-common@14.16.1': {}
|
||||
|
||||
'@azure/msal-node@2.16.3':
|
||||
dependencies:
|
||||
'@azure/msal-common': 14.16.1
|
||||
jsonwebtoken: 9.0.3
|
||||
uuid: 8.3.2
|
||||
|
||||
'@babel/code-frame@7.29.7':
|
||||
dependencies:
|
||||
'@babel/helper-validator-identifier': 7.29.7
|
||||
@@ -5096,6 +5287,25 @@ snapshots:
|
||||
|
||||
'@epic-web/invariant@1.0.0': {}
|
||||
|
||||
'@ewsjs/ntlm-client@3.0.1':
|
||||
dependencies:
|
||||
des.js: 1.1.0
|
||||
js-md4: 0.3.2
|
||||
optionalDependencies:
|
||||
extend: 3.0.2
|
||||
|
||||
'@ewsjs/xhr@3.1.3':
|
||||
dependencies:
|
||||
'@ewsjs/ntlm-client': 3.0.1
|
||||
axios: 1.18.1
|
||||
http-cookie-agent: 5.0.4(tough-cookie@4.1.4)
|
||||
tough-cookie: 4.1.4
|
||||
transitivePeerDependencies:
|
||||
- deasync
|
||||
- debug
|
||||
- supports-color
|
||||
- undici
|
||||
|
||||
'@exodus/bytes@1.15.1': {}
|
||||
|
||||
'@formatjs/fast-memoize@3.1.6': {}
|
||||
@@ -5384,8 +5594,17 @@ snapshots:
|
||||
'@jridgewell/resolve-uri': 3.1.2
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
'@js-temporal/polyfill@0.5.1':
|
||||
dependencies:
|
||||
jsbi: 4.3.2
|
||||
|
||||
'@lukeed/csprng@1.1.0': {}
|
||||
|
||||
'@microsoft/microsoft-graph-client@3.0.7':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.29.7
|
||||
tslib: 2.8.1
|
||||
|
||||
'@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)':
|
||||
dependencies:
|
||||
'@emnapi/core': 1.10.0
|
||||
@@ -6252,6 +6471,8 @@ snapshots:
|
||||
'@webassemblyjs/ast': 1.14.1
|
||||
'@xtuc/long': 4.2.2
|
||||
|
||||
'@xmldom/xmldom@0.8.13': {}
|
||||
|
||||
'@xtuc/ieee754@1.2.0': {}
|
||||
|
||||
'@xtuc/long@4.2.2': {}
|
||||
@@ -6283,6 +6504,14 @@ snapshots:
|
||||
|
||||
acorn@8.17.0: {}
|
||||
|
||||
agent-base@6.0.2:
|
||||
dependencies:
|
||||
debug: 4.4.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
agent-base@7.1.4: {}
|
||||
|
||||
ajv-formats@2.1.1(ajv@8.20.0):
|
||||
optionalDependencies:
|
||||
ajv: 8.20.0
|
||||
@@ -6372,6 +6601,18 @@ snapshots:
|
||||
|
||||
assertion-error@2.0.1: {}
|
||||
|
||||
asynckit@0.4.0: {}
|
||||
|
||||
axios@1.18.1:
|
||||
dependencies:
|
||||
follow-redirects: 1.16.0
|
||||
form-data: 4.0.6
|
||||
https-proxy-agent: 5.0.1
|
||||
proxy-from-env: 2.1.0
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
- supports-color
|
||||
|
||||
babel-walk@3.0.0-canary-5:
|
||||
dependencies:
|
||||
'@babel/types': 7.29.7
|
||||
@@ -6383,6 +6624,8 @@ snapshots:
|
||||
|
||||
balanced-match@4.0.4: {}
|
||||
|
||||
base-64@1.0.0: {}
|
||||
|
||||
base64-js@1.5.1: {}
|
||||
|
||||
baseline-browser-mapping@2.10.38: {}
|
||||
@@ -6600,6 +6843,10 @@ snapshots:
|
||||
|
||||
color-name@1.1.4: {}
|
||||
|
||||
combined-stream@1.0.8:
|
||||
dependencies:
|
||||
delayed-stream: 1.0.0
|
||||
|
||||
comma-separated-tokens@2.0.3: {}
|
||||
|
||||
commander@10.0.1:
|
||||
@@ -6838,10 +7085,17 @@ snapshots:
|
||||
|
||||
defu@6.1.7: {}
|
||||
|
||||
delayed-stream@1.0.0: {}
|
||||
|
||||
depd@2.0.0: {}
|
||||
|
||||
dequal@2.0.3: {}
|
||||
|
||||
des.js@1.1.0:
|
||||
dependencies:
|
||||
inherits: 2.0.4
|
||||
minimalistic-assert: 1.0.1
|
||||
|
||||
destr@2.0.5: {}
|
||||
|
||||
detect-indent@6.1.0:
|
||||
@@ -7017,6 +7271,13 @@ snapshots:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
|
||||
es-set-tostringtag@2.1.0:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
get-intrinsic: 1.3.0
|
||||
has-tostringtag: 1.0.2
|
||||
hasown: 2.0.4
|
||||
|
||||
escalade@3.2.0: {}
|
||||
|
||||
escape-goat@3.0.0:
|
||||
@@ -7060,6 +7321,21 @@ snapshots:
|
||||
|
||||
events@3.3.0: {}
|
||||
|
||||
ews-javascript-api@0.15.3:
|
||||
dependencies:
|
||||
'@azure/msal-node': 2.16.3
|
||||
'@ewsjs/xhr': 3.1.3
|
||||
'@xmldom/xmldom': 0.8.13
|
||||
base64-js: 1.5.1
|
||||
moment: 2.30.1
|
||||
moment-timezone: 0.5.48
|
||||
uuid: 9.0.1
|
||||
transitivePeerDependencies:
|
||||
- deasync
|
||||
- debug
|
||||
- supports-color
|
||||
- undici
|
||||
|
||||
execa@5.1.1:
|
||||
dependencies:
|
||||
cross-spawn: 7.0.6
|
||||
@@ -7163,6 +7439,8 @@ snapshots:
|
||||
rc: 1.2.8
|
||||
optional: true
|
||||
|
||||
follow-redirects@1.16.0: {}
|
||||
|
||||
foreground-child@3.3.1:
|
||||
dependencies:
|
||||
cross-spawn: 7.0.6
|
||||
@@ -7186,6 +7464,14 @@ snapshots:
|
||||
typescript: 5.9.3
|
||||
webpack: 5.106.2(@swc/core@1.15.41)(cssnano@7.1.9(postcss@8.5.15))(postcss@8.5.15)
|
||||
|
||||
form-data@4.0.6:
|
||||
dependencies:
|
||||
asynckit: 0.4.0
|
||||
combined-stream: 1.0.8
|
||||
es-set-tostringtag: 2.1.0
|
||||
hasown: 2.0.4
|
||||
mime-types: 2.1.35
|
||||
|
||||
forwarded@0.2.0: {}
|
||||
|
||||
fresh@2.0.0: {}
|
||||
@@ -7290,7 +7576,6 @@ snapshots:
|
||||
has-tostringtag@1.0.2:
|
||||
dependencies:
|
||||
has-symbols: 1.1.0
|
||||
optional: true
|
||||
|
||||
hasown@2.0.4:
|
||||
dependencies:
|
||||
@@ -7493,6 +7778,11 @@ snapshots:
|
||||
entities: 4.5.0
|
||||
optional: true
|
||||
|
||||
http-cookie-agent@5.0.4(tough-cookie@4.1.4):
|
||||
dependencies:
|
||||
agent-base: 7.1.4
|
||||
tough-cookie: 4.1.4
|
||||
|
||||
http-errors@2.0.1:
|
||||
dependencies:
|
||||
depd: 2.0.0
|
||||
@@ -7501,6 +7791,13 @@ snapshots:
|
||||
statuses: 2.0.2
|
||||
toidentifier: 1.0.1
|
||||
|
||||
https-proxy-agent@5.0.1:
|
||||
dependencies:
|
||||
agent-base: 6.0.2
|
||||
debug: 4.4.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
human-signals@2.1.0:
|
||||
optional: true
|
||||
|
||||
@@ -7645,6 +7942,8 @@ snapshots:
|
||||
js-cookie@3.0.8:
|
||||
optional: true
|
||||
|
||||
js-md4@0.3.2: {}
|
||||
|
||||
js-stringify@1.0.2:
|
||||
optional: true
|
||||
|
||||
@@ -7654,6 +7953,8 @@ snapshots:
|
||||
dependencies:
|
||||
argparse: 2.0.1
|
||||
|
||||
jsbi@4.3.2: {}
|
||||
|
||||
jsdom@29.1.1:
|
||||
dependencies:
|
||||
'@asamuzakjp/css-color': 5.1.11
|
||||
@@ -8278,6 +8579,8 @@ snapshots:
|
||||
|
||||
min-indent@1.0.1: {}
|
||||
|
||||
minimalistic-assert@1.0.1: {}
|
||||
|
||||
minimatch@10.2.5:
|
||||
dependencies:
|
||||
brace-expansion: 5.0.6
|
||||
@@ -8785,6 +9088,12 @@ snapshots:
|
||||
- uncss
|
||||
optional: true
|
||||
|
||||
moment-timezone@0.5.48:
|
||||
dependencies:
|
||||
moment: 2.30.1
|
||||
|
||||
moment@2.30.1: {}
|
||||
|
||||
ms@2.1.3: {}
|
||||
|
||||
multer@2.1.1:
|
||||
@@ -8863,6 +9172,11 @@ snapshots:
|
||||
|
||||
node-gyp-build@4.8.4: {}
|
||||
|
||||
node-ical@0.26.1:
|
||||
dependencies:
|
||||
rrule-temporal: 1.5.3
|
||||
temporal-polyfill: 0.3.2
|
||||
|
||||
node-releases@2.0.48: {}
|
||||
|
||||
nodemailer@8.0.11:
|
||||
@@ -9337,6 +9651,12 @@ snapshots:
|
||||
forwarded: 0.2.0
|
||||
ipaddr.js: 1.9.1
|
||||
|
||||
proxy-from-env@2.1.0: {}
|
||||
|
||||
psl@1.15.0:
|
||||
dependencies:
|
||||
punycode: 2.3.1
|
||||
|
||||
pug-attrs@3.0.0:
|
||||
dependencies:
|
||||
constantinople: 4.0.1
|
||||
@@ -9427,6 +9747,8 @@ snapshots:
|
||||
dependencies:
|
||||
side-channel: 1.1.1
|
||||
|
||||
querystringify@2.2.0: {}
|
||||
|
||||
range-parser@1.2.1: {}
|
||||
|
||||
raw-body@3.0.2:
|
||||
@@ -9648,6 +9970,8 @@ snapshots:
|
||||
|
||||
require-from-string@2.0.2: {}
|
||||
|
||||
requires-port@1.0.0: {}
|
||||
|
||||
resize-observer-polyfill@1.5.1: {}
|
||||
|
||||
resolve-from@4.0.0: {}
|
||||
@@ -9696,6 +10020,10 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
rrule-temporal@1.5.3:
|
||||
dependencies:
|
||||
'@js-temporal/polyfill': 0.5.1
|
||||
|
||||
run-applescript@5.0.0:
|
||||
dependencies:
|
||||
execa: 5.1.1
|
||||
@@ -9713,8 +10041,7 @@ snapshots:
|
||||
|
||||
safer-buffer@2.1.2: {}
|
||||
|
||||
sax@1.6.0:
|
||||
optional: true
|
||||
sax@1.6.0: {}
|
||||
|
||||
saxes@6.0.0:
|
||||
dependencies:
|
||||
@@ -9964,6 +10291,12 @@ snapshots:
|
||||
|
||||
tapable@2.3.3: {}
|
||||
|
||||
temporal-polyfill@0.3.2:
|
||||
dependencies:
|
||||
temporal-spec: 0.3.1
|
||||
|
||||
temporal-spec@0.3.1: {}
|
||||
|
||||
terser-webpack-plugin@5.6.1(@swc/core@1.15.41)(cssnano@7.1.9(postcss@8.5.15))(postcss@8.5.15)(webpack@5.106.2(@swc/core@1.15.41)(cssnano@7.1.9(postcss@8.5.15))(postcss@8.5.15)):
|
||||
dependencies:
|
||||
'@jridgewell/trace-mapping': 0.3.31
|
||||
@@ -10014,6 +10347,13 @@ snapshots:
|
||||
'@tokenizer/token': 0.3.0
|
||||
ieee754: 1.2.1
|
||||
|
||||
tough-cookie@4.1.4:
|
||||
dependencies:
|
||||
psl: 1.15.0
|
||||
punycode: 2.3.1
|
||||
universalify: 0.2.0
|
||||
url-parse: 1.5.10
|
||||
|
||||
tough-cookie@6.0.1:
|
||||
dependencies:
|
||||
tldts: 7.4.3
|
||||
@@ -10039,6 +10379,14 @@ snapshots:
|
||||
minimist: 1.2.8
|
||||
strip-bom: 3.0.0
|
||||
|
||||
tsdav@2.2.2:
|
||||
dependencies:
|
||||
base-64: 1.0.0
|
||||
debug: 4.4.3
|
||||
xml-js: 1.6.11
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
tslib@2.8.1: {}
|
||||
|
||||
turbo@2.9.18:
|
||||
@@ -10129,6 +10477,8 @@ snapshots:
|
||||
unist-util-is: 6.0.1
|
||||
unist-util-visit-parents: 6.0.2
|
||||
|
||||
universalify@0.2.0: {}
|
||||
|
||||
universalify@2.0.1: {}
|
||||
|
||||
unpipe@1.0.0: {}
|
||||
@@ -10143,6 +10493,11 @@ snapshots:
|
||||
dependencies:
|
||||
punycode: 2.3.1
|
||||
|
||||
url-parse@1.5.10:
|
||||
dependencies:
|
||||
querystringify: 2.2.0
|
||||
requires-port: 1.0.0
|
||||
|
||||
use-intl@4.13.0(react@19.2.7):
|
||||
dependencies:
|
||||
'@formatjs/fast-memoize': 3.1.6
|
||||
@@ -10155,8 +10510,9 @@ snapshots:
|
||||
|
||||
utils-merge@1.0.1: {}
|
||||
|
||||
uuid@9.0.1:
|
||||
optional: true
|
||||
uuid@8.3.2: {}
|
||||
|
||||
uuid@9.0.1: {}
|
||||
|
||||
valid-data-url@3.0.1:
|
||||
optional: true
|
||||
@@ -10353,6 +10709,10 @@ snapshots:
|
||||
|
||||
wrappy@1.0.2: {}
|
||||
|
||||
xml-js@1.6.11:
|
||||
dependencies:
|
||||
sax: 1.6.0
|
||||
|
||||
xml-name-validator@5.0.0: {}
|
||||
|
||||
xmlchars@2.2.0: {}
|
||||
|
||||
Reference in New Issue
Block a user