From 237ab8e19c17d6a2f6617481ca3c22d4693f1316 Mon Sep 17 00:00:00 2001 From: davedumto Date: Thu, 29 Jan 2026 02:23:17 +0100 Subject: [PATCH 1/2] feat: implement basic scheduled price fetching --- apps/ingestor/.env.example | 4 + apps/ingestor/package.json | 2 + apps/ingestor/src/app.module.ts | 11 +- apps/ingestor/src/modules/prices.module.ts | 5 +- .../services/price-fetcher.service.spec.ts | 167 ++++++++++++++++++ .../src/services/price-fetcher.service.ts | 22 ++- .../src/services/scheduler.service.spec.ts | 149 ++++++++++++++++ .../src/services/scheduler.service.ts | 82 +++++++++ package-lock.json | 91 +++++++++- 9 files changed, 524 insertions(+), 9 deletions(-) create mode 100644 apps/ingestor/src/services/price-fetcher.service.spec.ts create mode 100644 apps/ingestor/src/services/scheduler.service.spec.ts create mode 100644 apps/ingestor/src/services/scheduler.service.ts diff --git a/apps/ingestor/.env.example b/apps/ingestor/.env.example index 7e08520..8597475 100644 --- a/apps/ingestor/.env.example +++ b/apps/ingestor/.env.example @@ -1,6 +1,10 @@ # Server Configuration PORT=3000 +# Scheduled Price Fetching +FETCH_INTERVAL_MS=60000 +STOCK_SYMBOLS=AAPL,GOOGL,MSFT,TSLA + # External API Keys # ALPHA_VANTAGE_API_KEY=your_alpha_vantage_api_key # YAHOO_FINANCE_API_KEY=your_yahoo_finance_api_key diff --git a/apps/ingestor/package.json b/apps/ingestor/package.json index fb74a43..1b2ee09 100644 --- a/apps/ingestor/package.json +++ b/apps/ingestor/package.json @@ -17,8 +17,10 @@ "dependencies": { "@oracle-stocks/shared": "*", "@nestjs/common": "^10.0.0", + "@nestjs/config": "^3.0.0", "@nestjs/core": "^10.0.0", "@nestjs/platform-express": "^10.0.0", + "@nestjs/schedule": "^4.0.0", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1" }, diff --git a/apps/ingestor/src/app.module.ts b/apps/ingestor/src/app.module.ts index 6784a5c..80bcf7b 100644 --- a/apps/ingestor/src/app.module.ts +++ b/apps/ingestor/src/app.module.ts @@ -1,8 +1,17 @@ import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { ScheduleModule } from '@nestjs/schedule'; import { PricesModule } from './modules/prices.module'; @Module({ - imports: [PricesModule], + imports: [ + ConfigModule.forRoot({ + isGlobal: true, + envFilePath: '.env', + }), + ScheduleModule.forRoot(), + PricesModule, + ], controllers: [], providers: [], }) diff --git a/apps/ingestor/src/modules/prices.module.ts b/apps/ingestor/src/modules/prices.module.ts index 444c26e..cfdc8bd 100644 --- a/apps/ingestor/src/modules/prices.module.ts +++ b/apps/ingestor/src/modules/prices.module.ts @@ -1,10 +1,11 @@ import { Module } from '@nestjs/common'; import { PricesController } from '../controllers/prices.controller'; import { PriceFetcherService } from '../services/price-fetcher.service'; +import { SchedulerService } from '../services/scheduler.service'; @Module({ controllers: [PricesController], - providers: [PriceFetcherService], - exports: [PriceFetcherService], + providers: [PriceFetcherService, SchedulerService], + exports: [PriceFetcherService, SchedulerService], }) export class PricesModule {} diff --git a/apps/ingestor/src/services/price-fetcher.service.spec.ts b/apps/ingestor/src/services/price-fetcher.service.spec.ts new file mode 100644 index 0000000..5aea313 --- /dev/null +++ b/apps/ingestor/src/services/price-fetcher.service.spec.ts @@ -0,0 +1,167 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ConfigService } from '@nestjs/config'; +import { PriceFetcherService } from './price-fetcher.service'; + +describe('PriceFetcherService', () => { + let service: PriceFetcherService; + let configService: jest.Mocked; + + beforeEach(async () => { + const mockConfigService = { + get: jest.fn((key: string, defaultValue?: unknown) => { + const config: Record = { + STOCK_SYMBOLS: 'AAPL,GOOGL,MSFT', + }; + return config[key] ?? defaultValue; + }), + }; + + const module: TestingModule = await Test.createTestingModule({ + providers: [ + PriceFetcherService, + { provide: ConfigService, useValue: mockConfigService }, + ], + }).compile(); + + service = module.get(PriceFetcherService); + configService = module.get(ConfigService); + }); + + describe('constructor', () => { + it('should be defined', () => { + expect(service).toBeDefined(); + }); + + it('should read symbols from config', () => { + expect(configService.get).toHaveBeenCalledWith('STOCK_SYMBOLS', 'AAPL,GOOGL,MSFT,TSLA'); + }); + + it('should parse symbols correctly', () => { + expect(service.getSymbols()).toEqual(['AAPL', 'GOOGL', 'MSFT']); + }); + }); + + describe('fetchRawPrices', () => { + it('should fetch prices for all configured symbols', async () => { + const prices = await service.fetchRawPrices(); + + expect(prices).toHaveLength(3); // One price per symbol from MockProvider + expect(prices.map(p => p.symbol)).toEqual(['AAPL', 'GOOGL', 'MSFT']); + }); + + it('should return prices with correct structure', async () => { + const prices = await service.fetchRawPrices(); + + prices.forEach(price => { + expect(price).toHaveProperty('symbol'); + expect(price).toHaveProperty('price'); + expect(price).toHaveProperty('timestamp'); + expect(price).toHaveProperty('source'); + expect(typeof price.symbol).toBe('string'); + expect(typeof price.price).toBe('number'); + expect(typeof price.timestamp).toBe('number'); + expect(price.source).toBe('MockProvider'); + }); + }); + + it('should store fetched prices', async () => { + expect(service.getRawPrices()).toHaveLength(0); + + await service.fetchRawPrices(); + + expect(service.getRawPrices().length).toBeGreaterThan(0); + }); + }); + + describe('getRawPrices', () => { + it('should return empty array initially', () => { + expect(service.getRawPrices()).toEqual([]); + }); + + it('should return fetched prices', async () => { + await service.fetchRawPrices(); + const prices = service.getRawPrices(); + + expect(prices.length).toBeGreaterThan(0); + }); + }); + + describe('getSymbols', () => { + it('should return configured symbols', () => { + const symbols = service.getSymbols(); + + expect(Array.isArray(symbols)).toBe(true); + expect(symbols).toContain('AAPL'); + expect(symbols).toContain('GOOGL'); + expect(symbols).toContain('MSFT'); + }); + + it('should return a copy of symbols array', () => { + const symbols1 = service.getSymbols(); + const symbols2 = service.getSymbols(); + + expect(symbols1).not.toBe(symbols2); + expect(symbols1).toEqual(symbols2); + }); + }); + + describe('symbol parsing', () => { + it('should handle symbols with extra whitespace', async () => { + const mockConfigWithSpaces = { + get: jest.fn((key: string, defaultValue?: unknown) => { + if (key === 'STOCK_SYMBOLS') { + return ' AAPL , GOOGL , MSFT '; + } + return defaultValue; + }), + }; + + const module: TestingModule = await Test.createTestingModule({ + providers: [ + PriceFetcherService, + { provide: ConfigService, useValue: mockConfigWithSpaces }, + ], + }).compile(); + + const serviceWithSpaces = module.get(PriceFetcherService); + expect(serviceWithSpaces.getSymbols()).toEqual(['AAPL', 'GOOGL', 'MSFT']); + }); + + it('should filter out empty symbols', async () => { + const mockConfigWithEmpty = { + get: jest.fn((key: string, defaultValue?: unknown) => { + if (key === 'STOCK_SYMBOLS') { + return 'AAPL,,GOOGL,,,MSFT'; + } + return defaultValue; + }), + }; + + const module: TestingModule = await Test.createTestingModule({ + providers: [ + PriceFetcherService, + { provide: ConfigService, useValue: mockConfigWithEmpty }, + ], + }).compile(); + + const serviceWithEmpty = module.get(PriceFetcherService); + expect(serviceWithEmpty.getSymbols()).toEqual(['AAPL', 'GOOGL', 'MSFT']); + }); + + it('should use default symbols when env var is not set', async () => { + const mockConfigDefault = { + get: jest.fn((key: string, defaultValue?: unknown) => defaultValue), + }; + + const module: TestingModule = await Test.createTestingModule({ + providers: [ + PriceFetcherService, + { provide: ConfigService, useValue: mockConfigDefault }, + ], + }).compile(); + + const serviceDefault = module.get(PriceFetcherService); + expect(serviceDefault.getSymbols()).toEqual(['AAPL', 'GOOGL', 'MSFT', 'TSLA']); + }); + }); +}); diff --git a/apps/ingestor/src/services/price-fetcher.service.ts b/apps/ingestor/src/services/price-fetcher.service.ts index 96220ab..e620ce1 100644 --- a/apps/ingestor/src/services/price-fetcher.service.ts +++ b/apps/ingestor/src/services/price-fetcher.service.ts @@ -1,4 +1,5 @@ import { Injectable, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; import { RawPrice } from '@oracle-stocks/shared'; import { PriceProvider, MockProvider } from '../providers'; @@ -7,22 +8,29 @@ export class PriceFetcherService { private readonly logger = new Logger(PriceFetcherService.name); private rawPrices: RawPrice[] = []; private readonly providers: PriceProvider[] = []; + private readonly symbols: string[]; - constructor() { + constructor(private readonly configService: ConfigService) { this.providers.push(new MockProvider()); + + const symbolsEnv = this.configService.get('STOCK_SYMBOLS', 'AAPL,GOOGL,MSFT,TSLA'); + this.symbols = symbolsEnv + .split(',') + .map(s => s.trim()) + .filter(s => s.length > 0); + + this.logger.log(`Configured symbols: ${this.symbols.join(', ')}`); } async fetchRawPrices(): Promise { - const symbols = ['AAPL', 'GOOGL', 'MSFT', 'TSLA']; - - const pricePromises = this.providers.map(provider => provider.fetchPrices(symbols)); + const pricePromises = this.providers.map(provider => provider.fetchPrices(this.symbols)); const results = await Promise.all(pricePromises); this.rawPrices = results.flat(); this.logger.log(`Fetched ${this.rawPrices.length} raw prices from ${this.providers.length} provider(s)`); this.rawPrices.forEach(price => { this.logger.debug( - `${price.source} - ${price.symbol}: $${price.price.toFixed(2)} at ${new Date(price.timestamp).toISOString()}` + `${price.source} - ${price.symbol}: $${price.price.toFixed(2)} at ${new Date(price.timestamp).toISOString()}`, ); }); @@ -32,4 +40,8 @@ export class PriceFetcherService { getRawPrices(): RawPrice[] { return this.rawPrices; } + + getSymbols(): string[] { + return [...this.symbols]; + } } diff --git a/apps/ingestor/src/services/scheduler.service.spec.ts b/apps/ingestor/src/services/scheduler.service.spec.ts new file mode 100644 index 0000000..9779960 --- /dev/null +++ b/apps/ingestor/src/services/scheduler.service.spec.ts @@ -0,0 +1,149 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ConfigService } from '@nestjs/config'; +import { SchedulerService } from './scheduler.service'; +import { PriceFetcherService } from './price-fetcher.service'; + +describe('SchedulerService', () => { + let service: SchedulerService; + let priceFetcherService: jest.Mocked; + let configService: jest.Mocked; + + const mockPrices = [ + { symbol: 'AAPL', price: 150.25, timestamp: Date.now(), source: 'MockProvider' }, + { symbol: 'GOOGL', price: 2800.5, timestamp: Date.now(), source: 'MockProvider' }, + ]; + + beforeEach(async () => { + const mockPriceFetcherService = { + fetchRawPrices: jest.fn().mockResolvedValue(mockPrices), + getRawPrices: jest.fn().mockReturnValue(mockPrices), + getSymbols: jest.fn().mockReturnValue(['AAPL', 'GOOGL']), + }; + + const mockConfigService = { + get: jest.fn((key: string, defaultValue?: unknown) => { + const config: Record = { + FETCH_INTERVAL_MS: 1000, + STOCK_SYMBOLS: 'AAPL,GOOGL', + }; + return config[key] ?? defaultValue; + }), + }; + + const module: TestingModule = await Test.createTestingModule({ + providers: [ + SchedulerService, + { provide: PriceFetcherService, useValue: mockPriceFetcherService }, + { provide: ConfigService, useValue: mockConfigService }, + ], + }).compile(); + + service = module.get(SchedulerService); + priceFetcherService = module.get(PriceFetcherService); + configService = module.get(ConfigService); + }); + + afterEach(() => { + service.stopScheduler(); + jest.clearAllMocks(); + }); + + describe('constructor', () => { + it('should be defined', () => { + expect(service).toBeDefined(); + }); + + it('should read fetch interval from config', () => { + expect(configService.get).toHaveBeenCalledWith('FETCH_INTERVAL_MS', 60000); + expect(service.getIntervalMs()).toBe(1000); + }); + }); + + describe('startScheduler', () => { + it('should start the scheduler and execute fetch immediately', async () => { + service.startScheduler(); + + expect(service.isSchedulerRunning()).toBe(true); + // Wait for immediate fetch + await new Promise(resolve => setTimeout(resolve, 50)); + expect(priceFetcherService.fetchRawPrices).toHaveBeenCalled(); + }); + + it('should not start if already running', () => { + service.startScheduler(); + const firstCallCount = priceFetcherService.fetchRawPrices.mock.calls.length; + + service.startScheduler(); + // Should not trigger another immediate fetch + expect(priceFetcherService.fetchRawPrices.mock.calls.length).toBe(firstCallCount); + }); + + it('should execute periodic fetches', async () => { + service.startScheduler(); + + // Wait for initial fetch + one interval + await new Promise(resolve => setTimeout(resolve, 1100)); + expect(priceFetcherService.fetchRawPrices.mock.calls.length).toBeGreaterThanOrEqual(2); + }); + }); + + describe('stopScheduler', () => { + it('should stop the scheduler', () => { + service.startScheduler(); + expect(service.isSchedulerRunning()).toBe(true); + + service.stopScheduler(); + expect(service.isSchedulerRunning()).toBe(false); + }); + + it('should handle stopping when not running', () => { + expect(() => service.stopScheduler()).not.toThrow(); + }); + }); + + describe('isSchedulerRunning', () => { + it('should return false initially', () => { + expect(service.isSchedulerRunning()).toBe(false); + }); + + it('should return true when running', () => { + service.startScheduler(); + expect(service.isSchedulerRunning()).toBe(true); + }); + }); + + describe('getIntervalMs', () => { + it('should return configured interval', () => { + expect(service.getIntervalMs()).toBe(1000); + }); + }); + + describe('error handling', () => { + it('should handle fetch errors gracefully', async () => { + priceFetcherService.fetchRawPrices.mockRejectedValueOnce(new Error('Fetch failed')); + + service.startScheduler(); + await new Promise(resolve => setTimeout(resolve, 50)); + + // Should not throw, scheduler should still be running + expect(service.isSchedulerRunning()).toBe(true); + }); + }); + + describe('onModuleInit', () => { + it('should start scheduler on module init', () => { + const startSpy = jest.spyOn(service, 'startScheduler'); + service.onModuleInit(); + expect(startSpy).toHaveBeenCalled(); + }); + }); + + describe('onModuleDestroy', () => { + it('should stop scheduler on module destroy', () => { + service.startScheduler(); + const stopSpy = jest.spyOn(service, 'stopScheduler'); + service.onModuleDestroy(); + expect(stopSpy).toHaveBeenCalled(); + }); + }); +}); diff --git a/apps/ingestor/src/services/scheduler.service.ts b/apps/ingestor/src/services/scheduler.service.ts new file mode 100644 index 0000000..f62b944 --- /dev/null +++ b/apps/ingestor/src/services/scheduler.service.ts @@ -0,0 +1,82 @@ +import { Injectable, Logger, OnModuleInit, OnModuleDestroy } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { PriceFetcherService } from './price-fetcher.service'; + +@Injectable() +export class SchedulerService implements OnModuleInit, OnModuleDestroy { + private readonly logger = new Logger(SchedulerService.name); + private intervalId: NodeJS.Timeout | null = null; + private readonly fetchIntervalMs: number; + private isRunning = false; + + constructor( + private readonly configService: ConfigService, + private readonly priceFetcherService: PriceFetcherService, + ) { + this.fetchIntervalMs = this.configService.get('FETCH_INTERVAL_MS', 60000); + } + + onModuleInit(): void { + this.startScheduler(); + } + + onModuleDestroy(): void { + this.stopScheduler(); + } + + startScheduler(): void { + if (this.isRunning) { + this.logger.warn('Scheduler is already running'); + return; + } + + this.logger.log(`Starting price fetch scheduler with interval: ${this.fetchIntervalMs}ms`); + + // Execute immediately on startup + this.executeFetch(); + + // Then schedule periodic fetches + this.intervalId = setInterval(() => { + this.executeFetch(); + }, this.fetchIntervalMs); + + this.isRunning = true; + this.logger.log('Price fetch scheduler started successfully'); + } + + stopScheduler(): void { + if (this.intervalId) { + clearInterval(this.intervalId); + this.intervalId = null; + this.isRunning = false; + this.logger.log('Price fetch scheduler stopped'); + } + } + + isSchedulerRunning(): boolean { + return this.isRunning; + } + + getIntervalMs(): number { + return this.fetchIntervalMs; + } + + private async executeFetch(): Promise { + const startTime = Date.now(); + this.logger.log('Scheduled price fetch starting...'); + + try { + const prices = await this.priceFetcherService.fetchRawPrices(); + const duration = Date.now() - startTime; + + this.logger.log( + `Scheduled price fetch completed successfully: ${prices.length} prices fetched in ${duration}ms`, + ); + } catch (error) { + const duration = Date.now() - startTime; + this.logger.error( + `Scheduled price fetch failed after ${duration}ms: ${error instanceof Error ? error.message : 'Unknown error'}`, + ); + } + } +} diff --git a/package-lock.json b/package-lock.json index 73768ca..9849cba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1900,8 +1900,10 @@ "license": "MIT", "dependencies": { "@nestjs/common": "^10.0.0", + "@nestjs/config": "^3.0.0", "@nestjs/core": "^10.0.0", "@nestjs/platform-express": "^10.0.0", + "@nestjs/schedule": "^4.0.0", "@oracle-stocks/shared": "*", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1" @@ -4853,6 +4855,21 @@ } } }, + "node_modules/@nestjs/config": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-3.3.0.tgz", + "integrity": "sha512-pdGTp8m9d0ZCrjTpjkUbZx6gyf2IKf+7zlkrPNMsJzYZ4bFRRTpXrnj+556/5uiI6AfL5mMrJc2u7dB6bvM+VA==", + "license": "MIT", + "dependencies": { + "dotenv": "16.4.5", + "dotenv-expand": "10.0.0", + "lodash": "4.17.21" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "rxjs": "^7.1.0" + } + }, "node_modules/@nestjs/core": { "version": "10.4.22", "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.4.22.tgz", @@ -4912,6 +4929,20 @@ "@nestjs/core": "^10.0.0" } }, + "node_modules/@nestjs/schedule": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-4.1.2.tgz", + "integrity": "sha512-hCTQ1lNjIA5EHxeu8VvQu2Ed2DBLS1GSC6uKPYlBiQe6LL9a7zfE9iVSK+zuK8E2odsApteEBmfAQchc8Hx0Gg==", + "license": "MIT", + "dependencies": { + "cron": "3.2.1", + "uuid": "11.0.3" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "@nestjs/core": "^8.0.0 || ^9.0.0 || ^10.0.0" + } + }, "node_modules/@nestjs/schematics": { "version": "10.2.3", "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-10.2.3.tgz", @@ -5509,6 +5540,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/luxon": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.4.2.tgz", + "integrity": "sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==", + "license": "MIT" + }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", @@ -7684,6 +7721,16 @@ "dev": true, "license": "MIT" }, + "node_modules/cron": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/cron/-/cron-3.2.1.tgz", + "integrity": "sha512-w2n5l49GMmmkBFEsH9FIDhjZ1n1QgTMOCMGuQtOXs5veNiosZmso6bQGuqOJSYAXXrG84WQFVneNk+Yt0Ua9iw==", + "license": "MIT", + "dependencies": { + "@types/luxon": "~3.4.0", + "luxon": "~3.5.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -8060,6 +8107,27 @@ "node": ">=12" } }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz", + "integrity": "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -12096,7 +12164,6 @@ "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, "license": "MIT" }, "node_modules/lodash.memoize": { @@ -12152,6 +12219,15 @@ "yallist": "^3.0.2" } }, + "node_modules/luxon": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", + "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/lz-string": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", @@ -15503,6 +15579,19 @@ "node": ">= 0.4.0" } }, + "node_modules/uuid": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.3.tgz", + "integrity": "sha512-d0z310fCWv5dJwnX1Y/MncBAqGMKEzlBb1AOf7z9K8ALnd0utBX/msg/fA0+sbyN1ihbMsLhrBlnl1ak7Wa0rg==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", From e956d3806f7d8d0387543636beee100a309fd91d Mon Sep 17 00:00:00 2001 From: villarley Date: Thu, 29 Jan 2026 05:50:48 -0600 Subject: [PATCH 2/2] chore: update code to pass ci --- apps/aggregator/.eslintrc.js | 1 + package-lock.json | 65 ++++++++++++++++++++------------ packages/shared/eslint.config.js | 50 ++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 25 deletions(-) create mode 100644 packages/shared/eslint.config.js diff --git a/apps/aggregator/.eslintrc.js b/apps/aggregator/.eslintrc.js index 1eb94fa..7ebfe7c 100644 --- a/apps/aggregator/.eslintrc.js +++ b/apps/aggregator/.eslintrc.js @@ -20,5 +20,6 @@ module.exports = { '@typescript-eslint/explicit-function-return-type': 'off', '@typescript-eslint/explicit-module-boundary-types': 'off', '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], }, }; diff --git a/package-lock.json b/package-lock.json index 9849cba..ba4040a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -137,6 +137,7 @@ "integrity": "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -183,6 +184,7 @@ "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", @@ -280,6 +282,7 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -482,6 +485,7 @@ "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -679,6 +683,7 @@ "integrity": "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -725,6 +730,7 @@ "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", @@ -822,6 +828,7 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -1024,6 +1031,7 @@ "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -1369,6 +1377,7 @@ "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" @@ -1426,6 +1435,7 @@ "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", @@ -1533,6 +1543,7 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -1829,6 +1840,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -1841,6 +1853,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -2015,6 +2028,7 @@ "integrity": "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -2061,6 +2075,7 @@ "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", @@ -2158,6 +2173,7 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -2360,6 +2376,7 @@ "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -2557,6 +2574,7 @@ "integrity": "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -2603,6 +2621,7 @@ "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", @@ -2700,6 +2719,7 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -2902,6 +2922,7 @@ "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -3235,6 +3256,7 @@ "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", @@ -3837,7 +3859,6 @@ "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", @@ -3853,7 +3874,6 @@ "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@eslint/core": "^0.17.0" }, @@ -3867,7 +3887,6 @@ "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@types/json-schema": "^7.0.15" }, @@ -3881,7 +3900,6 @@ "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -3906,7 +3924,6 @@ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -3920,7 +3937,6 @@ "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -3934,7 +3950,6 @@ "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -3945,7 +3960,6 @@ "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" @@ -3960,7 +3974,6 @@ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=18.18.0" } @@ -3971,7 +3984,6 @@ "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" @@ -4024,7 +4036,6 @@ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=18.18" }, @@ -4830,6 +4841,7 @@ "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.4.22.tgz", "integrity": "sha512-fxJ4v85nDHaqT1PmfNCQ37b/jcv2OojtXTaK1P2uAXhzLf9qq6WNUOFvxBrV4fhQek1EQoT1o9oj5xAZmv3NRw==", "license": "MIT", + "peer": true, "dependencies": { "file-type": "20.4.1", "iterare": "1.2.1", @@ -4876,6 +4888,7 @@ "integrity": "sha512-6IX9+VwjiKtCjx+mXVPncpkQ5ZjKfmssOZPFexmT+6T9H9wZ3svpYACAo7+9e7Nr9DZSoRZw3pffkJP7Z0UjaA==", "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "@nuxtjs/opencollective": "0.3.2", "fast-safe-stringify": "2.1.1", @@ -6264,6 +6277,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6288,7 +6302,6 @@ "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10.13.0" }, @@ -6338,6 +6351,7 @@ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -7014,6 +7028,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -8630,6 +8645,7 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -8814,7 +8830,6 @@ "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -8832,7 +8847,6 @@ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -8846,7 +8860,6 @@ "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", @@ -9216,7 +9229,6 @@ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "flat-cache": "^4.0.0" }, @@ -9311,7 +9323,6 @@ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" @@ -10885,6 +10896,7 @@ "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -13253,6 +13265,7 @@ "integrity": "sha512-yEPsovQfpxYfgWNhCfECjG5AQaO+K3dp6XERmOepyPDVqcJm+bjyCVO3pmU+nAPe0N5dDvekfGezt/EIiRe1TA==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -13783,6 +13796,7 @@ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "tslib": "^2.1.0" } @@ -14675,6 +14689,7 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -14856,6 +14871,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -15386,6 +15402,7 @@ "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -15785,7 +15802,6 @@ "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3" }, @@ -15798,8 +15814,7 @@ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/webpack/node_modules/eslint-scope": { "version": "5.1.1", @@ -15807,7 +15822,6 @@ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -15822,7 +15836,6 @@ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "engines": { "node": ">=4.0" } @@ -15832,8 +15845,7 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/webpack/node_modules/schema-utils": { "version": "4.3.3", @@ -15841,7 +15853,6 @@ "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", @@ -16311,6 +16322,7 @@ "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", @@ -16408,6 +16420,7 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -16650,6 +16663,7 @@ "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", @@ -16747,6 +16761,7 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", diff --git a/packages/shared/eslint.config.js b/packages/shared/eslint.config.js new file mode 100644 index 0000000..87ae248 --- /dev/null +++ b/packages/shared/eslint.config.js @@ -0,0 +1,50 @@ +import { fileURLToPath } from 'node:url'; +import { dirname } from 'node:path'; +import tsParser from '@typescript-eslint/parser'; +import tsPlugin from '@typescript-eslint/eslint-plugin'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +export default [ + { + ignores: ['dist', '.eslintrc.cjs'], + }, + { + files: ['**/*.ts'], + languageOptions: { + parser: tsParser, + parserOptions: { + project: './tsconfig.json', + tsconfigRootDir: __dirname, + sourceType: 'module', + }, + globals: { + // Node + process: 'readonly', + __dirname: 'readonly', + __filename: 'readonly', + module: 'readonly', + require: 'readonly', + Buffer: 'readonly', + global: 'readonly', + // Jest + describe: 'readonly', + it: 'readonly', + expect: 'readonly', + beforeEach: 'readonly', + afterEach: 'readonly', + jest: 'readonly', + }, + }, + plugins: { + '@typescript-eslint': tsPlugin, + }, + rules: { + ...tsPlugin.configs.recommended.rules, + '@typescript-eslint/interface-name-prefix': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-explicit-any': 'off', + }, + }, +];