From 84bcaad322663ffbbccfd8ff46549aa03ceccc18 Mon Sep 17 00:00:00 2001 From: Maximus7474 Date: Thu, 3 Sep 2026 11:44:00 +0200 Subject: [PATCH 1/2] tweak(tsconfig): align linting settings across the project --- apps/core/tsconfig.json | 10 +--------- apps/resource/tsconfig.json | 5 ----- apps/resource/web/tsconfig.json | 8 +------- apps/resource/web/tsconfig.node.json | 11 ++++++++++- apps/webpanel/tsconfig.app.json | 4 +++- apps/webpanel/tsconfig.node.json | 6 ++++-- packages/shared/tsconfig.json | 9 +++++---- packages/ui/tsconfig.json | 10 ++++++++++ packages/ui/tsconfig.lint.json | 10 ++++++++++ tsconfig.json | 11 ++++++++++- 10 files changed, 54 insertions(+), 30 deletions(-) diff --git a/apps/core/tsconfig.json b/apps/core/tsconfig.json index 5e21838b..4971f6f4 100644 --- a/apps/core/tsconfig.json +++ b/apps/core/tsconfig.json @@ -17,14 +17,6 @@ // Best practices "strict": true, - "skipLibCheck": true, - "noFallthroughCasesInSwitch": true, - "noUncheckedIndexedAccess": true, - "noImplicitOverride": true, - - // Some stricter flags (disabled by default) - "noUnusedLocals": false, - "noUnusedParameters": false, - "noPropertyAccessFromIndexSignature": false + "skipLibCheck": true } } diff --git a/apps/resource/tsconfig.json b/apps/resource/tsconfig.json index 2ebcbdcc..8ed126da 100644 --- a/apps/resource/tsconfig.json +++ b/apps/resource/tsconfig.json @@ -6,12 +6,7 @@ "lib": ["es2022"], "resolveJsonModule": true, "esModuleInterop": true, - "allowUnreachableCode": false, - "strictFunctionTypes": true, "moduleResolution": "bundler", - "noImplicitAny": true, - "noImplicitThis": true, - "noUnusedLocals": true, "skipLibCheck": true }, "include": ["./src", "./web"] diff --git a/apps/resource/web/tsconfig.json b/apps/resource/web/tsconfig.json index 107bbe1f..014e93bf 100644 --- a/apps/resource/web/tsconfig.json +++ b/apps/resource/web/tsconfig.json @@ -19,13 +19,7 @@ "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, - "jsx": "react-jsx", - - /* Linting */ - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true + "jsx": "react-jsx" }, "include": ["./src", "./nui.d.ts"], "references": [{ "path": "./tsconfig.node.json" }] diff --git a/apps/resource/web/tsconfig.node.json b/apps/resource/web/tsconfig.node.json index 1caabefc..51b1debc 100644 --- a/apps/resource/web/tsconfig.node.json +++ b/apps/resource/web/tsconfig.node.json @@ -5,7 +5,16 @@ "module": "ESNext", "moduleResolution": "bundler", "allowSyntheticDefaultImports": true, - "strict": true + "strict": true, + + // Linting + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + "noUncheckedSideEffectImports": true, + "allowUnreachableCode": false }, "include": ["vite.config.ts"] } diff --git a/apps/webpanel/tsconfig.app.json b/apps/webpanel/tsconfig.app.json index 512ff2fa..7a24b6bf 100644 --- a/apps/webpanel/tsconfig.app.json +++ b/apps/webpanel/tsconfig.app.json @@ -20,9 +20,11 @@ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, - "erasableSyntaxOnly": true, "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, "noUncheckedSideEffectImports": true, + "allowUnreachableCode": false, "baseUrl": ".", "paths": { "@/*": ["./src/*"], diff --git a/apps/webpanel/tsconfig.node.json b/apps/webpanel/tsconfig.node.json index e75109e0..bf84f4d2 100644 --- a/apps/webpanel/tsconfig.node.json +++ b/apps/webpanel/tsconfig.node.json @@ -18,9 +18,11 @@ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, - "erasableSyntaxOnly": true, "noFallthroughCasesInSwitch": true, - "noUncheckedSideEffectImports": true + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + "noUncheckedSideEffectImports": true, + "allowUnreachableCode": false }, "include": ["vite.config.ts"] } diff --git a/packages/shared/tsconfig.json b/packages/shared/tsconfig.json index 3966a21b..033c95b9 100644 --- a/packages/shared/tsconfig.json +++ b/packages/shared/tsconfig.json @@ -21,10 +21,11 @@ "noUncheckedIndexedAccess": true, "noImplicitOverride": true, - // Some stricter flags (disabled by default) - "noUnusedLocals": false, - "noUnusedParameters": false, - "noPropertyAccessFromIndexSignature": false, + // Linting + "noUnusedLocals": true, + "noUnusedParameters": true, + "noUncheckedSideEffectImports": true, + "allowUnreachableCode": false, "types": ["bun"] } diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json index 6d21cd8f..abe9ed95 100644 --- a/packages/ui/tsconfig.json +++ b/packages/ui/tsconfig.json @@ -7,6 +7,16 @@ "jsx": "react-jsx", "skipLibCheck": true, "strict": true, + + // Linting + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + "noUncheckedSideEffectImports": true, + "allowUnreachableCode": false, + "noEmit": true, "baseUrl": ".", "paths": { diff --git a/packages/ui/tsconfig.lint.json b/packages/ui/tsconfig.lint.json index f673bf3e..a2dbde30 100644 --- a/packages/ui/tsconfig.lint.json +++ b/packages/ui/tsconfig.lint.json @@ -7,6 +7,16 @@ "jsx": "react-jsx", "skipLibCheck": true, "strict": true, + + // Linting + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + "noUncheckedSideEffectImports": true, + "allowUnreachableCode": false, + "outDir": "dist" }, "include": ["src", "turbo"], diff --git a/tsconfig.json b/tsconfig.json index c78dde43..0e365e08 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,15 @@ "moduleResolution": "bundler", "types": ["bun"], "skipLibCheck": true, - "strict": true + "strict": true, + + // Linting + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + "noUncheckedSideEffectImports": true, + "allowUnreachableCode": false } } From d2da54e1479137cf121d653a958a3811e82bbd89 Mon Sep 17 00:00:00 2001 From: Maximus7474 Date: Thu, 3 Sep 2026 12:16:54 +0200 Subject: [PATCH 2/2] fix: linting issues --- apps/core/src/common/contributors.test.ts | 2 +- apps/core/src/routes/api/setup.ts | 7 +- apps/core/src/routes/internal/server.ts | 1 - .../src/import/txadmin.importer.test.ts | 24 ++-- packages/database/src/import/txadmin.test.ts | 2 +- .../database/src/migrations/migration.test.ts | 8 +- .../database/src/repositories/admins.test.ts | 66 +++++----- .../src/repositories/api-tokens.test.ts | 12 +- .../database/src/repositories/audit.test.ts | 42 +++---- .../database/src/repositories/auth.test.ts | 6 +- .../database/src/repositories/bans.test.ts | 40 +++--- .../src/repositories/disconnects.test.ts | 2 +- .../database/src/repositories/migrate.test.ts | 2 +- .../src/repositories/perf-snapshots.test.ts | 8 +- .../src/repositories/player-sessions.test.ts | 6 +- .../database/src/repositories/players.test.ts | 116 +++++++++--------- packages/database/src/repositories/players.ts | 6 + .../src/repositories/whitelist.test.ts | 22 ++-- .../ui/src/components/icon-selectmenu.tsx | 4 +- packages/ui/src/lib/icons.ts | 2 +- 20 files changed, 188 insertions(+), 190 deletions(-) diff --git a/apps/core/src/common/contributors.test.ts b/apps/core/src/common/contributors.test.ts index af5996c5..eae42864 100644 --- a/apps/core/src/common/contributors.test.ts +++ b/apps/core/src/common/contributors.test.ts @@ -135,7 +135,7 @@ describe('createContributorsList', () => { expect(globalThis.fetch).toHaveBeenCalledTimes(REPO_COUNT); expect(result.external).toHaveLength(1); - expect(result.external[0].contributions).toBe(15); // 10 + 5 + expect(result.external[0]?.contributions).toBe(15); // 10 + 5 }); it('should use cached value and avoid network calls before TTL expires', async () => { diff --git a/apps/core/src/routes/api/setup.ts b/apps/core/src/routes/api/setup.ts index f7fce719..b47170f2 100644 --- a/apps/core/src/routes/api/setup.ts +++ b/apps/core/src/routes/api/setup.ts @@ -1,10 +1,9 @@ -import path from 'node:path'; import { Type, type Static } from '@sinclair/typebox'; import type { FastifyPluginAsync } from 'fastify'; import { repo } from '@fxmanager/database'; import { UserPermissions } from '@fxmanager/shared/constants'; import type { ApiResponse } from '@fxmanager/shared/types'; -import { COOKIE_NAME, fileExists, isFxManagerSetup } from '../../common/utils'; +import { COOKIE_NAME, isFxManagerSetup } from '../../common/utils'; import { ConfigManager } from '../../modules/config/manager'; import { setupTokenManager } from '../../modules/setup/token'; import type { RouteModule } from '../../types'; @@ -49,10 +48,6 @@ const SetupEndpoint: FastifyPluginAsync = async (fastify) => { } const cfg = ConfigManager.getInstance().getFxServerValues(); - const cfgPath = path.isAbsolute(cfg.serverConfigFile) - ? cfg.serverConfigFile - : path.join(cfg.serverDataPath, cfg.serverConfigFile); - const result = await ConfigManager.getInstance().checkFXServerPaths( cfg.executablePath, cfg.serverDataPath, diff --git a/apps/core/src/routes/internal/server.ts b/apps/core/src/routes/internal/server.ts index 91ec81ad..918b38b4 100644 --- a/apps/core/src/routes/internal/server.ts +++ b/apps/core/src/routes/internal/server.ts @@ -1,4 +1,3 @@ -import type { ResourceData } from '@fxmanager/shared/types'; import { resourceAuth } from '../../middleware/resource'; import type { RouteModule } from '../../types'; diff --git a/packages/database/src/import/txadmin.importer.test.ts b/packages/database/src/import/txadmin.importer.test.ts index 4f4d73a2..dbb0610b 100644 --- a/packages/database/src/import/txadmin.importer.test.ts +++ b/packages/database/src/import/txadmin.importer.test.ts @@ -54,8 +54,8 @@ describe('importTxAdmin - players', () => { const players = db.select().from(schema.players).all(); expect(players).toHaveLength(1); - expect(players[0].name).toBe('CaliChris'); - expect(players[0].playtime).toBe(1_440_000); + expect(players[0]!.name).toBe('CaliChris'); + expect(players[0]!.playtime).toBe(1_440_000); const ids = db.select().from(schema.playerIdentifiers).all(); expect(ids.map((i) => i.value).sort()).toEqual(['123', 'abc']); @@ -66,8 +66,8 @@ describe('importTxAdmin - players', () => { const notes = db.select().from(schema.playerNotes).all(); expect(notes).toHaveLength(1); - expect(notes[0].content).toBe('a note'); - expect(notes[0].issuer).toBeNull(); + expect(notes[0]!.content).toBe('a note'); + expect(notes[0]!.issuer).toBeNull(); }); it('is idempotent: re-running creates no duplicate players, ids or notes', () => { @@ -124,9 +124,9 @@ describe('importTxAdmin - actions matched to existing players', () => { const bans = db.select().from(schema.bans).all(); expect(bans).toHaveLength(1); - expect(bans[0].playerId).toBe(1); - expect(bans[0].reason).toBe('Cheating'); - expect(bans[0].issuer).toBeNull(); + expect(bans[0]!.playerId).toBe(1); + expect(bans[0]!.reason).toBe('Cheating'); + expect(bans[0]!.issuer).toBeNull(); }); }); @@ -152,15 +152,15 @@ describe('importTxAdmin - orphan actions', () => { const players = db.select().from(schema.players).all(); expect(players).toHaveLength(1); - expect(players[0].name).toBe('GhostBanned'); + expect(players[0]!.name).toBe('GhostBanned'); expect(summary.stubPlayers).toBe(1); const ids = db.select().from(schema.playerIdentifiers).all(); expect(ids).toHaveLength(1); - expect(ids[0].value).toBe('orphan'); + expect(ids[0]!.value).toBe('orphan'); const bans = db.select().from(schema.bans).all(); - expect(bans[0].playerId).toBe(players[0].id); + expect(bans[0]!.playerId).toBe(players[0]!.id); }); it('reuses the same stub player for two actions sharing an identifier', () => { @@ -207,7 +207,7 @@ describe('importTxAdmin - ban fields', () => { ], }; importTxAdmin(localDb, data); - return localDb.select().from(schema.bans).all()[0]; + return localDb.select().from(schema.bans).all()[0]!; } it('stores a permanent ban with null expiresAt', () => { @@ -245,7 +245,7 @@ describe('importTxAdmin - warn fields', () => { ], }; importTxAdmin(localDb, data); - return localDb.select().from(schema.warns).all()[0]; + return localDb.select().from(schema.warns).all()[0]!; } it('marks an acked warn as read', () => { diff --git a/packages/database/src/import/txadmin.test.ts b/packages/database/src/import/txadmin.test.ts index f49df369..2eba04b1 100644 --- a/packages/database/src/import/txadmin.test.ts +++ b/packages/database/src/import/txadmin.test.ts @@ -263,7 +263,7 @@ describe('parseTxAdminDb', () => { expect(result.players).toHaveLength(1); expect(result.actions).toHaveLength(1); expect(result.whitelist).toHaveLength(1); - expect(result.players[0].name).toBe('P1'); + expect(result.players[0]!.name).toBe('P1'); expect(result.whitelist[0]).toEqual({ type: 'discord', value: 'discord:999', diff --git a/packages/database/src/migrations/migration.test.ts b/packages/database/src/migrations/migration.test.ts index 60e78a46..32ceb709 100644 --- a/packages/database/src/migrations/migration.test.ts +++ b/packages/database/src/migrations/migration.test.ts @@ -75,10 +75,10 @@ describe('Database Migration Runner', () => { // Assert that migrations were tracked and documented chronologically by version number const history = getTrackedVersions(testSqlite); expect(history.length).toBe(2); - expect(history[0].version).toBe(1); - expect(history[0].description).toBe('Create users table'); - expect(history[1].version).toBe(2); - expect(history[1].description).toBe('Create logs table'); + expect(history[0]!.version).toBe(1); + expect(history[0]!.description).toBe('Create users table'); + expect(history[1]!.version).toBe(2); + expect(history[1]!.description).toBe('Create logs table'); }); it('should gracefully perform a no-op if the database is already fully up-to-date', () => { diff --git a/packages/database/src/repositories/admins.test.ts b/packages/database/src/repositories/admins.test.ts index ccb5de8b..de9679b5 100644 --- a/packages/database/src/repositories/admins.test.ts +++ b/packages/database/src/repositories/admins.test.ts @@ -59,9 +59,9 @@ describe('AdminsRepository', () => { expect(result.total).toBe(2); expect(result.page).toBe(1); expect(result.pageSize).toBe(20); - expect(result.items[0].username).toBe('admin_two'); - expect(result.items[1].username).toBe('moderator_one'); - expect(result.items[0].group).toBeDefined(); + expect(result.items[0]!.username).toBe('admin_two'); + expect(result.items[1]!.username).toBe('moderator_one'); + expect(result.items[0]!.group).toBeDefined(); }); it('should filter items accurately when a search query parameter is provided', () => { @@ -76,13 +76,13 @@ describe('AdminsRepository', () => { const result = adminsRepo.list(1, 10, { search: 'super' }); expect(result.total).toBe(1); - expect(result.items[0].username).toBe('super_admin'); + expect(result.items[0]!.username).toBe('super_admin'); }); }); describe('updatePermissions()', () => { it('should successfully update and sanitize permissions for standard accounts', async () => { - const [inserted] = testDb + const inserted = testDb .insert(adminUsers) .values({ username: 'staff_member', @@ -91,7 +91,7 @@ describe('AdminsRepository', () => { createdAt: new Date(), }) .returning() - .all(); + .get()!; const targetPerms = UserPermissions.KICK | UserPermissions.BAN; @@ -113,7 +113,7 @@ describe('AdminsRepository', () => { }); it('should enforce a failsafe preventing the acquisition of MASTER privileges', async () => { - const [inserted] = testDb + const inserted = testDb .insert(adminUsers) .values({ username: 'sneaky_mod', @@ -122,7 +122,7 @@ describe('AdminsRepository', () => { createdAt: new Date(), }) .returning() - .all(); + .get()!; const maliciousPerms = UserPermissions.KICK | UserPermissions.MASTER; @@ -136,7 +136,7 @@ describe('AdminsRepository', () => { }); it('should throw an error and halt execution if attempting to modify a MASTER account', async () => { - const [masterAdmin] = testDb + const masterAdmin = testDb .insert(adminUsers) .values({ username: 'root_owner', @@ -145,7 +145,7 @@ describe('AdminsRepository', () => { createdAt: new Date(), }) .returning() - .all(); + .get()!; expect( adminsRepo.updatePermissions(masterAdmin.id, UserPermissions.NONE), @@ -166,12 +166,12 @@ describe('AdminsRepository', () => { }); it('should compile profile configurations without audit logging maps when showAudit parameter is omitted', async () => { - const [player] = testDb + const player = testDb .insert(schema.players) .values({ name: 'Linked_User_Name' }) .returning() - .all(); - const [admin] = testDb + .get()!; + const admin = testDb .insert(adminUsers) .values({ username: 'profile_tester', @@ -181,7 +181,7 @@ describe('AdminsRepository', () => { createdAt: new Date(), }) .returning() - .all(); + .get()!; const profile = await adminsRepo.getProfile(admin.id); @@ -194,12 +194,12 @@ describe('AdminsRepository', () => { it('should pull, merge, and append resolving lookup entities into log matrix records when showAudit is high', async () => { // Prepare relational assets - const [targetPlayer] = testDb + const targetPlayer = testDb .insert(schema.players) .values({ name: 'Gamer_Tag_One' }) .returning() - .all(); - const [admin] = testDb + .get()!; + const admin = testDb .insert(adminUsers) .values({ username: 'audited_admin', @@ -208,7 +208,7 @@ describe('AdminsRepository', () => { createdAt: new Date(), }) .returning() - .all(); + .get()!; // Inject raw sequential operational audit metrics targeting different player profiles testDb @@ -234,25 +234,25 @@ describe('AdminsRepository', () => { expect(profile?.auditLogs).toHaveLength(2); // First audit entry (Most recent: KICK_PLAYER) - expect(profile?.auditLogs[0].action).toBe('KICK_PLAYER'); - expect(profile?.auditLogs[0].admin).toBe('audited_admin'); - expect(profile?.auditLogs[0].player).toBe('Gamer_Tag_One'); // Successfully resolved join lookup + expect(profile!.auditLogs[0]!.action).toBe('KICK_PLAYER'); + expect(profile!.auditLogs[0]!.admin).toBe('audited_admin'); + expect(profile!.auditLogs[0]!.player).toBe('Gamer_Tag_One'); // Successfully resolved join lookup // Second audit entry (Older execution: WARN_PLAYER) - expect(profile?.auditLogs[1].action).toBe('WARN_PLAYER'); - expect(profile?.auditLogs[1].player).toBeNull(); // Graceful fallback on missing relational records + expect(profile!.auditLogs[1]!.action).toBe('WARN_PLAYER'); + expect(profile!.auditLogs[1]!.player).toBeNull(); // Graceful fallback on missing relational records }); }); describe('updateLinkedPlayer()', () => { it('should successfully update the linked playerId for a standard administrator account', async () => { // Seed a baseline player and admin - const [player] = testDb + const player = testDb .insert(schema.players) .values({ name: 'Alpha_Player' }) .returning() - .all(); - const [admin] = testDb + .get()!; + const admin = testDb .insert(adminUsers) .values({ username: 'standard_mod', @@ -262,7 +262,7 @@ describe('AdminsRepository', () => { createdAt: new Date(), }) .returning() - .all(); + .get()!; const result = await adminsRepo.updateLinkedPlayer( admin.id, @@ -290,7 +290,7 @@ describe('AdminsRepository', () => { }); it('should block modification and throw an error if a MASTER admin is handled without the isMaster flag override', async () => { - const [masterAdmin] = testDb + const masterAdmin = testDb .insert(adminUsers) .values({ username: 'owner_account', @@ -299,7 +299,7 @@ describe('AdminsRepository', () => { createdAt: new Date(), }) .returning() - .all(); + .get()!; expect( adminsRepo.updateLinkedPlayer(masterAdmin.id, 5, false), // isMaster = false @@ -307,12 +307,12 @@ describe('AdminsRepository', () => { }); it('should bypass the master lock restriction and update successfully if isMaster flag is explicitly true', async () => { - const [player] = testDb + const player = testDb .insert(schema.players) .values({ name: 'Owner_InGame' }) .returning() - .all(); - const [masterAdmin] = testDb + .get()!; + const masterAdmin = testDb .insert(adminUsers) .values({ username: 'owner_account_two', @@ -321,7 +321,7 @@ describe('AdminsRepository', () => { createdAt: new Date(), }) .returning() - .all(); + .get()!; const result = await adminsRepo.updateLinkedPlayer( masterAdmin.id, diff --git a/packages/database/src/repositories/api-tokens.test.ts b/packages/database/src/repositories/api-tokens.test.ts index d6da3684..3dbc45d3 100644 --- a/packages/database/src/repositories/api-tokens.test.ts +++ b/packages/database/src/repositories/api-tokens.test.ts @@ -59,7 +59,7 @@ describe('APITokensRepository', () => { describe('validate()', () => { it('should return the token row and update the lastUsed timestamp field for valid tokens', () => { // Seed an active token directly into the database - const [seeded] = testDb + const seeded = testDb .insert(apiTokens) .values({ name: 'Grafana Hook', @@ -67,7 +67,7 @@ describe('APITokensRepository', () => { createdAt: new Date(Date.now() - 5000), // Created 5s ago }) .returning() - .all(); + .get()!; expect(seeded.lastUsed).toBeNull(); @@ -93,7 +93,7 @@ describe('APITokensRepository', () => { }); it('should return null and reject verification if the target token has been explicitly revoked', () => { - const [revokedToken] = testDb + const revokedToken = testDb .insert(apiTokens) .values({ name: 'Deprecated App', @@ -102,7 +102,7 @@ describe('APITokensRepository', () => { revokedAt: new Date(), // Already flagged as revoked }) .returning() - .all(); + .get()!; const result = tokensRepo.validate(revokedToken.token); @@ -112,7 +112,7 @@ describe('APITokensRepository', () => { describe('revoke()', () => { it('should stamp a revokedAt timestamp onto a token row using its identifier', () => { - const [target] = testDb + const target = testDb .insert(apiTokens) .values({ name: 'Temporary Script', @@ -120,7 +120,7 @@ describe('APITokensRepository', () => { createdAt: new Date(), }) .returning() - .all(); + .get()!; const revokedRow = tokensRepo.revoke(target.id); diff --git a/packages/database/src/repositories/audit.test.ts b/packages/database/src/repositories/audit.test.ts index 4aac09e3..f51b69bc 100644 --- a/packages/database/src/repositories/audit.test.ts +++ b/packages/database/src/repositories/audit.test.ts @@ -61,7 +61,7 @@ describe('AuditRepository', () => { describe('list()', () => { // Helper to seed foundational data to keep tests scannable const seedCoreRelations = () => { - const [admin] = testDb + const admin = testDb .insert(adminUsers) .values({ username: 'super_operator', @@ -69,15 +69,15 @@ describe('AuditRepository', () => { createdAt: new Date(), }) .returning() - .all(); + .get()!; - const [player] = testDb + const player = testDb .insert(players) .values({ name: 'John_Doe', }) .returning() - .all(); + .get()!; return { adminId: admin.id, playerId: player.id }; }; @@ -109,12 +109,12 @@ describe('AuditRepository', () => { expect(response.items.length).toBe(2); // Verify strict descending chronological order (newest entry listed first) - expect(response.items[0].action).toBe('player.kick'); - expect(response.items[0].admin).toBe('super_operator'); - expect(response.items[0].player).toBe('John_Doe'); - expect(response.items[0].metadata).toEqual({ reason: 'RDM' }); + expect(response.items[0]!.action).toBe('player.kick'); + expect(response.items[0]!.admin).toBe('super_operator'); + expect(response.items[0]!.player).toBe('John_Doe'); + expect(response.items[0]!.metadata).toEqual({ reason: 'RDM' }); - expect(response.items[1].action).toBe('server.start'); + expect(response.items[1]!.action).toBe('server.start'); }); it('should accurately isolate list records based on singular or multiple Action types', async () => { @@ -132,7 +132,7 @@ describe('AuditRepository', () => { // Match a singular string query const singularResult = await auditRepo.list(1, 10, 'player.warn'); expect(singularResult.total).toBe(1); - expect(singularResult.items[0].action).toBe('player.warn'); + expect(singularResult.items[0]!.action).toBe('player.warn'); // Match an array query condition const arrayResult = await auditRepo.list(1, 10, [ @@ -143,16 +143,16 @@ describe('AuditRepository', () => { }); it('should search across player fields using fuzzy substring matches', async () => { - const [playerTarget] = testDb + const playerTarget = testDb .insert(players) .values({ name: 'Dangerous_Gamer' }) .returning() - .all(); - const [playerOther] = testDb + .get()!; + const playerOther = testDb .insert(players) .values({ name: 'Safe_User' }) .returning() - .all(); + .get()!; testDb .insert(auditLog) @@ -173,15 +173,15 @@ describe('AuditRepository', () => { const response = await auditRepo.list(1, 10, undefined, 'Dangerous'); expect(response.total).toBe(1); - expect(response.items[0].player).toBe('Dangerous_Gamer'); - expect(response.items[0].action).toBe('player.ban'); + expect(response.items[0]!.player).toBe('Dangerous_Gamer'); + expect(response.items[0]!.action).toBe('player.ban'); }); it('should slice query records precisely according to target Admin identifier sets', async () => { const { adminId: allowedAdmin } = seedCoreRelations(); // otherAdmin is the full row object here - const [otherAdmin] = testDb + const otherAdmin = testDb .insert(adminUsers) .values({ username: 'stealth_mod', @@ -189,7 +189,7 @@ describe('AuditRepository', () => { createdAt: new Date(), }) .returning() - .all(); + .get()!; testDb .insert(auditLog) @@ -212,8 +212,8 @@ describe('AuditRepository', () => { ]); expect(response.total).toBe(1); - expect(response.items[0].admin).toBe('super_operator'); - expect(response.items[0].action).toBe('settings.update'); + expect(response.items[0]!.admin).toBe('super_operator'); + expect(response.items[0]!.action).toBe('settings.update'); }); it('should bind query constraints between explicit chronological date boundaries', async () => { @@ -254,7 +254,7 @@ describe('AuditRepository', () => { ); expect(response.total).toBe(1); - expect(response.items[0].action).toBe('server.restart'); + expect(response.items[0]!.action).toBe('server.restart'); }); it('should return empty values and a total of zero if constraints yield no rows', async () => { diff --git a/packages/database/src/repositories/auth.test.ts b/packages/database/src/repositories/auth.test.ts index c6a947db..c64195a0 100644 --- a/packages/database/src/repositories/auth.test.ts +++ b/packages/database/src/repositories/auth.test.ts @@ -115,10 +115,8 @@ describe('AuthRepository', () => { 'p', UserPermissions.KICK, ); - const session = authRepo.createSession(user.id); - - const deleted = await authRepo.deleteUser(user.id); - expect(deleted.id).toBe(user.id); + const session = authRepo.createSession(user.id); const deleted = (await authRepo.deleteUser(user.id))!; + expect(deleted.id).toBe(user.id); // Verify cascading rules removed active sessions from the database const lookupSession = testDb diff --git a/packages/database/src/repositories/bans.test.ts b/packages/database/src/repositories/bans.test.ts index 0cef0941..db9f8e31 100644 --- a/packages/database/src/repositories/bans.test.ts +++ b/packages/database/src/repositories/bans.test.ts @@ -39,7 +39,7 @@ describe('BansRepository', () => { // Helper utility to seed standard player relational parents const seedPlayerWithLicense = (name: string, licenseValue: string) => { - const [player] = testDb.insert(players).values({ name }).returning().all(); + const player = testDb.insert(players).values({ name }).returning().get()!; testDb .insert(playerIdentifiers) @@ -79,7 +79,7 @@ describe('BansRepository', () => { 'Reformed_Gamer', 'license:clean999', ); - const [initialBan] = testDb + const initialBan = testDb .insert(bans) .values({ playerId: player.id, @@ -87,7 +87,7 @@ describe('BansRepository', () => { createdAt: new Date(), }) .returning() - .all(); + .get()!; const revokedBan = bansRepo.revoke(initialBan.id); @@ -105,7 +105,7 @@ describe('BansRepository', () => { it('should return undefined when the ban is already revoked', () => { const player = seedPlayerWithLicense('Repeat_Appeal', 'license:appeal1'); - const [initialBan] = testDb + const initialBan = testDb .insert(bans) .values({ playerId: player.id, @@ -113,7 +113,7 @@ describe('BansRepository', () => { createdAt: new Date(), }) .returning() - .all(); + .get()!; bansRepo.revoke(initialBan.id); @@ -127,7 +127,7 @@ describe('BansRepository', () => { it('should not revoke when the scoped playerId does not own the ban', () => { const owner = seedPlayerWithLicense('Owner', 'license:owner1'); const other = seedPlayerWithLicense('Other', 'license:other1'); - const [ban] = testDb + const ban = testDb .insert(bans) .values({ playerId: owner.id, @@ -135,7 +135,7 @@ describe('BansRepository', () => { createdAt: new Date(), }) .returning() - .all(); + .get()!; expect(bansRepo.revoke(ban.id, other.id)).toBeUndefined(); @@ -250,13 +250,13 @@ describe('BansRepository', () => { // Verify Drizzle-ORM default nested mapping behavior for unselected multi-table queries // Shape must match: Array<{ bans: typeof bans.$inferSelect, players: typeof players.$inferSelect }> - expect(executionList[0].bans).toBeDefined(); - expect(executionList[0].players).toBeDefined(); + expect(executionList[0]!.bans).toBeDefined(); + expect(executionList[0]!.players).toBeDefined(); - expect(executionList[0].players.name).toBe('User_A'); - expect(executionList[0].bans.reason).toBe('Rule 1'); + expect(executionList[0]!.players.name).toBe('User_A'); + expect(executionList[0]!.bans.reason).toBe('Rule 1'); - expect(executionList[1].players.name).toBe('User_B'); + expect(executionList[1]!.players.name).toBe('User_B'); }); it('should calculate layout limits and offsets accurately during pagination calls', () => { @@ -287,7 +287,7 @@ describe('BansRepository', () => { const pageResult = bansRepo.list(2, 1); expect(pageResult.length).toBe(1); - expect(pageResult[0].bans.reason).toBe('Ban 2'); + expect(pageResult[0]!.bans.reason).toBe('Ban 2'); }); }); @@ -316,9 +316,9 @@ describe('BansRepository', () => { expect(result.length).toBe(2); // newest first - expect(result[0].reason).toBe('second'); - expect(result[0].name).toBe('Bob'); - expect(result[1].reason).toBe('first'); + expect(result[0]!.reason).toBe('second'); + expect(result[0]!.name).toBe('Bob'); + expect(result[1]!.reason).toBe('first'); // flat shape expect(result[0]).toMatchObject({ id: expect.any(Number), @@ -328,7 +328,7 @@ describe('BansRepository', () => { issuer: null, revokedAt: null, }); - expect(result[0].createdAt).toBeInstanceOf(Date); + expect(result[0]!.createdAt).toBeInstanceOf(Date); }); it('should filter by player name', () => { @@ -345,7 +345,7 @@ describe('BansRepository', () => { const result = bansRepo.search({ query: 'Ali' }); expect(result.length).toBe(1); - expect(result[0].name).toBe('Alice'); + expect(result[0]!.name).toBe('Alice'); }); it('should filter by identifier value without duplicating rows for multi-identifier players', () => { @@ -365,7 +365,7 @@ describe('BansRepository', () => { const byLicense = bansRepo.search({ query: 'ccc' }); expect(byLicense.length).toBe(1); - expect(byLicense[0].name).toBe('Charlie'); + expect(byLicense[0]!.name).toBe('Charlie'); const byName = bansRepo.search({ query: 'Charlie' }); expect(byName.length).toBe(1); @@ -388,7 +388,7 @@ describe('BansRepository', () => { const result = bansRepo.search({ query: 'aimbot' }); expect(result.length).toBe(1); - expect(result[0].reason).toBe('aimbot detected'); + expect(result[0]!.reason).toBe('aimbot detected'); }); }); }); diff --git a/packages/database/src/repositories/disconnects.test.ts b/packages/database/src/repositories/disconnects.test.ts index 22e791b3..d1a41b30 100644 --- a/packages/database/src/repositories/disconnects.test.ts +++ b/packages/database/src/repositories/disconnects.test.ts @@ -80,7 +80,7 @@ describe('DisconnectsRepository', () => { repo.recordEvent(b.id, 3500, 'kick'); const list = repo.listRecent(10); expect(list.map((s) => s.startedAt)).toEqual([3000, 2000, 1000]); - expect(list[0].kick).toBe(1); + expect(list[0]!.kick).toBe(1); expect(list.find((s) => s.id === a.id)!.kick).toBe(0); expect(list.find((s) => s.id === c.id)!.kick).toBe(0); }); diff --git a/packages/database/src/repositories/migrate.test.ts b/packages/database/src/repositories/migrate.test.ts index c7b02833..e63d58a1 100644 --- a/packages/database/src/repositories/migrate.test.ts +++ b/packages/database/src/repositories/migrate.test.ts @@ -78,7 +78,7 @@ describe('repo.migrate.fromTxAdmin', () => { expect(storedIds).toContain('license:orphan'); const wl = db.select().from(schema.whitelistedIdentifers).all(); - expect(wl[0].value).toBe('discord:999'); + expect(wl[0]!.value).toBe('discord:999'); }); it('is idempotent across runs', () => { diff --git a/packages/database/src/repositories/perf-snapshots.test.ts b/packages/database/src/repositories/perf-snapshots.test.ts index d9fd770b..2216b597 100644 --- a/packages/database/src/repositories/perf-snapshots.test.ts +++ b/packages/database/src/repositories/perf-snapshots.test.ts @@ -48,9 +48,9 @@ describe('PerfSnapshotsRepository', () => { const rows = repo.listForSession(s.id); expect(rows.length).toBe(1); - expect(rows[0].ts).toBe(1234); - expect(rows[0].players).toBe(12); - expect(rows[0].perf).toEqual(perf); + expect(rows[0]!.ts).toBe(1234); + expect(rows[0]!.players).toBe(12); + expect(rows[0]!.perf).toEqual(perf); }); it('returns snapshots ordered by ts asc', () => { @@ -126,7 +126,7 @@ describe('PerfSnapshotsRepository', () => { repo.insert({ sessionId: s.id, ts: 1000, players: 0, perf }); - const row = repo.listForSession(s.id)[0]; + const row = repo.listForSession(s.id)[0]!; expect(row.fxsMemory).toBeNull(); expect(row.nodeMemory).toBeNull(); }); diff --git a/packages/database/src/repositories/player-sessions.test.ts b/packages/database/src/repositories/player-sessions.test.ts index 8f7672cc..435b78e6 100644 --- a/packages/database/src/repositories/player-sessions.test.ts +++ b/packages/database/src/repositories/player-sessions.test.ts @@ -61,7 +61,7 @@ describe('PlayerSessionsRepository', () => { .get(); repo.open(pid, ss.id, new Date(1_000_000)); repo.closeDangling(new Date(9_000_000)); - const [row] = repo.listSessions(pid, 1, 10).items; + const row = repo.listSessions(pid, 1, 10).items[0]!; expect(row.disconnectedAt).toBe(1_500_000); expect(row.durationMs).toBe(500_000); expect(row.endReason).toBe('reconciled'); @@ -71,7 +71,7 @@ describe('PlayerSessionsRepository', () => { const pid = seedPlayer(); repo.open(pid, null, new Date(1_000_000)); repo.closeDangling(new Date(2_000_000)); - expect(repo.listSessions(pid, 1, 10).items[0].durationMs).toBe(1_000_000); + expect(repo.listSessions(pid, 1, 10).items[0]!.durationMs).toBe(1_000_000); }); it('getRangeActivity() buckets playtime by start-day and summarises', () => { @@ -107,6 +107,6 @@ describe('PlayerSessionsRepository', () => { const page = repo.listSessions(pid, 1, 2); expect(page.total).toBe(3); expect(page.items).toHaveLength(2); - expect(page.items[0].connectedAt).toBe(3_000_000); // newest first + expect(page.items[0]!.connectedAt).toBe(3_000_000); // newest first }); }); diff --git a/packages/database/src/repositories/players.test.ts b/packages/database/src/repositories/players.test.ts index 3e2b52e1..af85d708 100644 --- a/packages/database/src/repositories/players.test.ts +++ b/packages/database/src/repositories/players.test.ts @@ -47,7 +47,7 @@ describe('PlayersRepository', () => { // Helper utility to quickly build an admin staff account const seedAdminStaff = (username: string, playerId?: number) => { - const [admin] = testDb + const admin = testDb .insert(adminUsers) .values({ username, @@ -56,7 +56,7 @@ describe('PlayersRepository', () => { createdAt: new Date(), }) .returning() - .all(); + .get()!; return admin; }; @@ -64,11 +64,11 @@ describe('PlayersRepository', () => { describe('isStaff()', () => { it('should accurately return true if player mapping matches an admin account row', () => { - const [player] = testDb + const player = testDb .insert(players) .values({ name: 'Staff_Member' }) .returning() - .all(); + .get()!; seedAdminStaff('admin_one', player.id); expect(playersRepo.isStaff(player.id)).toBe(true); @@ -83,11 +83,11 @@ describe('PlayersRepository', () => { describe('findByLicense()', () => { it('should resolve comprehensive player information matching a designated license string', () => { - const [player] = testDb + const player = testDb .insert(players) .values({ name: 'Target_User' }) .returning() - .all(); + .get()!; testDb .insert(playerIdentifiers) .values([ @@ -114,11 +114,11 @@ describe('PlayersRepository', () => { describe('findByIdentifier()', () => { it('should resolve a player by a non-license identifier type', () => { - const [player] = testDb + const player = testDb .insert(players) .values({ name: 'Discord_User' }) .returning() - .all(); + .get()!; testDb .insert(playerIdentifiers) .values({ @@ -167,11 +167,11 @@ describe('PlayersRepository', () => { }); it('should mutate active properties and append missing attributes if license lookup succeeds', async () => { - const [player] = testDb + const player = testDb .insert(players) .values({ name: 'Old_Name' }) .returning() - .all(); + .get()!; testDb .insert(playerIdentifiers) @@ -199,7 +199,7 @@ describe('PlayersRepository', () => { }); it('should detect if a new player matches an admin user identifier and link them as staff', async () => { - const [admin] = testDb + const admin = testDb .insert(adminUsers) .values({ username: 'admin_test', @@ -209,7 +209,7 @@ describe('PlayersRepository', () => { cfxId: '987654321', }) .returning() - .all(); + .get()!; const created = await playersRepo.upsert('admin_player', { license: 'license:admin_key', @@ -235,11 +235,11 @@ describe('PlayersRepository', () => { describe('checkBanned()', () => { it('should locate a restrictive footprint match using any linked profile identifier', () => { - const [player] = testDb + const player = testDb .insert(players) .values({ name: 'Banned_Everywhere' }) .returning() - .all(); + .get()!; testDb .insert(playerIdentifiers) .values({ playerId: player.id, type: 'steam', value: 'steam:dirty_id' }) @@ -263,11 +263,11 @@ describe('PlayersRepository', () => { }); it('should ignore expired or explicitly revoked blocks during active validation passes', () => { - const [player] = testDb + const player = testDb .insert(players) .values({ name: 'Pardoned_User' }) .returning() - .all(); + .get()!; testDb .insert(playerIdentifiers) .values({ @@ -296,11 +296,11 @@ describe('PlayersRepository', () => { describe('updatePlaytime()', () => { it('should adjust tracking fields and touch access markers directly inside data rows', () => { - const [player] = testDb + const player = testDb .insert(players) .values({ name: 'Gamer' }) .returning() - .all(); + .get()!; playersRepo.updatePlaytime(player.id, 500000); @@ -318,16 +318,16 @@ describe('PlayersRepository', () => { describe('list()', () => { it('should filter across diverse records using global text fuzzy parameters', () => { - const [p1] = testDb + const p1 = testDb .insert(players) .values({ name: 'Alpha_Player' }) .returning() - .all(); - const [p2] = testDb + .get()!; + const p2 = testDb .insert(players) .values({ name: 'Beta_User' }) .returning() - .all(); + .get()!; testDb .insert(playerIdentifiers) @@ -340,7 +340,7 @@ describe('PlayersRepository', () => { // Search using a parameter that matches an identifier value instead of a name string const response = playersRepo.list(1, 10, { search: 'keyword' }); expect(response.total).toBe(1); - expect(response.items[0].id).toBe(p2.id); + expect(response.items[0]!.id).toBe(p2.id); }); it('should dynamically sort outputs based on specified structural criteria styles', () => { @@ -356,7 +356,7 @@ describe('PlayersRepository', () => { sortBy: 'playtime', sortOrder: 'desc', }); - expect(response.items[0].name).toBe('High_Playtime'); + expect(response.items[0]!.name).toBe('High_Playtime'); }); }); @@ -364,11 +364,11 @@ describe('PlayersRepository', () => { describe('updatePlayerNotes()', () => { it('should append structured feedback logs when processing unfamiliar content strings', async () => { - const [player] = testDb + const player = testDb .insert(players) .values({ name: 'Suspect_User' }) .returning() - .all(); + .get()!; const admin = seedAdminStaff('moderator_bob'); const note = await playersRepo.updatePlayerNotes( @@ -387,11 +387,11 @@ describe('PlayersRepository', () => { }); it('should clean out existing rows if an update parameter arrives empty', async () => { - const [player] = testDb + const player = testDb .insert(players) .values({ name: 'Logged_User' }) .returning() - .all(); + .get()!; const admin = seedAdminStaff('moderator_jack'); testDb @@ -420,11 +420,11 @@ describe('PlayersRepository', () => { }); it('should reject changes and throw if the note content length falls below strict thresholds', () => { - const [player] = testDb + const player = testDb .insert(players) .values({ name: 'User' }) .returning() - .all(); + .get()!; const admin = seedAdminStaff('mod'); expect( @@ -435,11 +435,11 @@ describe('PlayersRepository', () => { describe('addBan() Lifecycle Upgrades', () => { it('should intercept operations and return false if a permanent ban currently restricts the user profile', async () => { - const [player] = testDb + const player = testDb .insert(players) .values({ name: 'Permanently_Restricted' }) .returning() - .all(); + .get()!; const admin = seedAdminStaff('root_system'); testDb @@ -463,11 +463,11 @@ describe('PlayersRepository', () => { }); it('should soft-expire active sub-bans if an administrative action escalates the duration penalty', async () => { - const [player] = testDb + const player = testDb .insert(players) .values({ name: 'Escalation_Target' }) .returning() - .all(); + .get()!; const admin = seedAdminStaff('senior_mod'); const originalExpiry = new Date('2026-06-20T00:00:00Z'); @@ -498,11 +498,11 @@ describe('PlayersRepository', () => { }); it('should record a null issuer for external (ingame API) bans', async () => { - const [player] = testDb + const player = testDb .insert(players) .values({ name: 'Ingame_Banned' }) .returning() - .all(); + .get()!; const result = await playersRepo.addBan( player.id, @@ -521,11 +521,11 @@ describe('PlayersRepository', () => { describe('addKick() and addWarn() Standard Logging', () => { it('should successfully commit warning entries linked directly to active records', async () => { - const [player] = testDb + const player = testDb .insert(players) .values({ name: 'Warned_User' }) .returning() - .all(); + .get()!; const admin = seedAdminStaff('helper_mod'); const warning = await playersRepo.addWarn( @@ -545,11 +545,11 @@ describe('PlayersRepository', () => { }); it('should successfully commit kick entries linked directly to active records', async () => { - const [player] = testDb + const player = testDb .insert(players) .values({ name: 'Kicked_User' }) .returning() - .all(); + .get()!; const admin = seedAdminStaff('helper_mod_2'); const kick = await playersRepo.addKick( @@ -569,11 +569,11 @@ describe('PlayersRepository', () => { }); it('should record warns and kicks with a null issuer for external actions', async () => { - const [player] = testDb + const player = testDb .insert(players) .values({ name: 'External_Actioned' }) .returning() - .all(); + .get()!; const warn = await playersRepo.addWarn(player.id, 'ingame warn', null); const kick = await playersRepo.addKick(player.id, 'ingame kick', null); @@ -585,11 +585,11 @@ describe('PlayersRepository', () => { describe('revokeWarn()', () => { it('should flag an active warn as revoked and return the updated row', async () => { - const [player] = testDb + const player = testDb .insert(players) .values({ name: 'Warned_User' }) .returning() - .all(); + .get()!; const warn = await playersRepo.addWarn(player.id, 'Spamming', null); const revoked = playersRepo.revokeWarn(warn.id); @@ -608,11 +608,11 @@ describe('PlayersRepository', () => { }); it('should return undefined when the warn is already revoked', async () => { - const [player] = testDb + const player = testDb .insert(players) .values({ name: 'Warned_User' }) .returning() - .all(); + .get()!; const warn = await playersRepo.addWarn(player.id, 'Spamming', null); playersRepo.revokeWarn(warn.id); @@ -624,16 +624,16 @@ describe('PlayersRepository', () => { }); it('should not revoke when the scoped playerId does not own the warn', async () => { - const [owner] = testDb + const owner = testDb .insert(players) .values({ name: 'Owner' }) .returning() - .all(); - const [other] = testDb + .get()!; + const other = testDb .insert(players) .values({ name: 'Other' }) .returning() - .all(); + .get()!; const warn = await playersRepo.addWarn(owner.id, 'Spamming', null); expect(playersRepo.revokeWarn(warn.id, other.id)).toBeUndefined(); @@ -649,11 +649,11 @@ describe('PlayersRepository', () => { describe('revokeKick()', () => { it('should flag an active kick as revoked and return the updated row', async () => { - const [player] = testDb + const player = testDb .insert(players) .values({ name: 'Kicked_User' }) .returning() - .all(); + .get()!; const kick = await playersRepo.addKick(player.id, 'AFK', null); const revoked = playersRepo.revokeKick(kick.id); @@ -672,11 +672,11 @@ describe('PlayersRepository', () => { }); it('should return undefined when the kick is already revoked', async () => { - const [player] = testDb + const player = testDb .insert(players) .values({ name: 'Kicked_User' }) .returning() - .all(); + .get()!; const kick = await playersRepo.addKick(player.id, 'AFK', null); playersRepo.revokeKick(kick.id); @@ -688,16 +688,16 @@ describe('PlayersRepository', () => { }); it('should not revoke when the scoped playerId does not own the kick', async () => { - const [owner] = testDb + const owner = testDb .insert(players) .values({ name: 'Owner' }) .returning() - .all(); - const [other] = testDb + .get()!; + const other = testDb .insert(players) .values({ name: 'Other' }) .returning() - .all(); + .get()!; const kick = await playersRepo.addKick(owner.id, 'AFK', null); expect(playersRepo.revokeKick(kick.id, other.id)).toBeUndefined(); diff --git a/packages/database/src/repositories/players.ts b/packages/database/src/repositories/players.ts index f7113e77..380d9f27 100644 --- a/packages/database/src/repositories/players.ts +++ b/packages/database/src/repositories/players.ts @@ -523,6 +523,8 @@ class PlayersRepository { }) .returning(); + if (!newBan) throw new Error('Failed to insert ban'); + const player = tx .select() .from(players) @@ -558,6 +560,8 @@ class PlayersRepository { }) .returning(); + if (!newKick) throw new Error('Failed to insert kick'); + return { ...newKick, player, @@ -587,6 +591,8 @@ class PlayersRepository { }) .returning(); + if (!newWarn) throw new Error('Failed to insert warn'); + return { ...newWarn, player, diff --git a/packages/database/src/repositories/whitelist.test.ts b/packages/database/src/repositories/whitelist.test.ts index 616684a2..54e75022 100644 --- a/packages/database/src/repositories/whitelist.test.ts +++ b/packages/database/src/repositories/whitelist.test.ts @@ -147,8 +147,8 @@ describe('WhitelistRepository Integration Tests', () => { expect(response.items.length).toBe(1); // Asserts business logic transformations are executed accurately - expect(response.items[0].addedByAdmin).toBe('deleted_admin'); - expect(response.items[0].playerName).toBe('N/A'); + expect(response.items[0]!.addedByAdmin).toBe('deleted_admin'); + expect(response.items[0]!.playerName).toBe('N/A'); }); it('should correctly prioritize and map the system indicator string when flags are high', async () => { @@ -163,12 +163,12 @@ describe('WhitelistRepository Integration Tests', () => { .run(); const response = await whitelistRepo.list(1, 10); - expect(response.items[0].addedByAdmin).toBe('system'); + expect(response.items[0]!.addedByAdmin).toBe('system'); }); it('should execute deep multi-table text filtering when passing down search strings', async () => { // Setup a cascading chain: Admin profile + Player + Player Identifier -> Whitelist Link - const [admin] = testDb + const admin = testDb .insert(adminUsers) .values({ username: 'Staff_Alpha', @@ -176,13 +176,13 @@ describe('WhitelistRepository Integration Tests', () => { createdAt: new Date(), }) .returning() - .all(); + .get()!; - const [player] = testDb + const player = testDb .insert(players) .values({ name: 'Charlie_Properties' }) .returning() - .all(); + .get()!; testDb .insert(playerIdentifiers) .values({ @@ -207,8 +207,8 @@ describe('WhitelistRepository Integration Tests', () => { search: 'Charlie', }); expect(searchByName.total).toBe(1); - expect(searchByName.items[0].playerName).toBe('Charlie_Properties'); - expect(searchByName.items[0].addedByAdmin).toBe('Staff_Alpha'); + expect(searchByName.items[0]!.playerName).toBe('Charlie_Properties'); + expect(searchByName.items[0]!.addedByAdmin).toBe('Staff_Alpha'); // Execute search on administrative username substring const searchByAdmin = await whitelistRepo.list(1, 10, { @@ -222,7 +222,7 @@ describe('WhitelistRepository Integration Tests', () => { describe('revoke()', () => { it('should delete a whitelist record completely and return the removed element details', () => { - const [inserted] = testDb + const inserted = testDb .insert(whitelistedIdentifers) .values({ type: 'fivem', @@ -230,7 +230,7 @@ describe('WhitelistRepository Integration Tests', () => { addedAt: new Date(), }) .returning() - .all(); + .get()!; const revoked = whitelistRepo.revoke(inserted.id); expect(revoked).toBeDefined(); diff --git a/packages/ui/src/components/icon-selectmenu.tsx b/packages/ui/src/components/icon-selectmenu.tsx index 7a3b0ae1..3896fcbe 100644 --- a/packages/ui/src/components/icon-selectmenu.tsx +++ b/packages/ui/src/components/icon-selectmenu.tsx @@ -1,7 +1,7 @@ 'use client'; import * as React from 'react'; -import { Check, ChevronsUpDown, HelpCircle } from 'lucide-react'; +import { Check, ChevronsUpDown, FileQuestion, HelpCircle } from 'lucide-react'; import { Popover, PopoverContent, PopoverTrigger } from './popover'; import { cn } from '../lib/utils'; import { Button } from './button'; @@ -72,7 +72,7 @@ export function IconPicker({ No icon found. {filteredIcons.map((iconName) => { - const IconComponent = lucideIconMap[iconName as IconName]; + const IconComponent = lucideIconMap[iconName as IconName] ?? FileQuestion; return ( >((acc, [key, value]) => { - if (key[0] === key[0].toUpperCase()) { + if (key[0] === key[0]?.toUpperCase()) { acc[key] = value as LucideIcon; } return acc;