diff --git a/.gitignore b/.gitignore index b202dfcc..968bead5 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,9 @@ npm-debug.log* yarn-debug.log* yarn-error.log* +# Snowflake SDK logs +snowflake.log + # Misc .DS_Store *.pem diff --git a/apps/lfx-one/.env.example b/apps/lfx-one/.env.example index fdec6dc3..94707564 100644 --- a/apps/lfx-one/.env.example +++ b/apps/lfx-one/.env.example @@ -30,6 +30,37 @@ SUPABASE_STORAGE_BUCKET=your-supabase-bucket-name # Internal k8s service DNS for NATS cluster NATS_URL=nats://lfx-platform-nats.lfx.svc.cluster.local:4222 +############### SNOWFLAKE CONFIG ############### +# Snowflake Connection Configuration +# Used for analytics endpoints: active-weeks-streak, pull-requests-merged, code-commits +# Account identifier in format: orgname-accountname (e.g., jnmhvwd-xpb85243) +SNOWFLAKE_ACCOUNT=your-org-account +# Service user for read-only analytical queries +SNOWFLAKE_USERNAME=your-username +# Warehouse for query execution +SNOWFLAKE_WAREHOUSE=your-warehouse +# Analytics database name +SNOWFLAKE_DATABASE=your-database +# User role with SELECT-only permissions +SNOWFLAKE_ROLE=your-read-role + +# Snowflake Authentication (choose one method) +# Method 1: Direct API Key (recommended for containers/Docker) +# SNOWFLAKE_API_KEY=your-private-key-here + +# Method 2: Private Key File (recommended for local development) +# Place rsa_key.p8 file in this directory (same location as .env) +# The service will automatically detect and use it +# SNOWFLAKE_PRIVATE_KEY_PASSPHRASE=your-passphrase-here + +# Optional: Connection Pool Configuration (defaults shown) +# SNOWFLAKE_MIN_CONNECTIONS=2 +# SNOWFLAKE_MAX_CONNECTIONS=10 + +# Optional: Lock Strategy for Query Deduplication (defaults to 'memory') +# SNOWFLAKE_LOCK_STRATEGY=memory +############### END SNOWFLAKE CONFIG ############### + # AI Service Configuration # OpenAI-compatible proxy for meeting agenda generation AI_PROXY_URL=https://litellm.tools.lfx.dev/chat/completions diff --git a/apps/lfx-one/.gitignore b/apps/lfx-one/.gitignore index 48644cdc..1eb5b4a3 100644 --- a/apps/lfx-one/.gitignore +++ b/apps/lfx-one/.gitignore @@ -48,3 +48,10 @@ Thumbs.db /playwright/.auth/ /playwright-report/ /test-results/ + +# Snowflake +rsa_key.p8 +rsa_key.pub + +# Logs +snowflake.log diff --git a/apps/lfx-one/angular.json b/apps/lfx-one/angular.json index f9912bdc..8939cdf5 100644 --- a/apps/lfx-one/angular.json +++ b/apps/lfx-one/angular.json @@ -51,7 +51,9 @@ "outputMode": "server", "ssr": { "entry": "src/server/server.ts" - } + }, + "allowedCommonJsDependencies": ["@linuxfoundation/lfx-ui-core"], + "externalDependencies": ["snowflake-sdk"] }, "configurations": { "production": { diff --git a/apps/lfx-one/package.json b/apps/lfx-one/package.json index 827a5f5b..4fa8e32b 100644 --- a/apps/lfx-one/package.json +++ b/apps/lfx-one/package.json @@ -50,6 +50,7 @@ "pino-http": "^10.5.0", "primeng": "^19.1.4", "rxjs": "~7.8.2", + "snowflake-sdk": "^2.3.1", "tslib": "^2.8.1" }, "devDependencies": { diff --git a/apps/lfx-one/src/app/app.component.ts b/apps/lfx-one/src/app/app.component.ts index 92da9806..0e7c3c90 100644 --- a/apps/lfx-one/src/app/app.component.ts +++ b/apps/lfx-one/src/app/app.component.ts @@ -8,7 +8,7 @@ import { AuthContext } from '@lfx-one/shared/interfaces'; import { ToastModule } from 'primeng/toast'; import { HeaderComponent } from './shared/components/header/header.component'; -import { AnalyticsService } from './shared/services/analytics.service'; +import { SegmentService } from './shared/services/segment.service'; import { UserService } from './shared/services/user.service'; @Component({ @@ -19,15 +19,15 @@ import { UserService } from './shared/services/user.service'; }) export class AppComponent { private readonly userService = inject(UserService); - private readonly analyticsService = inject(AnalyticsService); + private readonly segmentService = inject(SegmentService); public auth: AuthContext | undefined; public transferState = inject(TransferState); public serverKey = makeStateKey('auth'); public constructor() { - // Initialize Segment analytics - this.analyticsService.initialize(); + // Initialize Segment tracking + this.segmentService.initialize(); const reqContext = inject(REQUEST_CONTEXT, { optional: true }) as { auth: AuthContext; @@ -51,8 +51,8 @@ export class AppComponent { this.userService.authenticated.set(true); this.userService.user.set(this.auth.user); - // Identify user with Segment analytics (pass entire Auth0 user object) - this.analyticsService.identifyUser(this.auth.user); + // Identify user with Segment tracking (pass entire Auth0 user object) + this.segmentService.identifyUser(this.auth.user); } } } diff --git a/apps/lfx-one/src/app/layouts/profile-layout/profile-layout.component.ts b/apps/lfx-one/src/app/layouts/profile-layout/profile-layout.component.ts index ca42a491..0d543a55 100644 --- a/apps/lfx-one/src/app/layouts/profile-layout/profile-layout.component.ts +++ b/apps/lfx-one/src/app/layouts/profile-layout/profile-layout.component.ts @@ -145,7 +145,7 @@ export class ProfileLayoutComponent { const profile = this.profile(); if (!profile?.profile) return ''; - return profile.profile.title || ''; + return profile.profile.job_title || ''; }); } diff --git a/apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.html b/apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.html index d818f00b..07d31162 100644 --- a/apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.html +++ b/apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.html @@ -17,8 +17,8 @@

My Meetings

-
-
+
+
@if (todayMeetings().length > 0 || upcomingMeetings().length > 0) { @if (todayMeetings().length > 0) { diff --git a/apps/lfx-one/src/app/modules/dashboards/components/recent-progress/recent-progress.component.ts b/apps/lfx-one/src/app/modules/dashboards/components/recent-progress/recent-progress.component.ts index f753958e..0a8a6a2b 100644 --- a/apps/lfx-one/src/app/modules/dashboards/components/recent-progress/recent-progress.component.ts +++ b/apps/lfx-one/src/app/modules/dashboards/components/recent-progress/recent-progress.component.ts @@ -3,11 +3,13 @@ import { CommonModule } from '@angular/common'; import { Component, computed, ElementRef, inject, ViewChild } from '@angular/core'; +import { toSignal } from '@angular/core/rxjs-interop'; +import { AnalyticsService } from '@app/shared/services/analytics.service'; import { PersonaService } from '@app/shared/services/persona.service'; import { ChartComponent } from '@components/chart/chart.component'; import { CORE_DEVELOPER_PROGRESS_METRICS, MAINTAINER_PROGRESS_METRICS } from '@lfx-one/shared/constants'; -import type { ProgressItemWithChart } from '@lfx-one/shared/interfaces'; +import type { ActiveWeeksStreakResponse, ProgressItemWithChart, UserCodeCommitsResponse, UserPullRequestsResponse } from '@lfx-one/shared/interfaces'; @Component({ selector: 'lfx-recent-progress', @@ -20,20 +22,66 @@ export class RecentProgressComponent { @ViewChild('progressScroll') protected progressScrollContainer!: ElementRef; private readonly personaService = inject(PersonaService); + private readonly analyticsService = inject(AnalyticsService); + + /** + * Active weeks streak data from Snowflake + */ + private readonly activeWeeksStreakData = toSignal(this.analyticsService.getActiveWeeksStreak(), { + initialValue: { + data: [], + currentStreak: 0, + totalWeeks: 0, + }, + }); + + /** + * Pull requests merged data from Snowflake + */ + private readonly pullRequestsMergedData = toSignal(this.analyticsService.getPullRequestsMerged(), { + initialValue: { + data: [], + totalPullRequests: 0, + totalDays: 0, + }, + }); + + /** + * Code commits data from Snowflake + */ + private readonly codeCommitsData = toSignal(this.analyticsService.getCodeCommits(), { + initialValue: { + data: [], + totalCommits: 0, + totalDays: 0, + }, + }); /** * Computed signal that returns progress metrics based on the current persona + * Merges hardcoded metrics with real data from Snowflake */ protected readonly progressItems = computed(() => { const persona = this.personaService.currentPersona(); + const activeWeeksData = this.activeWeeksStreakData(); + const pullRequestsData = this.pullRequestsMergedData(); + const codeCommitsDataValue = this.codeCommitsData(); + + const baseMetrics = persona === 'maintainer' ? MAINTAINER_PROGRESS_METRICS : CORE_DEVELOPER_PROGRESS_METRICS; - switch (persona) { - case 'maintainer': - return MAINTAINER_PROGRESS_METRICS; - case 'core-developer': - default: - return CORE_DEVELOPER_PROGRESS_METRICS; - } + // Replace metrics with real data if available + return baseMetrics.map((metric) => { + if (metric.label === 'Active Weeks Streak') { + return this.transformActiveWeeksStreak(activeWeeksData); + } + if (metric.label === 'Pull Requests Merged') { + return this.transformPullRequestsMerged(pullRequestsData); + } + if (metric.label === 'Code Commits') { + return this.transformCodeCommits(codeCommitsDataValue); + } + return metric; + }); }); protected scrollLeft(): void { @@ -45,4 +93,167 @@ export class RecentProgressComponent { const container = this.progressScrollContainer.nativeElement; container.scrollBy({ left: 300, behavior: 'smooth' }); } + + /** + * Transform Active Weeks Streak API response to chart format + * API returns data in ascending order by WEEKS_AGO (0, 1, 2, 3...) + * Display as-is with newest week (week 0) on the left + */ + private transformActiveWeeksStreak(data: ActiveWeeksStreakResponse): ProgressItemWithChart { + // Use data as-is: week 0 (newest) on the left, older weeks on the right + const chartData = data.data; + + return { + label: 'Active Weeks Streak', + value: data.currentStreak.toString(), + trend: data.currentStreak > 0 ? 'up' : 'down', + subtitle: 'Current streak', + chartType: 'bar', + chartData: { + labels: chartData.map((row) => `Week ${row.WEEKS_AGO}`), + datasets: [ + { + data: chartData.map((row) => row.IS_ACTIVE), + borderColor: '#10b981', + backgroundColor: 'rgba(16, 185, 129, 0.1)', + fill: true, + tension: 0.4, + borderWidth: 2, + pointRadius: 0, + }, + ], + }, + chartOptions: { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { display: false }, + tooltip: { + enabled: true, + callbacks: { + title: (context) => context[0].label, + label: (context) => { + const isActive = context.parsed.y === 1; + return isActive ? 'Active' : 'Inactive'; + }, + }, + }, + }, + scales: { + x: { display: false }, + y: { display: false, min: 0, max: 1 }, + }, + }, + }; + } + + /** + * Transform Pull Requests Merged API response to chart format + * API returns data in ascending order by ACTIVITY_DATE + * Display as-is with oldest date on the left, newest on the right + */ + private transformPullRequestsMerged(data: UserPullRequestsResponse): ProgressItemWithChart { + const chartData = data.data; + + return { + label: 'Pull Requests Merged', + value: data.totalPullRequests.toString(), + trend: data.totalPullRequests > 0 ? 'up' : 'down', + subtitle: 'Last 30 days', + chartType: 'line', + chartData: { + labels: chartData.map((row) => row.ACTIVITY_DATE), + datasets: [ + { + data: chartData.map((row) => row.DAILY_COUNT), + borderColor: '#0094FF', + backgroundColor: 'rgba(0, 148, 255, 0.1)', + fill: true, + tension: 0, + borderWidth: 2, + pointRadius: 0, + }, + ], + }, + chartOptions: { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { display: false }, + tooltip: { + enabled: true, + callbacks: { + title: (context) => { + const date = new Date(context[0].label); + return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); + }, + label: (context) => { + const count = context.parsed.y; + return `PRs Merged: ${count}`; + }, + }, + }, + }, + scales: { + x: { display: false }, + y: { display: false }, + }, + }, + }; + } + + /** + * Transform Code Commits API response to chart format + * API returns data in ascending order by ACTIVITY_DATE + * Display as-is with oldest date on the left, newest on the right + */ + private transformCodeCommits(data: UserCodeCommitsResponse): ProgressItemWithChart { + const chartData = data.data; + + return { + label: 'Code Commits', + value: data.totalCommits.toString(), + trend: data.totalCommits > 0 ? 'up' : 'down', + subtitle: 'Last 30 days', + chartType: 'line', + chartData: { + labels: chartData.map((row) => row.ACTIVITY_DATE), + datasets: [ + { + data: chartData.map((row) => row.DAILY_COUNT), + borderColor: '#0094FF', + backgroundColor: 'rgba(0, 148, 255, 0.1)', + fill: true, + tension: 0.4, + borderWidth: 2, + pointRadius: 0, + }, + ], + }, + chartOptions: { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { display: false }, + tooltip: { + enabled: true, + callbacks: { + title: (context) => { + const date = new Date(context[0].label); + return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); + }, + label: (context) => { + const count = context.parsed.y; + return `Commits: ${count}`; + }, + }, + }, + }, + scales: { + x: { display: false }, + y: { display: false }, + }, + }, + }; + } } diff --git a/apps/lfx-one/src/app/modules/profile/edit/profile-edit.component.html b/apps/lfx-one/src/app/modules/profile/edit/profile-edit.component.html index 50a9e576..9a0cfd6b 100644 --- a/apps/lfx-one/src/app/modules/profile/edit/profile-edit.component.html +++ b/apps/lfx-one/src/app/modules/profile/edit/profile-edit.component.html @@ -97,7 +97,7 @@

= { given_name: formValue.given_name || undefined, family_name: formValue.family_name || undefined, - title: formValue.title || undefined, + job_title: formValue.job_title || undefined, organization: formValue.organization || undefined, country: formValue.country || undefined, state_province: formValue.state_province || undefined, @@ -224,7 +224,7 @@ export class ProfileEditComponent implements OnInit { given_name: profile.user.first_name || '', family_name: profile.user.last_name || '', username: profile.user.username || '', - title: profile.profile?.title || '', + job_title: profile.profile?.job_title || '', organization: profile.profile?.organization || '', country: countryValue, state_province: profile.profile?.state_province || '', diff --git a/apps/lfx-one/src/app/shared/components/header/header.component.html b/apps/lfx-one/src/app/shared/components/header/header.component.html index e0c47264..d1cef1d8 100644 --- a/apps/lfx-one/src/app/shared/components/header/header.component.html +++ b/apps/lfx-one/src/app/shared/components/header/header.component.html @@ -36,7 +36,7 @@
{{ userProfile()?.user?.first_name }} {{ userProfile()?.user?.last_name }} {{ userProfile()?.profile?.title }} at {{ userProfile()?.profile?.organization }}{{ userProfile()?.profile?.job_title }} at {{ userProfile()?.profile?.organization }}

diff --git a/apps/lfx-one/src/app/shared/components/header/header.component.ts b/apps/lfx-one/src/app/shared/components/header/header.component.ts index d5d1485e..94c68dc7 100644 --- a/apps/lfx-one/src/app/shared/components/header/header.component.ts +++ b/apps/lfx-one/src/app/shared/components/header/header.component.ts @@ -54,7 +54,9 @@ export class HeaderComponent { public userProfile: Signal = this.initializeUserProfile(); public initials = computed(() => this.userProfile()?.user.first_name?.slice(0, 1)); - public fullName = computed(() => this.userProfile()?.user.first_name + ' ' + this.userProfile()?.user?.last_name); + public fullName = computed(() => { + return this.userProfile()?.user.first_name + ' ' + this.userProfile()?.user?.last_name; + }); // Search form protected readonly searchForm = new FormGroup({ diff --git a/apps/lfx-one/src/app/shared/services/analytics.service.ts b/apps/lfx-one/src/app/shared/services/analytics.service.ts index 6d4dd54a..e1792cd8 100644 --- a/apps/lfx-one/src/app/shared/services/analytics.service.ts +++ b/apps/lfx-one/src/app/shared/services/analytics.service.ts @@ -1,192 +1,69 @@ // Copyright The Linux Foundation and each contributor to LFX. // SPDX-License-Identifier: MIT -import { afterNextRender, DestroyRef, inject, Injectable } from '@angular/core'; -import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { NavigationEnd, Router } from '@angular/router'; -import { environment } from '@environments/environment'; -import { LfxSegmentAnalytics, LfxSegmentAnalyticsClass } from '@lfx-one/shared/interfaces'; -import { filter } from 'rxjs'; - -declare global { - interface Window { - LfxAnalytics?: { - LfxSegmentsAnalytics: LfxSegmentAnalyticsClass; - }; - } -} +import { HttpClient } from '@angular/common/http'; +import { inject, Injectable } from '@angular/core'; +import { ActiveWeeksStreakResponse, UserCodeCommitsResponse, UserPullRequestsResponse } from '@lfx-one/shared/interfaces'; +import { catchError, Observable, of } from 'rxjs'; /** - * Analytics service for Segment integration - * Uses Angular 19's afterNextRender for SSR-safe script loading + * Analytics service for fetching analytics data from Snowflake + * Provides dashboard metrics and visualizations */ @Injectable({ providedIn: 'root', }) export class AnalyticsService { - private readonly router = inject(Router); - private readonly destroyRef = inject(DestroyRef); - - private scriptLoaded = false; - private analyticsReady = false; - private analytics?: LfxSegmentAnalytics; - private identifyQueue: Array<{ user: unknown }> = []; - - /** - * Initialize the analytics service - should be called from app component - */ - public initialize(): void { - // SSR-safe initialization using afterNextRender - afterNextRender(() => { - this.loadSegmentScript(); - this.setupRouteTracking(); - }); - } - - /** - * Track a page view - * @param pageName Page name - * @param properties Optional page properties - */ - public trackPage(pageName: string, properties?: Record): void { - if (!this.analyticsReady || !this.analytics) { - return; - } - - try { - this.analytics.page(pageName, properties); - } catch (error) { - console.error('Error tracking page with Segment:', error); - } - } - - /** - * Track a custom event - * @param eventName Event name - * @param properties Event properties - */ - public trackEvent(eventName: string, properties?: Record): void { - if (!this.analyticsReady || !this.analytics) { - return; - } - - try { - this.analytics.track(eventName, properties); - } catch (error) { - console.error('Error tracking event with Segment:', error); - } - } + private readonly http = inject(HttpClient); /** - * Identify an Auth0 user with Segment - * @param auth0User Auth0 user object + * Get active weeks streak data for the current user + * @returns Observable of active weeks streak response */ - public identifyUser(auth0User: unknown): void { - if (!auth0User) { - return; - } - - // If analytics is not ready yet, queue the identify call - if (!this.analyticsReady || !this.analytics) { - this.identifyQueue.push({ user: auth0User }); - return; - } - - try { - this.analytics.identifyAuth0User(auth0User); - } catch (error) { - console.error('Error identifying user with Segment:', error); - } - } - - /** - * Load Segment script from CDN - */ - private loadSegmentScript(): void { - if (!environment.segment?.enabled || this.scriptLoaded) { - return; - } - - try { - const script = document.createElement('script'); - script.src = environment.segment.cdnUrl; - script.async = true; - script.defer = true; - - script.onload = () => { - this.scriptLoaded = true; - this.waitForAnalytics(); - }; - - script.onerror = (error) => { - console.error('Failed to load Segment analytics script:', error); - }; - - document.head.appendChild(script); - } catch (error) { - console.error('Error initializing Segment:', error); - } - } - - /** - * Wait for analytics object to be available and initialize it - */ - private async waitForAnalytics(): Promise { - const maxAttempts = 100; // 10 seconds max wait - let attempts = 0; - - while (attempts < maxAttempts) { - if (window?.LfxAnalytics?.LfxSegmentsAnalytics) { - this.analytics = window.LfxAnalytics.LfxSegmentsAnalytics.getInstance(); - - try { - await this.analytics.init(); - this.analyticsReady = true; - - // Process any queued identify calls - this.processIdentifyQueue(); - return; - } catch (error) { - console.error('Error initializing LfxSegmentsAnalytics:', error); - return; - } - } - - await new Promise((resolve) => setTimeout(resolve, 100)); - attempts++; - } - - console.error('LfxSegmentsAnalytics not available after timeout'); + public getActiveWeeksStreak(): Observable { + return this.http.get('/api/analytics/active-weeks-streak').pipe( + catchError((error) => { + console.error('Failed to fetch active weeks streak:', error); + return of({ + data: [], + currentStreak: 0, + totalWeeks: 0, + }); + }) + ); } /** - * Process queued identify calls + * Get pull requests merged data for the current user + * @returns Observable of pull requests merged response */ - private processIdentifyQueue(): void { - while (this.identifyQueue.length > 0) { - const item = this.identifyQueue.shift(); - if (item) { - this.identifyUser(item.user); - } - } + public getPullRequestsMerged(): Observable { + return this.http.get('/api/analytics/pull-requests-merged').pipe( + catchError((error) => { + console.error('Failed to fetch pull requests merged:', error); + return of({ + data: [], + totalPullRequests: 0, + totalDays: 0, + }); + }) + ); } /** - * Set up automatic page tracking on route navigation + * Get code commits data for the current user + * @returns Observable of code commits response */ - private setupRouteTracking(): void { - this.router.events - .pipe( - filter((event) => event instanceof NavigationEnd), - takeUntilDestroyed(this.destroyRef) - ) - .subscribe((event: NavigationEnd) => { - const pageName = event.urlAfterRedirects.split('/').pop() || 'Home'; - this.trackPage(pageName, { - path: event.urlAfterRedirects, - url: window.location.href, - title: document.title, + public getCodeCommits(): Observable { + return this.http.get('/api/analytics/code-commits').pipe( + catchError((error) => { + console.error('Failed to fetch code commits:', error); + return of({ + data: [], + totalCommits: 0, + totalDays: 0, }); - }); + }) + ); } } diff --git a/apps/lfx-one/src/app/shared/services/segment.service.ts b/apps/lfx-one/src/app/shared/services/segment.service.ts new file mode 100644 index 00000000..c14b0df1 --- /dev/null +++ b/apps/lfx-one/src/app/shared/services/segment.service.ts @@ -0,0 +1,192 @@ +// Copyright The Linux Foundation and each contributor to LFX. +// SPDX-License-Identifier: MIT + +import { afterNextRender, DestroyRef, inject, Injectable } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { NavigationEnd, Router } from '@angular/router'; +import { environment } from '@environments/environment'; +import { LfxSegmentAnalytics, LfxSegmentAnalyticsClass } from '@lfx-one/shared/interfaces'; +import { filter } from 'rxjs'; + +declare global { + interface Window { + LfxAnalytics?: { + LfxSegmentsAnalytics: LfxSegmentAnalyticsClass; + }; + } +} + +/** + * Segment tracking service for Segment integration + * Uses Angular 19's afterNextRender for SSR-safe script loading + */ +@Injectable({ + providedIn: 'root', +}) +export class SegmentService { + private readonly router = inject(Router); + private readonly destroyRef = inject(DestroyRef); + + private scriptLoaded = false; + private analyticsReady = false; + private analytics?: LfxSegmentAnalytics; + private identifyQueue: Array<{ user: unknown }> = []; + + /** + * Initialize the analytics service - should be called from app component + */ + public initialize(): void { + // SSR-safe initialization using afterNextRender + afterNextRender(() => { + this.loadSegmentScript(); + this.setupRouteTracking(); + }); + } + + /** + * Track a page view + * @param pageName Page name + * @param properties Optional page properties + */ + public trackPage(pageName: string, properties?: Record): void { + if (!this.analyticsReady || !this.analytics) { + return; + } + + try { + this.analytics.page(pageName, properties); + } catch (error) { + console.error('Error tracking page with Segment:', error); + } + } + + /** + * Track a custom event + * @param eventName Event name + * @param properties Event properties + */ + public trackEvent(eventName: string, properties?: Record): void { + if (!this.analyticsReady || !this.analytics) { + return; + } + + try { + this.analytics.track(eventName, properties); + } catch (error) { + console.error('Error tracking event with Segment:', error); + } + } + + /** + * Identify an Auth0 user with Segment + * @param auth0User Auth0 user object + */ + public identifyUser(auth0User: unknown): void { + if (!auth0User) { + return; + } + + // If analytics is not ready yet, queue the identify call + if (!this.analyticsReady || !this.analytics) { + this.identifyQueue.push({ user: auth0User }); + return; + } + + try { + this.analytics.identifyAuth0User(auth0User); + } catch (error) { + console.error('Error identifying user with Segment:', error); + } + } + + /** + * Load Segment script from CDN + */ + private loadSegmentScript(): void { + if (!environment.segment?.enabled || this.scriptLoaded) { + return; + } + + try { + const script = document.createElement('script'); + script.src = environment.segment.cdnUrl; + script.async = true; + script.defer = true; + + script.onload = () => { + this.scriptLoaded = true; + this.waitForAnalytics(); + }; + + script.onerror = (error) => { + console.error('Failed to load Segment analytics script:', error); + }; + + document.head.appendChild(script); + } catch (error) { + console.error('Error initializing Segment:', error); + } + } + + /** + * Wait for analytics object to be available and initialize it + */ + private async waitForAnalytics(): Promise { + const maxAttempts = 100; // 10 seconds max wait + let attempts = 0; + + while (attempts < maxAttempts) { + if (window?.LfxAnalytics?.LfxSegmentsAnalytics) { + this.analytics = window.LfxAnalytics.LfxSegmentsAnalytics.getInstance(); + + try { + await this.analytics.init(); + this.analyticsReady = true; + + // Process any queued identify calls + this.processIdentifyQueue(); + return; + } catch (error) { + console.error('Error initializing LfxSegmentsAnalytics:', error); + return; + } + } + + await new Promise((resolve) => setTimeout(resolve, 100)); + attempts++; + } + + console.error('LfxSegmentsAnalytics not available after timeout'); + } + + /** + * Process queued identify calls + */ + private processIdentifyQueue(): void { + while (this.identifyQueue.length > 0) { + const item = this.identifyQueue.shift(); + if (item) { + this.identifyUser(item.user); + } + } + } + + /** + * Set up automatic page tracking on route navigation + */ + private setupRouteTracking(): void { + this.router.events + .pipe( + filter((event) => event instanceof NavigationEnd), + takeUntilDestroyed(this.destroyRef) + ) + .subscribe((event: NavigationEnd) => { + const pageName = event.urlAfterRedirects.split('/').pop() || 'Home'; + this.trackPage(pageName, { + path: event.urlAfterRedirects, + url: window.location.href, + title: document.title, + }); + }); + } +} diff --git a/apps/lfx-one/src/server/controllers/analytics.controller.ts b/apps/lfx-one/src/server/controllers/analytics.controller.ts new file mode 100644 index 00000000..4ce1ed0c --- /dev/null +++ b/apps/lfx-one/src/server/controllers/analytics.controller.ts @@ -0,0 +1,228 @@ +// Copyright The Linux Foundation and each contributor to LFX. +// SPDX-License-Identifier: MIT + +import { + ActiveWeeksStreakResponse, + ActiveWeeksStreakRow, + UserCodeCommitsResponse, + UserCodeCommitsRow, + UserPullRequestsResponse, + UserPullRequestsRow, +} from '@lfx-one/shared/interfaces'; +import { NextFunction, Request, Response } from 'express'; + +import { AuthenticationError, ResourceNotFoundError } from '../errors'; +import { Logger } from '../helpers/logger'; +import { SnowflakeService } from '../services/snowflake.service'; + +/** + * Controller for handling analytics HTTP requests + * Fetches analytics data from Snowflake for dashboard visualizations + */ +export class AnalyticsController { + private snowflakeService: SnowflakeService | null = null; + + /** + * GET /api/analytics/active-weeks-streak + * Get active weeks streak data for the authenticated user + */ + public async getActiveWeeksStreak(req: Request, res: Response, next: NextFunction): Promise { + const startTime = Logger.start(req, 'get_active_weeks_streak'); + + try { + // Get user email from authenticated session + const userEmail = req.oidc?.user?.['email']; + + if (!userEmail) { + throw new AuthenticationError('User email not found in authentication context', { + operation: 'get_active_weeks_streak', + }); + } + + // Execute Snowflake query with parameterized email + const query = ` + SELECT WEEKS_AGO, IS_ACTIVE + FROM ANALYTICS_DEV.DEV_JEVANS_PLATINUM_LFX_ONE.ACTIVE_WEEKS_STREAK + WHERE EMAIL = ? + ORDER BY WEEKS_AGO ASC + LIMIT 52 + `; + + const result = await this.getSnowflakeService().execute(query, [userEmail]); + + // If no data found for user, return 404 + if (result.rows.length === 0) { + throw new ResourceNotFoundError('Active weeks streak data', userEmail, { + operation: 'get_active_weeks_streak', + }); + } + + // Calculate current streak (consecutive weeks of activity from week 0) + // Data is ordered by WEEKS_AGO ASC, so index 0 is week 0 (current week) + let currentStreak = 0; + + for (const row of result.rows) { + if (row.IS_ACTIVE === 1) { + currentStreak++; + } else { + break; // Stop at first inactive week + } + } + + // Build response + const response: ActiveWeeksStreakResponse = { + data: result.rows, + currentStreak, + totalWeeks: result.rows.length, + }; + + Logger.success(req, 'get_active_weeks_streak', startTime, { + email: userEmail, + total_weeks: result.rows.length, + current_streak: currentStreak, + }); + + res.json(response); + } catch (error) { + Logger.error(req, 'get_active_weeks_streak', startTime, error); + next(error); + } + } + + /** + * GET /api/analytics/pull-requests-merged + * Get pull requests merged activity data for the authenticated user + */ + public async getPullRequestsMerged(req: Request, res: Response, next: NextFunction): Promise { + const startTime = Logger.start(req, 'get_pull_requests_merged'); + + try { + // Get user email from authenticated session + const userEmail = req.oidc?.user?.['email']; + + if (!userEmail) { + throw new AuthenticationError('User email not found in authentication context', { + operation: 'get_pull_requests_merged', + }); + } + + // Execute Snowflake query with parameterized email + // Use window function to calculate total in SQL for accuracy + // Filter for last 30 days of data + const query = ` + SELECT + ACTIVITY_DATE, + DAILY_COUNT, + SUM(DAILY_COUNT) OVER () as TOTAL_COUNT + FROM ANALYTICS_DEV.DEV_JEVANS_PLATINUM_LFX_ONE.USER_PULL_REQUESTS + WHERE EMAIL = ? + AND ACTIVITY_DATE >= DATEADD(DAY, -30, CURRENT_DATE()) + ORDER BY ACTIVITY_DATE ASC + `; + + const result = await this.getSnowflakeService().execute(query, [userEmail]); + + // If no data found for user, return 404 + if (result.rows.length === 0) { + throw new ResourceNotFoundError('Pull requests data', userEmail, { + operation: 'get_pull_requests_merged', + }); + } + + // Get total from SQL calculation (same value on all rows from window function) + const totalPullRequests = result.rows[0].TOTAL_COUNT; + + // Build response + const response: UserPullRequestsResponse = { + data: result.rows, + totalPullRequests, + totalDays: result.rows.length, + }; + + Logger.success(req, 'get_pull_requests_merged', startTime, { + email: userEmail, + total_days: result.rows.length, + total_pull_requests: totalPullRequests, + }); + + res.json(response); + } catch (error) { + Logger.error(req, 'get_pull_requests_merged', startTime, error); + next(error); + } + } + + /** + * GET /api/analytics/code-commits + * Get code commits activity data for the authenticated user + */ + public async getCodeCommits(req: Request, res: Response, next: NextFunction): Promise { + const startTime = Logger.start(req, 'get_code_commits'); + + try { + // Get user email from authenticated session + const userEmail = req.oidc?.user?.['email']; + + if (!userEmail) { + throw new AuthenticationError('User email not found in authentication context', { + operation: 'get_code_commits', + }); + } + + // Execute Snowflake query with parameterized email + // Use window function to calculate total in SQL for accuracy + // Filter for last 30 days of data + const query = ` + SELECT + ACTIVITY_DATE, + DAILY_COUNT, + SUM(DAILY_COUNT) OVER () as TOTAL_COUNT + FROM ANALYTICS_DEV.DEV_JEVANS_PLATINUM_LFX_ONE.USER_CODE_COMMITS + WHERE EMAIL = ? + AND ACTIVITY_DATE >= DATEADD(DAY, -30, CURRENT_DATE()) + ORDER BY ACTIVITY_DATE ASC + `; + + const result = await this.getSnowflakeService().execute(query, [userEmail]); + + // If no data found for user, return 404 + if (result.rows.length === 0) { + throw new ResourceNotFoundError('Code commits data', userEmail, { + operation: 'get_code_commits', + }); + } + + // Get total from SQL calculation (same value on all rows from window function) + const totalCommits = result.rows[0].TOTAL_COUNT; + + // Build response + const response: UserCodeCommitsResponse = { + data: result.rows, + totalCommits, + totalDays: result.rows.length, + }; + + Logger.success(req, 'get_code_commits', startTime, { + email: userEmail, + total_days: result.rows.length, + total_commits: totalCommits, + }); + + res.json(response); + } catch (error) { + Logger.error(req, 'get_code_commits', startTime, error); + next(error); + } + } + + /** + * Lazy initialization of SnowflakeService + * Ensures serverLogger is fully initialized before creating the service + */ + private getSnowflakeService(): SnowflakeService { + if (!this.snowflakeService) { + this.snowflakeService = new SnowflakeService(); + } + return this.snowflakeService; + } +} diff --git a/apps/lfx-one/src/server/controllers/profile.controller.ts b/apps/lfx-one/src/server/controllers/profile.controller.ts index c337ebc0..7b3338ba 100644 --- a/apps/lfx-one/src/server/controllers/profile.controller.ts +++ b/apps/lfx-one/src/server/controllers/profile.controller.ts @@ -8,6 +8,7 @@ import { UpdateEmailPreferencesRequest, UserMetadata, UserMetadataUpdateRequest, + UserProfile, } from '@lfx-one/shared/interfaces'; import { NextFunction, Request, Response } from 'express'; @@ -26,16 +27,17 @@ export class ProfileController { /** * GET /api/profile - Get current user's combined profile + * Uses NATS as the sole authoritative source for user metadata */ public async getCurrentUserProfile(req: Request, res: Response, next: NextFunction): Promise { const startTime = Logger.start(req, 'get_current_user_profile'); try { - // Get user ID from auth context - const userId = await getUsernameFromAuth(req); + // Get username from auth context + const username = await getUsernameFromAuth(req); - if (!userId) { - Logger.error(req, 'get_current_user_profile', startTime, new Error('User not authenticated or user ID not found')); + if (!username) { + Logger.error(req, 'get_current_user_profile', startTime, new Error('User not authenticated or username not found')); const validationError = ServiceValidationError.forField('user_id', 'User authentication required', { operation: 'get_current_user_profile', @@ -46,99 +48,69 @@ export class ProfileController { return next(validationError); } - let combinedProfile: CombinedProfile | null = null; + // Get OIDC user data to construct UserProfile + const oidcUser = req.oidc?.user; + + if (!oidcUser) { + Logger.error(req, 'get_current_user_profile', startTime, new Error('OIDC user data not available')); + + const validationError = ServiceValidationError.forField('user_id', 'User authentication data not available', { + operation: 'get_current_user_profile', + service: 'profile_controller', + path: req.path, + }); + + return next(validationError); + } - // Step 1: Try to get user metadata from NATS first (authoritative source) + // Get user metadata from NATS (authoritative source) let natsUserData: UserMetadata | null = null; try { - const natsResponse = await this.userService.getUserInfo(req, userId); - req.log.info({ userId, natsSuccess: natsResponse.success }, 'Fetched user data from NATS'); + const natsResponse = await this.userService.getUserInfo(req, username); + req.log.info({ username, natsSuccess: natsResponse.success }, 'Fetched user metadata from NATS'); if (natsResponse.success && natsResponse.data) { natsUserData = natsResponse.data; + } else { + req.log.warn( + { + username, + error: natsResponse.error, + }, + 'Failed to fetch user metadata from NATS' + ); } } catch (error) { - // Log but don't fail - we'll fall back to Supabase req.log.warn( { - userId, + username, error: error instanceof Error ? error.message : error, }, - 'Failed to fetch user data from NATS, will use Supabase as fallback' + 'Exception while fetching user metadata from NATS' ); } - // Step 2: Get user from Supabase - const user = await this.supabaseService.getUser(userId); - - if (!user) { - const validationError = ServiceValidationError.forField('user_id', 'User profile not found', { - operation: 'get_current_user_profile', - service: 'profile_controller', - path: req.path, - }); - - return next(validationError); - } - - // Step 3: Merge NATS data into Supabase user if available - if (natsUserData) { - // Merge fields from NATS, preferring NATS data when available - if (natsUserData.given_name) user.first_name = natsUserData.given_name; - if (natsUserData.family_name) user.last_name = natsUserData.family_name; - // Note: email and username are not part of UserMetadata, they come from the user table - } - - combinedProfile = { - user, - profile: null, + // Construct UserProfile from OIDC token data + const userProfile: UserProfile = { + id: oidcUser['sub'] as string, + email: oidcUser['email'] as string, + first_name: (natsUserData?.given_name || oidcUser['given_name'] || oidcUser['first_name'] || null) as string | null, + last_name: (natsUserData?.family_name || oidcUser['family_name'] || oidcUser['last_name'] || null) as string | null, + username: (oidcUser['username'] || oidcUser['preferred_username'] || username) as string, + created_at: (oidcUser['created_at'] || new Date().toISOString()) as string, + updated_at: (oidcUser['updated_at'] || new Date().toISOString()) as string, }; - // Step 4: Get profile details from Supabase - const profile = await this.supabaseService.getProfile(user.id); - - // If no profile details exist, create them - if (!profile) { - await this.supabaseService.createProfileIfNotExists(user.id); - // Refetch the combined profile with the newly created profile - const updatedProfile = await this.supabaseService.getProfile(user.id); - combinedProfile.profile = updatedProfile || null; - } else { - combinedProfile.profile = profile; - } - - // Step 5: Merge NATS metadata into profile if available - if (natsUserData && combinedProfile.profile) { - // Merge all available fields from NATS into the profile - const fieldsToMerge: (keyof UserMetadata)[] = [ - 'name', - 'given_name', - 'family_name', - 'picture', - 'phone_number', - 'address', - 'city', - 'state_province', - 'postal_code', - 'country', - 'organization', - 'title', - 't_shirt_size', - 'zoneinfo', - ]; - - const natsData = natsUserData; - fieldsToMerge.forEach((field) => { - if (natsData[field] !== undefined && natsData[field] !== null) { - (combinedProfile.profile as UserMetadata)[field] = natsData[field]; - } - }); - } + // Build CombinedProfile response + const combinedProfile: CombinedProfile = { + user: userProfile, + profile: natsUserData, + }; Logger.success(req, 'get_current_user_profile', startTime, { - user_id: user.id, - has_profile_details: !!combinedProfile.profile, - used_nats_data: !!natsUserData, + user_id: userProfile.id, + username, + has_metadata: !!natsUserData, }); res.json(combinedProfile); diff --git a/apps/lfx-one/src/server/routes/analytics.route.ts b/apps/lfx-one/src/server/routes/analytics.route.ts new file mode 100644 index 00000000..5ae29c11 --- /dev/null +++ b/apps/lfx-one/src/server/routes/analytics.route.ts @@ -0,0 +1,17 @@ +// Copyright The Linux Foundation and each contributor to LFX. +// SPDX-License-Identifier: MIT + +import { Router } from 'express'; + +import { AnalyticsController } from '../controllers/analytics.controller'; + +const router = Router(); + +const analyticsController = new AnalyticsController(); + +// Analytics data routes +router.get('/active-weeks-streak', (req, res, next) => analyticsController.getActiveWeeksStreak(req, res, next)); +router.get('/pull-requests-merged', (req, res, next) => analyticsController.getPullRequestsMerged(req, res, next)); +router.get('/code-commits', (req, res, next) => analyticsController.getCodeCommits(req, res, next)); + +export default router; diff --git a/apps/lfx-one/src/server/server.ts b/apps/lfx-one/src/server/server.ts index e73f3cf2..dec08c25 100644 --- a/apps/lfx-one/src/server/server.ts +++ b/apps/lfx-one/src/server/server.ts @@ -17,6 +17,7 @@ import pinoPretty from 'pino-pretty'; import { validateAndSanitizeUrl } from './helpers/url-validation'; import { authMiddleware } from './middleware/auth.middleware'; import { apiErrorHandler } from './middleware/error-handler.middleware'; +import analyticsRouter from './routes/analytics.route'; import committeesRouter from './routes/committees.route'; import meetingsRouter from './routes/meetings.route'; import organizationsRouter from './routes/organizations.route'; @@ -160,7 +161,7 @@ const authConfig: ConfigParams = { authorizationParams: { response_type: 'code', audience: process.env['PCC_AUTH0_AUDIENCE'] || 'https://example.com', - scope: 'openid email profile access:api offline_access', + scope: 'openid email profile access:api offline_access update:current_user_metadata', }, clientSecret: process.env['PCC_AUTH0_CLIENT_SECRET'] || 'bar', routes: { @@ -205,6 +206,7 @@ app.use('/api/organizations', organizationsRouter); app.use('/api/past-meetings', pastMeetingsRouter); app.use('/api/profile', profileRouter); app.use('/api/search', searchRouter); +app.use('/api/analytics', analyticsRouter); // Add API error handler middleware app.use('/api/*', apiErrorHandler); diff --git a/apps/lfx-one/src/server/services/snowflake.service.ts b/apps/lfx-one/src/server/services/snowflake.service.ts new file mode 100644 index 00000000..72252dc2 --- /dev/null +++ b/apps/lfx-one/src/server/services/snowflake.service.ts @@ -0,0 +1,348 @@ +// Copyright The Linux Foundation and each contributor to LFX. +// SPDX-License-Identifier: MIT + +import { SNOWFLAKE_CONFIG } from '@lfx-one/shared/constants'; +import { SnowflakeLockStrategy } from '@lfx-one/shared/enums'; +import { LockStats, SnowflakePoolStats, SnowflakeQueryOptions, SnowflakeQueryResult } from '@lfx-one/shared/interfaces'; +import snowflakeSdk from 'snowflake-sdk'; + +import { serverLogger } from '../server'; +import { LockManager } from '../utils/lock-manager'; + +import type { Bind, Connection, ConnectionOptions, LogLevel, Pool, PoolOptions, RowStatement, SnowflakeError } from 'snowflake-sdk'; +const { createPool } = snowflakeSdk; + +/** + * Service for executing read-only queries against Snowflake DBT + * + * Features: + * - Connection pooling for efficient resource management + * - Query deduplication to prevent duplicate execution + * - SQL injection protection via parameterized queries + * - Read-only enforcement at multiple layers + * - Private key JWT authentication + */ +export class SnowflakeService { + private pool: Pool | null = null; + private poolPromise: Promise> | null = null; + private lockManager: LockManager; + + public constructor() { + // Configure Snowflake SDK logging (defaults to ERROR to minimize verbose logs) + // Valid levels: 'OFF', 'ERROR', 'WARN', 'INFO', 'DEBUG', 'TRACE' + const logLevel = (process.env['SNOWFLAKE_LOG_LEVEL'] || 'ERROR') as LogLevel; + snowflakeSdk.configure({ logLevel }); + + // Initialize lock manager with configured strategy + const lockStrategy = (process.env['SNOWFLAKE_LOCK_STRATEGY'] || 'memory') as SnowflakeLockStrategy; + this.lockManager = new LockManager(lockStrategy); + + serverLogger.info( + { + lock_strategy: lockStrategy, + snowflake_log_level: logLevel, + }, + 'SnowflakeService initialized' + ); + } + + /** + * Execute a read-only query with deduplication and connection pooling + * + * @param sqlText - SQL query text with ? placeholders for bind parameters + * @param binds - Array of bind parameters (prevents SQL injection, Date objects will be converted to ISO strings) + * @param options - Query execution options (timeout, fetchAsString) + * @returns Query result with rows and metadata + */ + public async execute(sqlText: string, binds?: (Bind | Date)[], options?: SnowflakeQueryOptions): Promise> { + // Validate that query is read-only + this.validateReadOnlyQuery(sqlText); + + // Generate query hash for deduplication + const queryHash = this.lockManager.hashQuery(sqlText, binds); + + // Execute with lock to prevent duplicate queries + return this.lockManager.executeLocked(queryHash, async () => { + const startTime = Date.now(); + const pool = await this.ensurePool(); + + try { + serverLogger.info( + { + query_hash: queryHash, + sql_preview: sqlText.substring(0, 100), + bind_count: binds?.length || 0, + }, + 'Executing Snowflake query' + ); + + // Execute query with parameterized binds + const result: any = await new Promise((resolve, reject) => { + pool.use(async (connection: Connection) => { + connection.execute({ + sqlText, + binds: binds as any[], + fetchAsString: options?.fetchAsString, + complete: (err: SnowflakeError | undefined, stmt: RowStatement, rows: any[] | undefined) => { + if (err) { + reject(err); + } else { + resolve({ + rows, + metadata: stmt.getColumns(), + statementHandle: stmt.getQueryId(), + }); + } + }, + }); + }); + }); + + const duration = Date.now() - startTime; + const poolStats = this.getPoolStats(); + + serverLogger.info( + { + query_hash: queryHash, + duration_ms: duration, + row_count: result.rows.length, + pool_active: poolStats.activeConnections, + pool_idle: poolStats.idleConnections, + }, + 'Snowflake query executed successfully' + ); + + return result as SnowflakeQueryResult; + } catch (error) { + const duration = Date.now() - startTime; + + serverLogger.error( + { + query_hash: queryHash, + duration_ms: duration, + error: error instanceof Error ? error.message : error, + sql_preview: sqlText.substring(0, 100), + }, + 'Snowflake query execution failed' + ); + + throw error; + } + }); + } + + /** + * Check if service is connected to Snowflake + */ + public isConnected(): boolean { + return this.pool !== null; + } + + /** + * Get connection pool statistics + * + * Maps generic-pool properties to our interface: + * - borrowed → activeConnections (resources currently in use) + * - available → idleConnections (unused resources in pool) + * - pending → waitingRequests (callers waiting for a resource) + * - size → totalConnections (total resources: borrowed + available) + */ + public getPoolStats(): SnowflakePoolStats { + if (!this.pool) { + return { + activeConnections: 0, + idleConnections: 0, + waitingRequests: 0, + totalConnections: 0, + }; + } + + return { + activeConnections: this.pool.borrowed || 0, + idleConnections: this.pool.available || 0, + waitingRequests: this.pool.pending || 0, + totalConnections: this.pool.size || 0, + }; + } + + /** + * Get lock manager statistics + */ + public getLockStats(): LockStats { + return this.lockManager.getStats(); + } + + /** + * Gracefully shutdown the service + */ + public async shutdown(): Promise { + serverLogger.info('Shutting down SnowflakeService'); + + // Shutdown lock manager + this.lockManager.shutdown(); + + // Drain connection pool + if (this.pool) { + try { + // Race drain operation against timeout + const drainPromise = this.pool.drain(); + const timeoutPromise = new Promise((_, reject) => { + setTimeout(() => reject(new Error('Pool drain timeout after 30 seconds')), 30000); + }); + + await Promise.race([drainPromise, timeoutPromise]); + serverLogger.info('Snowflake connection pool drained successfully'); + } catch (error) { + serverLogger.error({ error: error instanceof Error ? error.message : error }, 'Error during Snowflake pool shutdown'); + } + + this.pool = null; + } + } + + /** + * Ensure connection pool exists with lazy initialization + * @private + */ + private async ensurePool(): Promise> { + // Return existing pool if valid + if (this.pool) { + return this.pool; + } + + // If already creating pool, wait for it + if (this.poolPromise) { + return this.poolPromise; + } + + // Create new pool with thread safety + this.poolPromise = this.createPool(); + + try { + this.pool = await this.poolPromise; + return this.pool; + } catch (error) { + // Reset promise on failure + this.poolPromise = null; + throw error; + } finally { + // Reset promise after completion + this.poolPromise = null; + } + } + + /** + * Create a new Snowflake connection pool + * @private + */ + private async createPool(): Promise> { + serverLogger.info('Creating Snowflake connection pool'); + + // Get private key from environment variable + const privateKey: string | undefined = process.env['SNOWFLAKE_API_KEY']; + + if (!privateKey) { + throw new Error('Snowflake authentication failed: SNOWFLAKE_API_KEY environment variable must be set'); + } + + serverLogger.info('Using SNOWFLAKE_API_KEY from environment variable'); + + // Pool configuration + const minConnections = Number(process.env['SNOWFLAKE_MIN_CONNECTIONS']) || SNOWFLAKE_CONFIG.MIN_CONNECTIONS; + const maxConnections = Number(process.env['SNOWFLAKE_MAX_CONNECTIONS']) || SNOWFLAKE_CONFIG.MAX_CONNECTIONS; + + const connectionOptions: ConnectionOptions = { + account: process.env['SNOWFLAKE_ACCOUNT'] as string, + username: process.env['SNOWFLAKE_USERNAME'] as string, + role: process.env['SNOWFLAKE_ROLE'] as string, + authenticator: 'SNOWFLAKE_JWT', + privateKey: privateKey, + schema: 'PUBLIC', + database: process.env['SNOWFLAKE_DATABASE'] as string, + warehouse: process.env['SNOWFLAKE_WAREHOUSE'] as string, + timeout: SNOWFLAKE_CONFIG.CONNECTION_TIMEOUT, + }; + + const poolOptions: PoolOptions = { + max: maxConnections, + min: minConnections, + }; + + try { + const pool = createPool(connectionOptions, poolOptions); + + serverLogger.info( + { + min_connections: minConnections, + max_connections: maxConnections, + account: process.env['SNOWFLAKE_ACCOUNT'], + warehouse: process.env['SNOWFLAKE_WAREHOUSE'], + database: process.env['SNOWFLAKE_DATABASE'], + }, + 'Snowflake connection pool created successfully' + ); + + return pool; + } catch (error) { + serverLogger.error( + { + error: error instanceof Error ? error.message : error, + account: process.env['SNOWFLAKE_ACCOUNT'], + }, + 'Failed to create Snowflake connection pool' + ); + throw error; + } + } + + /** + * Validate that a query is read-only + * Blocks INSERT, UPDATE, DELETE, DROP, CREATE, ALTER, TRUNCATE, MERGE anywhere in query + * This includes write operations inside CTEs (Common Table Expressions) + * @private + */ + private validateReadOnlyQuery(sqlText: string): void { + const normalizedSql = sqlText.trim().toUpperCase(); + + // Block write operations ANYWHERE in the query (including inside CTEs) + // Using \b for word boundaries to avoid false positives in identifiers + const writePatterns = [ + /\bINSERT\s+INTO\b/i, + /\bUPDATE\s+/i, + /\bDELETE\s+FROM\b/i, + /\bDROP\s+/i, + /\bCREATE\s+/i, + /\bALTER\s+/i, + /\bTRUNCATE\s+/i, + /\bMERGE\s+INTO\b/i, + /\bGRANT\s+/i, + /\bREVOKE\s+/i, + /\bEXECUTE\s+/i, + /\bCALL\s+/i, + ]; + + for (const pattern of writePatterns) { + if (pattern.test(normalizedSql)) { + serverLogger.error( + { + sql_preview: normalizedSql.substring(0, 100), + matched_pattern: pattern.toString(), + }, + 'Blocked query with write operation (including CTEs)' + ); + throw new Error('Only SELECT queries are allowed. Write operations detected.'); + } + } + + // Ensure query starts with SELECT or WITH (for CTEs) + if (!/^\s*SELECT\b/i.test(normalizedSql) && !/^\s*WITH\b/i.test(normalizedSql)) { + serverLogger.error( + { + sql_preview: normalizedSql.substring(0, 100), + }, + 'Blocked non-SELECT query (not starting with SELECT or WITH)' + ); + throw new Error('Only SELECT queries are allowed'); + } + } +} diff --git a/apps/lfx-one/src/server/services/user.service.ts b/apps/lfx-one/src/server/services/user.service.ts index 8ee0d94c..3545fd18 100644 --- a/apps/lfx-one/src/server/services/user.service.ts +++ b/apps/lfx-one/src/server/services/user.service.ts @@ -6,8 +6,8 @@ import { NatsSubjects } from '@lfx-one/shared/enums'; import { UserMetadata, UserMetadataUpdateRequest, UserMetadataUpdateResponse } from '@lfx-one/shared/interfaces'; import { Request } from 'express'; -import { serverLogger } from '../server'; import { ResourceNotFoundError } from '../errors'; +import { serverLogger } from '../server'; import { NatsService } from './nats.service'; /** @@ -205,7 +205,7 @@ export class UserService { } // Validate job title if provided (basic length check) - if (metadata?.title && metadata.title.length > 200) { + if (metadata?.job_title && metadata.job_title.length > 200) { throw new Error('Job title is too long'); } diff --git a/apps/lfx-one/src/server/utils/auth-helper.ts b/apps/lfx-one/src/server/utils/auth-helper.ts index b3141566..54c4e5d6 100644 --- a/apps/lfx-one/src/server/utils/auth-helper.ts +++ b/apps/lfx-one/src/server/utils/auth-helper.ts @@ -13,35 +13,7 @@ export async function getUsernameFromAuth(req: Request): Promise if (token) { // If token starts with "authelia", query the authelia userinfo endpoint if (token.startsWith('authelia')) { - try { - const response = await fetch('https://auth.k8s.orb.local/api/oidc/userinfo', { - headers: { - Authorization: `Bearer ${token}`, - ['Content-Type']: 'application/json', - }, - }); - - if (response.ok) { - const userInfo = await response.json(); - return userInfo.preferred_username || userInfo.username || null; - } - req.log.warn( - { - status: response.status, - statusText: response.statusText, - }, - 'Failed to fetch authelia userinfo' - ); - return null; - } catch (error) { - req.log.warn( - { - error: error instanceof Error ? error.message : error, - }, - 'Error fetching authelia userinfo' - ); - return null; - } + return req.oidc?.user?.['preferred_username'] || null; } } diff --git a/apps/lfx-one/src/server/utils/lock-manager.ts b/apps/lfx-one/src/server/utils/lock-manager.ts new file mode 100644 index 00000000..1dc63fe3 --- /dev/null +++ b/apps/lfx-one/src/server/utils/lock-manager.ts @@ -0,0 +1,245 @@ +// Copyright The Linux Foundation and each contributor to LFX. +// SPDX-License-Identifier: MIT + +import { SNOWFLAKE_CONFIG } from '@lfx-one/shared/constants'; +import { SnowflakeLockStrategy } from '@lfx-one/shared/enums'; +import { LockEntry, LockStats } from '@lfx-one/shared/interfaces'; +import crypto from 'crypto'; +import type { Bind } from 'snowflake-sdk'; + +import { serverLogger } from '../server'; + +/** + * Lock manager for query deduplication (fetch lock pattern) + * Prevents multiple concurrent executions of identical queries + * + * Supports two strategies: + * - memory: In-memory Map for single-instance deployments + * - redis: Distributed locking for multi-instance deployments (future) + */ +export class LockManager { + private strategy: SnowflakeLockStrategy; + private memoryLocks: Map; + private cleanupInterval: NodeJS.Timeout | null = null; + private totalHits = 0; + private totalMisses = 0; + + // Future Redis integration + // private redisClient?: Redis; + + public constructor(strategy: SnowflakeLockStrategy = SnowflakeLockStrategy.MEMORY) { + this.strategy = strategy; + this.memoryLocks = new Map(); + + if (strategy === SnowflakeLockStrategy.MEMORY) { + this.startCleanup(); + serverLogger.info({ strategy: 'memory' }, 'LockManager initialized with in-memory strategy'); + } + + // Future Redis integration + // if (strategy === SnowflakeLockStrategy.REDIS) { + // this.redisClient = new Redis(process.env['REDIS_URL']); + // serverLogger.info({ strategy: 'redis' }, 'LockManager initialized with Redis strategy'); + // } + } + + /** + * Execute a function with locking to prevent duplicate executions + * If the same key is already locked, returns the existing Promise + * + * @param key - Unique identifier for the operation (typically query hash) + * @param executor - Function to execute if lock is acquired + * @returns Result of the executor function + */ + public async executeLocked(key: string, executor: () => Promise): Promise { + if (this.strategy === SnowflakeLockStrategy.MEMORY) { + return this.executeLockedMemory(key, executor); + } + + // Future Redis implementation + // if (this.strategy === SnowflakeLockStrategy.REDIS) { + // return this.executeLockedRedis(key, executor); + // } + + throw new Error(`Unsupported lock strategy: ${this.strategy}`); + } + + /** + * Generate a deterministic hash for query + binds + * Used as the key for deduplication + * + * @param sqlText - SQL query text + * @param binds - Query bind parameters (supports Date objects for normalization) + * @returns SHA256 hash of normalized query + */ + public hashQuery(sqlText: string, binds?: (Bind | Date)[]): string { + const normalized = { + sql: sqlText.trim().toLowerCase().replace(/\s+/g, ' '), + binds: this.normalizeBinds(binds), + }; + + return crypto.createHash('sha256').update(JSON.stringify(normalized)).digest('hex'); + } + + /** + * Get lock manager statistics + */ + public getStats(): LockStats { + const total = this.totalHits + this.totalMisses; + + return { + activeLocks: this.memoryLocks.size, + totalHits: this.totalHits, + totalMisses: this.totalMisses, + deduplicationRate: total > 0 ? (this.totalHits / total) * 100 : 0, + }; + } + + /** + * Cleanup resources + */ + public shutdown(): void { + if (this.cleanupInterval) { + clearInterval(this.cleanupInterval); + this.cleanupInterval = null; + } + + this.memoryLocks.clear(); + + // Future Redis cleanup + // if (this.redisClient) { + // this.redisClient.quit(); + // } + + serverLogger.info('LockManager shutdown complete'); + } + + /** + * Execute with in-memory locking + * @private + */ + private async executeLockedMemory(key: string, executor: () => Promise): Promise { + const existing = this.memoryLocks.get(key); + + if (existing) { + // Query already executing - wait for it (deduplication hit) + existing.waiters++; + this.totalHits++; + + serverLogger.info( + { + query_hash: key, + waiters: existing.waiters, + is_dedupe_hit: true, + }, + 'Query deduplication hit - reusing existing execution' + ); + + return existing.promise; + } + + // New query - execute and store (deduplication miss) + this.totalMisses++; + + const promise = executor(); + const lockEntry: LockEntry = { + promise, + timestamp: Date.now(), + waiters: 0, + }; + + this.memoryLocks.set(key, lockEntry); + + serverLogger.info( + { + query_hash: key, + is_dedupe_hit: false, + }, + 'Executing new query' + ); + + try { + const result = await promise; + return result; + } finally { + // Clean up lock after execution with buffer time + setTimeout(() => { + this.memoryLocks.delete(key); + }, SNOWFLAKE_CONFIG.LOCK_TTL_BUFFER); + } + } + + /** + * Future: Execute with Redis distributed locking + * @private + */ + // private async executeLockedRedis(key: string, executor: () => Promise): Promise { + // const lockKey = `snowflake:query:${key}`; + // const lockValue = uuidv4(); + // const ttl = SNOWFLAKE_CONFIG.DEFAULT_QUERY_TIMEOUT + SNOWFLAKE_CONFIG.LOCK_TTL_BUFFER; + // + // // Try to acquire lock with SET NX + // const acquired = await this.redisClient.set(lockKey, lockValue, 'NX', 'PX', ttl); + // + // if (!acquired) { + // // Lock held by another instance - implement wait/poll logic + // return this.waitForRedisLock(lockKey, executor); + // } + // + // try { + // const result = await executor(); + // return result; + // } finally { + // // Release lock if we still own it (compare-and-delete) + // const script = ` + // if redis.call("get", KEYS[1]) == ARGV[1] then + // return redis.call("del", KEYS[1]) + // else + // return 0 + // end + // `; + // await this.redisClient.eval(script, 1, lockKey, lockValue); + // } + // } + + /** + * Normalize bind parameters for deterministic hashing + * Converts Date objects to ISO strings for consistent comparison + * @private + */ + private normalizeBinds(binds?: (Bind | Date)[]): Bind[] { + if (!binds) return []; + + return binds.map((bind) => { + // Convert dates to ISO strings for consistent comparison + if (bind instanceof Date) { + return bind.toISOString(); + } + return bind; + }); + } + + /** + * Start periodic cleanup of stale locks + * @private + */ + private startCleanup(): void { + this.cleanupInterval = setInterval(() => { + const now = Date.now(); + const maxAge = SNOWFLAKE_CONFIG.DEFAULT_QUERY_TIMEOUT + SNOWFLAKE_CONFIG.LOCK_TTL_BUFFER; + + for (const [key, entry] of this.memoryLocks.entries()) { + if (now - entry.timestamp > maxAge) { + this.memoryLocks.delete(key); + serverLogger.info( + { + query_hash: key, + age_ms: now - entry.timestamp, + }, + 'Cleaned up stale lock' + ); + } + } + }, SNOWFLAKE_CONFIG.LOCK_CLEANUP_INTERVAL); + } +} diff --git a/docs/architecture/backend/snowflake-integration.md b/docs/architecture/backend/snowflake-integration.md new file mode 100644 index 00000000..13cf166c --- /dev/null +++ b/docs/architecture/backend/snowflake-integration.md @@ -0,0 +1,1093 @@ +# Snowflake Integration + +## 🚀 Overview + +The LFX One application integrates with **Snowflake** for read-only analytical queries against the DBT (Data Build Tool) data warehouse. This integration provides high-performance data access with enterprise-grade security, connection pooling, and intelligent query deduplication to optimize resource utilization and reduce costs. + +### Key Features + +- **Read-Only Access**: Multi-layer enforcement ensures only SELECT queries execute +- **SQL Injection Protection**: Parameterized queries with strict validation +- **Connection Pooling**: Efficient resource management with configurable pool sizing +- **Query Deduplication**: Intelligent fetch lock prevents duplicate execution of identical queries +- **Private Key Authentication**: Secure JWT-based authentication +- **Future-Ready**: Redis-ready architecture for distributed locking across service instances + +## 🏗 Architecture + +### Snowflake Integration Pattern + +```text +LFX One ←→ Snowflake Service ←→ Connection Pool ←→ Snowflake DBT + ↓ ↓ ↓ ↓ +Analytics Query Connection Data +Services Deduplication Management Warehouse + (Lock Manager) +``` + +### Core Components + +- **Snowflake Service** (`/server/services/snowflake.service.ts`): Main service managing queries, pooling, and security +- **Lock Manager** (`/server/utils/lock-manager.ts`): Query deduplication utility with in-memory and Redis strategies +- **Connection Pool**: Snowflake SDK managed pool for concurrent query execution +- **Shared Interfaces** (`@lfx-one/shared`): TypeScript interfaces, constants, and enums + +## 🔧 Implementation Details + +### Snowflake Service Architecture + +```typescript +export class SnowflakeService { + private pool: Pool | null = null; + private poolPromise: Promise> | null = null; + private lockManager: LockManager; + + public constructor() { + // Lazy initialization - pool created on first query + const lockStrategy = (process.env['SNOWFLAKE_LOCK_STRATEGY'] || 'memory') as SnowflakeLockStrategy; + this.lockManager = new LockManager(lockStrategy); + } +} +``` + +## 🏊 Connection Pooling + +### Pool Configuration + +The service uses Snowflake SDK's connection pooling for efficient resource management: + +```typescript +private async createPool(): Promise> { + // Method 1: Try direct env var first (for containerized deployments) + let privateKey: string | undefined = process.env['SNOWFLAKE_API_KEY']; + + // Method 2: Fall back to rsa_key.p8 file in app root (same location as .env) + if (!privateKey) { + const privateKeyPath = path.join(__dirname, '../../../rsa_key.p8'); + + if (!existsSync(privateKeyPath)) { + throw new Error('Either SNOWFLAKE_API_KEY or rsa_key.p8 file must be present'); + } + + const privateKeyFile = readFileSync(privateKeyPath); + + const privateKeyObject = crypto.createPrivateKey({ + key: privateKeyFile, + format: 'pem', + passphrase: process.env['SNOWFLAKE_PRIVATE_KEY_PASSPHRASE'], + }); + + privateKey = privateKeyObject.export({ + format: 'pem', + type: 'pkcs8', + }) as string; + } + + const connectionOptions: ConnectionOptions = { + account: process.env['SNOWFLAKE_ACCOUNT'] as string, + username: process.env['SNOWFLAKE_USERNAME'] as string, + role: process.env['SNOWFLAKE_ROLE'] as string, + authenticator: 'SNOWFLAKE_JWT', + privateKey: privateKey, + schema: 'PUBLIC', // Hardcoded schema + database: process.env['SNOWFLAKE_DATABASE'] as string, + warehouse: process.env['SNOWFLAKE_WAREHOUSE'] as string, + timeout: SNOWFLAKE_CONFIG.CONNECTION_TIMEOUT, + }; + + const poolOptions: PoolOptions = { + max: Number(process.env['SNOWFLAKE_MAX_CONNECTIONS']) || SNOWFLAKE_CONFIG.MAX_CONNECTIONS, + min: Number(process.env['SNOWFLAKE_MIN_CONNECTIONS']) || SNOWFLAKE_CONFIG.MIN_CONNECTIONS, + }; + + const pool = createPool(connectionOptions, poolOptions); + + return pool; +} +``` + +### Pool Benefits + +- **Concurrent Execution**: Multiple queries execute simultaneously without blocking +- **Resource Optimization**: Reuses connections instead of creating new ones +- **Auto-Management**: SDK handles connection health, timeouts, and lifecycle +- **Graceful Degradation**: Configurable behavior when pool exhausted + +### Pool Monitoring + +The Snowflake SDK uses the `generic-pool` library which exposes the following properties: + +- **`borrowed`** - Resources currently in use +- **`available`** - Unused resources in the pool +- **`pending`** - Callers waiting to acquire a resource +- **`size`** - Total resources (borrowed + available) + +```typescript +/** + * Get connection pool statistics + * + * Maps generic-pool properties to our interface: + * - borrowed → activeConnections (resources currently in use) + * - available → idleConnections (unused resources in pool) + * - pending → waitingRequests (callers waiting for a resource) + * - size → totalConnections (total resources: borrowed + available) + */ +public getPoolStats(): SnowflakePoolStats { + if (!this.pool) { + return { + activeConnections: 0, + idleConnections: 0, + waitingRequests: 0, + totalConnections: 0, + }; + } + + // Snowflake SDK uses generic-pool which exposes: borrowed, available, pending, size + const poolAny = this.pool as any; + + return { + activeConnections: poolAny.borrowed || 0, + idleConnections: poolAny.available || 0, + waitingRequests: poolAny.pending || 0, + totalConnections: poolAny.size || 0, + }; +} +``` + +## 🔒 Query Deduplication (Fetch Lock) + +### Problem Statement + +When multiple callers request the same data simultaneously (cache stampede), we want to execute the query once and share the result rather than hitting Snowflake multiple times with identical queries. + +### Solution Architecture + +```text +Request 1 (SELECT * FROM projects WHERE id = '123') ──┐ + ├──> Query Hash ──> Check Lock +Request 2 (SELECT * FROM projects WHERE id = '123') ──┤ ↓ ↓ + │ [abc123hash] Lock Exists? +Request 3 (SELECT * FROM projects WHERE id = '123') ──┘ ↓ ↓ + If EXISTS: If NEW: + Return Execute Query + Existing Store Promise + Promise Share Result + ↓ ↓ + All requests get same result +``` + +### Query Hash Generation + +```typescript +// In lock-manager.ts +public hashQuery(sqlText: string, binds?: (Bind | Date)[]): string { + const normalized = { + sql: sqlText.trim().toLowerCase().replace(/\s+/g, ' '), + binds: this.normalizeBinds(binds), + }; + + return crypto + .createHash('sha256') + .update(JSON.stringify(normalized)) + .digest('hex'); +} + +private normalizeBinds(binds?: (Bind | Date)[]): Bind[] { + if (!binds) return []; + + return binds.map((bind) => { + if (bind instanceof Date) { + return bind.toISOString(); + } + return bind; + }); +} +``` + +### Lock Manager Implementation + +```typescript +// /server/utils/lock-manager.ts +export class LockManager { + private strategy: SnowflakeLockStrategy; + private memoryLocks: Map; + private cleanupInterval: NodeJS.Timeout | null = null; + + constructor(strategy: SnowflakeLockStrategy = SnowflakeLockStrategy.MEMORY) { + this.strategy = strategy; + this.memoryLocks = new Map(); + + if (strategy === SnowflakeLockStrategy.MEMORY) { + this.startCleanup(); + } + + // Future Redis integration: + // if (strategy === SnowflakeLockStrategy.REDIS) { + // this.redisClient = new Redis(process.env['REDIS_URL']); + // } + } + + async executeLocked(key: string, executor: () => Promise): Promise { + if (this.strategy === SnowflakeLockStrategy.MEMORY) { + return this.executeLockedMemory(key, executor); + } + + // Future Redis implementation: + // return this.executeLockedRedis(key, executor); + + throw new Error(`Unsupported lock strategy: ${this.strategy}`); + } + + private async executeLockedMemory(key: string, executor: () => Promise): Promise { + const existing = this.memoryLocks.get(key); + + if (existing) { + // Query already executing - wait for it + existing.waiters++; + serverLogger.info( + { + query_hash: key, + waiters: existing.waiters, + is_dedupe_hit: true, + }, + 'Query deduplication hit - reusing existing execution' + ); + return existing.promise; + } + + // New query - execute and store + const promise = executor(); + const lockEntry: LockEntry = { + promise, + timestamp: Date.now(), + waiters: 0, + }; + + this.memoryLocks.set(key, lockEntry); + + try { + const result = await promise; + return result; + } finally { + // Clean up lock after execution + setTimeout(() => { + this.memoryLocks.delete(key); + }, SNOWFLAKE_CONFIG.LOCK_TTL_BUFFER); + } + } +} +``` + +### Memory Management + +The in-memory lock manager automatically cleans up stale locks: + +```typescript +private startCleanup(): void { + this.cleanupInterval = setInterval(() => { + const now = Date.now(); + const maxAge = SNOWFLAKE_CONFIG.DEFAULT_QUERY_TIMEOUT + SNOWFLAKE_CONFIG.LOCK_TTL_BUFFER; + + for (const [key, entry] of this.memoryLocks.entries()) { + if (now - entry.timestamp > maxAge) { + this.memoryLocks.delete(key); + serverLogger.info({ query_hash: key }, 'Cleaned up stale lock'); + } + } + }, SNOWFLAKE_CONFIG.LOCK_CLEANUP_INTERVAL); +} +``` + +### Lock Statistics + +```typescript +public getStats(): LockStats { + return { + activeLocks: this.memoryLocks.size, + totalHits: this.totalHits, + totalMisses: this.totalMisses, + deduplicationRate: this.totalHits + this.totalMisses > 0 + ? (this.totalHits / (this.totalHits + this.totalMisses)) * 100 + : 0, + }; +} +``` + +## 🛡️ Security Features + +### SQL Injection Protection + +The service uses multiple layers of protection against SQL injection: + +#### 1. Parameterized Queries (Primary Defense) + +```typescript +public async execute( + sqlText: string, + binds?: (Bind | Date)[], + options?: SnowflakeQueryOptions +): Promise> { + // Validate read-only + this.validateReadOnlyQuery(sqlText); + + // Generate query hash for deduplication + const queryHash = this.lockManager.hashQuery(sqlText, binds); + + // Execute with lock to prevent duplicate queries + return this.lockManager.executeLocked(queryHash, async () => { + const pool = await this.ensurePool(); + + const result: any = await new Promise((resolve, reject) => { + pool.use(async (connection: Connection) => { + connection.execute({ + sqlText, + binds: binds as any[], // ← Parameterized binds prevent injection + fetchAsString: options?.fetchAsString, + complete: (err: SnowflakeError | undefined, stmt: RowStatement, rows: any[] | undefined) => { + if (err) { + reject(err); + } else { + resolve({ + rows, + metadata: stmt.getColumns(), + statementHandle: stmt.getQueryId(), + }); + } + }, + }); + }); + }); + + return result as SnowflakeQueryResult; + }); +} +``` + +#### 2. Read-Only Validation (Secondary Defense) + +The service validates queries are read-only by checking for write operations **anywhere** in the query, including inside CTEs (Common Table Expressions): + +```typescript +/** + * Validate that a query is read-only + * Blocks INSERT, UPDATE, DELETE, DROP, CREATE, ALTER, TRUNCATE, MERGE anywhere in query + * This includes write operations inside CTEs (Common Table Expressions) + */ +private validateReadOnlyQuery(sqlText: string): void { + const normalizedSql = sqlText.trim().toUpperCase(); + + // Block write operations ANYWHERE in the query (including inside CTEs) + // Using \b for word boundaries to avoid false positives in identifiers + const writePatterns = [ + /\bINSERT\s+INTO\b/i, + /\bUPDATE\s+/i, + /\bDELETE\s+FROM\b/i, + /\bDROP\s+/i, + /\bCREATE\s+/i, + /\bALTER\s+/i, + /\bTRUNCATE\s+/i, + /\bMERGE\s+INTO\b/i, + /\bGRANT\s+/i, + /\bREVOKE\s+/i, + /\bEXECUTE\s+/i, + /\bCALL\s+/i, + ]; + + for (const pattern of writePatterns) { + if (pattern.test(normalizedSql)) { + serverLogger.error( + { + sql_preview: normalizedSql.substring(0, 100), + matched_pattern: pattern.toString(), + }, + 'Blocked query with write operation (including CTEs)' + ); + throw new Error('Only SELECT queries are allowed. Write operations detected.'); + } + } + + // Ensure query starts with SELECT or WITH (for CTEs) + if (!/^\s*SELECT\b/i.test(normalizedSql) && !/^\s*WITH\b/i.test(normalizedSql)) { + throw new Error('Only SELECT queries are allowed'); + } +} +``` + +**Security Enhancement**: This validation prevents CTE-based bypass attempts like: + +```sql +-- ❌ BLOCKED: Write operation inside CTE +WITH updated AS ( + UPDATE users SET active = false RETURNING * +) +SELECT * FROM updated; + +-- ✅ ALLOWED: Read-only CTE +WITH user_stats AS ( + SELECT user_id, COUNT(*) as activity_count + FROM activities + GROUP BY user_id +) +SELECT * FROM user_stats WHERE activity_count > 10; +``` + +The validation uses word boundaries (`\b`) to avoid false positives while catching write operations anywhere in the query text. + +#### 3. Database-Level Permissions (Tertiary Defense) + +The Snowflake role used by the service should have SELECT-only permissions: + +```sql +-- Snowflake DBA Setup +GRANT USAGE ON DATABASE analytics_db TO ROLE lfx_one_reader; +GRANT USAGE ON SCHEMA analytics_db.dbt TO ROLE lfx_one_reader; +GRANT SELECT ON ALL TABLES IN SCHEMA analytics_db.dbt TO ROLE lfx_one_reader; +GRANT SELECT ON FUTURE TABLES IN SCHEMA analytics_db.dbt TO ROLE lfx_one_reader; + +-- Revoke any write permissions +REVOKE INSERT, UPDATE, DELETE, TRUNCATE ON ALL TABLES IN SCHEMA analytics_db.dbt FROM ROLE lfx_one_reader; +``` + +### Private Key Authentication + +The service uses JWT-based authentication with RSA key pairs: + +#### Key Generation + +```bash +# Generate private key +openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out snowflake_rsa_key.p8 -nocrypt + +# Generate public key +openssl rsa -in snowflake_rsa_key.p8 -pubout -out snowflake_rsa_key.pub + +# Get public key fingerprint for Snowflake +openssl rsa -pubin -in snowflake_rsa_key.pub -outform DER | openssl dgst -sha256 -binary | openssl enc -base64 +``` + +#### Snowflake Configuration + +```sql +-- Add public key to Snowflake user +ALTER USER lfx_one_service_user SET RSA_PUBLIC_KEY='MIIBIjANBgk...'; +``` + +#### Service Configuration + +The service supports two authentication methods: + +**Method 1: Direct API Key** (recommended for containers/Docker) + +```typescript +// Set in environment variable +SNOWFLAKE_API_KEY = your - private - key - here; +``` + +**Method 2: File-based** (recommended for local development) + +```typescript +// Place rsa_key.p8 file in app root directory (apps/lfx-one/) +// Service automatically looks for it at: path.join(__dirname, '../../../rsa_key.p8') +// Optional passphrase can be set via: +SNOWFLAKE_PRIVATE_KEY_PASSPHRASE = your_passphrase; +``` + +## ⚙️ Configuration + +### Environment Variables + +```bash +############### SNOWFLAKE CONFIG ############### +# Snowflake Connection Configuration +# Account identifier in format: orgname-accountname +SNOWFLAKE_ACCOUNT=jnmhvwd-xpb85243 +SNOWFLAKE_USERNAME=DEV_ADESILVA +SNOWFLAKE_WAREHOUSE=LF_DEVELOPMENT_WH +SNOWFLAKE_DATABASE=ANALYTICS +SNOWFLAKE_ROLE=LF_DEVELOPER_R_ROLE + +# Snowflake Authentication (choose one method) +# Method 1: Direct API Key (recommended for containers/Docker) +# SNOWFLAKE_API_KEY=your-private-key-here + +# Method 2: Private Key File (recommended for local development) +# Place rsa_key.p8 file in app root directory (same location as .env) +# The service will automatically detect and use it +# SNOWFLAKE_PRIVATE_KEY_PASSPHRASE=your-passphrase-here + +# Optional: Connection Pool Configuration (defaults shown) +# SNOWFLAKE_MIN_CONNECTIONS=2 +# SNOWFLAKE_MAX_CONNECTIONS=10 + +# Optional: Lock Strategy for Query Deduplication (defaults to 'memory') +# SNOWFLAKE_LOCK_STRATEGY=memory +############### END SNOWFLAKE CONFIG ############### + +# Redis (future - for distributed locking) +# REDIS_URL=redis://localhost:6379 +``` + +**Example values shown above are for development environment. For production:** + +- Use production account identifier +- Configure appropriate warehouse size +- Use production-specific role with audited permissions +- Store private keys in secure secrets management (Kubernetes Secrets, AWS Secrets Manager, etc.) + +### Configuration Constants + +```typescript +// packages/shared/src/constants/snowflake.constant.ts +export const SNOWFLAKE_CONFIG = { + // Timeouts + DEFAULT_QUERY_TIMEOUT: 60000, // 60 seconds + CONNECTION_TIMEOUT: 30000, // 30 seconds + + // Pool configuration + MIN_CONNECTIONS: 2, + MAX_CONNECTIONS: 10, + + // Lock management + LOCK_CLEANUP_INTERVAL: 60000, // 60 seconds + LOCK_TTL_BUFFER: 5000, // 5 seconds + + // Retry + MAX_RETRIES: 3, +} as const; +``` + +## 📦 TypeScript Interfaces + +### Core Interfaces + +```typescript +// packages/shared/src/interfaces/snowflake.interface.ts +import type { Column, DataType } from 'snowflake-sdk'; + +export interface SnowflakeQueryResult { + rows: T[]; + metadata: Column[]; // Using SDK's Column type + statementHandle?: string; +} + +export interface SnowflakeQueryOptions { + timeout?: number; + fetchAsString?: DataType[]; // SDK's DataType enum for type conversion +} + +export interface SnowflakePoolStats { + activeConnections: number; + idleConnections: number; + waitingRequests: number; + totalConnections: number; +} + +export interface LockStats { + activeLocks: number; + totalHits: number; + totalMisses: number; + deduplicationRate: number; // Percentage +} + +interface LockEntry { + promise: Promise; + timestamp: number; + waiters: number; +} +``` + +### Enums + +```typescript +// packages/shared/src/enums/snowflake.enum.ts + +export enum SnowflakeLockStrategy { + MEMORY = 'memory', + REDIS = 'redis', +} +``` + +## 🔗 Service Integration + +### Using the Snowflake Service + +The `SnowflakeService` provides a simple, secure interface for executing read-only analytical queries: + +```typescript +import { SnowflakeService } from '../services/snowflake.service'; +import type { Bind } from 'snowflake-sdk'; + +export class AnalyticsController { + private snowflakeService: SnowflakeService | null = null; + + /** + * Lazy initialization of SnowflakeService + */ + private getSnowflakeService(): SnowflakeService { + if (!this.snowflakeService) { + this.snowflakeService = new SnowflakeService(); + } + return this.snowflakeService; + } + + /** + * Example: Query user activity data + */ + async getUserActivity(userEmail: string, startDate: Date) { + // Execute parameterized query with automatic connection pooling and deduplication + const result = await this.getSnowflakeService().execute( + `SELECT + activity_date, + activity_count + FROM analytics_db.user_activity + WHERE email = ? + AND activity_date >= ? + ORDER BY activity_date ASC`, + [userEmail, startDate] // Date objects are automatically normalized + ); + + return result.rows; + } +} +``` + +### Best Practices for Callers + +1. **Lazy Initialization**: Create `SnowflakeService` instances on-demand to avoid startup overhead +2. **Parameterized Queries**: Always use `?` placeholders with bind parameters - never concatenate user input +3. **Date Handling**: Pass `Date` objects directly as bind parameters - they're automatically converted to ISO strings +4. **Type Safety**: Define TypeScript interfaces for query result rows +5. **Error Handling**: Catch and handle Snowflake-specific errors appropriately +6. **Query Optimization**: Use specific column selection, appropriate WHERE clauses, and leverage Snowflake features + +### Example with Advanced Features + +```typescript +async getAggregatedMetrics(filters: MetricFilters) { + const binds: (Bind | Date)[] = [filters.startDate, filters.endDate]; + + // Build dynamic query with parameterized filters + let sql = ` + SELECT + DATE_TRUNC('day', recorded_at) as date, + COUNT(*) as event_count, + SUM(metric_value) as total_value + FROM analytics_events + WHERE recorded_at BETWEEN ? AND ? + `; + + // Add optional filters with proper parameterization + if (filters.projectIds?.length) { + sql += ` AND project_id IN (${filters.projectIds.map(() => '?').join(',')})`; + binds.push(...filters.projectIds); + } + + sql += " GROUP BY DATE_TRUNC('day', recorded_at) ORDER BY date"; + + // Execute with type-safe result + const result = await this.getSnowflakeService().execute(sql, binds); + + return result.rows; +} +``` + +### Service Lifecycle Management + +```typescript +// Graceful shutdown on application termination +async shutdown(): Promise { + if (this.snowflakeService) { + await this.snowflakeService.shutdown(); + } +} +``` + +## 📊 Monitoring and Logging + +### Structured Logging + +```typescript +// Query execution logging +serverLogger.info( + { + query_hash: 'abc123...', + is_dedupe_hit: false, + pool_active: 5, + pool_idle: 3, + pool_waiting: 0, + execution_time_ms: 1234, + row_count: 150, + }, + 'Snowflake query executed successfully' +); + +// Deduplication hit +serverLogger.info( + { + query_hash: 'def456...', + is_dedupe_hit: true, + waiters: 3, + saved_queries: 3, + }, + 'Query deduplication hit - reused existing execution' +); + +// Pool exhaustion warning +serverLogger.warn( + { + pool_active: 10, + pool_idle: 0, + pool_waiting: 5, + pool_utilization: 100, + }, + 'Snowflake pool exhausted - consider increasing max connections' +); +``` + +### Key Metrics + +Track these metrics for operational visibility: + +- **Query Performance**: + - Average query execution time + - P95/P99 query latency + - Slow query count (>5s) + +- **Pool Utilization**: + - Active connections / Max connections (%) + - Average idle connections + - Pool exhaustion events + - Connection acquire wait time + +- **Deduplication Effectiveness**: + - Deduplication hit rate (%) + - Average waiters per deduplicated query + - Queries saved per minute + - Active locks count + +- **Error Rates**: + - Query failures (%) + - Connection failures (%) + - Timeout errors + - Authentication failures + +### Health Check Integration + +```typescript +// Add to server health endpoint +app.get('/health', (req, res) => { + const snowflakeStats = snowflakeService.getPoolStats(); + const lockStats = snowflakeService.getLockStats(); + + const healthStatus = { + snowflake: { + connected: snowflakeService.isConnected(), + pool: { + active: snowflakeStats.activeConnections, + idle: snowflakeStats.idleConnections, + waiting: snowflakeStats.waitingRequests, + total: snowflakeStats.totalConnections, + utilization: (snowflakeStats.activeConnections / snowflakeStats.totalConnections) * 100, + }, + locks: { + active: lockStats.activeLocks, + deduplication_rate: lockStats.deduplicationRate, + }, + }, + }; + + res.json(healthStatus); +}); +``` + +## 🔧 Development and Troubleshooting + +### Local Development Setup + +For local development, ensure you have: + +1. **Private Key File**: Store securely outside repository +2. **Environment Variables**: Configure in `.env` file +3. **Snowflake Access**: Verify role has SELECT permissions + +```bash +# .env (development example) +SNOWFLAKE_ACCOUNT=jnmhvwd-xpb85243 +SNOWFLAKE_USERNAME=DEV_ADESILVA +SNOWFLAKE_WAREHOUSE=LF_DEVELOPMENT_WH +SNOWFLAKE_DATABASE=ANALYTICS +SNOWFLAKE_ROLE=LF_DEVELOPER_R_ROLE + +# Authentication Method 1: Direct API Key (recommended for containers) +SNOWFLAKE_API_KEY=your-private-key-here + +# Authentication Method 2: Private Key File (recommended for local development) +# Place rsa_key.p8 file in app root directory (same location as .env) +# The service will automatically detect and use it +# SNOWFLAKE_PRIVATE_KEY_PASSPHRASE=your_passphrase + +# Optional: Override default pool settings +# SNOWFLAKE_MIN_CONNECTIONS=2 +# SNOWFLAKE_MAX_CONNECTIONS=5 +``` + +**Note**: The example values above are for the development environment. Copy `.env.example` to `.env` and update with your actual credentials. + +### Common Issues and Solutions + +#### 1. Authentication Failures + +```text +Error: JWT token verification failed +Cause: Invalid private key or public key not registered in Snowflake +Solution: + 1. Verify private key file exists and is readable + 2. Check public key is correctly added to Snowflake user + 3. Ensure key fingerprint matches +``` + +#### 2. Pool Exhaustion + +```text +Error: Timeout acquiring connection from pool +Cause: All connections busy, max pool size reached +Solution: + 1. Increase SNOWFLAKE_MAX_CONNECTIONS + 2. Optimize slow queries + 3. Implement query result caching + 4. Check for connection leaks (unreleased connections) +``` + +#### 3. Query Timeout + +```text +Error: Query execution timeout +Cause: Query taking longer than configured timeout +Solution: + 1. Optimize query with indexes/partitions + 2. Increase SNOWFLAKE_CONFIG.DEFAULT_QUERY_TIMEOUT + 3. Consider breaking into smaller queries + 4. Check Snowflake warehouse size +``` + +#### 4. Read-Only Validation Failure + +```text +Error: Only SELECT queries are allowed +Cause: Attempted to execute write operation +Solution: + 1. Verify query is a SELECT statement + 2. Check for CTEs or subqueries with writes + 3. Review application logic for query construction +``` + +### Debugging Commands + +```bash +# Test Snowflake connection +snowsql -a your_account \ + -u lfx_one_service_user \ + --private-key-path /path/to/key.p8 \ + -w compute_wh \ + -d analytics_db \ + -s dbt + +# Check user permissions +SHOW GRANTS TO ROLE lfx_one_reader; + +# Monitor warehouse usage +SELECT * +FROM SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY +WHERE USER_NAME = 'LFX_ONE_SERVICE_USER' + AND START_TIME >= DATEADD(hour, -1, CURRENT_TIMESTAMP()) +ORDER BY START_TIME DESC; + +# Check connection pool stats +curl http://localhost:4000/health | jq '.snowflake.pool' +``` + +## 🎯 Best Practices + +### Performance Optimization + +1. **Query Design**: + - Use specific column selection instead of `SELECT *` + - Add appropriate WHERE clauses to limit data scanned + - Leverage Snowflake clustering keys for large tables + - Use result_scan() for repeated access to same result + +2. **Connection Management**: + - Monitor pool utilization and adjust sizing + - Release connections promptly in finally blocks + - Use connection pooling for all queries + - Avoid long-running transactions + +3. **Deduplication Strategy**: + - Enable for frequently-requested data + - Monitor hit rate and adjust as needed + - Consider result caching for extremely hot queries + - Use appropriate TTL based on data freshness requirements + +4. **Warehouse Sizing**: + - Start with smaller warehouse, scale as needed + - Use auto-suspend and auto-resume + - Consider dedicated warehouse for service queries + - Monitor query queue time + +### Security Best Practices + +1. **Key Management**: + - Never commit private keys to repository + - Use Kubernetes secrets for production + - Rotate keys regularly (every 90 days) + - Restrict file permissions (chmod 600) + +2. **Role Permissions**: + - Grant minimum required permissions (SELECT only) + - Use separate roles for different services + - Audit permissions regularly + - Enable Snowflake session policies + +3. **Query Validation**: + - Always use parameterized queries + - Never concatenate user input into SQL + - Validate input data types + - Log all query attempts with context + +### Code Quality + +1. **Type Safety**: + - Use TypeScript interfaces for query results + - Define result types explicitly + - Leverage SDK's `Bind` type for parameters (accepts `Bind | Date`) + - Use const assertions for configuration + +2. **Error Handling**: + - Catch and log all errors with context + - Provide meaningful error messages + - Implement retry logic for transient failures + - Use try-finally for resource cleanup + +3. **Testing**: + - Mock SnowflakeService for unit tests + - Use test database for integration tests + - Test SQL injection protection + - Verify read-only enforcement + - Load test connection pool behavior + +## 🔮 Future Enhancements + +### Redis Distributed Locking + +When scaling to multiple service instances, migrate to Redis-based locking: + +#### Architecture + +```text +Service Instance 1 ──┐ +Service Instance 2 ──┼──> Redis ──> Distributed Lock ──> Snowflake +Service Instance 3 ──┘ +``` + +#### Implementation + +```typescript +// Future: lock-manager.ts with Redis strategy +private async executeLockedRedis( + key: string, + executor: () => Promise +): Promise { + const lockKey = `snowflake:query:${key}`; + const lockValue = uuidv4(); + const ttl = SNOWFLAKE_CONFIG.DEFAULT_QUERY_TIMEOUT + SNOWFLAKE_CONFIG.LOCK_TTL_BUFFER; + + // Try to acquire lock + const acquired = await this.redisClient.set(lockKey, lockValue, 'NX', 'PX', ttl); + + if (!acquired) { + // Lock held by another instance - wait and poll + return this.waitForLock(lockKey, executor); + } + + try { + const result = await executor(); + return result; + } finally { + // Release lock if we still own it + const script = ` + if redis.call("get", KEYS[1]) == ARGV[1] then + return redis.call("del", KEYS[1]) + else + return 0 + end + `; + await this.redisClient.eval(script, 1, lockKey, lockValue); + } +} +``` + +#### Migration Path + +1. Add `ioredis` dependency +2. Implement `RedisLockManager` strategy in `lock-manager.ts` +3. Configure `REDIS_URL` and `SNOWFLAKE_LOCK_STRATEGY=redis` +4. Deploy with zero downtime (both strategies supported) +5. Monitor deduplication effectiveness across instances + +### Query Result Caching + +Future enhancement to cache query results in Redis: + +```typescript +async execute(sql: string, binds?: (Bind | Date)[]): Promise { + const cacheKey = this.generateCacheKey(sql, binds); + + // Check cache + const cached = await this.cache.get(cacheKey); + if (cached) return cached; + + // Execute with deduplication + const result = await this.executeLocked(sql, binds); + + // Store in cache + await this.cache.set(cacheKey, result, { ttl: 300 }); // 5 min TTL + + return result; +} +``` + +## 📈 Performance Metrics + +### Benchmark Results + +Expected performance characteristics (varies by query complexity): + +- **Simple queries** (<1000 rows): 100-500ms +- **Complex aggregations**: 1-5 seconds +- **Large result sets** (>10K rows): 5-30 seconds +- **Pool acquisition**: <10ms (when connections available) +- **Deduplication overhead**: <1ms (hash computation) + +### Resource Utilization + +Typical resource usage per service instance: + +- **Memory**: 50-100MB baseline + ~1MB per 1000 active locks +- **CPU**: Minimal (<5%) for query management, dependent on Snowflake for execution +- **Network**: Dependent on result set size, typically <10MB/s +- **Connections**: 2-10 concurrent connections to Snowflake + +## 🔗 Related Documentation + +- [Backend Architecture Overview](./README.md) +- [NATS Integration](./nats-integration.md) +- [Authentication & Authorization](./authentication.md) +- [Environment Configuration](../../CLAUDE.md#environment-configuration) + +## 📚 External Resources + +- [Snowflake Node.js Driver Documentation](https://docs.snowflake.com/en/user-guide/nodejs-driver) +- [Snowflake Key Pair Authentication](https://docs.snowflake.com/en/user-guide/key-pair-auth) +- [Snowflake Query Performance Optimization](https://docs.snowflake.com/en/user-guide/ui-snowsight-query-profile) +- [Connection Pooling Best Practices](https://docs.snowflake.com/en/developer-guide/node-js/nodejs-driver-use#using-connection-pools) +- [Redis Distributed Locks](https://redis.io/docs/manual/patterns/distributed-locks/) diff --git a/packages/shared/package.json b/packages/shared/package.json index 3bdd4c0b..48906aac 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -54,7 +54,8 @@ "peerDependencies": { "@angular/forms": "^19.0.0", "@fullcalendar/core": "^6.1.19", - "chart.js": "^4.5.0" + "chart.js": "^4.5.0", + "snowflake-sdk": "^2.3.1" }, "dependencies": { "date-fns": "^4.1.0", diff --git a/packages/shared/src/constants/index.ts b/packages/shared/src/constants/index.ts index e53ca585..fd081094 100644 --- a/packages/shared/src/constants/index.ts +++ b/packages/shared/src/constants/index.ts @@ -16,3 +16,4 @@ export * from './timezones.constants'; export * from './tshirt-sizes.constants'; export * from './action-items.constants'; export * from './progress-metrics.constants'; +export * from './snowflake.constant'; diff --git a/packages/shared/src/constants/snowflake.constant.ts b/packages/shared/src/constants/snowflake.constant.ts new file mode 100644 index 00000000..1c37d660 --- /dev/null +++ b/packages/shared/src/constants/snowflake.constant.ts @@ -0,0 +1,57 @@ +// Copyright The Linux Foundation and each contributor to LFX. +// SPDX-License-Identifier: MIT + +/** + * Configuration constants for Snowflake integration + */ +export const SNOWFLAKE_CONFIG = { + /** + * Default query execution timeout in milliseconds + */ + DEFAULT_QUERY_TIMEOUT: 60000, // 60 seconds + + /** + * Connection timeout in milliseconds + */ + CONNECTION_TIMEOUT: 30000, // 30 seconds + + /** + * Minimum number of connections in the pool + */ + MIN_CONNECTIONS: 2, + + /** + * Maximum number of connections in the pool + */ + MAX_CONNECTIONS: 10, + + /** + * Timeout for acquiring a connection from the pool in milliseconds + */ + CONNECTION_ACQUIRE_TIMEOUT: 30000, // 30 seconds + + /** + * Idle timeout for connections in milliseconds + */ + IDLE_TIMEOUT: 600000, // 10 minutes + + /** + * Maximum lifetime for a connection in milliseconds + */ + MAX_CONNECTION_LIFETIME: 3600000, // 1 hour + + /** + * Interval for cleaning up stale locks in milliseconds + */ + LOCK_CLEANUP_INTERVAL: 60000, // 60 seconds + + /** + * Buffer time added to lock TTL in milliseconds + */ + LOCK_TTL_BUFFER: 5000, // 5 seconds + + /** + * Maximum number of retry attempts for transient failures + */ + MAX_RETRIES: 3, +} as const; diff --git a/packages/shared/src/enums/index.ts b/packages/shared/src/enums/index.ts index 5cc0ba22..ed580361 100644 --- a/packages/shared/src/enums/index.ts +++ b/packages/shared/src/enums/index.ts @@ -5,3 +5,4 @@ export * from './error.enum'; export * from './meeting.enum'; export * from './committee-member.enum'; export * from './nats.enum'; +export * from './snowflake.enum'; diff --git a/packages/shared/src/enums/snowflake.enum.ts b/packages/shared/src/enums/snowflake.enum.ts new file mode 100644 index 00000000..1769ace4 --- /dev/null +++ b/packages/shared/src/enums/snowflake.enum.ts @@ -0,0 +1,17 @@ +// Copyright The Linux Foundation and each contributor to LFX. +// SPDX-License-Identifier: MIT + +/** + * Lock strategy for query deduplication + */ +export enum SnowflakeLockStrategy { + /** + * In-memory locking using Map (single instance) + */ + MEMORY = 'memory', + + /** + * Redis-based distributed locking (multi-instance) + */ + REDIS = 'redis', +} diff --git a/packages/shared/src/interfaces/analytics-data.interface.ts b/packages/shared/src/interfaces/analytics-data.interface.ts new file mode 100644 index 00000000..f7263854 --- /dev/null +++ b/packages/shared/src/interfaces/analytics-data.interface.ts @@ -0,0 +1,120 @@ +// Copyright The Linux Foundation and each contributor to LFX. +// SPDX-License-Identifier: MIT + +/** + * Active Weeks Streak row from Snowflake ACTIVE_WEEKS_STREAK table + * Represents a single week's activity data for a user + */ +export interface ActiveWeeksStreakRow { + /** + * Number of weeks ago from current date (0 = current week, 1 = last week, etc.) + */ + WEEKS_AGO: number; + + /** + * Whether the user was active during this week (1 = active, 0 = inactive) + */ + IS_ACTIVE: number; +} + +/** + * API response for Active Weeks Streak query + */ +export interface ActiveWeeksStreakResponse { + /** + * Array of weekly activity data + */ + data: ActiveWeeksStreakRow[]; + + /** + * Current active streak count (consecutive weeks) + */ + currentStreak: number; + + /** + * Total number of weeks with data + */ + totalWeeks: number; +} + +/** + * User Pull Requests row from Snowflake USER_PULL_REQUESTS table + * Represents daily pull request activity + */ +export interface UserPullRequestsRow { + /** + * Date of the activity (YYYY-MM-DD format) + */ + ACTIVITY_DATE: string; + + /** + * Number of pull requests merged on this date + */ + DAILY_COUNT: number; + + /** + * Total count across all dates (from SQL window function) + */ + TOTAL_COUNT: number; +} + +/** + * API response for User Pull Requests query + */ +export interface UserPullRequestsResponse { + /** + * Array of daily pull request activity data + */ + data: UserPullRequestsRow[]; + + /** + * Total pull requests merged across all dates + */ + totalPullRequests: number; + + /** + * Number of days with data + */ + totalDays: number; +} + +/** + * User Code Commits row from Snowflake USER_CODE_COMMITS table + * Represents daily code commit activity + */ +export interface UserCodeCommitsRow { + /** + * Date of the activity (YYYY-MM-DD format) + */ + ACTIVITY_DATE: string; + + /** + * Number of commits on this date + */ + DAILY_COUNT: number; + + /** + * Total count across all dates (from SQL window function) + */ + TOTAL_COUNT: number; +} + +/** + * API response for User Code Commits query + */ +export interface UserCodeCommitsResponse { + /** + * Array of daily code commit activity data + */ + data: UserCodeCommitsRow[]; + + /** + * Total commits across all dates + */ + totalCommits: number; + + /** + * Number of days with data + */ + totalDays: number; +} diff --git a/packages/shared/src/interfaces/index.ts b/packages/shared/src/interfaces/index.ts index 758a056b..e092f839 100644 --- a/packages/shared/src/interfaces/index.ts +++ b/packages/shared/src/interfaces/index.ts @@ -56,7 +56,11 @@ export * from './user-profile.interface'; export * from './user-statistics.interface'; // Analytics interfaces -export * from './analytics.interface'; +export * from './segment.interface'; +export * from './analytics-data.interface'; // Persona interfaces export * from './persona.interface'; + +// Snowflake interfaces +export * from './snowflake.interface'; diff --git a/packages/shared/src/interfaces/analytics.interface.ts b/packages/shared/src/interfaces/segment.interface.ts similarity index 100% rename from packages/shared/src/interfaces/analytics.interface.ts rename to packages/shared/src/interfaces/segment.interface.ts diff --git a/packages/shared/src/interfaces/snowflake.interface.ts b/packages/shared/src/interfaces/snowflake.interface.ts new file mode 100644 index 00000000..746ec691 --- /dev/null +++ b/packages/shared/src/interfaces/snowflake.interface.ts @@ -0,0 +1,89 @@ +// Copyright The Linux Foundation and each contributor to LFX. +// SPDX-License-Identifier: MIT + +import type { Column, DataType } from 'snowflake-sdk'; + +/** + * Result of a Snowflake query execution + * Uses SDK's Column type for metadata + */ +export interface SnowflakeQueryResult { + rows: T[]; + metadata: Column[]; + statementHandle?: string; +} + +/** + * Options for Snowflake query execution + * Extends SDK's StatementOption properties + */ +export interface SnowflakeQueryOptions { + /** + * Query execution timeout in milliseconds + */ + timeout?: number; + + /** + * Array of data types to fetch as strings (for large numbers, etc.) + */ + fetchAsString?: DataType[]; +} + +/** + * Statistics for the Snowflake connection pool + */ +export interface SnowflakePoolStats { + /** + * Number of connections currently executing queries + */ + activeConnections: number; + + /** + * Number of idle connections available in the pool + */ + idleConnections: number; + + /** + * Number of requests waiting for a connection + */ + waitingRequests: number; + + /** + * Total number of connections in the pool + */ + totalConnections: number; +} + +/** + * Statistics for the lock manager (query deduplication) + */ +export interface LockStats { + /** + * Number of currently active locks + */ + activeLocks: number; + + /** + * Total number of deduplication hits (queries reused) + */ + totalHits: number; + + /** + * Total number of deduplication misses (new queries) + */ + totalMisses: number; + + /** + * Deduplication effectiveness as a percentage + */ + deduplicationRate: number; +} + +/** + * Internal lock entry structure for in-memory locking + */ +export interface LockEntry { + promise: Promise; + timestamp: number; + waiters: number; +} diff --git a/packages/shared/src/interfaces/user-profile.interface.ts b/packages/shared/src/interfaces/user-profile.interface.ts index 7baf50ce..4cc8c0c5 100644 --- a/packages/shared/src/interfaces/user-profile.interface.ts +++ b/packages/shared/src/interfaces/user-profile.interface.ts @@ -119,7 +119,7 @@ export interface UserMetadata { name?: string; given_name?: string; family_name?: string; - title?: string; + job_title?: string; organization?: string; country?: string; state_province?: string; diff --git a/yarn.lock b/yarn.lock index f514049f..fd5a49e5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -519,6 +519,907 @@ __metadata: languageName: node linkType: hard +"@aws-crypto/crc32@npm:5.2.0": + version: 5.2.0 + resolution: "@aws-crypto/crc32@npm:5.2.0" + dependencies: + "@aws-crypto/util": "npm:^5.2.0" + "@aws-sdk/types": "npm:^3.222.0" + tslib: "npm:^2.6.2" + checksum: 10c0/eab9581d3363af5ea498ae0e72de792f54d8890360e14a9d8261b7b5c55ebe080279fb2556e07994d785341cdaa99ab0b1ccf137832b53b5904cd6928f2b094b + languageName: node + linkType: hard + +"@aws-crypto/crc32c@npm:5.2.0": + version: 5.2.0 + resolution: "@aws-crypto/crc32c@npm:5.2.0" + dependencies: + "@aws-crypto/util": "npm:^5.2.0" + "@aws-sdk/types": "npm:^3.222.0" + tslib: "npm:^2.6.2" + checksum: 10c0/223efac396cdebaf5645568fa9a38cd0c322c960ae1f4276bedfe2e1031d0112e49d7d39225d386354680ecefae29f39af469a84b2ddfa77cb6692036188af77 + languageName: node + linkType: hard + +"@aws-crypto/sha1-browser@npm:5.2.0": + version: 5.2.0 + resolution: "@aws-crypto/sha1-browser@npm:5.2.0" + dependencies: + "@aws-crypto/supports-web-crypto": "npm:^5.2.0" + "@aws-crypto/util": "npm:^5.2.0" + "@aws-sdk/types": "npm:^3.222.0" + "@aws-sdk/util-locate-window": "npm:^3.0.0" + "@smithy/util-utf8": "npm:^2.0.0" + tslib: "npm:^2.6.2" + checksum: 10c0/51fed0bf078c10322d910af179871b7d299dde5b5897873ffbeeb036f427e5d11d23db9794439226544b73901920fd19f4d86bbc103ed73cc0cfdea47a83c6ac + languageName: node + linkType: hard + +"@aws-crypto/sha256-browser@npm:5.2.0": + version: 5.2.0 + resolution: "@aws-crypto/sha256-browser@npm:5.2.0" + dependencies: + "@aws-crypto/sha256-js": "npm:^5.2.0" + "@aws-crypto/supports-web-crypto": "npm:^5.2.0" + "@aws-crypto/util": "npm:^5.2.0" + "@aws-sdk/types": "npm:^3.222.0" + "@aws-sdk/util-locate-window": "npm:^3.0.0" + "@smithy/util-utf8": "npm:^2.0.0" + tslib: "npm:^2.6.2" + checksum: 10c0/05f6d256794df800fe9aef5f52f2ac7415f7f3117d461f85a6aecaa4e29e91527b6fd503681a17136fa89e9dd3d916e9c7e4cfb5eba222875cb6c077bdc1d00d + languageName: node + linkType: hard + +"@aws-crypto/sha256-js@npm:5.2.0, @aws-crypto/sha256-js@npm:^5.2.0": + version: 5.2.0 + resolution: "@aws-crypto/sha256-js@npm:5.2.0" + dependencies: + "@aws-crypto/util": "npm:^5.2.0" + "@aws-sdk/types": "npm:^3.222.0" + tslib: "npm:^2.6.2" + checksum: 10c0/6c48701f8336341bb104dfde3d0050c89c288051f6b5e9bdfeb8091cf3ffc86efcd5c9e6ff2a4a134406b019c07aca9db608128f8d9267c952578a3108db9fd1 + languageName: node + linkType: hard + +"@aws-crypto/supports-web-crypto@npm:^5.2.0": + version: 5.2.0 + resolution: "@aws-crypto/supports-web-crypto@npm:5.2.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10c0/4d2118e29d68ca3f5947f1e37ce1fbb3239a0c569cc938cdc8ab8390d595609b5caf51a07c9e0535105b17bf5c52ea256fed705a07e9681118120ab64ee73af2 + languageName: node + linkType: hard + +"@aws-crypto/util@npm:5.2.0, @aws-crypto/util@npm:^5.2.0": + version: 5.2.0 + resolution: "@aws-crypto/util@npm:5.2.0" + dependencies: + "@aws-sdk/types": "npm:^3.222.0" + "@smithy/util-utf8": "npm:^2.0.0" + tslib: "npm:^2.6.2" + checksum: 10c0/0362d4c197b1fd64b423966945130207d1fe23e1bb2878a18e361f7743c8d339dad3f8729895a29aa34fff6a86c65f281cf5167c4bf253f21627ae80b6dd2951 + languageName: node + linkType: hard + +"@aws-sdk/client-s3@npm:^3.726.0": + version: 3.914.0 + resolution: "@aws-sdk/client-s3@npm:3.914.0" + dependencies: + "@aws-crypto/sha1-browser": "npm:5.2.0" + "@aws-crypto/sha256-browser": "npm:5.2.0" + "@aws-crypto/sha256-js": "npm:5.2.0" + "@aws-sdk/core": "npm:3.914.0" + "@aws-sdk/credential-provider-node": "npm:3.914.0" + "@aws-sdk/middleware-bucket-endpoint": "npm:3.914.0" + "@aws-sdk/middleware-expect-continue": "npm:3.914.0" + "@aws-sdk/middleware-flexible-checksums": "npm:3.914.0" + "@aws-sdk/middleware-host-header": "npm:3.914.0" + "@aws-sdk/middleware-location-constraint": "npm:3.914.0" + "@aws-sdk/middleware-logger": "npm:3.914.0" + "@aws-sdk/middleware-recursion-detection": "npm:3.914.0" + "@aws-sdk/middleware-sdk-s3": "npm:3.914.0" + "@aws-sdk/middleware-ssec": "npm:3.914.0" + "@aws-sdk/middleware-user-agent": "npm:3.914.0" + "@aws-sdk/region-config-resolver": "npm:3.914.0" + "@aws-sdk/signature-v4-multi-region": "npm:3.914.0" + "@aws-sdk/types": "npm:3.914.0" + "@aws-sdk/util-endpoints": "npm:3.914.0" + "@aws-sdk/util-user-agent-browser": "npm:3.914.0" + "@aws-sdk/util-user-agent-node": "npm:3.914.0" + "@aws-sdk/xml-builder": "npm:3.914.0" + "@smithy/config-resolver": "npm:^4.4.0" + "@smithy/core": "npm:^3.17.0" + "@smithy/eventstream-serde-browser": "npm:^4.2.3" + "@smithy/eventstream-serde-config-resolver": "npm:^4.3.3" + "@smithy/eventstream-serde-node": "npm:^4.2.3" + "@smithy/fetch-http-handler": "npm:^5.3.4" + "@smithy/hash-blob-browser": "npm:^4.2.4" + "@smithy/hash-node": "npm:^4.2.3" + "@smithy/hash-stream-node": "npm:^4.2.3" + "@smithy/invalid-dependency": "npm:^4.2.3" + "@smithy/md5-js": "npm:^4.2.3" + "@smithy/middleware-content-length": "npm:^4.2.3" + "@smithy/middleware-endpoint": "npm:^4.3.4" + "@smithy/middleware-retry": "npm:^4.4.4" + "@smithy/middleware-serde": "npm:^4.2.3" + "@smithy/middleware-stack": "npm:^4.2.3" + "@smithy/node-config-provider": "npm:^4.3.3" + "@smithy/node-http-handler": "npm:^4.4.2" + "@smithy/protocol-http": "npm:^5.3.3" + "@smithy/smithy-client": "npm:^4.9.0" + "@smithy/types": "npm:^4.8.0" + "@smithy/url-parser": "npm:^4.2.3" + "@smithy/util-base64": "npm:^4.3.0" + "@smithy/util-body-length-browser": "npm:^4.2.0" + "@smithy/util-body-length-node": "npm:^4.2.1" + "@smithy/util-defaults-mode-browser": "npm:^4.3.3" + "@smithy/util-defaults-mode-node": "npm:^4.2.5" + "@smithy/util-endpoints": "npm:^3.2.3" + "@smithy/util-middleware": "npm:^4.2.3" + "@smithy/util-retry": "npm:^4.2.3" + "@smithy/util-stream": "npm:^4.5.3" + "@smithy/util-utf8": "npm:^4.2.0" + "@smithy/util-waiter": "npm:^4.2.3" + "@smithy/uuid": "npm:^1.1.0" + tslib: "npm:^2.6.2" + checksum: 10c0/b97cb38831cc789d68ef7d3d253743c5c225a6389b4e41da3ea81c0cde0993e75e63461d45d819981061e2bc11a6d42b631ebc775f60de99dae1e0027f42cd83 + languageName: node + linkType: hard + +"@aws-sdk/client-sso@npm:3.914.0": + version: 3.914.0 + resolution: "@aws-sdk/client-sso@npm:3.914.0" + dependencies: + "@aws-crypto/sha256-browser": "npm:5.2.0" + "@aws-crypto/sha256-js": "npm:5.2.0" + "@aws-sdk/core": "npm:3.914.0" + "@aws-sdk/middleware-host-header": "npm:3.914.0" + "@aws-sdk/middleware-logger": "npm:3.914.0" + "@aws-sdk/middleware-recursion-detection": "npm:3.914.0" + "@aws-sdk/middleware-user-agent": "npm:3.914.0" + "@aws-sdk/region-config-resolver": "npm:3.914.0" + "@aws-sdk/types": "npm:3.914.0" + "@aws-sdk/util-endpoints": "npm:3.914.0" + "@aws-sdk/util-user-agent-browser": "npm:3.914.0" + "@aws-sdk/util-user-agent-node": "npm:3.914.0" + "@smithy/config-resolver": "npm:^4.4.0" + "@smithy/core": "npm:^3.17.0" + "@smithy/fetch-http-handler": "npm:^5.3.4" + "@smithy/hash-node": "npm:^4.2.3" + "@smithy/invalid-dependency": "npm:^4.2.3" + "@smithy/middleware-content-length": "npm:^4.2.3" + "@smithy/middleware-endpoint": "npm:^4.3.4" + "@smithy/middleware-retry": "npm:^4.4.4" + "@smithy/middleware-serde": "npm:^4.2.3" + "@smithy/middleware-stack": "npm:^4.2.3" + "@smithy/node-config-provider": "npm:^4.3.3" + "@smithy/node-http-handler": "npm:^4.4.2" + "@smithy/protocol-http": "npm:^5.3.3" + "@smithy/smithy-client": "npm:^4.9.0" + "@smithy/types": "npm:^4.8.0" + "@smithy/url-parser": "npm:^4.2.3" + "@smithy/util-base64": "npm:^4.3.0" + "@smithy/util-body-length-browser": "npm:^4.2.0" + "@smithy/util-body-length-node": "npm:^4.2.1" + "@smithy/util-defaults-mode-browser": "npm:^4.3.3" + "@smithy/util-defaults-mode-node": "npm:^4.2.5" + "@smithy/util-endpoints": "npm:^3.2.3" + "@smithy/util-middleware": "npm:^4.2.3" + "@smithy/util-retry": "npm:^4.2.3" + "@smithy/util-utf8": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10c0/cb7fd05ddb40f38cf0ee7d012f8a69df41144aa695bbb33b680fd5ba857b59ef7b342eac3ebd6a1100be77000e59773c2efdea0d1d64e9ea5ea0366541beb120 + languageName: node + linkType: hard + +"@aws-sdk/client-sts@npm:^3.899.0": + version: 3.914.0 + resolution: "@aws-sdk/client-sts@npm:3.914.0" + dependencies: + "@aws-crypto/sha256-browser": "npm:5.2.0" + "@aws-crypto/sha256-js": "npm:5.2.0" + "@aws-sdk/core": "npm:3.914.0" + "@aws-sdk/credential-provider-node": "npm:3.914.0" + "@aws-sdk/middleware-host-header": "npm:3.914.0" + "@aws-sdk/middleware-logger": "npm:3.914.0" + "@aws-sdk/middleware-recursion-detection": "npm:3.914.0" + "@aws-sdk/middleware-user-agent": "npm:3.914.0" + "@aws-sdk/region-config-resolver": "npm:3.914.0" + "@aws-sdk/types": "npm:3.914.0" + "@aws-sdk/util-endpoints": "npm:3.914.0" + "@aws-sdk/util-user-agent-browser": "npm:3.914.0" + "@aws-sdk/util-user-agent-node": "npm:3.914.0" + "@smithy/config-resolver": "npm:^4.4.0" + "@smithy/core": "npm:^3.17.0" + "@smithy/fetch-http-handler": "npm:^5.3.4" + "@smithy/hash-node": "npm:^4.2.3" + "@smithy/invalid-dependency": "npm:^4.2.3" + "@smithy/middleware-content-length": "npm:^4.2.3" + "@smithy/middleware-endpoint": "npm:^4.3.4" + "@smithy/middleware-retry": "npm:^4.4.4" + "@smithy/middleware-serde": "npm:^4.2.3" + "@smithy/middleware-stack": "npm:^4.2.3" + "@smithy/node-config-provider": "npm:^4.3.3" + "@smithy/node-http-handler": "npm:^4.4.2" + "@smithy/protocol-http": "npm:^5.3.3" + "@smithy/smithy-client": "npm:^4.9.0" + "@smithy/types": "npm:^4.8.0" + "@smithy/url-parser": "npm:^4.2.3" + "@smithy/util-base64": "npm:^4.3.0" + "@smithy/util-body-length-browser": "npm:^4.2.0" + "@smithy/util-body-length-node": "npm:^4.2.1" + "@smithy/util-defaults-mode-browser": "npm:^4.3.3" + "@smithy/util-defaults-mode-node": "npm:^4.2.5" + "@smithy/util-endpoints": "npm:^3.2.3" + "@smithy/util-middleware": "npm:^4.2.3" + "@smithy/util-retry": "npm:^4.2.3" + "@smithy/util-utf8": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10c0/d5b39243e6cb6d6cfff93285355cac58549e778da3f9e0065701ff1f41d62a2a78aa5d00d469c52b391a3800c804e3004cbee9ed4b239f228f016eaf9d5743da + languageName: node + linkType: hard + +"@aws-sdk/core@npm:3.914.0": + version: 3.914.0 + resolution: "@aws-sdk/core@npm:3.914.0" + dependencies: + "@aws-sdk/types": "npm:3.914.0" + "@aws-sdk/xml-builder": "npm:3.914.0" + "@smithy/core": "npm:^3.17.0" + "@smithy/node-config-provider": "npm:^4.3.3" + "@smithy/property-provider": "npm:^4.2.3" + "@smithy/protocol-http": "npm:^5.3.3" + "@smithy/signature-v4": "npm:^5.3.3" + "@smithy/smithy-client": "npm:^4.9.0" + "@smithy/types": "npm:^4.8.0" + "@smithy/util-base64": "npm:^4.3.0" + "@smithy/util-middleware": "npm:^4.2.3" + "@smithy/util-utf8": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10c0/5ad6bb8cefbf0385cdea4d0b4cd488b3249fba387f0250c27baec75a022659f0620d69de19dfcb75439a166f4f89a8479fd0644afab80e3187b07eccb59e0807 + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-env@npm:3.914.0": + version: 3.914.0 + resolution: "@aws-sdk/credential-provider-env@npm:3.914.0" + dependencies: + "@aws-sdk/core": "npm:3.914.0" + "@aws-sdk/types": "npm:3.914.0" + "@smithy/property-provider": "npm:^4.2.3" + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/2f9bcabbb6510dff10cefda37c37dae66d0b08d0560abb529965e73ee3a48b93284b9d3729cd54660e33064a1a308bc9a05cc90e0f56c7922d2cc682a7950de8 + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-http@npm:3.914.0": + version: 3.914.0 + resolution: "@aws-sdk/credential-provider-http@npm:3.914.0" + dependencies: + "@aws-sdk/core": "npm:3.914.0" + "@aws-sdk/types": "npm:3.914.0" + "@smithy/fetch-http-handler": "npm:^5.3.4" + "@smithy/node-http-handler": "npm:^4.4.2" + "@smithy/property-provider": "npm:^4.2.3" + "@smithy/protocol-http": "npm:^5.3.3" + "@smithy/smithy-client": "npm:^4.9.0" + "@smithy/types": "npm:^4.8.0" + "@smithy/util-stream": "npm:^4.5.3" + tslib: "npm:^2.6.2" + checksum: 10c0/d68e36f68a1d233c3990ee1e10322725aa129e64d560b29b6fe92ef1360feddb80c3baaab3794eec6e9b5ab1da4ed5768e1f214180e4e527ce0c549336672e40 + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-ini@npm:3.914.0": + version: 3.914.0 + resolution: "@aws-sdk/credential-provider-ini@npm:3.914.0" + dependencies: + "@aws-sdk/core": "npm:3.914.0" + "@aws-sdk/credential-provider-env": "npm:3.914.0" + "@aws-sdk/credential-provider-http": "npm:3.914.0" + "@aws-sdk/credential-provider-process": "npm:3.914.0" + "@aws-sdk/credential-provider-sso": "npm:3.914.0" + "@aws-sdk/credential-provider-web-identity": "npm:3.914.0" + "@aws-sdk/nested-clients": "npm:3.914.0" + "@aws-sdk/types": "npm:3.914.0" + "@smithy/credential-provider-imds": "npm:^4.2.3" + "@smithy/property-provider": "npm:^4.2.3" + "@smithy/shared-ini-file-loader": "npm:^4.3.3" + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/936e84e6806c2fcd1c1a0475575fc9505cad357364a526cfda9e23404a548b3bfe27652dbac4b06c8a2160ace3fc8d39185408d302dd7e97ef0ce91c4db8cc5f + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-node@npm:3.914.0, @aws-sdk/credential-provider-node@npm:^3.823.0": + version: 3.914.0 + resolution: "@aws-sdk/credential-provider-node@npm:3.914.0" + dependencies: + "@aws-sdk/credential-provider-env": "npm:3.914.0" + "@aws-sdk/credential-provider-http": "npm:3.914.0" + "@aws-sdk/credential-provider-ini": "npm:3.914.0" + "@aws-sdk/credential-provider-process": "npm:3.914.0" + "@aws-sdk/credential-provider-sso": "npm:3.914.0" + "@aws-sdk/credential-provider-web-identity": "npm:3.914.0" + "@aws-sdk/types": "npm:3.914.0" + "@smithy/credential-provider-imds": "npm:^4.2.3" + "@smithy/property-provider": "npm:^4.2.3" + "@smithy/shared-ini-file-loader": "npm:^4.3.3" + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/c8b289c4de938bf5735e55c3aa76a43607ff0aedda1ce45586fab2c176c871454847d48368e9a048bc9e1570090cd87b74c04a8a5c11b98316380f76088cefde + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-process@npm:3.914.0": + version: 3.914.0 + resolution: "@aws-sdk/credential-provider-process@npm:3.914.0" + dependencies: + "@aws-sdk/core": "npm:3.914.0" + "@aws-sdk/types": "npm:3.914.0" + "@smithy/property-provider": "npm:^4.2.3" + "@smithy/shared-ini-file-loader": "npm:^4.3.3" + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/0283039f303d8d2f6d41afd564da8b7988bbd1d818c6ddd2b59dc85e4d50b164cc1a5f43705f1e60ed8acd97ce978d3d93241738606571542303f2d13df76f1c + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-sso@npm:3.914.0": + version: 3.914.0 + resolution: "@aws-sdk/credential-provider-sso@npm:3.914.0" + dependencies: + "@aws-sdk/client-sso": "npm:3.914.0" + "@aws-sdk/core": "npm:3.914.0" + "@aws-sdk/token-providers": "npm:3.914.0" + "@aws-sdk/types": "npm:3.914.0" + "@smithy/property-provider": "npm:^4.2.3" + "@smithy/shared-ini-file-loader": "npm:^4.3.3" + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/94cea74468919afa42dc746b42a0726944176b84516eaae4f1395a27d93c55780442523fa9ec258c6263e7956c824b097589af706c0438c12db4e706f0aa6997 + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-web-identity@npm:3.914.0": + version: 3.914.0 + resolution: "@aws-sdk/credential-provider-web-identity@npm:3.914.0" + dependencies: + "@aws-sdk/core": "npm:3.914.0" + "@aws-sdk/nested-clients": "npm:3.914.0" + "@aws-sdk/types": "npm:3.914.0" + "@smithy/property-provider": "npm:^4.2.3" + "@smithy/shared-ini-file-loader": "npm:^4.3.3" + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/665350b4e4e209e7b45d88162ce97579ce5bc14cf46f125d31857a40ee3aad2721cc325fa64916cbc669b84ba761aac44c6faa53a6da68de439443dede0868ca + languageName: node + linkType: hard + +"@aws-sdk/ec2-metadata-service@npm:^3.826.0": + version: 3.915.0 + resolution: "@aws-sdk/ec2-metadata-service@npm:3.915.0" + dependencies: + "@aws-sdk/types": "npm:3.914.0" + "@smithy/node-config-provider": "npm:^4.3.3" + "@smithy/node-http-handler": "npm:^4.4.2" + "@smithy/protocol-http": "npm:^5.3.3" + "@smithy/types": "npm:^4.8.0" + "@smithy/util-stream": "npm:^4.5.3" + tslib: "npm:^2.6.2" + checksum: 10c0/4b0bcaefb1899a0dec843c18354e41013dbbf32499708588ee3fabe97b20a6527759ff01b33da1deff932392957f991bd27c1b5213cef17cbb21066ea6cddc62 + languageName: node + linkType: hard + +"@aws-sdk/middleware-bucket-endpoint@npm:3.914.0": + version: 3.914.0 + resolution: "@aws-sdk/middleware-bucket-endpoint@npm:3.914.0" + dependencies: + "@aws-sdk/types": "npm:3.914.0" + "@aws-sdk/util-arn-parser": "npm:3.893.0" + "@smithy/node-config-provider": "npm:^4.3.3" + "@smithy/protocol-http": "npm:^5.3.3" + "@smithy/types": "npm:^4.8.0" + "@smithy/util-config-provider": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10c0/3bc67cf5a1cd8d9ba8bc9d63a515cf2cc47c391f0740145e21f3b7953f99bbf9b7261cea24bbeff580820d2653da98ce16dcb4819a15e24dc1ac2d8a9c658ba9 + languageName: node + linkType: hard + +"@aws-sdk/middleware-expect-continue@npm:3.914.0": + version: 3.914.0 + resolution: "@aws-sdk/middleware-expect-continue@npm:3.914.0" + dependencies: + "@aws-sdk/types": "npm:3.914.0" + "@smithy/protocol-http": "npm:^5.3.3" + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/64743e0182d5dc22aa6463dffc458be04f93081ad23f41ba15a145fc200ba6ef13b4516cd9532e8973b92708a736cdf33f110a2c3e2290012d2331f3cca35b54 + languageName: node + linkType: hard + +"@aws-sdk/middleware-flexible-checksums@npm:3.914.0": + version: 3.914.0 + resolution: "@aws-sdk/middleware-flexible-checksums@npm:3.914.0" + dependencies: + "@aws-crypto/crc32": "npm:5.2.0" + "@aws-crypto/crc32c": "npm:5.2.0" + "@aws-crypto/util": "npm:5.2.0" + "@aws-sdk/core": "npm:3.914.0" + "@aws-sdk/types": "npm:3.914.0" + "@smithy/is-array-buffer": "npm:^4.2.0" + "@smithy/node-config-provider": "npm:^4.3.3" + "@smithy/protocol-http": "npm:^5.3.3" + "@smithy/types": "npm:^4.8.0" + "@smithy/util-middleware": "npm:^4.2.3" + "@smithy/util-stream": "npm:^4.5.3" + "@smithy/util-utf8": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10c0/350b98055707b55edb0d276e4c90daa4b92ed7195d40bba1bbdb27fecfbafc63e458b0df9d20e6bec7d027b2615d476d53dae40b6d09e559d34f05c38ce120ba + languageName: node + linkType: hard + +"@aws-sdk/middleware-host-header@npm:3.914.0": + version: 3.914.0 + resolution: "@aws-sdk/middleware-host-header@npm:3.914.0" + dependencies: + "@aws-sdk/types": "npm:3.914.0" + "@smithy/protocol-http": "npm:^5.3.3" + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/f5141335b89132a7e54b799fb5e849a2162a79c9f3ff287e497938a6ddc9f81e2ebca7888759f83633cc4347c2510f937442695e24b9c5de07964e3545345cdd + languageName: node + linkType: hard + +"@aws-sdk/middleware-location-constraint@npm:3.914.0": + version: 3.914.0 + resolution: "@aws-sdk/middleware-location-constraint@npm:3.914.0" + dependencies: + "@aws-sdk/types": "npm:3.914.0" + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/65204117f9dbfd5ff2719e1304b65ce234cbd114e11c75b7025bf0b21f99c402153e3e72cf359722499727f0c718f2aaa7a7e9a71193a97935780e8d09dd8dd5 + languageName: node + linkType: hard + +"@aws-sdk/middleware-logger@npm:3.914.0": + version: 3.914.0 + resolution: "@aws-sdk/middleware-logger@npm:3.914.0" + dependencies: + "@aws-sdk/types": "npm:3.914.0" + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/d9011740d92d20b451a82e442069f81e57b5e86608573d753e4dedf80d9bfb5660fe2651aa542f26be7a16d97ec2aad708a80c1ae197ef0700ddfc6bc90e4554 + languageName: node + linkType: hard + +"@aws-sdk/middleware-recursion-detection@npm:3.914.0": + version: 3.914.0 + resolution: "@aws-sdk/middleware-recursion-detection@npm:3.914.0" + dependencies: + "@aws-sdk/types": "npm:3.914.0" + "@aws/lambda-invoke-store": "npm:^0.0.1" + "@smithy/protocol-http": "npm:^5.3.3" + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/45f5dfb4352e2554642eed38cee2bb7265ecbff2144671656f87d8f52948f11a9c401e08e6e1e054cd84e9ade32e86648f8de5266a342cc2d1efd6709c7f1574 + languageName: node + linkType: hard + +"@aws-sdk/middleware-sdk-s3@npm:3.914.0": + version: 3.914.0 + resolution: "@aws-sdk/middleware-sdk-s3@npm:3.914.0" + dependencies: + "@aws-sdk/core": "npm:3.914.0" + "@aws-sdk/types": "npm:3.914.0" + "@aws-sdk/util-arn-parser": "npm:3.893.0" + "@smithy/core": "npm:^3.17.0" + "@smithy/node-config-provider": "npm:^4.3.3" + "@smithy/protocol-http": "npm:^5.3.3" + "@smithy/signature-v4": "npm:^5.3.3" + "@smithy/smithy-client": "npm:^4.9.0" + "@smithy/types": "npm:^4.8.0" + "@smithy/util-config-provider": "npm:^4.2.0" + "@smithy/util-middleware": "npm:^4.2.3" + "@smithy/util-stream": "npm:^4.5.3" + "@smithy/util-utf8": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10c0/ed6a5038800a382bf073551de87e574b12d52394edcbc99cb242d1fa0291635dad484328e1d9faadfecbeb37dc59f2bd3f870b6d21267a5198e667dab7a835c9 + languageName: node + linkType: hard + +"@aws-sdk/middleware-ssec@npm:3.914.0": + version: 3.914.0 + resolution: "@aws-sdk/middleware-ssec@npm:3.914.0" + dependencies: + "@aws-sdk/types": "npm:3.914.0" + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/b7a0c24a43a3abae81e2fc423868ac7c290cc2253835953ccd6ff9dc64de31c534d85302c135267077f62b46476ea6b29173643043177fb75e7e605801635230 + languageName: node + linkType: hard + +"@aws-sdk/middleware-user-agent@npm:3.914.0": + version: 3.914.0 + resolution: "@aws-sdk/middleware-user-agent@npm:3.914.0" + dependencies: + "@aws-sdk/core": "npm:3.914.0" + "@aws-sdk/types": "npm:3.914.0" + "@aws-sdk/util-endpoints": "npm:3.914.0" + "@smithy/core": "npm:^3.17.0" + "@smithy/protocol-http": "npm:^5.3.3" + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/3c79673bc7b51595f4d414eb4ebc27c5a2dabd22f270ef7229888d1be588c5551f574eccfcd12337fefb0777bfa5bafd298fbffa18df48ea0a05ab49ecffaa7c + languageName: node + linkType: hard + +"@aws-sdk/nested-clients@npm:3.914.0": + version: 3.914.0 + resolution: "@aws-sdk/nested-clients@npm:3.914.0" + dependencies: + "@aws-crypto/sha256-browser": "npm:5.2.0" + "@aws-crypto/sha256-js": "npm:5.2.0" + "@aws-sdk/core": "npm:3.914.0" + "@aws-sdk/middleware-host-header": "npm:3.914.0" + "@aws-sdk/middleware-logger": "npm:3.914.0" + "@aws-sdk/middleware-recursion-detection": "npm:3.914.0" + "@aws-sdk/middleware-user-agent": "npm:3.914.0" + "@aws-sdk/region-config-resolver": "npm:3.914.0" + "@aws-sdk/types": "npm:3.914.0" + "@aws-sdk/util-endpoints": "npm:3.914.0" + "@aws-sdk/util-user-agent-browser": "npm:3.914.0" + "@aws-sdk/util-user-agent-node": "npm:3.914.0" + "@smithy/config-resolver": "npm:^4.4.0" + "@smithy/core": "npm:^3.17.0" + "@smithy/fetch-http-handler": "npm:^5.3.4" + "@smithy/hash-node": "npm:^4.2.3" + "@smithy/invalid-dependency": "npm:^4.2.3" + "@smithy/middleware-content-length": "npm:^4.2.3" + "@smithy/middleware-endpoint": "npm:^4.3.4" + "@smithy/middleware-retry": "npm:^4.4.4" + "@smithy/middleware-serde": "npm:^4.2.3" + "@smithy/middleware-stack": "npm:^4.2.3" + "@smithy/node-config-provider": "npm:^4.3.3" + "@smithy/node-http-handler": "npm:^4.4.2" + "@smithy/protocol-http": "npm:^5.3.3" + "@smithy/smithy-client": "npm:^4.9.0" + "@smithy/types": "npm:^4.8.0" + "@smithy/url-parser": "npm:^4.2.3" + "@smithy/util-base64": "npm:^4.3.0" + "@smithy/util-body-length-browser": "npm:^4.2.0" + "@smithy/util-body-length-node": "npm:^4.2.1" + "@smithy/util-defaults-mode-browser": "npm:^4.3.3" + "@smithy/util-defaults-mode-node": "npm:^4.2.5" + "@smithy/util-endpoints": "npm:^3.2.3" + "@smithy/util-middleware": "npm:^4.2.3" + "@smithy/util-retry": "npm:^4.2.3" + "@smithy/util-utf8": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10c0/085846ebeb41186693f2a25ed1132e3357bed88b4c93a4c88e2e88803d92bfff4a528023eb92572f18ef5655654333c5ae4457a7eb4d66318d1ca9ec439c8b2f + languageName: node + linkType: hard + +"@aws-sdk/region-config-resolver@npm:3.914.0": + version: 3.914.0 + resolution: "@aws-sdk/region-config-resolver@npm:3.914.0" + dependencies: + "@aws-sdk/types": "npm:3.914.0" + "@smithy/config-resolver": "npm:^4.4.0" + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/a473db628478a4403c34121231ed03c4b9c148659d797f8daa1ed7608b86bb23b1f85c63a99abb0efe9f540659d424d9e720b2e689cbcd7d61d888de610d4513 + languageName: node + linkType: hard + +"@aws-sdk/signature-v4-multi-region@npm:3.914.0": + version: 3.914.0 + resolution: "@aws-sdk/signature-v4-multi-region@npm:3.914.0" + dependencies: + "@aws-sdk/middleware-sdk-s3": "npm:3.914.0" + "@aws-sdk/types": "npm:3.914.0" + "@smithy/protocol-http": "npm:^5.3.3" + "@smithy/signature-v4": "npm:^5.3.3" + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/96d3145ad721b2e58206a5ec460b7da29709839fe6bcd0a82ce36104cbfe887b7049aae8ae62d40d7515fc00d884cb1a0ab3c4262211246c47befb10c3f15aec + languageName: node + linkType: hard + +"@aws-sdk/token-providers@npm:3.914.0": + version: 3.914.0 + resolution: "@aws-sdk/token-providers@npm:3.914.0" + dependencies: + "@aws-sdk/core": "npm:3.914.0" + "@aws-sdk/nested-clients": "npm:3.914.0" + "@aws-sdk/types": "npm:3.914.0" + "@smithy/property-provider": "npm:^4.2.3" + "@smithy/shared-ini-file-loader": "npm:^4.3.3" + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/7091fd21152e1f3bc40f67389e46c9dfc3288d823df2da109b779d646aec6e43b3b20a6b97a4ed5c78e0438025621cdb6e0050b6b2d3b3c1463f379df2996afa + languageName: node + linkType: hard + +"@aws-sdk/types@npm:3.914.0, @aws-sdk/types@npm:^3.222.0": + version: 3.914.0 + resolution: "@aws-sdk/types@npm:3.914.0" + dependencies: + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/71de24f076587ffc53acdc62ef16de711bd0c00f9a40491cd12a2c762e794c751e4ab79e0fb798c06a6a0e731cf0716f7833add085b1c85b7bfa2fba75e83937 + languageName: node + linkType: hard + +"@aws-sdk/util-arn-parser@npm:3.893.0": + version: 3.893.0 + resolution: "@aws-sdk/util-arn-parser@npm:3.893.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10c0/c8bbc1e258674e791929f1259a3f2422433c0b8c5470808a958ef4320bb9ca7c27783b617da3b9e04d9a1cd1d0b547da2858249dbec816f1098c02731b551aac + languageName: node + linkType: hard + +"@aws-sdk/util-endpoints@npm:3.914.0": + version: 3.914.0 + resolution: "@aws-sdk/util-endpoints@npm:3.914.0" + dependencies: + "@aws-sdk/types": "npm:3.914.0" + "@smithy/types": "npm:^4.8.0" + "@smithy/url-parser": "npm:^4.2.3" + "@smithy/util-endpoints": "npm:^3.2.3" + tslib: "npm:^2.6.2" + checksum: 10c0/e98edbcc7026cce46b684307a62f8fc609eea1f839882e77362043a0eeb4e7aec0fb5157c2fd3f56f3e6c774b3e89546c8e10af141464b3b8d5ac1b177088a80 + languageName: node + linkType: hard + +"@aws-sdk/util-locate-window@npm:^3.0.0": + version: 3.893.0 + resolution: "@aws-sdk/util-locate-window@npm:3.893.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10c0/ed2232d1eff567a7fa96bed87d56f03ac183dc20ba0ea262edb35f0b66aea201b987f447a5c383adc5694c80275700345946c0ad3183b30a6f9ec2f89be789d8 + languageName: node + linkType: hard + +"@aws-sdk/util-user-agent-browser@npm:3.914.0": + version: 3.914.0 + resolution: "@aws-sdk/util-user-agent-browser@npm:3.914.0" + dependencies: + "@aws-sdk/types": "npm:3.914.0" + "@smithy/types": "npm:^4.8.0" + bowser: "npm:^2.11.0" + tslib: "npm:^2.6.2" + checksum: 10c0/d9adf87efbd9ecb242cf92b489f1f490008eb0149c182a72452f3352dd4b6bf0a4bf974f18369d780ed5f2341bb0c526e81611da36e5f5bf0613aca1eea3bdab + languageName: node + linkType: hard + +"@aws-sdk/util-user-agent-node@npm:3.914.0": + version: 3.914.0 + resolution: "@aws-sdk/util-user-agent-node@npm:3.914.0" + dependencies: + "@aws-sdk/middleware-user-agent": "npm:3.914.0" + "@aws-sdk/types": "npm:3.914.0" + "@smithy/node-config-provider": "npm:^4.3.3" + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + peerDependencies: + aws-crt: ">=1.0.0" + peerDependenciesMeta: + aws-crt: + optional: true + checksum: 10c0/b2aeba88121931a3075f04f5d203bb80d3233c3b13b84c421583bc337d3b86825af71e8763154ff5435d972c4dd89c555be4d78f4e0e525e46892187191e0a6a + languageName: node + linkType: hard + +"@aws-sdk/xml-builder@npm:3.914.0": + version: 3.914.0 + resolution: "@aws-sdk/xml-builder@npm:3.914.0" + dependencies: + "@smithy/types": "npm:^4.8.0" + fast-xml-parser: "npm:5.2.5" + tslib: "npm:^2.6.2" + checksum: 10c0/d229350c17594a04165ed812e79f01f7ddc1ae3c562a95684e70e897c5feb5ae9b58a54443808ed5f883544d7a8ab720d8a6395dfdb84a6a43587171772ee184 + languageName: node + linkType: hard + +"@aws/lambda-invoke-store@npm:^0.0.1": + version: 0.0.1 + resolution: "@aws/lambda-invoke-store@npm:0.0.1" + checksum: 10c0/0bbf3060014a462177fb743e132e9b106a6743ad9cd905df4bd26e9ca8bfe2cc90473b03a79938fa908934e45e43f366f57af56a697991abda71d9ac92f5018f + languageName: node + linkType: hard + +"@azure/abort-controller@npm:^2.0.0, @azure/abort-controller@npm:^2.1.2": + version: 2.1.2 + resolution: "@azure/abort-controller@npm:2.1.2" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10c0/3771b6820e33ebb56e79c7c68e2288296b8c2529556fbd29cf4cf2fbff7776e7ce1120072972d8df9f1bf50e2c3224d71a7565362b589595563f710b8c3d7b79 + languageName: node + linkType: hard + +"@azure/core-auth@npm:^1.10.0, @azure/core-auth@npm:^1.4.0, @azure/core-auth@npm:^1.9.0": + version: 1.10.1 + resolution: "@azure/core-auth@npm:1.10.1" + dependencies: + "@azure/abort-controller": "npm:^2.1.2" + "@azure/core-util": "npm:^1.13.0" + tslib: "npm:^2.6.2" + checksum: 10c0/83fd96e43cf8ca3e1cf6c7677915ca1433d6e331cb7352b64a3f93d9fd71dcddf77e8b46f2bb2a5db49ce87016ed30ebaca88034a0acf321e86ba17c0eb3329e + languageName: node + linkType: hard + +"@azure/core-client@npm:^1.10.0, @azure/core-client@npm:^1.6.2, @azure/core-client@npm:^1.9.2": + version: 1.10.1 + resolution: "@azure/core-client@npm:1.10.1" + dependencies: + "@azure/abort-controller": "npm:^2.1.2" + "@azure/core-auth": "npm:^1.10.0" + "@azure/core-rest-pipeline": "npm:^1.22.0" + "@azure/core-tracing": "npm:^1.3.0" + "@azure/core-util": "npm:^1.13.0" + "@azure/logger": "npm:^1.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/f88b3df77e50c07eccc1a4bc1c12e626620be12027dd100682116664c4cc676ee1f78427e55ce8750a311762f75fdd41f99ce289c06b78a3b18e491d622d0579 + languageName: node + linkType: hard + +"@azure/core-http-compat@npm:^2.0.0": + version: 2.3.1 + resolution: "@azure/core-http-compat@npm:2.3.1" + dependencies: + "@azure/abort-controller": "npm:^2.1.2" + "@azure/core-client": "npm:^1.10.0" + "@azure/core-rest-pipeline": "npm:^1.22.0" + checksum: 10c0/e4a2744781c0d446b274677919d0f4621ad932aba907a54ee2275e3f62afaa636641ae3601497939543c6cf8acd07a2aaef3d4e94b49c9cc715d975a26d3601a + languageName: node + linkType: hard + +"@azure/core-lro@npm:^2.2.0": + version: 2.7.2 + resolution: "@azure/core-lro@npm:2.7.2" + dependencies: + "@azure/abort-controller": "npm:^2.0.0" + "@azure/core-util": "npm:^1.2.0" + "@azure/logger": "npm:^1.0.0" + tslib: "npm:^2.6.2" + checksum: 10c0/bee809e47661b40021bbbedf88de54019715fdfcc95ac552b1d901719c29d78e293eeab51257b8f5155aac768eb4ea420715004d00d6e32109f5f97db5960d39 + languageName: node + linkType: hard + +"@azure/core-paging@npm:^1.1.1": + version: 1.6.2 + resolution: "@azure/core-paging@npm:1.6.2" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10c0/c727782f8dc66eff50c03421af2ca55f497f33e14ec845f5918d76661c57bc8e3a7ca9fa3d39181287bfbfa45f28cb3d18b67c31fd36bbe34146387dbd07b440 + languageName: node + linkType: hard + +"@azure/core-rest-pipeline@npm:^1.10.1, @azure/core-rest-pipeline@npm:^1.17.0, @azure/core-rest-pipeline@npm:^1.22.0": + version: 1.22.1 + resolution: "@azure/core-rest-pipeline@npm:1.22.1" + dependencies: + "@azure/abort-controller": "npm:^2.1.2" + "@azure/core-auth": "npm:^1.10.0" + "@azure/core-tracing": "npm:^1.3.0" + "@azure/core-util": "npm:^1.13.0" + "@azure/logger": "npm:^1.3.0" + "@typespec/ts-http-runtime": "npm:^0.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/90c2e9775801aa2dca76de16dbb04e661dff18496ae94d1b0494d0178de8b4ff6acd30991efb8016f921f5fd609e6578c16bfef09c53890cba96966ded5b7156 + languageName: node + linkType: hard + +"@azure/core-tracing@npm:^1.0.0, @azure/core-tracing@npm:^1.1.2, @azure/core-tracing@npm:^1.3.0": + version: 1.3.1 + resolution: "@azure/core-tracing@npm:1.3.1" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10c0/0cb26db9ab5336a1867cc9cd0bd42b1702406d0f76420385789d1a96c8702a38cb081838ea73cd707bb7b340c4386499cf6e77538cacfda4467c251fe2ffa32b + languageName: node + linkType: hard + +"@azure/core-util@npm:^1.11.0, @azure/core-util@npm:^1.13.0, @azure/core-util@npm:^1.2.0, @azure/core-util@npm:^1.6.1": + version: 1.13.1 + resolution: "@azure/core-util@npm:1.13.1" + dependencies: + "@azure/abort-controller": "npm:^2.1.2" + "@typespec/ts-http-runtime": "npm:^0.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/37067621cdac933c51775c26648fdcea315f07b08bd875cff4610e403eabf9c12532525f0bf094e258dadc03a55d35f12c9242f662526847b32c85cdcc2d6603 + languageName: node + linkType: hard + +"@azure/core-xml@npm:^1.4.3": + version: 1.5.0 + resolution: "@azure/core-xml@npm:1.5.0" + dependencies: + fast-xml-parser: "npm:^5.0.7" + tslib: "npm:^2.8.1" + checksum: 10c0/95c82da0014837796b7e8c22bc4db93d7c7b5984c35638c7c33868c7a0275b8b87a9ad68697ea24dfb7b4524d68ad0334d7096c5e4d34136d1c17b0b3447faee + languageName: node + linkType: hard + +"@azure/identity@npm:^4.10.1": + version: 4.13.0 + resolution: "@azure/identity@npm:4.13.0" + dependencies: + "@azure/abort-controller": "npm:^2.0.0" + "@azure/core-auth": "npm:^1.9.0" + "@azure/core-client": "npm:^1.9.2" + "@azure/core-rest-pipeline": "npm:^1.17.0" + "@azure/core-tracing": "npm:^1.0.0" + "@azure/core-util": "npm:^1.11.0" + "@azure/logger": "npm:^1.0.0" + "@azure/msal-browser": "npm:^4.2.0" + "@azure/msal-node": "npm:^3.5.0" + open: "npm:^10.1.0" + tslib: "npm:^2.2.0" + checksum: 10c0/d49b4d22a0ab4e2fa4688a357970806af57d1ecab302e0f321d986b83e4ef78604c592f8f3fd120e156531adaae4a5f94d032875b63d7e09259dd75547d90d31 + languageName: node + linkType: hard + +"@azure/logger@npm:^1.0.0, @azure/logger@npm:^1.3.0": + version: 1.3.0 + resolution: "@azure/logger@npm:1.3.0" + dependencies: + "@typespec/ts-http-runtime": "npm:^0.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/aaa6a88fd4f26d41100865ff2c53b400347f632d315d9ae8ffa28db03974d35461e743031bdca40cad617ace172d1ba598ffdd18c345ebc564f63a51c32c4a29 + languageName: node + linkType: hard + +"@azure/msal-browser@npm:^4.2.0": + version: 4.25.1 + resolution: "@azure/msal-browser@npm:4.25.1" + dependencies: + "@azure/msal-common": "npm:15.13.0" + checksum: 10c0/3702b2bac470bf946086d6199527125818f91b3e25e03c2b1ca6d547d96135d99adb2b3ce2cc1bb0744e3843a5d1aaf06d2ad8594883c9c74b45e825f1f17606 + languageName: node + linkType: hard + +"@azure/msal-common@npm:15.13.0": + version: 15.13.0 + resolution: "@azure/msal-common@npm:15.13.0" + checksum: 10c0/19cd545779bd2f9d50e6f3c899fcb313b56a541493d85dfe65305c22a0d12f6a672ebfbcff05a539b8908b1466b7e48f1067fb4d4a9c85928a0fd115070cd203 + languageName: node + linkType: hard + +"@azure/msal-node@npm:^3.5.0": + version: 3.8.0 + resolution: "@azure/msal-node@npm:3.8.0" + dependencies: + "@azure/msal-common": "npm:15.13.0" + jsonwebtoken: "npm:^9.0.0" + uuid: "npm:^8.3.0" + checksum: 10c0/80dd7f535856d1e99b5344ce4d584eeb4631560ccfef3a66b9a8dfb590299f41628fbb8150c84613c69977f428575a2eab1bd76443e4084d406d7ebf9dd3b281 + languageName: node + linkType: hard + +"@azure/storage-blob@npm:12.26.x": + version: 12.26.0 + resolution: "@azure/storage-blob@npm:12.26.0" + dependencies: + "@azure/abort-controller": "npm:^2.1.2" + "@azure/core-auth": "npm:^1.4.0" + "@azure/core-client": "npm:^1.6.2" + "@azure/core-http-compat": "npm:^2.0.0" + "@azure/core-lro": "npm:^2.2.0" + "@azure/core-paging": "npm:^1.1.1" + "@azure/core-rest-pipeline": "npm:^1.10.1" + "@azure/core-tracing": "npm:^1.1.2" + "@azure/core-util": "npm:^1.6.1" + "@azure/core-xml": "npm:^1.4.3" + "@azure/logger": "npm:^1.0.0" + events: "npm:^3.0.0" + tslib: "npm:^2.2.0" + checksum: 10c0/069b7a85dddb33ee793efd74fbc1a3377c6d14dbb11094c2ebae87e324f16d23292806d5dcdf04280456dafc4d960e847968f6f01e384039b47363d61faf1017 + languageName: node + linkType: hard + "@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.26.2, @babel/code-frame@npm:^7.27.1": version: 7.27.1 resolution: "@babel/code-frame@npm:7.27.1" @@ -1743,6 +2644,13 @@ __metadata: languageName: node linkType: hard +"@colors/colors@npm:1.6.0, @colors/colors@npm:^1.6.0": + version: 1.6.0 + resolution: "@colors/colors@npm:1.6.0" + checksum: 10c0/9328a0778a5b0db243af54455b79a69e3fb21122d6c15ef9e9fcc94881d8d17352d8b2b2590f9bdd46fac5c2d6c1636dcfc14358a20c70e22daf89e1a759b629 + languageName: node + linkType: hard + "@commitlint/cli@npm:^19.8.1": version: 19.8.1 resolution: "@commitlint/cli@npm:19.8.1" @@ -1940,6 +2848,17 @@ __metadata: languageName: node linkType: hard +"@dabh/diagnostics@npm:^2.0.8": + version: 2.0.8 + resolution: "@dabh/diagnostics@npm:2.0.8" + dependencies: + "@so-ric/colorspace": "npm:^1.1.6" + enabled: "npm:2.0.x" + kuler: "npm:^2.0.0" + checksum: 10c0/64701c272f7de02800039fea99796507670fe5f67d4eb7718599351ec156936efd123fcab7ee18f9d7874939caaacc08e7c7a6bb05ff8cda6d930ad041cc555c + languageName: node + linkType: hard + "@discoveryjs/json-ext@npm:0.6.3": version: 0.6.3 resolution: "@discoveryjs/json-ext@npm:0.6.3" @@ -2444,6 +3363,53 @@ __metadata: languageName: node linkType: hard +"@google-cloud/paginator@npm:^5.0.0": + version: 5.0.2 + resolution: "@google-cloud/paginator@npm:5.0.2" + dependencies: + arrify: "npm:^2.0.0" + extend: "npm:^3.0.2" + checksum: 10c0/aac4ed986c2b274ac9fdca3f68d5ba6ee95f4c35370b11db25c288bf485352e2ec5df16bf9c3cff554a2e73a07e62f10044d273788df61897b81fe47bb18106d + languageName: node + linkType: hard + +"@google-cloud/projectify@npm:^4.0.0": + version: 4.0.0 + resolution: "@google-cloud/projectify@npm:4.0.0" + checksum: 10c0/0d0a6ceca76a138973fcb3ad577f209acdbd9d9aed1c645b09f98d5e5a258053dbbe6c1f13e6f85310cc0d9308f5f3a84f8fa4f1a132549a68d86174fb21067f + languageName: node + linkType: hard + +"@google-cloud/promisify@npm:<4.1.0": + version: 4.0.0 + resolution: "@google-cloud/promisify@npm:4.0.0" + checksum: 10c0/4332cbd923d7c6943ecdf46f187f1417c84bb9c801525cd74d719c766bfaad650f7964fb74576345f6537b6d6273a4f2992c8d79ebec6c8b8401b23d626b8dd3 + languageName: node + linkType: hard + +"@google-cloud/storage@npm:^7.7.0": + version: 7.17.2 + resolution: "@google-cloud/storage@npm:7.17.2" + dependencies: + "@google-cloud/paginator": "npm:^5.0.0" + "@google-cloud/projectify": "npm:^4.0.0" + "@google-cloud/promisify": "npm:<4.1.0" + abort-controller: "npm:^3.0.0" + async-retry: "npm:^1.3.3" + duplexify: "npm:^4.1.3" + fast-xml-parser: "npm:^4.4.1" + gaxios: "npm:^6.0.2" + google-auth-library: "npm:^9.6.3" + html-entities: "npm:^2.5.2" + mime: "npm:^3.0.0" + p-limit: "npm:^3.0.1" + retry-request: "npm:^7.0.0" + teeny-request: "npm:^9.0.0" + uuid: "npm:^8.0.0" + checksum: 10c0/0f178d5936818e6e1d90cfdad87a5ca955cbef5f28a206b3722f3c5b5b8b63b89278e7590f7468a0d7057c64d36883fa5372c91cb254d124a32a16127104645e + languageName: node + linkType: hard + "@hapi/hoek@npm:^9.0.0, @hapi/hoek@npm:^9.3.0": version: 9.3.0 resolution: "@hapi/hoek@npm:9.3.0" @@ -2880,6 +3846,7 @@ __metadata: "@angular/forms": ^19.0.0 "@fullcalendar/core": ^6.1.19 chart.js: ^4.5.0 + snowflake-sdk: ^2.3.1 languageName: unknown linkType: soft @@ -3930,10 +4897,628 @@ __metadata: languageName: node linkType: hard -"@sindresorhus/merge-streams@npm:^2.1.0": - version: 2.3.0 - resolution: "@sindresorhus/merge-streams@npm:2.3.0" - checksum: 10c0/69ee906f3125fb2c6bb6ec5cdd84e8827d93b49b3892bce8b62267116cc7e197b5cccf20c160a1d32c26014ecd14470a72a5e3ee37a58f1d6dadc0db1ccf3894 +"@sindresorhus/merge-streams@npm:^2.1.0": + version: 2.3.0 + resolution: "@sindresorhus/merge-streams@npm:2.3.0" + checksum: 10c0/69ee906f3125fb2c6bb6ec5cdd84e8827d93b49b3892bce8b62267116cc7e197b5cccf20c160a1d32c26014ecd14470a72a5e3ee37a58f1d6dadc0db1ccf3894 + languageName: node + linkType: hard + +"@smithy/abort-controller@npm:^4.2.3": + version: 4.2.3 + resolution: "@smithy/abort-controller@npm:4.2.3" + dependencies: + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/ddb1333f85df6e61e2b4ab9d7ae8c0135fa44e7d81703d18accdefb1c280c00c8f339e157e48ca8a19fbabe3d73e728df8dfca732418268961d6ba93c7b0fb7e + languageName: node + linkType: hard + +"@smithy/chunked-blob-reader-native@npm:^4.2.1": + version: 4.2.1 + resolution: "@smithy/chunked-blob-reader-native@npm:4.2.1" + dependencies: + "@smithy/util-base64": "npm:^4.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/63831fe47a5b3a1ea6821846a5fb009298da57159e4818238e8110b77245805c1a07cb854df7955a39de1f5f2dfb7c8803ac942117e622665e089d715cb2041c + languageName: node + linkType: hard + +"@smithy/chunked-blob-reader@npm:^5.2.0": + version: 5.2.0 + resolution: "@smithy/chunked-blob-reader@npm:5.2.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10c0/9fe95b788e022ce2b59c8cab607c8f71d73cce367329871d2a7eafdc0d77cec8d1939fe8141f446bbe4051dcfffce864a562762ac2691c368df3b6c2f6ed62b3 + languageName: node + linkType: hard + +"@smithy/config-resolver@npm:^4.4.0": + version: 4.4.0 + resolution: "@smithy/config-resolver@npm:4.4.0" + dependencies: + "@smithy/node-config-provider": "npm:^4.3.3" + "@smithy/types": "npm:^4.8.0" + "@smithy/util-config-provider": "npm:^4.2.0" + "@smithy/util-endpoints": "npm:^3.2.3" + "@smithy/util-middleware": "npm:^4.2.3" + tslib: "npm:^2.6.2" + checksum: 10c0/f33e1c4569980e614dea8b709dbd8e120b725caf4a7cc557d9d31a91df1755ccc1a2179cf26681eebf713e755ad38d6b58f7aca00dfe2c3390e9481221530e43 + languageName: node + linkType: hard + +"@smithy/core@npm:^3.17.0, @smithy/core@npm:^3.17.1": + version: 3.17.1 + resolution: "@smithy/core@npm:3.17.1" + dependencies: + "@smithy/middleware-serde": "npm:^4.2.3" + "@smithy/protocol-http": "npm:^5.3.3" + "@smithy/types": "npm:^4.8.0" + "@smithy/util-base64": "npm:^4.3.0" + "@smithy/util-body-length-browser": "npm:^4.2.0" + "@smithy/util-middleware": "npm:^4.2.3" + "@smithy/util-stream": "npm:^4.5.4" + "@smithy/util-utf8": "npm:^4.2.0" + "@smithy/uuid": "npm:^1.1.0" + tslib: "npm:^2.6.2" + checksum: 10c0/2b3e9f9c06b73e4e12361bec6bcf59ae6a33f60252a85114be91656765aaa9ff77fa7a9e0129875aba81b21c6630455720b8b174e1145be72aec86245c2dbf4b + languageName: node + linkType: hard + +"@smithy/credential-provider-imds@npm:^4.2.3": + version: 4.2.3 + resolution: "@smithy/credential-provider-imds@npm:4.2.3" + dependencies: + "@smithy/node-config-provider": "npm:^4.3.3" + "@smithy/property-provider": "npm:^4.2.3" + "@smithy/types": "npm:^4.8.0" + "@smithy/url-parser": "npm:^4.2.3" + tslib: "npm:^2.6.2" + checksum: 10c0/9ea3eb9f80779180f50e2b5f68fac66f4f144b14d10419e411fcf3d44576be148af5795dde6951816784352d58e6950a0c8b45830f8f1fae9d98ffdf00ba8479 + languageName: node + linkType: hard + +"@smithy/eventstream-codec@npm:^4.2.3": + version: 4.2.3 + resolution: "@smithy/eventstream-codec@npm:4.2.3" + dependencies: + "@aws-crypto/crc32": "npm:5.2.0" + "@smithy/types": "npm:^4.8.0" + "@smithy/util-hex-encoding": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10c0/3ee59573ccbd90b063d818df9498c08384319a71aeb45b25b8dfe9292773cdb6c073b6acf5241deecca35991d8a961df04de9a11c8aff5bc900194b9153c1c01 + languageName: node + linkType: hard + +"@smithy/eventstream-serde-browser@npm:^4.2.3": + version: 4.2.3 + resolution: "@smithy/eventstream-serde-browser@npm:4.2.3" + dependencies: + "@smithy/eventstream-serde-universal": "npm:^4.2.3" + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/8b8c957c78f941a34cb8324f9ca0902424e75fe4eef66542296903f91bf3637dedc8ce2e0ada8941df2a0a73bfc379e1b855dc46e940ec55399a3b360a0cd5f0 + languageName: node + linkType: hard + +"@smithy/eventstream-serde-config-resolver@npm:^4.3.3": + version: 4.3.3 + resolution: "@smithy/eventstream-serde-config-resolver@npm:4.3.3" + dependencies: + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/2f90d12bca32d561cb28161ccedb0e72e41e0bed547ae84c4d42d1e3e250327386da0b7906745b5dfcee5195cd5b7a90470f2446ebc4471b3c9f5f28adcb4a09 + languageName: node + linkType: hard + +"@smithy/eventstream-serde-node@npm:^4.2.3": + version: 4.2.3 + resolution: "@smithy/eventstream-serde-node@npm:4.2.3" + dependencies: + "@smithy/eventstream-serde-universal": "npm:^4.2.3" + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/821960a2dfa7a50060d89097c6204fedea724f1888c7cc128f554202fac7874d2bcfb56b9737596471f72e18537d0a1788da2bb5d7df0a5c28b43fb1aa847cf8 + languageName: node + linkType: hard + +"@smithy/eventstream-serde-universal@npm:^4.2.3": + version: 4.2.3 + resolution: "@smithy/eventstream-serde-universal@npm:4.2.3" + dependencies: + "@smithy/eventstream-codec": "npm:^4.2.3" + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/52ad09c0150d8102a4f96eae58e703c79613258329626b932e6b1259665182f2c8ee455d68b92bdd5f48bab10c91f37b96befe121077697d10edbc326b83c8cd + languageName: node + linkType: hard + +"@smithy/fetch-http-handler@npm:^5.3.4": + version: 5.3.4 + resolution: "@smithy/fetch-http-handler@npm:5.3.4" + dependencies: + "@smithy/protocol-http": "npm:^5.3.3" + "@smithy/querystring-builder": "npm:^4.2.3" + "@smithy/types": "npm:^4.8.0" + "@smithy/util-base64": "npm:^4.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/93ab064fbdc66021975abf070544739d428be79a5c349ada84ca2eac9c8dd23dc1aacc5a13c50d1a0043a91062291659471645a029700a2e1e0ce59b0baba5a0 + languageName: node + linkType: hard + +"@smithy/hash-blob-browser@npm:^4.2.4": + version: 4.2.4 + resolution: "@smithy/hash-blob-browser@npm:4.2.4" + dependencies: + "@smithy/chunked-blob-reader": "npm:^5.2.0" + "@smithy/chunked-blob-reader-native": "npm:^4.2.1" + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/7c9ce6d103c0589b72dcd929bc146c7a843ab090cac2722264d26c910dc3b59e3b69c7ee9254dac8b6836c0a5cb83b8f31a0cbec2e0252d36d58bdd1e93e7964 + languageName: node + linkType: hard + +"@smithy/hash-node@npm:^4.2.3": + version: 4.2.3 + resolution: "@smithy/hash-node@npm:4.2.3" + dependencies: + "@smithy/types": "npm:^4.8.0" + "@smithy/util-buffer-from": "npm:^4.2.0" + "@smithy/util-utf8": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10c0/8e2945ad4e7dc4e6ef24afea4532a29e79ff787c670085079e9441951ec4a0ebe7fe65127e47dba1d6132d277b8ea619cc4920931f990fb45f209b6a393b8de7 + languageName: node + linkType: hard + +"@smithy/hash-stream-node@npm:^4.2.3": + version: 4.2.3 + resolution: "@smithy/hash-stream-node@npm:4.2.3" + dependencies: + "@smithy/types": "npm:^4.8.0" + "@smithy/util-utf8": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10c0/a342ca5dda588443137fc0d314cf6e82d84c0bf9b267e0a39309dda534bab79042a0b6f037d5cdfe894a31d354b06ddc8529d99a593aa339f010a088680794b9 + languageName: node + linkType: hard + +"@smithy/invalid-dependency@npm:^4.2.3": + version: 4.2.3 + resolution: "@smithy/invalid-dependency@npm:4.2.3" + dependencies: + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/426cde94fc851007ede20cb7b201c03eb91a0d11ef5e0fb0308eb4d2fedbf2d82bc48367c765aa0491cf88fe731eb0c995ca6bcffe7381fd98b748795e2292a4 + languageName: node + linkType: hard + +"@smithy/is-array-buffer@npm:^2.2.0": + version: 2.2.0 + resolution: "@smithy/is-array-buffer@npm:2.2.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10c0/2f2523cd8cc4538131e408eb31664983fecb0c8724956788b015aaf3ab85a0c976b50f4f09b176f1ed7bbe79f3edf80743be7a80a11f22cd9ce1285d77161aaf + languageName: node + linkType: hard + +"@smithy/is-array-buffer@npm:^4.2.0": + version: 4.2.0 + resolution: "@smithy/is-array-buffer@npm:4.2.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10c0/8e3e21cff5929d627bbf4a9beded28bd54555cfd37772226290964af6950cc10d700776a2ce7553f34ddf88a2e7e3d4681de58c94e9805592d901fc0f32cb597 + languageName: node + linkType: hard + +"@smithy/md5-js@npm:^4.2.3": + version: 4.2.3 + resolution: "@smithy/md5-js@npm:4.2.3" + dependencies: + "@smithy/types": "npm:^4.8.0" + "@smithy/util-utf8": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10c0/6495f9592ef731468392f2b17bbf781f931555d8e853c70fa2ed41f9e612df133092dffa916f1b46fa06ea414db399f40c3845f19d975c368afeae76b010ce21 + languageName: node + linkType: hard + +"@smithy/middleware-content-length@npm:^4.2.3": + version: 4.2.3 + resolution: "@smithy/middleware-content-length@npm:4.2.3" + dependencies: + "@smithy/protocol-http": "npm:^5.3.3" + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/67aabc8c2257299e2985eba32cf0f728c23aeb1ba6f17bab1a99bfde1970a841d709592a855d6fa1ffb10c4c004a76e70d2804622529b010e9ee2e7b243630e6 + languageName: node + linkType: hard + +"@smithy/middleware-endpoint@npm:^4.3.4, @smithy/middleware-endpoint@npm:^4.3.5": + version: 4.3.5 + resolution: "@smithy/middleware-endpoint@npm:4.3.5" + dependencies: + "@smithy/core": "npm:^3.17.1" + "@smithy/middleware-serde": "npm:^4.2.3" + "@smithy/node-config-provider": "npm:^4.3.3" + "@smithy/shared-ini-file-loader": "npm:^4.3.3" + "@smithy/types": "npm:^4.8.0" + "@smithy/url-parser": "npm:^4.2.3" + "@smithy/util-middleware": "npm:^4.2.3" + tslib: "npm:^2.6.2" + checksum: 10c0/397cad4fff38b3d388315bac04499eca34f77cd558115d9b621f145d933a851bcac99a6c4c546f43f119f46d1f6abffb25bafdcf124d1851347cacce7017a626 + languageName: node + linkType: hard + +"@smithy/middleware-retry@npm:^4.4.4": + version: 4.4.5 + resolution: "@smithy/middleware-retry@npm:4.4.5" + dependencies: + "@smithy/node-config-provider": "npm:^4.3.3" + "@smithy/protocol-http": "npm:^5.3.3" + "@smithy/service-error-classification": "npm:^4.2.3" + "@smithy/smithy-client": "npm:^4.9.1" + "@smithy/types": "npm:^4.8.0" + "@smithy/util-middleware": "npm:^4.2.3" + "@smithy/util-retry": "npm:^4.2.3" + "@smithy/uuid": "npm:^1.1.0" + tslib: "npm:^2.6.2" + checksum: 10c0/fe196af3b5e91c4c64649425d51f61133e0776a4cb9ea3151a7aab53dea7abdf19fc7582168b615c0f90be570c0e6c2e048927ea012108b708419b1f62217dc6 + languageName: node + linkType: hard + +"@smithy/middleware-serde@npm:^4.2.3": + version: 4.2.3 + resolution: "@smithy/middleware-serde@npm:4.2.3" + dependencies: + "@smithy/protocol-http": "npm:^5.3.3" + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/c8cda503c62e380167595efb1d44b5cc81e23e88c9941f3b7cc485c1948c9d0c379252acdf46419281abb844bc5080a31c54f7e00427c411d8ae929dad1050a1 + languageName: node + linkType: hard + +"@smithy/middleware-stack@npm:^4.2.3": + version: 4.2.3 + resolution: "@smithy/middleware-stack@npm:4.2.3" + dependencies: + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/4a2cc539a6850501831a502a35eb135d5801003c3061e71e6cfc2cceba94a4db1a81e0c7f06f867871d20fc9b24868bddec513f3759ca484b299b153adf32dae + languageName: node + linkType: hard + +"@smithy/node-config-provider@npm:^4.3.3": + version: 4.3.3 + resolution: "@smithy/node-config-provider@npm:4.3.3" + dependencies: + "@smithy/property-provider": "npm:^4.2.3" + "@smithy/shared-ini-file-loader": "npm:^4.3.3" + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/f8a6839f5850c289be5cc366fe45c9a0553752c5851f3b2eeb2e0b8cdeda183535767d41f7e9e99bfe3bfe7f6fd50c5861a6acd802e0ec49a12caa4789dde1b8 + languageName: node + linkType: hard + +"@smithy/node-http-handler@npm:^4.0.1, @smithy/node-http-handler@npm:^4.4.2, @smithy/node-http-handler@npm:^4.4.3": + version: 4.4.3 + resolution: "@smithy/node-http-handler@npm:4.4.3" + dependencies: + "@smithy/abort-controller": "npm:^4.2.3" + "@smithy/protocol-http": "npm:^5.3.3" + "@smithy/querystring-builder": "npm:^4.2.3" + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/343ca2d2ce8e8b11f849edaa69b52b439be93cf2351cac78c0c545e41af621b0300eaa32fdeea06e282a6b5801db3b9dc3b339674f7dd427aa1e26fd46968f2a + languageName: node + linkType: hard + +"@smithy/property-provider@npm:^4.2.3": + version: 4.2.3 + resolution: "@smithy/property-provider@npm:4.2.3" + dependencies: + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/1dc7a673023f3f4bb9e6fb879c5e02972d5e8dbc9d3aab36f6a1ef2ee897223416c4f98a2455e7837e42dcd923cbe18dcffd60843c6f8eef632fdfa4ebd529b8 + languageName: node + linkType: hard + +"@smithy/protocol-http@npm:^5.1.3, @smithy/protocol-http@npm:^5.3.3": + version: 5.3.3 + resolution: "@smithy/protocol-http@npm:5.3.3" + dependencies: + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/f94d43aefcabc89b3de881b6d11b40ca46367a938be7cb7ebea85a289d373f18b68453c7374e9d254805b84c336b8654fd2c6bbbf34b3974752de2b0774c7023 + languageName: node + linkType: hard + +"@smithy/querystring-builder@npm:^4.2.3": + version: 4.2.3 + resolution: "@smithy/querystring-builder@npm:4.2.3" + dependencies: + "@smithy/types": "npm:^4.8.0" + "@smithy/util-uri-escape": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10c0/a29741ddde6f9e9e4e3774b4008a12ecc189a9296b6ac6df4a563bd7e3e18352674e754bfbf2a87b2c0e9cede77e51bee88c54fb169d99b30f3f803571cb985e + languageName: node + linkType: hard + +"@smithy/querystring-parser@npm:^4.2.3": + version: 4.2.3 + resolution: "@smithy/querystring-parser@npm:4.2.3" + dependencies: + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/974c20463b18dfc90b9446713ab959c1998ffd2269e512942fd16d831f44ef2684db700ba82afe9510c8a59b06e0765cc70a1eacc1ce4d7902a20c3e441e715e + languageName: node + linkType: hard + +"@smithy/service-error-classification@npm:^4.2.3": + version: 4.2.3 + resolution: "@smithy/service-error-classification@npm:4.2.3" + dependencies: + "@smithy/types": "npm:^4.8.0" + checksum: 10c0/8cd7327515125632a5b7535a9db24ab3f3d733253224d260ac6bf94e04ed3abf99ebd6cfb723d160b3fe49aa87c573dc51775656f4a661939851521e1fd750bb + languageName: node + linkType: hard + +"@smithy/shared-ini-file-loader@npm:^4.3.3": + version: 4.3.3 + resolution: "@smithy/shared-ini-file-loader@npm:4.3.3" + dependencies: + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/8e5092d0be0a45b265d0a469dfded89aa3da93e751262efa71e358ca95902a3e34f6ae1465790c7fea131e6a945f322a357b5d681c1a5f66baf4acbfff347937 + languageName: node + linkType: hard + +"@smithy/signature-v4@npm:^5.2.1, @smithy/signature-v4@npm:^5.3.3": + version: 5.3.3 + resolution: "@smithy/signature-v4@npm:5.3.3" + dependencies: + "@smithy/is-array-buffer": "npm:^4.2.0" + "@smithy/protocol-http": "npm:^5.3.3" + "@smithy/types": "npm:^4.8.0" + "@smithy/util-hex-encoding": "npm:^4.2.0" + "@smithy/util-middleware": "npm:^4.2.3" + "@smithy/util-uri-escape": "npm:^4.2.0" + "@smithy/util-utf8": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10c0/454c5bc2847b066741fed57c7ea4ace1d6c3cb92387c78f3fd1ebf4c4a8629a65322bd13dd6f3dc62ef1f6b2f8d6d4ed88dfe8ce892c2fc6945d78e34e18727c + languageName: node + linkType: hard + +"@smithy/smithy-client@npm:^4.9.0, @smithy/smithy-client@npm:^4.9.1": + version: 4.9.1 + resolution: "@smithy/smithy-client@npm:4.9.1" + dependencies: + "@smithy/core": "npm:^3.17.1" + "@smithy/middleware-endpoint": "npm:^4.3.5" + "@smithy/middleware-stack": "npm:^4.2.3" + "@smithy/protocol-http": "npm:^5.3.3" + "@smithy/types": "npm:^4.8.0" + "@smithy/util-stream": "npm:^4.5.4" + tslib: "npm:^2.6.2" + checksum: 10c0/6b50a5d00e2b68e72e78d08e6d90587159ed80d6fb9a6fb341cc78a55feca9f12157cb309eb8e22ab8fe6508a63ac4bfa2e42cf6f7077a8d571a30cb33232ce8 + languageName: node + linkType: hard + +"@smithy/types@npm:^4.8.0": + version: 4.8.0 + resolution: "@smithy/types@npm:4.8.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10c0/342173aeaa80b3837dce51c393a3fcab7db9b3ec1481cbc6d00298566076481b88e274c258c2dab54112641d66ab678c7ed7dc2c2a4500ffcf407a6d61c33fd0 + languageName: node + linkType: hard + +"@smithy/url-parser@npm:^4.2.3": + version: 4.2.3 + resolution: "@smithy/url-parser@npm:4.2.3" + dependencies: + "@smithy/querystring-parser": "npm:^4.2.3" + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/fe624e0ed2447a6e27c32560aaf8371847982185743cfefe4d5fd46ddeb05bff12cd9fcdb4e568d1ce0c7dc1e5317e0e3cc5e78096c3ceb0fb77da93f86e7f56 + languageName: node + linkType: hard + +"@smithy/util-base64@npm:^4.3.0": + version: 4.3.0 + resolution: "@smithy/util-base64@npm:4.3.0" + dependencies: + "@smithy/util-buffer-from": "npm:^4.2.0" + "@smithy/util-utf8": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10c0/02dd536b9257914cc9a595a865faac64fc96db10468d52d0cba475df78764fc25ba255707ccd061ee197fca189d7859d70af8cf89b0b0c3e27c1c693676eb6e4 + languageName: node + linkType: hard + +"@smithy/util-body-length-browser@npm:^4.2.0": + version: 4.2.0 + resolution: "@smithy/util-body-length-browser@npm:4.2.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10c0/15553c249088d59406c6917c19ed19810c7dbcc0967c44e5f3fbb2cc870c004b35f388c082b77f370a2c440a69ec7e8336c7a066af904812a66944dd5cb4c8cc + languageName: node + linkType: hard + +"@smithy/util-body-length-node@npm:^4.2.1": + version: 4.2.1 + resolution: "@smithy/util-body-length-node@npm:4.2.1" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10c0/3c32306735af5b62f75375e976a531ab45f171dfb0dc23ee035478d2132eaf21f244c31b0f3e861c514ff97d8112055e74c98ed44595ad24bd31434d5fdaf4bf + languageName: node + linkType: hard + +"@smithy/util-buffer-from@npm:^2.2.0": + version: 2.2.0 + resolution: "@smithy/util-buffer-from@npm:2.2.0" + dependencies: + "@smithy/is-array-buffer": "npm:^2.2.0" + tslib: "npm:^2.6.2" + checksum: 10c0/223d6a508b52ff236eea01cddc062b7652d859dd01d457a4e50365af3de1e24a05f756e19433f6ccf1538544076b4215469e21a4ea83dc1d58d829725b0dbc5a + languageName: node + linkType: hard + +"@smithy/util-buffer-from@npm:^4.2.0": + version: 4.2.0 + resolution: "@smithy/util-buffer-from@npm:4.2.0" + dependencies: + "@smithy/is-array-buffer": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10c0/4842d5607240c11400db30762ef6cb4def8d13e3474c5a901a4e2a1783198f5b163ab6011cf24a7f0acbba9a4d7cc79db1d811dc8aa9da446448e52773223997 + languageName: node + linkType: hard + +"@smithy/util-config-provider@npm:^4.2.0": + version: 4.2.0 + resolution: "@smithy/util-config-provider@npm:4.2.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10c0/0699b9980ef94eac8f491c2ac557dc47e01c6ae71dabcb4464cc064f8dbf0855797461dbec8ba1925d45f076e968b0df02f0691c636cd1043e560f67541a1d27 + languageName: node + linkType: hard + +"@smithy/util-defaults-mode-browser@npm:^4.3.3": + version: 4.3.4 + resolution: "@smithy/util-defaults-mode-browser@npm:4.3.4" + dependencies: + "@smithy/property-provider": "npm:^4.2.3" + "@smithy/smithy-client": "npm:^4.9.1" + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/b3e48214a2f9e3b55995e7b44b4531b37c4e83960582afae8991ba98d2552132e1fc1962408be476ec45301cf03887194d5941cc76db500ad2f4fc748552bfee + languageName: node + linkType: hard + +"@smithy/util-defaults-mode-node@npm:^4.2.5": + version: 4.2.6 + resolution: "@smithy/util-defaults-mode-node@npm:4.2.6" + dependencies: + "@smithy/config-resolver": "npm:^4.4.0" + "@smithy/credential-provider-imds": "npm:^4.2.3" + "@smithy/node-config-provider": "npm:^4.3.3" + "@smithy/property-provider": "npm:^4.2.3" + "@smithy/smithy-client": "npm:^4.9.1" + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/edd0918a5cff0787dd2fadf8aa36ef603922eaff5f3b2ec620c2c522e2bbda127327c26debe26028544ff34d5726004cfbf8c0a90505b657b852989a94f5c8ab + languageName: node + linkType: hard + +"@smithy/util-endpoints@npm:^3.2.3": + version: 3.2.3 + resolution: "@smithy/util-endpoints@npm:3.2.3" + dependencies: + "@smithy/node-config-provider": "npm:^4.3.3" + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/49daffb28d384e7c58f04ac3610f295f41b3ceef78f295ebff6d737efa142e9c95e4eed6db06ca085de7497fc95a4dbb2be99050ca31d07f399aaf63ab0adf35 + languageName: node + linkType: hard + +"@smithy/util-hex-encoding@npm:^4.2.0": + version: 4.2.0 + resolution: "@smithy/util-hex-encoding@npm:4.2.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10c0/aaa94a69f03d14d3f28125cc915ca421065735e2d05d7305f0958a50021b2fce4fc68a248328e6b5b612dbaa49e471d481ff513bf89554f659f0a49573e97312 + languageName: node + linkType: hard + +"@smithy/util-middleware@npm:^4.2.3": + version: 4.2.3 + resolution: "@smithy/util-middleware@npm:4.2.3" + dependencies: + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/e98d461754d849b543a69e6a0fa74448cf00cf0954d6f484f735bcbf6c10a51cf63ad2090bcd8f276c19cbe7c413075ee49e6018cad8c15c3844480fa0efd0a1 + languageName: node + linkType: hard + +"@smithy/util-retry@npm:^4.2.3": + version: 4.2.3 + resolution: "@smithy/util-retry@npm:4.2.3" + dependencies: + "@smithy/service-error-classification": "npm:^4.2.3" + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/9340714056d3ae6513c2527ba0806712aa4e86532ead5150dd0d34210a9f6d10ea1bb55cf4efa3711234444b58dca561a2708d885e61b2dd926a7cb6cf74baa2 + languageName: node + linkType: hard + +"@smithy/util-stream@npm:^4.5.3, @smithy/util-stream@npm:^4.5.4": + version: 4.5.4 + resolution: "@smithy/util-stream@npm:4.5.4" + dependencies: + "@smithy/fetch-http-handler": "npm:^5.3.4" + "@smithy/node-http-handler": "npm:^4.4.3" + "@smithy/types": "npm:^4.8.0" + "@smithy/util-base64": "npm:^4.3.0" + "@smithy/util-buffer-from": "npm:^4.2.0" + "@smithy/util-hex-encoding": "npm:^4.2.0" + "@smithy/util-utf8": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10c0/9c0ea9062b6800a1261524d4268cbbd943e3681367a5c0ff3b3186889b76c28516acfdaf1c47ad71f72a909c796d97204b1e4cd338cd52e54ace808658a826b5 + languageName: node + linkType: hard + +"@smithy/util-uri-escape@npm:^4.2.0": + version: 4.2.0 + resolution: "@smithy/util-uri-escape@npm:4.2.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10c0/1933e8d939dc52e1ee5e7d2397f4c208a9eac0283397a19ee72078d04db997ebe3ad39709b56aac586ffce10d1cf5ab17dfc068ea6ab030098fc06fe3532e085 + languageName: node + linkType: hard + +"@smithy/util-utf8@npm:^2.0.0": + version: 2.3.0 + resolution: "@smithy/util-utf8@npm:2.3.0" + dependencies: + "@smithy/util-buffer-from": "npm:^2.2.0" + tslib: "npm:^2.6.2" + checksum: 10c0/e18840c58cc507ca57fdd624302aefd13337ee982754c9aa688463ffcae598c08461e8620e9852a424d662ffa948fc64919e852508028d09e89ced459bd506ab + languageName: node + linkType: hard + +"@smithy/util-utf8@npm:^4.2.0": + version: 4.2.0 + resolution: "@smithy/util-utf8@npm:4.2.0" + dependencies: + "@smithy/util-buffer-from": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10c0/689a1f2295d52bec0dde7215a075d79ef32ad8b146cb610a529b2cab747d96978401fd31469c225e31f3042830c54403e64d39b28033df013c8de27a84b405a2 + languageName: node + linkType: hard + +"@smithy/util-waiter@npm:^4.2.3": + version: 4.2.3 + resolution: "@smithy/util-waiter@npm:4.2.3" + dependencies: + "@smithy/abort-controller": "npm:^4.2.3" + "@smithy/types": "npm:^4.8.0" + tslib: "npm:^2.6.2" + checksum: 10c0/dcb37f2e3987f4ffda091e0048a8bc04fa68d67ca12d3306c5da8d88678f89247591e5f4da505f11a598137c575c6860cd39648e4c3d55991977a8b13fa0a35a + languageName: node + linkType: hard + +"@smithy/uuid@npm:^1.1.0": + version: 1.1.0 + resolution: "@smithy/uuid@npm:1.1.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10c0/f8a8bfcc0e241457636884e778e261d45d8a3aaad533775111170cac36ac666275b59ec6d86d3d5b8d470ff4b864202d2a1a188b3c0e0ed0c86a0b693acf1ecf + languageName: node + linkType: hard + +"@so-ric/colorspace@npm:^1.1.6": + version: 1.1.6 + resolution: "@so-ric/colorspace@npm:1.1.6" + dependencies: + color: "npm:^5.0.2" + text-hex: "npm:1.0.x" + checksum: 10c0/f3ad26afefbb8d6101ea7c385cd5f402d4291c2ffc9cabe37030d5fdb8bda980ee534a0d7c250f8233fc3a59b99272410177cd98b219f6b3770f91a0fdb6eb3e languageName: node linkType: hard @@ -3946,6 +5531,26 @@ __metadata: languageName: node linkType: hard +"@techteamer/ocsp@npm:1.0.1": + version: 1.0.1 + resolution: "@techteamer/ocsp@npm:1.0.1" + dependencies: + asn1.js: "npm:^5.4.1" + asn1.js-rfc2560: "npm:^5.0.1" + asn1.js-rfc5280: "npm:^3.0.0" + async: "npm:^3.2.4" + simple-lru-cache: "npm:^0.0.2" + checksum: 10c0/cbc744c4ce8247f0cf06b79e77bebf2f5bf2d9901b1b1c29540edc2525868e154f51e982f8b7338d010c6186257cb041cefd50f7095d10c86d45e56ac7e33578 + languageName: node + linkType: hard + +"@tootallnate/once@npm:2": + version: 2.0.0 + resolution: "@tootallnate/once@npm:2.0.0" + checksum: 10c0/073bfa548026b1ebaf1659eb8961e526be22fa77139b10d60e712f46d2f0f05f4e6c8bec62a087d41088ee9e29faa7f54838568e475ab2f776171003c3920858 + languageName: node + linkType: hard + "@tootallnate/quickjs-emscripten@npm:^0.23.0": version: 0.23.0 resolution: "@tootallnate/quickjs-emscripten@npm:0.23.0" @@ -4001,6 +5606,13 @@ __metadata: languageName: node linkType: hard +"@types/caseless@npm:*": + version: 0.12.5 + resolution: "@types/caseless@npm:0.12.5" + checksum: 10c0/b1f8b8a38ce747b643115d37a40ea824c658bd7050e4b69427a10e9d12d1606ed17a0f6018241c08291cd59f70aeb3c1f3754ad61e45f8dbba708ec72dde7ec8 + languageName: node + linkType: hard + "@types/compression@npm:^1": version: 1.8.1 resolution: "@types/compression@npm:1.8.1" @@ -4223,6 +5835,18 @@ __metadata: languageName: node linkType: hard +"@types/request@npm:^2.48.8": + version: 2.48.13 + resolution: "@types/request@npm:2.48.13" + dependencies: + "@types/caseless": "npm:*" + "@types/node": "npm:*" + "@types/tough-cookie": "npm:*" + form-data: "npm:^2.5.5" + checksum: 10c0/1c6798d926a6577f213dbc04aa09945590f260ea367537c20824ff337b0a49d56e5199a6a6029e625568d97c3bbb98908bdb8d9158eb421f70a0d03ae230ff72 + languageName: node + linkType: hard + "@types/responselike@npm:^1.0.0": version: 1.0.3 resolution: "@types/responselike@npm:1.0.3" @@ -4278,6 +5902,20 @@ __metadata: languageName: node linkType: hard +"@types/tough-cookie@npm:*": + version: 4.0.5 + resolution: "@types/tough-cookie@npm:4.0.5" + checksum: 10c0/68c6921721a3dcb40451543db2174a145ef915bc8bcbe7ad4e59194a0238e776e782b896c7a59f4b93ac6acefca9161fccb31d1ce3b3445cb6faa467297fb473 + languageName: node + linkType: hard + +"@types/triple-beam@npm:^1.3.2": + version: 1.3.5 + resolution: "@types/triple-beam@npm:1.3.5" + checksum: 10c0/d5d7f25da612f6d79266f4f1bb9c1ef8f1684e9f60abab251e1261170631062b656ba26ff22631f2760caeafd372abc41e64867cde27fba54fafb73a35b9056a + languageName: node + linkType: hard + "@types/ws@npm:^8.5.10": version: 8.18.1 resolution: "@types/ws@npm:8.18.1" @@ -4424,6 +6062,17 @@ __metadata: languageName: node linkType: hard +"@typespec/ts-http-runtime@npm:^0.3.0": + version: 0.3.1 + resolution: "@typespec/ts-http-runtime@npm:0.3.1" + dependencies: + http-proxy-agent: "npm:^7.0.0" + https-proxy-agent: "npm:^7.0.0" + tslib: "npm:^2.6.2" + checksum: 10c0/9444cc2a073a55f7218ae6e1946b61184f6ec941add16892fdf4417066cf976fb071071c918a00a80a4b80e203dd5f189bc57b513bb8225d171c4e0cedb5a469 + languageName: node + linkType: hard + "@vitejs/plugin-basic-ssl@npm:1.2.0": version: 1.2.0 resolution: "@vitejs/plugin-basic-ssl@npm:1.2.0" @@ -4624,6 +6273,15 @@ __metadata: languageName: node linkType: hard +"abort-controller@npm:^3.0.0": + version: 3.0.0 + resolution: "abort-controller@npm:3.0.0" + dependencies: + event-target-shim: "npm:^5.0.0" + checksum: 10c0/90ccc50f010250152509a344eb2e71977fbf8db0ab8f1061197e3275ddf6c61a41a6edfd7b9409c664513131dd96e962065415325ef23efa5db931b382d24ca5 + languageName: node + linkType: hard + "accepts@npm:~1.3.4, accepts@npm:~1.3.8": version: 1.3.8 resolution: "accepts@npm:1.3.8" @@ -4662,6 +6320,15 @@ __metadata: languageName: node linkType: hard +"agent-base@npm:6": + version: 6.0.2 + resolution: "agent-base@npm:6.0.2" + dependencies: + debug: "npm:4" + checksum: 10c0/dc4f757e40b5f3e3d674bc9beb4f1048f4ee83af189bae39be99f57bf1f48dde166a8b0a5342a84b5944ee8e6ed1e5a9d801858f4ad44764e84957122fe46261 + languageName: node + linkType: hard + "agent-base@npm:^7.0.2, agent-base@npm:^7.1.0, agent-base@npm:^7.1.2": version: 7.1.4 resolution: "agent-base@npm:7.1.4" @@ -4961,6 +6628,45 @@ __metadata: languageName: node linkType: hard +"arrify@npm:^2.0.0": + version: 2.0.1 + resolution: "arrify@npm:2.0.1" + checksum: 10c0/3fb30b5e7c37abea1907a60b28a554d2f0fc088757ca9bf5b684786e583fdf14360721eb12575c1ce6f995282eab936712d3c4389122682eafab0e0b57f78dbb + languageName: node + linkType: hard + +"asn1.js-rfc2560@npm:^5.0.0, asn1.js-rfc2560@npm:^5.0.1": + version: 5.0.1 + resolution: "asn1.js-rfc2560@npm:5.0.1" + dependencies: + asn1.js-rfc5280: "npm:^3.0.0" + peerDependencies: + asn1.js: ^5.0.0 + checksum: 10c0/01599f2d9ea711a61edeb9dc35b0f6551b5a76638d6a103a6f80c722a3bc3b18aacdac49a6b13cb34e3a9bf136fc5bfb772529041766be778bc4c2ab62233734 + languageName: node + linkType: hard + +"asn1.js-rfc5280@npm:^3.0.0": + version: 3.0.0 + resolution: "asn1.js-rfc5280@npm:3.0.0" + dependencies: + asn1.js: "npm:^5.0.0" + checksum: 10c0/5697696dc0bb72dad9dcd88b584fe1fd6d2e5b8cbd9d27f94a5ba3be443bdb285334aaf805d887f354a4e07cceeb2ffa33d79526e56ab0a26505847d6111baa6 + languageName: node + linkType: hard + +"asn1.js@npm:^5.0.0, asn1.js@npm:^5.4.1": + version: 5.4.1 + resolution: "asn1.js@npm:5.4.1" + dependencies: + bn.js: "npm:^4.0.0" + inherits: "npm:^2.0.1" + minimalistic-assert: "npm:^1.0.0" + safer-buffer: "npm:^2.1.0" + checksum: 10c0/b577232fa6069cc52bb128e564002c62b2b1fe47f7137bdcd709c0b8495aa79cee0f8cc458a831b2d8675900eea0d05781b006be5e1aa4f0ae3577a73ec20324 + languageName: node + linkType: hard + "ast-types@npm:^0.13.4": version: 0.13.4 resolution: "ast-types@npm:0.13.4" @@ -4977,6 +6683,15 @@ __metadata: languageName: node linkType: hard +"async-retry@npm:^1.3.3": + version: 1.3.3 + resolution: "async-retry@npm:1.3.3" + dependencies: + retry: "npm:0.13.1" + checksum: 10c0/cabced4fb46f8737b95cc88dc9c0ff42656c62dc83ce0650864e891b6c155a063af08d62c446269b51256f6fbcb69a6563b80e76d0ea4a5117b0c0377b6b19d8 + languageName: node + linkType: hard + "async@npm:^2.6.3, async@npm:~2.6.1": version: 2.6.4 resolution: "async@npm:2.6.4" @@ -4986,13 +6701,20 @@ __metadata: languageName: node linkType: hard -"async@npm:^3.2.0, async@npm:~3.2.0, async@npm:~3.2.6": +"async@npm:^3.2.0, async@npm:^3.2.3, async@npm:^3.2.4, async@npm:~3.2.0, async@npm:~3.2.6": version: 3.2.6 resolution: "async@npm:3.2.6" checksum: 10c0/36484bb15ceddf07078688d95e27076379cc2f87b10c03b6dd8a83e89475a3c8df5848859dd06a4c95af1e4c16fc973de0171a77f18ea00be899aca2a4f85e70 languageName: node linkType: hard +"asynckit@npm:^0.4.0": + version: 0.4.0 + resolution: "asynckit@npm:0.4.0" + checksum: 10c0/d73e2ddf20c4eb9337e1b3df1a0f6159481050a5de457c55b14ea2e5cb6d90bb69e004c9af54737a5ee0917fcf2c9e25de67777bbe58261847846066ba75bc9d + languageName: node + linkType: hard + "atomic-sleep@npm:^1.0.0": version: 1.0.0 resolution: "atomic-sleep@npm:1.0.0" @@ -5045,6 +6767,17 @@ __metadata: languageName: node linkType: hard +"axios@npm:^1.12.2": + version: 1.12.2 + resolution: "axios@npm:1.12.2" + dependencies: + follow-redirects: "npm:^1.15.6" + form-data: "npm:^4.0.4" + proxy-from-env: "npm:^1.1.0" + checksum: 10c0/80b063e318cf05cd33a4d991cea0162f3573481946f9129efb7766f38fde4c061c34f41a93a9f9521f02b7c9565ccbc197c099b0186543ac84a24580017adfed + languageName: node + linkType: hard + "axobject-query@npm:4.1.0": version: 4.1.0 resolution: "axobject-query@npm:4.1.0" @@ -5108,7 +6841,7 @@ __metadata: languageName: node linkType: hard -"base64-js@npm:^1.3.1": +"base64-js@npm:^1.3.0, base64-js@npm:^1.3.1": version: 1.5.1 resolution: "base64-js@npm:1.5.1" checksum: 10c0/f23823513b63173a001030fae4f2dabe283b99a9d324ade3ad3d148e218134676f1ee8568c877cd79ec1c53158dcf2d2ba527a97c606618928ba99dd930102bf @@ -5152,6 +6885,13 @@ __metadata: languageName: node linkType: hard +"big-integer@npm:^1.6.43": + version: 1.6.52 + resolution: "big-integer@npm:1.6.52" + checksum: 10c0/9604224b4c2ab3c43c075d92da15863077a9f59e5d4205f4e7e76acd0cd47e8d469ec5e5dba8d9b32aa233951893b29329ca56ac80c20ce094b4a647a66abae0 + languageName: node + linkType: hard + "big.js@npm:^5.2.2": version: 5.2.2 resolution: "big.js@npm:5.2.2" @@ -5159,6 +6899,13 @@ __metadata: languageName: node linkType: hard +"bignumber.js@npm:^9.0.0, bignumber.js@npm:^9.1.2": + version: 9.3.1 + resolution: "bignumber.js@npm:9.3.1" + checksum: 10c0/61342ba5fe1c10887f0ecf5be02ff6709271481aff48631f86b4d37d55a99b87ce441cfd54df3d16d10ee07ceab7e272fc0be430c657ffafbbbf7b7d631efb75 + languageName: node + linkType: hard + "binary-extensions@npm:^2.0.0": version: 2.3.0 resolution: "binary-extensions@npm:2.3.0" @@ -5186,6 +6933,20 @@ __metadata: languageName: node linkType: hard +"bn.js@npm:^4.0.0": + version: 4.12.2 + resolution: "bn.js@npm:4.12.2" + checksum: 10c0/09a249faa416a9a1ce68b5f5ec8bbca87fe54e5dd4ef8b1cc8a4969147b80035592bddcb1e9cc814c3ba79e573503d5c5178664b722b509fb36d93620dba9b57 + languageName: node + linkType: hard + +"bn.js@npm:^5.2.1": + version: 5.2.2 + resolution: "bn.js@npm:5.2.2" + checksum: 10c0/cb97827d476aab1a0194df33cd84624952480d92da46e6b4a19c32964aa01553a4a613502396712704da2ec8f831cf98d02e74ca03398404bd78a037ba93f2ab + languageName: node + linkType: hard + "bodec@npm:^0.1.0": version: 0.1.0 resolution: "bodec@npm:0.1.0" @@ -5230,6 +6991,13 @@ __metadata: languageName: node linkType: hard +"bowser@npm:^2.11.0": + version: 2.12.1 + resolution: "bowser@npm:2.12.1" + checksum: 10c0/017e8cc63ce2dec75037340626e1408f68334dac95f953ba7db33a266c019f1d262346d2be3994f9a12b7e9c02f57c562078719b8c5e8e8febe01053c613ffbc + languageName: node + linkType: hard + "brace-expansion@npm:^1.1.7": version: 1.1.12 resolution: "brace-expansion@npm:1.1.12" @@ -5258,6 +7026,13 @@ __metadata: languageName: node linkType: hard +"browser-request@npm:^0.3.3": + version: 0.3.3 + resolution: "browser-request@npm:0.3.3" + checksum: 10c0/0aece68471469efb3877a449811d4293f1bde615cf024fe05576b3f9a3adb6a47ca80cbc3c5e8c454496419e452d53c2ac1bba0df5e4e5fb65b012f7085b0b71 + languageName: node + linkType: hard + "browserslist@npm:^4.21.5, browserslist@npm:^4.23.0, browserslist@npm:^4.23.3, browserslist@npm:^4.24.0, browserslist@npm:^4.24.4, browserslist@npm:^4.25.1": version: 4.25.1 resolution: "browserslist@npm:4.25.1" @@ -5272,6 +7047,13 @@ __metadata: languageName: node linkType: hard +"buffer-equal-constant-time@npm:^1.0.1": + version: 1.0.1 + resolution: "buffer-equal-constant-time@npm:1.0.1" + checksum: 10c0/fb2294e64d23c573d0dd1f1e7a466c3e978fe94a4e0f8183937912ca374619773bef8e2aceb854129d2efecbbc515bbd0cc78d2734a3e3031edb0888531bbc8e + languageName: node + linkType: hard + "buffer-from@npm:^1.0.0": version: 1.1.2 resolution: "buffer-from@npm:1.1.2" @@ -5611,6 +7393,22 @@ __metadata: languageName: node linkType: hard +"color-convert@npm:^3.0.1": + version: 3.1.2 + resolution: "color-convert@npm:3.1.2" + dependencies: + color-name: "npm:^2.0.0" + checksum: 10c0/5b83147015024931a06b57b197d09fc1f67f2efc93dfea5f042aba4788a95b13aebe511b0a929e0e837e442fd91a60c27de8e6761ff30e1a1e2fb634cca8a976 + languageName: node + linkType: hard + +"color-name@npm:^2.0.0": + version: 2.0.2 + resolution: "color-name@npm:2.0.2" + checksum: 10c0/40372a581fdeca099b824b6a14dac095387ae83457ed0fafe6f37053515c1094365f0d26b5f29df941be748051b490a0aa3f2ea0c29126a90ab2add482942701 + languageName: node + linkType: hard + "color-name@npm:~1.1.4": version: 1.1.4 resolution: "color-name@npm:1.1.4" @@ -5618,6 +7416,25 @@ __metadata: languageName: node linkType: hard +"color-string@npm:^2.0.0": + version: 2.1.2 + resolution: "color-string@npm:2.1.2" + dependencies: + color-name: "npm:^2.0.0" + checksum: 10c0/d1d3e8123b2a6a6715e539b347ce000925305946092d566697bb872b1b8951a8699a842b4e5e6324733bef7e4cd3517c50aeecf2a6aae12efc7ca5697ac95178 + languageName: node + linkType: hard + +"color@npm:^5.0.2": + version: 5.0.2 + resolution: "color@npm:5.0.2" + dependencies: + color-convert: "npm:^3.0.1" + color-string: "npm:^2.0.0" + checksum: 10c0/a5eeee197651a5fe84ab578a8477827e2c2e56b82832aae2b6c60469240be3bc1f03f99686223b1c4e48107c9e20b980475524faab7e6bab1cb9104313910f0e + languageName: node + linkType: hard + "colorette@npm:^2.0.10, colorette@npm:^2.0.20, colorette@npm:^2.0.7": version: 2.0.20 resolution: "colorette@npm:2.0.20" @@ -5625,6 +7442,15 @@ __metadata: languageName: node linkType: hard +"combined-stream@npm:^1.0.8": + version: 1.0.8 + resolution: "combined-stream@npm:1.0.8" + dependencies: + delayed-stream: "npm:~1.0.0" + checksum: 10c0/0dbb829577e1b1e839fa82b40c07ffaf7de8a09b935cadd355a73652ae70a88b4320db322f6634a4ad93424292fa80973ac6480986247f1734a1137debf271d5 + languageName: node + linkType: hard + "commander@npm:2.15.1": version: 2.15.1 resolution: "commander@npm:2.15.1" @@ -5938,6 +7764,13 @@ __metadata: languageName: node linkType: hard +"data-uri-to-buffer@npm:^4.0.0": + version: 4.0.1 + resolution: "data-uri-to-buffer@npm:4.0.1" + checksum: 10c0/20a6b93107597530d71d4cb285acee17f66bcdfc03fd81040921a81252f19db27588d87fc8fc69e1950c55cfb0bf8ae40d0e5e21d907230813eb5d5a7f9eb45b + languageName: node + linkType: hard + "data-uri-to-buffer@npm:^6.0.2": version: 6.0.2 resolution: "data-uri-to-buffer@npm:6.0.2" @@ -6146,6 +7979,13 @@ __metadata: languageName: node linkType: hard +"delayed-stream@npm:~1.0.0": + version: 1.0.0 + resolution: "delayed-stream@npm:1.0.0" + checksum: 10c0/d758899da03392e6712f042bec80aa293bbe9e9ff1b2634baae6a360113e708b91326594c8a486d475c69d6259afb7efacdc3537bfcda1c6c648e390ce601b19 + languageName: node + linkType: hard + "depd@npm:2.0.0": version: 2.0.0 resolution: "depd@npm:2.0.0" @@ -6287,6 +8127,18 @@ __metadata: languageName: node linkType: hard +"duplexify@npm:^4.1.3": + version: 4.1.3 + resolution: "duplexify@npm:4.1.3" + dependencies: + end-of-stream: "npm:^1.4.1" + inherits: "npm:^2.0.3" + readable-stream: "npm:^3.1.1" + stream-shift: "npm:^1.0.2" + checksum: 10c0/8a7621ae95c89f3937f982fe36d72ea997836a708471a75bb2a0eecde3330311b1e128a6dad510e0fd64ace0c56bff3484ed2e82af0e465600c82117eadfbda5 + languageName: node + linkType: hard + "eastasianwidth@npm:^0.2.0": version: 0.2.0 resolution: "eastasianwidth@npm:0.2.0" @@ -6294,6 +8146,15 @@ __metadata: languageName: node linkType: hard +"ecdsa-sig-formatter@npm:1.0.11, ecdsa-sig-formatter@npm:^1.0.11": + version: 1.0.11 + resolution: "ecdsa-sig-formatter@npm:1.0.11" + dependencies: + safe-buffer: "npm:^5.0.1" + checksum: 10c0/ebfbf19d4b8be938f4dd4a83b8788385da353d63307ede301a9252f9f7f88672e76f2191618fd8edfc2f24679236064176fab0b78131b161ee73daa37125408c + languageName: node + linkType: hard + "ee-first@npm:1.1.1": version: 1.1.1 resolution: "ee-first@npm:1.1.1" @@ -6336,6 +8197,13 @@ __metadata: languageName: node linkType: hard +"enabled@npm:2.0.x": + version: 2.0.0 + resolution: "enabled@npm:2.0.0" + checksum: 10c0/3b2c2af9bc7f8b9e291610f2dde4a75cf6ee52a68f4dd585482fbdf9a55d65388940e024e56d40bb03e05ef6671f5f53021fa8b72a20e954d7066ec28166713f + languageName: node + linkType: hard + "encodeurl@npm:~1.0.2": version: 1.0.2 resolution: "encodeurl@npm:1.0.2" @@ -6359,7 +8227,7 @@ __metadata: languageName: node linkType: hard -"end-of-stream@npm:^1.1.0": +"end-of-stream@npm:^1.1.0, end-of-stream@npm:^1.4.1": version: 1.4.5 resolution: "end-of-stream@npm:1.4.5" dependencies: @@ -6992,6 +8860,13 @@ __metadata: languageName: node linkType: hard +"event-target-shim@npm:^5.0.0": + version: 5.0.1 + resolution: "event-target-shim@npm:5.0.1" + checksum: 10c0/0255d9f936215fd206156fd4caa9e8d35e62075d720dc7d847e89b417e5e62cf1ce6c9b4e0a1633a9256de0efefaf9f8d26924b1f3c8620cffb9db78e7d3076b + languageName: node + linkType: hard + "eventemitter2@npm:5.0.1, eventemitter2@npm:~5.0.1": version: 5.0.1 resolution: "eventemitter2@npm:5.0.1" @@ -7020,13 +8895,22 @@ __metadata: languageName: node linkType: hard -"events@npm:^3.2.0": +"events@npm:^3.0.0, events@npm:^3.2.0": version: 3.3.0 resolution: "events@npm:3.3.0" checksum: 10c0/d6b6f2adbccbcda74ddbab52ed07db727ef52e31a61ed26db9feb7dc62af7fc8e060defa65e5f8af9449b86b52cc1a1f6a79f2eafcf4e62add2b7a1fa4a432f6 languageName: node linkType: hard +"expand-tilde@npm:^2.0.2": + version: 2.0.2 + resolution: "expand-tilde@npm:2.0.2" + dependencies: + homedir-polyfill: "npm:^1.0.1" + checksum: 10c0/205a60497422746d1c3acbc1d65bd609b945066f239a2b785e69a7a651ac4cbeb4e08555b1ea0023abbe855e6fcb5bbf27d0b371367fdccd303d4fb2b4d66845 + languageName: node + linkType: hard + "exponential-backoff@npm:^3.1.1": version: 3.1.2 resolution: "exponential-backoff@npm:3.1.2" @@ -7094,6 +8978,13 @@ __metadata: languageName: node linkType: hard +"extend@npm:^3.0.2": + version: 3.0.2 + resolution: "extend@npm:3.0.2" + checksum: 10c0/73bf6e27406e80aa3e85b0d1c4fd987261e628064e170ca781125c0b635a3dabad5e05adbf07595ea0cf1e6c5396cacb214af933da7cbaf24fe75ff14818e8f9 + languageName: node + linkType: hard + "external-editor@npm:^3.1.0": version: 3.1.0 resolution: "external-editor@npm:3.1.0" @@ -7183,6 +9074,46 @@ __metadata: languageName: node linkType: hard +"fast-xml-parser@npm:5.2.5": + version: 5.2.5 + resolution: "fast-xml-parser@npm:5.2.5" + dependencies: + strnum: "npm:^2.1.0" + bin: + fxparser: src/cli/cli.js + checksum: 10c0/d1057d2e790c327ccfc42b872b91786a4912a152d44f9507bf053f800102dfb07ece3da0a86b33ff6a0caa5a5cad86da3326744f6ae5efb0c6c571d754fe48cd + languageName: node + linkType: hard + +"fast-xml-parser@npm:^4.2.5, fast-xml-parser@npm:^4.4.1": + version: 4.5.3 + resolution: "fast-xml-parser@npm:4.5.3" + dependencies: + strnum: "npm:^1.1.1" + bin: + fxparser: src/cli/cli.js + checksum: 10c0/bf9ccadacfadc95f6e3f0e7882a380a7f219cf0a6f96575149f02cb62bf44c3b7f0daee75b8ff3847bcfd7fbcb201e402c71045936c265cf6d94b141ec4e9327 + languageName: node + linkType: hard + +"fast-xml-parser@npm:^5.0.7": + version: 5.3.0 + resolution: "fast-xml-parser@npm:5.3.0" + dependencies: + strnum: "npm:^2.1.0" + bin: + fxparser: src/cli/cli.js + checksum: 10c0/9e398f83ac7be548bdbdd4186bef79d25b518b091ce658ba02ef45187aad1e1cfcf0a6c12b85cd904a2d829f2248a4db51c06652a617e73516d99e9c88c646b2 + languageName: node + linkType: hard + +"fastest-levenshtein@npm:^1.0.16": + version: 1.0.16 + resolution: "fastest-levenshtein@npm:1.0.16" + checksum: 10c0/7e3d8ae812a7f4fdf8cad18e9cde436a39addf266a5986f653ea0d81e0de0900f50c0f27c6d5aff3f686bcb48acbd45be115ae2216f36a6a13a7dbbf5cad878b + languageName: node + linkType: hard + "fastq@npm:^1.6.0": version: 1.19.1 resolution: "fastq@npm:1.19.1" @@ -7220,6 +9151,23 @@ __metadata: languageName: node linkType: hard +"fecha@npm:^4.2.0": + version: 4.2.3 + resolution: "fecha@npm:4.2.3" + checksum: 10c0/0e895965959cf6a22bb7b00f0bf546f2783836310f510ddf63f463e1518d4c96dec61ab33fdfd8e79a71b4856a7c865478ce2ee8498d560fe125947703c9b1cf + languageName: node + linkType: hard + +"fetch-blob@npm:^3.1.2, fetch-blob@npm:^3.1.4": + version: 3.2.0 + resolution: "fetch-blob@npm:3.2.0" + dependencies: + node-domexception: "npm:^1.0.0" + web-streams-polyfill: "npm:^3.0.3" + checksum: 10c0/60054bf47bfa10fb0ba6cb7742acec2f37c1f56344f79a70bb8b1c48d77675927c720ff3191fa546410a0442c998d27ab05e9144c32d530d8a52fbe68f843b69 + languageName: node + linkType: hard + "file-entry-cache@npm:^8.0.0": version: 8.0.0 resolution: "file-entry-cache@npm:8.0.0" @@ -7320,6 +9268,13 @@ __metadata: languageName: node linkType: hard +"fn.name@npm:1.x.x": + version: 1.1.0 + resolution: "fn.name@npm:1.1.0" + checksum: 10c0/8ad62aa2d4f0b2a76d09dba36cfec61c540c13a0fd72e5d94164e430f987a7ce6a743112bbeb14877c810ef500d1f73d7f56e76d029d2e3413f20d79e3460a9a + languageName: node + linkType: hard + "follow-redirects@npm:^1.0.0": version: 1.15.9 resolution: "follow-redirects@npm:1.15.9" @@ -7330,7 +9285,7 @@ __metadata: languageName: node linkType: hard -"follow-redirects@npm:^1.14.0": +"follow-redirects@npm:^1.14.0, follow-redirects@npm:^1.15.6": version: 1.15.11 resolution: "follow-redirects@npm:1.15.11" peerDependenciesMeta: @@ -7359,6 +9314,42 @@ __metadata: languageName: node linkType: hard +"form-data@npm:^2.5.5": + version: 2.5.5 + resolution: "form-data@npm:2.5.5" + dependencies: + asynckit: "npm:^0.4.0" + combined-stream: "npm:^1.0.8" + es-set-tostringtag: "npm:^2.1.0" + hasown: "npm:^2.0.2" + mime-types: "npm:^2.1.35" + safe-buffer: "npm:^5.2.1" + checksum: 10c0/7fb70447849fc9bce4d01fe9a626f6587441f85779a2803b67f803e1ab52b0bd78db0a7acd80d944c665f68ca90936c327f1244b730719b638a0219e98b20488 + languageName: node + linkType: hard + +"form-data@npm:^4.0.4": + version: 4.0.4 + resolution: "form-data@npm:4.0.4" + dependencies: + asynckit: "npm:^0.4.0" + combined-stream: "npm:^1.0.8" + es-set-tostringtag: "npm:^2.1.0" + hasown: "npm:^2.0.2" + mime-types: "npm:^2.1.12" + checksum: 10c0/373525a9a034b9d57073e55eab79e501a714ffac02e7a9b01be1c820780652b16e4101819785e1e18f8d98f0aee866cc654d660a435c378e16a72f2e7cac9695 + languageName: node + linkType: hard + +"formdata-polyfill@npm:^4.0.10": + version: 4.0.10 + resolution: "formdata-polyfill@npm:4.0.10" + dependencies: + fetch-blob: "npm:^3.1.2" + checksum: 10c0/5392ec484f9ce0d5e0d52fb5a78e7486637d516179b0eb84d81389d7eccf9ca2f663079da56f761355c0a65792810e3b345dc24db9a8bbbcf24ef3c8c88570c6 + languageName: node + linkType: hard + "forwarded@npm:0.2.0": version: 0.2.0 resolution: "forwarded@npm:0.2.0" @@ -7471,6 +9462,59 @@ __metadata: languageName: node linkType: hard +"gaxios@npm:^6.0.0, gaxios@npm:^6.0.2, gaxios@npm:^6.1.1": + version: 6.7.1 + resolution: "gaxios@npm:6.7.1" + dependencies: + extend: "npm:^3.0.2" + https-proxy-agent: "npm:^7.0.1" + is-stream: "npm:^2.0.0" + node-fetch: "npm:^2.6.9" + uuid: "npm:^9.0.1" + checksum: 10c0/53e92088470661c5bc493a1de29d05aff58b1f0009ec5e7903f730f892c3642a93e264e61904383741ccbab1ce6e519f12a985bba91e13527678b32ee6d7d3fd + languageName: node + linkType: hard + +"gaxios@npm:^7.0.0": + version: 7.1.2 + resolution: "gaxios@npm:7.1.2" + dependencies: + extend: "npm:^3.0.2" + https-proxy-agent: "npm:^7.0.1" + node-fetch: "npm:^3.3.2" + checksum: 10c0/f08b3541766719eb622952b7c33b0af5729a3a29e1fc98eb39ec5a8949f7a8b74020729d06e323b491f5aadb16ba05b001145bd2402bd5bfd52cdc8467e268ef + languageName: node + linkType: hard + +"gcp-metadata@npm:^6.1.0": + version: 6.1.1 + resolution: "gcp-metadata@npm:6.1.1" + dependencies: + gaxios: "npm:^6.1.1" + google-logging-utils: "npm:^0.0.2" + json-bigint: "npm:^1.0.0" + checksum: 10c0/71f6ad4800aa622c246ceec3955014c0c78cdcfe025971f9558b9379f4019f5e65772763428ee8c3244fa81b8631977316eaa71a823493f82e5c44d7259ffac8 + languageName: node + linkType: hard + +"gcp-metadata@npm:^8.0.0": + version: 8.1.1 + resolution: "gcp-metadata@npm:8.1.1" + dependencies: + gaxios: "npm:^7.0.0" + google-logging-utils: "npm:^1.0.0" + json-bigint: "npm:^1.0.0" + checksum: 10c0/6b05b0afa35d7fbdfdf22c96f03c1fe978f6c33deb59633990ac57e01cf946317ebbef967ef183c109a64886d08d4a9001ff731428245dc99b6b387b98598d89 + languageName: node + linkType: hard + +"generic-pool@npm:^3.8.2": + version: 3.9.0 + resolution: "generic-pool@npm:3.9.0" + checksum: 10c0/6b314d0d71170d5cbaf7162c423f53f8d6556b2135626a65bcdc03c089840b0a2f59eeb2d907939b8200e945eaf71ceb6630426f22d2128a1d242aec4b232aa7 + languageName: node + linkType: hard + "gensync@npm:^1.0.0-beta.2": version: 1.0.0-beta.2 resolution: "gensync@npm:1.0.0-beta.2" @@ -7603,7 +9647,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^10.2.2, glob@npm:^10.3.10": +"glob@npm:^10.0.0, glob@npm:^10.2.2, glob@npm:^10.3.10": version: 10.4.5 resolution: "glob@npm:10.4.5" dependencies: @@ -7659,6 +9703,49 @@ __metadata: languageName: node linkType: hard +"google-auth-library@npm:^10.1.0": + version: 10.4.1 + resolution: "google-auth-library@npm:10.4.1" + dependencies: + base64-js: "npm:^1.3.0" + ecdsa-sig-formatter: "npm:^1.0.11" + gaxios: "npm:^7.0.0" + gcp-metadata: "npm:^8.0.0" + google-logging-utils: "npm:^1.0.0" + gtoken: "npm:^8.0.0" + jws: "npm:^4.0.0" + checksum: 10c0/40513b987511710d16cd83dee80730f5395453691985285b30714657679c3194db5a3e0d7b5b249b7445d6fe1ee38c1d119fa8e05721729acacacf7c90304a96 + languageName: node + linkType: hard + +"google-auth-library@npm:^9.6.3": + version: 9.15.1 + resolution: "google-auth-library@npm:9.15.1" + dependencies: + base64-js: "npm:^1.3.0" + ecdsa-sig-formatter: "npm:^1.0.11" + gaxios: "npm:^6.1.1" + gcp-metadata: "npm:^6.1.0" + gtoken: "npm:^7.0.0" + jws: "npm:^4.0.0" + checksum: 10c0/6eef36d9a9cb7decd11e920ee892579261c6390104b3b24d3e0f3889096673189fe2ed0ee43fd563710e2560de98e63ad5aa4967b91e7f4e69074a422d5f7b65 + languageName: node + linkType: hard + +"google-logging-utils@npm:^0.0.2": + version: 0.0.2 + resolution: "google-logging-utils@npm:0.0.2" + checksum: 10c0/9a4bbd470dd101c77405e450fffca8592d1d7114f245a121288d04a957aca08c9dea2dd1a871effe71e41540d1bb0494731a0b0f6fea4358e77f06645e4268c1 + languageName: node + linkType: hard + +"google-logging-utils@npm:^1.0.0": + version: 1.1.1 + resolution: "google-logging-utils@npm:1.1.1" + checksum: 10c0/a99ca790b39a4eb854310366f4583dab6d5842d2fa70b0f46de1ef23909a4baad8e6eedd8dcde0756f968131b053c5a4a6e8cb4825595f12ea6289a7ba6360df + languageName: node + linkType: hard + "gopd@npm:^1.0.1, gopd@npm:^1.2.0": version: 1.2.0 resolution: "gopd@npm:1.2.0" @@ -7699,6 +9786,26 @@ __metadata: languageName: node linkType: hard +"gtoken@npm:^7.0.0": + version: 7.1.0 + resolution: "gtoken@npm:7.1.0" + dependencies: + gaxios: "npm:^6.0.0" + jws: "npm:^4.0.0" + checksum: 10c0/0a3dcacb1a3c4578abe1ee01c7d0bf20bffe8ded3ee73fc58885d53c00f6eb43b4e1372ff179f0da3ed5cfebd5b7c6ab8ae2776f1787e90d943691b4fe57c716 + languageName: node + linkType: hard + +"gtoken@npm:^8.0.0": + version: 8.0.0 + resolution: "gtoken@npm:8.0.0" + dependencies: + gaxios: "npm:^7.0.0" + jws: "npm:^4.0.0" + checksum: 10c0/058538e5bbe081d30ada5f1fd34d3a8194357c2e6ecbf7c8a98daeefbf13f7e06c15649c7dace6a1d4cc3bc6dc5483bd484d6d7adc5852021896d7c05c439f37 + languageName: node + linkType: hard + "handle-thing@npm:^2.0.0": version: 2.0.1 resolution: "handle-thing@npm:2.0.1" @@ -7770,6 +9877,15 @@ __metadata: languageName: node linkType: hard +"homedir-polyfill@npm:^1.0.1": + version: 1.0.3 + resolution: "homedir-polyfill@npm:1.0.3" + dependencies: + parse-passwd: "npm:^1.0.0" + checksum: 10c0/3c099844f94b8b438f124bd5698bdcfef32b2d455115fb8050d7148e7f7b95fc89ba9922586c491f0e1cdebf437b1053c84ecddb8d596e109e9ac69c5b4a9e27 + languageName: node + linkType: hard + "hosted-git-info@npm:^8.0.0": version: 8.1.0 resolution: "hosted-git-info@npm:8.1.0" @@ -7791,6 +9907,13 @@ __metadata: languageName: node linkType: hard +"html-entities@npm:^2.5.2": + version: 2.6.0 + resolution: "html-entities@npm:2.6.0" + checksum: 10c0/7c8b15d9ea0cd00dc9279f61bab002ba6ca8a7a0f3c36ed2db3530a67a9621c017830d1d2c1c65beb9b8e3436ea663e9cf8b230472e0e413359399413b27c8b7 + languageName: node + linkType: hard + "htmlparser2@npm:^10.0.0": version: 10.0.0 resolution: "htmlparser2@npm:10.0.0" @@ -7862,6 +9985,17 @@ __metadata: languageName: node linkType: hard +"http-proxy-agent@npm:^5.0.0": + version: 5.0.0 + resolution: "http-proxy-agent@npm:5.0.0" + dependencies: + "@tootallnate/once": "npm:2" + agent-base: "npm:6" + debug: "npm:4" + checksum: 10c0/32a05e413430b2c1e542e5c74b38a9f14865301dd69dff2e53ddb684989440e3d2ce0c4b64d25eb63cf6283e6265ff979a61cf93e3ca3d23047ddfdc8df34a32 + languageName: node + linkType: hard + "http-proxy-agent@npm:^7.0.0, http-proxy-agent@npm:^7.0.1": version: 7.0.2 resolution: "http-proxy-agent@npm:7.0.2" @@ -7925,7 +10059,7 @@ __metadata: languageName: node linkType: hard -"https-proxy-agent@npm:7.0.6, https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.3, https-proxy-agent@npm:^7.0.6": +"https-proxy-agent@npm:7.0.6, https-proxy-agent@npm:^7.0.0, https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.2, https-proxy-agent@npm:^7.0.3, https-proxy-agent@npm:^7.0.6": version: 7.0.6 resolution: "https-proxy-agent@npm:7.0.6" dependencies: @@ -7935,6 +10069,16 @@ __metadata: languageName: node linkType: hard +"https-proxy-agent@npm:^5.0.0": + version: 5.0.1 + resolution: "https-proxy-agent@npm:5.0.1" + dependencies: + agent-base: "npm:6" + debug: "npm:4" + checksum: 10c0/6dd639f03434003577c62b27cafdb864784ef19b2de430d8ae2a1d45e31c4fd60719e5637b44db1a88a046934307da7089e03d6089ec3ddacc1189d8de8897d1 + languageName: node + linkType: hard + "husky@npm:^9.1.7": version: 9.1.7 resolution: "husky@npm:9.1.7" @@ -8221,6 +10365,15 @@ __metadata: languageName: node linkType: hard +"is-docker@npm:^2.0.0": + version: 2.2.1 + resolution: "is-docker@npm:2.2.1" + bin: + is-docker: cli.js + checksum: 10c0/e828365958d155f90c409cdbe958f64051d99e8aedc2c8c4cd7c89dcf35329daed42f7b99346f7828df013e27deb8f721cf9408ba878c76eb9e8290235fbcdcc + languageName: node + linkType: hard + "is-docker@npm:^3.0.0": version: 3.0.0 resolution: "is-docker@npm:3.0.0" @@ -8404,6 +10557,13 @@ __metadata: languageName: node linkType: hard +"is-stream@npm:^2.0.0": + version: 2.0.1 + resolution: "is-stream@npm:2.0.1" + checksum: 10c0/7c284241313fc6efc329b8d7f08e16c0efeb6baab1b4cd0ba579eb78e5af1aa5da11e68559896a2067cd6c526bd29241dda4eb1225e627d5aa1a89a76d4635a5 + languageName: node + linkType: hard + "is-string@npm:^1.1.1": version: 1.1.1 resolution: "is-string@npm:1.1.1" @@ -8483,6 +10643,15 @@ __metadata: languageName: node linkType: hard +"is-wsl@npm:^2.1.1": + version: 2.2.0 + resolution: "is-wsl@npm:2.2.0" + dependencies: + is-docker: "npm:^2.0.0" + checksum: 10c0/a6fa2d370d21be487c0165c7a440d567274fbba1a817f2f0bfa41cc5e3af25041d84267baa22df66696956038a43973e72fca117918c91431920bdef490fa25e + languageName: node + linkType: hard + "is-wsl@npm:^3.1.0": version: 3.1.0 resolution: "is-wsl@npm:3.1.0" @@ -8673,6 +10842,15 @@ __metadata: languageName: node linkType: hard +"json-bigint@npm:^1.0.0": + version: 1.0.0 + resolution: "json-bigint@npm:1.0.0" + dependencies: + bignumber.js: "npm:^9.0.0" + checksum: 10c0/e3f34e43be3284b573ea150a3890c92f06d54d8ded72894556357946aeed9877fd795f62f37fe16509af189fd314ab1104d0fd0f163746ad231b9f378f5b33f4 + languageName: node + linkType: hard + "json-buffer@npm:3.0.1": version: 3.0.1 resolution: "json-buffer@npm:3.0.1" @@ -8756,6 +10934,66 @@ __metadata: languageName: node linkType: hard +"jsonwebtoken@npm:^9.0.0": + version: 9.0.2 + resolution: "jsonwebtoken@npm:9.0.2" + dependencies: + jws: "npm:^3.2.2" + lodash.includes: "npm:^4.3.0" + lodash.isboolean: "npm:^3.0.3" + lodash.isinteger: "npm:^4.0.4" + lodash.isnumber: "npm:^3.0.3" + lodash.isplainobject: "npm:^4.0.6" + lodash.isstring: "npm:^4.0.1" + lodash.once: "npm:^4.0.0" + ms: "npm:^2.1.1" + semver: "npm:^7.5.4" + checksum: 10c0/d287a29814895e866db2e5a0209ce730cbc158441a0e5a70d5e940eb0d28ab7498c6bf45029cc8b479639bca94056e9a7f254e2cdb92a2f5750c7f358657a131 + languageName: node + linkType: hard + +"jwa@npm:^1.4.1": + version: 1.4.2 + resolution: "jwa@npm:1.4.2" + dependencies: + buffer-equal-constant-time: "npm:^1.0.1" + ecdsa-sig-formatter: "npm:1.0.11" + safe-buffer: "npm:^5.0.1" + checksum: 10c0/210a544a42ca22203e8fc538835205155ba3af6a027753109f9258bdead33086bac3c25295af48ac1981f87f9c5f941bc8f70303670f54ea7dcaafb53993d92c + languageName: node + linkType: hard + +"jwa@npm:^2.0.0": + version: 2.0.1 + resolution: "jwa@npm:2.0.1" + dependencies: + buffer-equal-constant-time: "npm:^1.0.1" + ecdsa-sig-formatter: "npm:1.0.11" + safe-buffer: "npm:^5.0.1" + checksum: 10c0/ab3ebc6598e10dc11419d4ed675c9ca714a387481466b10e8a6f3f65d8d9c9237e2826f2505280a739cf4cbcf511cb288eeec22b5c9c63286fc5a2e4f97e78cf + languageName: node + linkType: hard + +"jws@npm:^3.2.2": + version: 3.2.2 + resolution: "jws@npm:3.2.2" + dependencies: + jwa: "npm:^1.4.1" + safe-buffer: "npm:^5.0.1" + checksum: 10c0/e770704533d92df358adad7d1261fdecad4d7b66fa153ba80d047e03ca0f1f73007ce5ed3fbc04d2eba09ba6e7e6e645f351e08e5ab51614df1b0aa4f384dfff + languageName: node + linkType: hard + +"jws@npm:^4.0.0": + version: 4.0.0 + resolution: "jws@npm:4.0.0" + dependencies: + jwa: "npm:^2.0.0" + safe-buffer: "npm:^5.0.1" + checksum: 10c0/f1ca77ea5451e8dc5ee219cb7053b8a4f1254a79cb22417a2e1043c1eb8a569ae118c68f24d72a589e8a3dd1824697f47d6bd4fb4bebb93a3bdf53545e721661 + languageName: node + linkType: hard + "karma-source-map-support@npm:1.4.0": version: 1.4.0 resolution: "karma-source-map-support@npm:1.4.0" @@ -8781,6 +11019,13 @@ __metadata: languageName: node linkType: hard +"kuler@npm:^2.0.0": + version: 2.0.0 + resolution: "kuler@npm:2.0.0" + checksum: 10c0/0a4e99d92ca373f8f74d1dc37931909c4d0d82aebc94cf2ba265771160fc12c8df34eaaac80805efbda367e2795cb1f1dd4c3d404b6b1cf38aec94035b503d2d + languageName: node + linkType: hard + "launch-editor@npm:^2.6.1": version: 2.10.0 resolution: "launch-editor@npm:2.10.0" @@ -8911,6 +11156,7 @@ __metadata: prettier-plugin-tailwindcss: "npm:^0.6.14" primeng: "npm:^19.1.4" rxjs: "npm:~7.8.2" + snowflake-sdk: "npm:^2.3.1" tailwindcss: "npm:^3.4.17" tailwindcss-primeui: "npm:^0.6.1" tslib: "npm:^2.8.1" @@ -9109,6 +11355,34 @@ __metadata: languageName: node linkType: hard +"lodash.includes@npm:^4.3.0": + version: 4.3.0 + resolution: "lodash.includes@npm:4.3.0" + checksum: 10c0/7ca498b9b75bf602d04e48c0adb842dfc7d90f77bcb2a91a2b2be34a723ad24bc1c8b3683ec6b2552a90f216c723cdea530ddb11a3320e08fa38265703978f4b + languageName: node + linkType: hard + +"lodash.isboolean@npm:^3.0.3": + version: 3.0.3 + resolution: "lodash.isboolean@npm:3.0.3" + checksum: 10c0/0aac604c1ef7e72f9a6b798e5b676606042401dd58e49f051df3cc1e3adb497b3d7695635a5cbec4ae5f66456b951fdabe7d6b387055f13267cde521f10ec7f7 + languageName: node + linkType: hard + +"lodash.isinteger@npm:^4.0.4": + version: 4.0.4 + resolution: "lodash.isinteger@npm:4.0.4" + checksum: 10c0/4c3e023a2373bf65bf366d3b8605b97ec830bca702a926939bcaa53f8e02789b6a176e7f166b082f9365bfec4121bfeb52e86e9040cb8d450e64c858583f61b7 + languageName: node + linkType: hard + +"lodash.isnumber@npm:^3.0.3": + version: 3.0.3 + resolution: "lodash.isnumber@npm:3.0.3" + checksum: 10c0/2d01530513a1ee4f72dd79528444db4e6360588adcb0e2ff663db2b3f642d4bb3d687051ae1115751ca9082db4fdef675160071226ca6bbf5f0c123dbf0aa12d + languageName: node + linkType: hard + "lodash.isplainobject@npm:^4.0.6": version: 4.0.6 resolution: "lodash.isplainobject@npm:4.0.6" @@ -9116,6 +11390,13 @@ __metadata: languageName: node linkType: hard +"lodash.isstring@npm:^4.0.1": + version: 4.0.1 + resolution: "lodash.isstring@npm:4.0.1" + checksum: 10c0/09eaf980a283f9eef58ef95b30ec7fee61df4d6bf4aba3b5f096869cc58f24c9da17900febc8ffd67819b4e29de29793190e88dc96983db92d84c95fa85d1c92 + languageName: node + linkType: hard + "lodash.kebabcase@npm:^4.1.1": version: 4.1.1 resolution: "lodash.kebabcase@npm:4.1.1" @@ -9137,6 +11418,13 @@ __metadata: languageName: node linkType: hard +"lodash.once@npm:^4.0.0": + version: 4.1.1 + resolution: "lodash.once@npm:4.1.1" + checksum: 10c0/46a9a0a66c45dd812fcc016e46605d85ad599fe87d71a02f6736220554b52ffbe82e79a483ad40f52a8a95755b0d1077fba259da8bfb6694a7abbf4a48f1fc04 + languageName: node + linkType: hard + "lodash.snakecase@npm:^4.1.1": version: 4.1.1 resolution: "lodash.snakecase@npm:4.1.1" @@ -9195,6 +11483,20 @@ __metadata: languageName: node linkType: hard +"logform@npm:^2.7.0": + version: 2.7.0 + resolution: "logform@npm:2.7.0" + dependencies: + "@colors/colors": "npm:1.6.0" + "@types/triple-beam": "npm:^1.3.2" + fecha: "npm:^4.2.0" + ms: "npm:^2.1.1" + safe-stable-stringify: "npm:^2.3.1" + triple-beam: "npm:^1.3.0" + checksum: 10c0/4789b4b37413c731d1835734cb799240d31b865afde6b7b3e06051d6a4127bfda9e88c99cfbf296d084a315ccbed2647796e6a56b66e725bcb268c586f57558f + languageName: node + linkType: hard + "lowercase-keys@npm:^2.0.0": version: 2.0.0 resolution: "lowercase-keys@npm:2.0.0" @@ -9364,7 +11666,7 @@ __metadata: languageName: node linkType: hard -"mime-types@npm:^2.1.27, mime-types@npm:^2.1.31, mime-types@npm:~2.1.17, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34": +"mime-types@npm:^2.1.12, mime-types@npm:^2.1.27, mime-types@npm:^2.1.29, mime-types@npm:^2.1.31, mime-types@npm:^2.1.35, mime-types@npm:~2.1.17, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34": version: 2.1.35 resolution: "mime-types@npm:2.1.35" dependencies: @@ -9382,6 +11684,15 @@ __metadata: languageName: node linkType: hard +"mime@npm:^3.0.0": + version: 3.0.0 + resolution: "mime@npm:3.0.0" + bin: + mime: cli.js + checksum: 10c0/402e792a8df1b2cc41cb77f0dcc46472b7944b7ec29cb5bbcd398624b6b97096728f1239766d3fdeb20551dd8d94738344c195a6ea10c4f906eb0356323b0531 + languageName: node + linkType: hard + "mimic-fn@npm:^2.1.0": version: 2.1.0 resolution: "mimic-fn@npm:2.1.0" @@ -9572,6 +11883,22 @@ __metadata: languageName: node linkType: hard +"moment-timezone@npm:^0.5.15": + version: 0.5.48 + resolution: "moment-timezone@npm:0.5.48" + dependencies: + moment: "npm:^2.29.4" + checksum: 10c0/ab14ec9d94bc33f29ac18e5417b7f8aca0b17130b952c5cc9697b8fea839e5ece9313af5fd3c9703a05db472b1560ddbfc7ad2aa24aac9afd047d6da6c3c6033 + languageName: node + linkType: hard + +"moment@npm:^2.29.4": + version: 2.30.1 + resolution: "moment@npm:2.30.1" + checksum: 10c0/865e4279418c6de666fca7786607705fd0189d8a7b7624e2e56be99290ac846f90878a6f602e34b4e0455c549b85385b1baf9966845962b313699e7cb847543a + languageName: node + linkType: hard + "mrmime@npm:2.0.1": version: 2.0.1 resolution: "mrmime@npm:2.0.1" @@ -9812,6 +12139,38 @@ __metadata: languageName: node linkType: hard +"node-domexception@npm:^1.0.0": + version: 1.0.0 + resolution: "node-domexception@npm:1.0.0" + checksum: 10c0/5e5d63cda29856402df9472335af4bb13875e1927ad3be861dc5ebde38917aecbf9ae337923777af52a48c426b70148815e890a5d72760f1b4d758cc671b1a2b + languageName: node + linkType: hard + +"node-fetch@npm:^2.6.9": + version: 2.7.0 + resolution: "node-fetch@npm:2.7.0" + dependencies: + whatwg-url: "npm:^5.0.0" + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + checksum: 10c0/b55786b6028208e6fbe594ccccc213cab67a72899c9234eb59dba51062a299ea853210fcf526998eaa2867b0963ad72338824450905679ff0fa304b8c5093ae8 + languageName: node + linkType: hard + +"node-fetch@npm:^3.3.2": + version: 3.3.2 + resolution: "node-fetch@npm:3.3.2" + dependencies: + data-uri-to-buffer: "npm:^4.0.0" + fetch-blob: "npm:^3.1.4" + formdata-polyfill: "npm:^4.0.10" + checksum: 10c0/f3d5e56190562221398c9f5750198b34cf6113aa304e34ee97c94fd300ec578b25b2c2906edba922050fce983338fde0d5d34fcb0fc3336ade5bd0e429ad7538 + languageName: node + linkType: hard + "node-forge@npm:^1": version: 1.3.1 resolution: "node-forge@npm:1.3.1" @@ -9974,6 +12333,13 @@ __metadata: languageName: node linkType: hard +"oauth4webapi@npm:^3.0.1": + version: 3.8.2 + resolution: "oauth4webapi@npm:3.8.2" + checksum: 10c0/a12e9c51b36b3b114dd78a9db4b5f317c9d5629d399e4b6b4366cf5154defc9d980a01a770fd1b63d9371948eed5259967a30c2ca1df8dce40f651c1589de158 + languageName: node + linkType: hard + "object-assign@npm:^4.0.1": version: 4.1.1 resolution: "object-assign@npm:4.1.1" @@ -10104,6 +12470,15 @@ __metadata: languageName: node linkType: hard +"one-time@npm:^1.0.0": + version: 1.0.0 + resolution: "one-time@npm:1.0.0" + dependencies: + fn.name: "npm:1.x.x" + checksum: 10c0/6e4887b331edbb954f4e915831cbec0a7b9956c36f4feb5f6de98c448ac02ff881fd8d9b55a6b1b55030af184c6b648f340a76eb211812f4ad8c9b4b8692fdaa + languageName: node + linkType: hard + "onetime@npm:^5.1.0": version: 5.1.2 resolution: "onetime@npm:5.1.2" @@ -10134,7 +12509,7 @@ __metadata: languageName: node linkType: hard -"open@npm:^10.0.3": +"open@npm:^10.0.3, open@npm:^10.1.0": version: 10.2.0 resolution: "open@npm:10.2.0" dependencies: @@ -10146,6 +12521,16 @@ __metadata: languageName: node linkType: hard +"open@npm:^7.3.1": + version: 7.4.2 + resolution: "open@npm:7.4.2" + dependencies: + is-docker: "npm:^2.0.0" + is-wsl: "npm:^2.1.1" + checksum: 10c0/77573a6a68f7364f3a19a4c80492712720746b63680ee304555112605ead196afe91052bd3c3d165efdf4e9d04d255e87de0d0a77acec11ef47fd5261251813f + languageName: node + linkType: hard + "openid-client@npm:^4.9.1": version: 4.9.1 resolution: "openid-client@npm:4.9.1" @@ -10224,7 +12609,7 @@ __metadata: languageName: node linkType: hard -"p-limit@npm:^3.0.2": +"p-limit@npm:^3.0.1, p-limit@npm:^3.0.2": version: 3.1.0 resolution: "p-limit@npm:3.1.0" dependencies: @@ -10373,6 +12758,13 @@ __metadata: languageName: node linkType: hard +"parse-passwd@npm:^1.0.0": + version: 1.0.0 + resolution: "parse-passwd@npm:1.0.0" + checksum: 10c0/1c05c05f95f184ab9ca604841d78e4fe3294d46b8e3641d305dcc28e930da0e14e602dbda9f3811cd48df5b0e2e27dbef7357bf0d7c40e41b18c11c3a8b8d17b + languageName: node + linkType: hard + "parse5-html-rewriting-stream@npm:7.0.0": version: 7.0.0 resolution: "parse5-html-rewriting-stream@npm:7.0.0" @@ -11242,7 +13634,7 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:^3.0.6, readable-stream@npm:^3.4.0": +"readable-stream@npm:^3.0.6, readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0, readable-stream@npm:^3.6.2": version: 3.6.2 resolution: "readable-stream@npm:3.6.2" dependencies: @@ -11496,20 +13888,31 @@ __metadata: languageName: node linkType: hard -"retry@npm:^0.12.0": - version: 0.12.0 - resolution: "retry@npm:0.12.0" - checksum: 10c0/59933e8501727ba13ad73ef4a04d5280b3717fd650408460c987392efe9d7be2040778ed8ebe933c5cbd63da3dcc37919c141ef8af0a54a6e4fca5a2af177bfe +"retry-request@npm:^7.0.0": + version: 7.0.2 + resolution: "retry-request@npm:7.0.2" + dependencies: + "@types/request": "npm:^2.48.8" + extend: "npm:^3.0.2" + teeny-request: "npm:^9.0.0" + checksum: 10c0/c79936695a43db1bc82a7bad348a1e0be1c363799be2e1fa87b8c3aeb5dabf0ccb023b811aa5000c000ee73e196b88febff7d3e22cbb63a77175228514256155 languageName: node linkType: hard -"retry@npm:^0.13.1": +"retry@npm:0.13.1, retry@npm:^0.13.1": version: 0.13.1 resolution: "retry@npm:0.13.1" checksum: 10c0/9ae822ee19db2163497e074ea919780b1efa00431d197c7afdb950e42bf109196774b92a49fc9821f0b8b328a98eea6017410bfc5e8a0fc19c85c6d11adb3772 languageName: node linkType: hard +"retry@npm:^0.12.0": + version: 0.12.0 + resolution: "retry@npm:0.12.0" + checksum: 10c0/59933e8501727ba13ad73ef4a04d5280b3717fd650408460c987392efe9d7be2040778ed8ebe933c5cbd63da3dcc37919c141ef8af0a54a6e4fca5a2af177bfe + languageName: node + linkType: hard + "reusify@npm:^1.0.4": version: 1.1.0 resolution: "reusify@npm:1.1.0" @@ -11725,7 +14128,7 @@ __metadata: languageName: node linkType: hard -"safe-buffer@npm:5.2.1, safe-buffer@npm:>=5.1.0, safe-buffer@npm:^5.1.0, safe-buffer@npm:^5.2.1, safe-buffer@npm:~5.2.0": +"safe-buffer@npm:5.2.1, safe-buffer@npm:>=5.1.0, safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:^5.2.1, safe-buffer@npm:~5.2.0": version: 5.2.1 resolution: "safe-buffer@npm:5.2.1" checksum: 10c0/6501914237c0a86e9675d4e51d89ca3c21ffd6a31642efeba25ad65720bce6921c9e7e974e5be91a786b25aa058b5303285d3c15dbabf983a919f5f630d349f3 @@ -11767,7 +14170,7 @@ __metadata: languageName: node linkType: hard -"safer-buffer@npm:>= 2.1.2 < 3, safer-buffer@npm:>= 2.1.2 < 3.0.0": +"safer-buffer@npm:>= 2.1.2 < 3, safer-buffer@npm:>= 2.1.2 < 3.0.0, safer-buffer@npm:^2.1.0": version: 2.1.2 resolution: "safer-buffer@npm:2.1.2" checksum: 10c0/7e3c8b2e88a1841c9671094bbaeebd94448111dd90a81a1f606f3f67708a6ec57763b3b47f06da09fc6054193e0e6709e77325415dc8422b04497a8070fa02d4 @@ -12130,6 +14533,13 @@ __metadata: languageName: node linkType: hard +"simple-lru-cache@npm:^0.0.2": + version: 0.0.2 + resolution: "simple-lru-cache@npm:0.0.2" + checksum: 10c0/1fa03f9b61cdf2d385f38a8bb2dd9eb41748dea2a6fc4c4d157bdb3633c556317ea7281feebce8942c8835f26364db443f3b9029d7bc09784ca3005e093cb79d + languageName: node + linkType: hard + "slash@npm:^5.1.0": version: 5.1.0 resolution: "slash@npm:5.1.0" @@ -12164,6 +14574,52 @@ __metadata: languageName: node linkType: hard +"snowflake-sdk@npm:^2.3.1": + version: 2.3.1 + resolution: "snowflake-sdk@npm:2.3.1" + dependencies: + "@aws-crypto/sha256-js": "npm:^5.2.0" + "@aws-sdk/client-s3": "npm:^3.726.0" + "@aws-sdk/client-sts": "npm:^3.899.0" + "@aws-sdk/credential-provider-node": "npm:^3.823.0" + "@aws-sdk/ec2-metadata-service": "npm:^3.826.0" + "@azure/identity": "npm:^4.10.1" + "@azure/storage-blob": "npm:12.26.x" + "@google-cloud/storage": "npm:^7.7.0" + "@smithy/node-http-handler": "npm:^4.0.1" + "@smithy/protocol-http": "npm:^5.1.3" + "@smithy/signature-v4": "npm:^5.2.1" + "@techteamer/ocsp": "npm:1.0.1" + asn1.js-rfc2560: "npm:^5.0.0" + asn1.js-rfc5280: "npm:^3.0.0" + axios: "npm:^1.12.2" + big-integer: "npm:^1.6.43" + bignumber.js: "npm:^9.1.2" + bn.js: "npm:^5.2.1" + browser-request: "npm:^0.3.3" + expand-tilde: "npm:^2.0.2" + fast-xml-parser: "npm:^4.2.5" + fastest-levenshtein: "npm:^1.0.16" + generic-pool: "npm:^3.8.2" + glob: "npm:^10.0.0" + google-auth-library: "npm:^10.1.0" + https-proxy-agent: "npm:^7.0.2" + jsonwebtoken: "npm:^9.0.0" + mime-types: "npm:^2.1.29" + moment: "npm:^2.29.4" + moment-timezone: "npm:^0.5.15" + oauth4webapi: "npm:^3.0.1" + open: "npm:^7.3.1" + simple-lru-cache: "npm:^0.0.2" + toml: "npm:^3.0.0" + uuid: "npm:^8.3.2" + winston: "npm:^3.1.0" + peerDependencies: + asn1.js: ^5.4.1 + checksum: 10c0/661711cc1330828d5a6a45af6f1fe6b0ac892e9394e0578c7f7b15416972609dc9467c3854288681ac8d3764fda8b8c42fe258d2d8431960ffed6970179b5476 + languageName: node + linkType: hard + "sockjs@npm:^0.3.24": version: 0.3.24 resolution: "sockjs@npm:0.3.24" @@ -12339,6 +14795,13 @@ __metadata: languageName: node linkType: hard +"stack-trace@npm:0.0.x": + version: 0.0.10 + resolution: "stack-trace@npm:0.0.10" + checksum: 10c0/9ff3dabfad4049b635a85456f927a075c9d0c210e3ea336412d18220b2a86cbb9b13ec46d6c37b70a302a4ea4d49e30e5d4944dd60ae784073f1cde778ac8f4b + languageName: node + linkType: hard + "statuses@npm:2.0.1": version: 2.0.1 resolution: "statuses@npm:2.0.1" @@ -12363,6 +14826,22 @@ __metadata: languageName: node linkType: hard +"stream-events@npm:^1.0.5": + version: 1.0.5 + resolution: "stream-events@npm:1.0.5" + dependencies: + stubs: "npm:^3.0.0" + checksum: 10c0/5d235a5799a483e94ea8829526fe9d95d76460032d5e78555fe4f801949ac6a27ea2212e4e0827c55f78726b3242701768adf2d33789465f51b31ed8ebd6b086 + languageName: node + linkType: hard + +"stream-shift@npm:^1.0.2": + version: 1.0.3 + resolution: "stream-shift@npm:1.0.3" + checksum: 10c0/939cd1051ca750d240a0625b106a2b988c45fb5a3be0cebe9a9858cb01bc1955e8c7b9fac17a9462976bea4a7b704e317c5c2200c70f0ca715a3363b9aa4fd3b + languageName: node + linkType: hard + "string-argv@npm:^0.3.2": version: 0.3.2 resolution: "string-argv@npm:0.3.2" @@ -12498,6 +14977,27 @@ __metadata: languageName: node linkType: hard +"strnum@npm:^1.1.1": + version: 1.1.2 + resolution: "strnum@npm:1.1.2" + checksum: 10c0/a0fce2498fa3c64ce64a40dada41beb91cabe3caefa910e467dc0518ef2ebd7e4d10f8c2202a6104f1410254cae245066c0e94e2521fb4061a5cb41831952392 + languageName: node + linkType: hard + +"strnum@npm:^2.1.0": + version: 2.1.1 + resolution: "strnum@npm:2.1.1" + checksum: 10c0/1f9bd1f9b4c68333f25c2b1f498ea529189f060cd50aa59f1876139c994d817056de3ce57c12c970f80568d75df2289725e218bd9e3cdf73cd1a876c9c102733 + languageName: node + linkType: hard + +"stubs@npm:^3.0.0": + version: 3.0.0 + resolution: "stubs@npm:3.0.0" + checksum: 10c0/841a4ab8c76795d34aefe129185763b55fbf2e4693208215627caea4dd62e1299423dcd96f708d3128e3dfa0e669bae2cb912e6e906d7d81eaf6493196570923 + languageName: node + linkType: hard + "sucrase@npm:^3.35.0": version: 3.35.0 resolution: "sucrase@npm:3.35.0" @@ -12634,6 +15134,19 @@ __metadata: languageName: node linkType: hard +"teeny-request@npm:^9.0.0": + version: 9.0.0 + resolution: "teeny-request@npm:9.0.0" + dependencies: + http-proxy-agent: "npm:^5.0.0" + https-proxy-agent: "npm:^5.0.0" + node-fetch: "npm:^2.6.9" + stream-events: "npm:^1.0.5" + uuid: "npm:^9.0.0" + checksum: 10c0/1c51a284075b57b7b7f970fc8d855d611912f0e485aa1d1dfda3c0be3f2df392e4ce83b1b39877134041abb7c255f3777f175b27323ef5bf008839e42a1958bc + languageName: node + linkType: hard + "terser-webpack-plugin@npm:^5.3.11": version: 5.3.14 resolution: "terser-webpack-plugin@npm:5.3.14" @@ -12691,6 +15204,13 @@ __metadata: languageName: node linkType: hard +"text-hex@npm:1.0.x": + version: 1.0.0 + resolution: "text-hex@npm:1.0.0" + checksum: 10c0/57d8d320d92c79d7c03ffb8339b825bb9637c2cbccf14304309f51d8950015c44464b6fd1b6820a3d4821241c68825634f09f5a2d9d501e84f7c6fd14376860d + languageName: node + linkType: hard + "thenify-all@npm:^1.0.0": version: 1.6.0 resolution: "thenify-all@npm:1.6.0" @@ -12783,6 +15303,20 @@ __metadata: languageName: node linkType: hard +"toml@npm:^3.0.0": + version: 3.0.0 + resolution: "toml@npm:3.0.0" + checksum: 10c0/8d7ed3e700ca602e5419fca343e1c595eb7aa177745141f0761a5b20874b58ee5c878cd045c408da9d130cb2b611c639912210ba96ce2f78e443569aa8060c18 + languageName: node + linkType: hard + +"tr46@npm:~0.0.3": + version: 0.0.3 + resolution: "tr46@npm:0.0.3" + checksum: 10c0/047cb209a6b60c742f05c9d3ace8fa510bff609995c129a37ace03476a9b12db4dbf975e74600830ef0796e18882b2381fb5fb1f6b4f96b832c374de3ab91a11 + languageName: node + linkType: hard + "tree-dump@npm:^1.0.1": version: 1.0.3 resolution: "tree-dump@npm:1.0.3" @@ -12801,6 +15335,13 @@ __metadata: languageName: node linkType: hard +"triple-beam@npm:^1.3.0": + version: 1.4.1 + resolution: "triple-beam@npm:1.4.1" + checksum: 10c0/4bf1db71e14fe3ff1c3adbe3c302f1fdb553b74d7591a37323a7badb32dc8e9c290738996cbb64f8b10dc5a3833645b5d8c26221aaaaa12e50d1251c9aba2fea + languageName: node + linkType: hard + "ts-api-utils@npm:^2.1.0": version: 2.1.0 resolution: "ts-api-utils@npm:2.1.0" @@ -12836,7 +15377,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:2.8.1, tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.8.0, tslib@npm:^2.8.1": +"tslib@npm:2.8.1, tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.1.0, tslib@npm:^2.2.0, tslib@npm:^2.3.0, tslib@npm:^2.6.2, tslib@npm:^2.8.0, tslib@npm:^2.8.1": version: 2.8.1 resolution: "tslib@npm:2.8.1" checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62 @@ -13221,7 +15762,7 @@ __metadata: languageName: node linkType: hard -"uuid@npm:^8.3.2": +"uuid@npm:^8.0.0, uuid@npm:^8.3.0, uuid@npm:^8.3.2": version: 8.3.2 resolution: "uuid@npm:8.3.2" bin: @@ -13230,6 +15771,15 @@ __metadata: languageName: node linkType: hard +"uuid@npm:^9.0.0, uuid@npm:^9.0.1": + version: 9.0.1 + resolution: "uuid@npm:9.0.1" + bin: + uuid: dist/bin/uuid + checksum: 10c0/1607dd32ac7fc22f2d8f77051e6a64845c9bce5cd3dd8aa0070c074ec73e666a1f63c7b4e0f4bf2bc8b9d59dc85a15e17807446d9d2b17c8485fbc2147b27f9b + languageName: node + linkType: hard + "validate-npm-package-license@npm:^3.0.4": version: 3.0.4 resolution: "validate-npm-package-license@npm:3.0.4" @@ -13363,6 +15913,20 @@ __metadata: languageName: node linkType: hard +"web-streams-polyfill@npm:^3.0.3": + version: 3.3.3 + resolution: "web-streams-polyfill@npm:3.3.3" + checksum: 10c0/64e855c47f6c8330b5436147db1c75cb7e7474d924166800e8e2aab5eb6c76aac4981a84261dd2982b3e754490900b99791c80ae1407a9fa0dcff74f82ea3a7f + languageName: node + linkType: hard + +"webidl-conversions@npm:^3.0.0": + version: 3.0.1 + resolution: "webidl-conversions@npm:3.0.1" + checksum: 10c0/5612d5f3e54760a797052eb4927f0ddc01383550f542ccd33d5238cfd65aeed392a45ad38364970d0a0f4fea32e1f4d231b3d8dac4a3bdd385e5cf802ae097db + languageName: node + linkType: hard + "webpack-dev-middleware@npm:7.4.2, webpack-dev-middleware@npm:^7.4.2": version: 7.4.2 resolution: "webpack-dev-middleware@npm:7.4.2" @@ -13514,6 +16078,16 @@ __metadata: languageName: node linkType: hard +"whatwg-url@npm:^5.0.0": + version: 5.0.0 + resolution: "whatwg-url@npm:5.0.0" + dependencies: + tr46: "npm:~0.0.3" + webidl-conversions: "npm:^3.0.0" + checksum: 10c0/1588bed84d10b72d5eec1d0faa0722ba1962f1821e7539c535558fb5398d223b0c50d8acab950b8c488b4ba69043fd833cc2697056b167d8ad46fac3995a55d5 + languageName: node + linkType: hard + "which-boxed-primitive@npm:^1.1.0, which-boxed-primitive@npm:^1.1.1": version: 1.1.1 resolution: "which-boxed-primitive@npm:1.1.1" @@ -13604,6 +16178,36 @@ __metadata: languageName: node linkType: hard +"winston-transport@npm:^4.9.0": + version: 4.9.0 + resolution: "winston-transport@npm:4.9.0" + dependencies: + logform: "npm:^2.7.0" + readable-stream: "npm:^3.6.2" + triple-beam: "npm:^1.3.0" + checksum: 10c0/e2990a172e754dbf27e7823772214a22dc8312f7ec9cfba831e5ef30a5d5528792e5ea8f083c7387ccfc5b2af20e3691f64738546c8869086110a26f98671095 + languageName: node + linkType: hard + +"winston@npm:^3.1.0": + version: 3.18.3 + resolution: "winston@npm:3.18.3" + dependencies: + "@colors/colors": "npm:^1.6.0" + "@dabh/diagnostics": "npm:^2.0.8" + async: "npm:^3.2.3" + is-stream: "npm:^2.0.0" + logform: "npm:^2.7.0" + one-time: "npm:^1.0.0" + readable-stream: "npm:^3.4.0" + safe-stable-stringify: "npm:^2.3.1" + stack-trace: "npm:0.0.x" + triple-beam: "npm:^1.3.0" + winston-transport: "npm:^4.9.0" + checksum: 10c0/0bd666590d7f1f2e1fa1273b699463e14b2fcf2bab503e16bc62f275c4b52f14c3dda7bb255d5cc4cef046dd3e112c45518ec8f3c3536ab666421b7265d8c45b + languageName: node + linkType: hard + "word-wrap@npm:^1.2.5": version: 1.2.5 resolution: "word-wrap@npm:1.2.5"