fix(types): enhance type safety across various components and utilities

This commit is contained in:
2025-12-30 00:47:10 +00:00
parent 4dbea7a9b0
commit 4d451dab46
29 changed files with 248 additions and 92 deletions

View File

@@ -3,9 +3,10 @@ import { NextResponse } from 'next/server'
import { readJson } from '@/lib/api/read-json'
import { setPackageData } from '@/lib/db/packages/set-package-data'
import type { JsonValue } from '@/types/utility-types'
type PackageDataPayload = {
data?: Record<string, any[]>
data?: Record<string, JsonValue[]>
}
interface RouteParams {

View File

@@ -1,7 +1,7 @@
import { render } from '@testing-library/react'
import { describe, expect, it } from 'vitest'
import { Icon } from './Icon'
import { Icon, type IconName } from './Icon'
describe('Icon', () => {
it.each([
@@ -20,7 +20,7 @@ describe('Icon', () => {
})
it('returns null for unknown icon', () => {
const { container } = render(<Icon name={'UnknownIcon' as any} />)
const { container } = render(<Icon name={'UnknownIcon' as IconName} />)
expect(container.querySelector('svg')).toBeNull()
})
})

View File

@@ -17,6 +17,8 @@ import {
} from '@/components/ui'
import type { LuaScript } from '@/lib/level-types'
import { getLuaExampleCode, getLuaExamplesList } from '@/lib/lua-examples'
import type { LuaExampleKey } from '@/lib/lua-examples'
import type { editor } from 'monaco-editor'
import { useLuaMonacoConfig } from './useLuaMonacoConfig'
@@ -37,7 +39,7 @@ export const LuaCodeEditorSection = ({
onShowSnippetLibraryChange,
onUpdateScript,
}: LuaCodeEditorSectionProps) => {
const editorRef = useRef<any>(null)
const editorRef = useRef<editor.IStandaloneCodeEditor | null>(null)
const monaco = useMonaco()
useLuaMonacoConfig(monaco)
@@ -67,7 +69,7 @@ export const LuaCodeEditorSection = ({
}
const handleExampleLoad = (value: string) => {
const exampleCode = getLuaExampleCode(value as any)
const exampleCode = getLuaExampleCode(value as LuaExampleKey)
onUpdateScript({ code: exampleCode })
toast.success('Example loaded')
}

View File

@@ -15,7 +15,7 @@ export const useLuaMonacoConfig = (monaco: Monaco | null) => {
endColumn: word.endColumn,
}
const suggestions: any[] = [
const suggestions: monaco.languages.CompletionItem[] = [
{
label: 'context.data',
kind: monaco.languages.CompletionItemKind.Property,

View File

@@ -15,8 +15,8 @@ interface RecordFormProps {
model: ModelSchema
schema: SchemaConfig
currentApp: string
record?: any
onSave: (record: any) => void
record?: Record<string, unknown>
onSave: (record: Record<string, unknown>) => void
}
export function RecordForm({
@@ -28,7 +28,7 @@ export function RecordForm({
record,
onSave,
}: RecordFormProps) {
const [formData, setFormData] = useState<any>(record || createEmptyRecord(model))
const [formData, setFormData] = useState<Record<string, unknown>>(record || createEmptyRecord(model))
const [errors, setErrors] = useState<Record<string, string>>({})
useEffect(() => {
@@ -40,8 +40,8 @@ export function RecordForm({
setErrors({})
}, [record, model, open])
const handleFieldChange = (fieldName: string, value: any) => {
setFormData((prev: any) => ({
const handleFieldChange = (fieldName: string, value: unknown) => {
setFormData((prev) => ({
...prev,
[fieldName]: value,
}))

View File

@@ -9,15 +9,15 @@ import { IRCWebchat } from './functions/i-r-c-webchat'
* This is a convenience wrapper. Prefer importing individual functions.
*/
export class IRCWebchatUtils {
static IRCWebchat(...args: any[]) {
return IRCWebchat(...(args as any))
static IRCWebchat(...args: Parameters<typeof IRCWebchat>) {
return IRCWebchat(...args)
}
static handleSendMessage(...args: any[]) {
return handleSendMessage(...(args as any))
static handleSendMessage(...args: Parameters<typeof handleSendMessage>) {
return handleSendMessage(...args)
}
static formatTime(...args: any[]) {
return formatTime(...(args as any))
static formatTime(...args: Parameters<typeof formatTime>) {
return formatTime(...args)
}
}

View File

@@ -9,15 +9,15 @@ import { IRCWebchatDeclarative } from './functions/i-r-c-webchat-declarative'
* This is a convenience wrapper. Prefer importing individual functions.
*/
export class IRCWebchatDeclarativeUtils {
static IRCWebchatDeclarative(...args: any[]) {
return IRCWebchatDeclarative(...(args as any))
static IRCWebchatDeclarative(...args: Parameters<typeof IRCWebchatDeclarative>) {
return IRCWebchatDeclarative(...args)
}
static async handleSendMessage(...args: any[]) {
return await handleSendMessage(...(args as any))
static async handleSendMessage(...args: Parameters<typeof handleSendMessage>) {
return await handleSendMessage(...args)
}
static async formatTime(...args: any[]) {
return await formatTime(...(args as any))
static async formatTime(...args: Parameters<typeof formatTime>) {
return await formatTime(...args)
}
}

View File

@@ -3,19 +3,24 @@
* Shared type definitions for Lua execution
*/
import type { JsonValue } from '@/types/utility-types'
/** Fengari Lua state - opaque type from fengari-web */
export type LuaState = ReturnType<typeof import('fengari-web').lauxlib.luaL_newstate>
export interface LuaExecutionContext {
data?: any
user?: any
data?: JsonValue
user?: JsonValue
kv?: {
get: (key: string) => Promise<any>
set: (key: string, value: any) => Promise<void>
get: (key: string) => Promise<JsonValue>
set: (key: string, value: JsonValue) => Promise<void>
}
log?: (...args: any[]) => void
log?: (...args: unknown[]) => void
}
export interface LuaExecutionResult {
success: boolean
result?: any
result?: JsonValue
error?: string
logs: string[]
}
@@ -26,7 +31,7 @@ export interface LuaExecutionResult {
*/
export interface LuaEngineState {
/** The Fengari Lua state */
L: any
L: LuaState
/** Logs captured during execution */
logs: string[]
}

View File

@@ -15,39 +15,39 @@ import { registerPage } from './functions/register-page'
* This is a convenience wrapper. Prefer importing individual functions.
*/
export class PageRendererUtils {
static async registerPage(...args: any[]) {
return await registerPage(...(args as any))
static async registerPage(...args: Parameters<typeof registerPage>) {
return await registerPage(...args)
}
static async loadPages(...args: any[]) {
return await loadPages(...(args as any))
static async loadPages(...args: Parameters<typeof loadPages>) {
return await loadPages(...args)
}
static getPage(...args: any[]) {
return getPage(...(args as any))
static getPage(...args: Parameters<typeof getPage>) {
return getPage(...args)
}
static getPagesByLevel(...args: any[]) {
return getPagesByLevel(...(args as any))
static getPagesByLevel(...args: Parameters<typeof getPagesByLevel>) {
return getPagesByLevel(...args)
}
static async executeLuaScript(...args: any[]) {
return await executeLuaScript(...(args as any))
static async executeLuaScript(...args: Parameters<typeof executeLuaScript>) {
return await executeLuaScript(...args)
}
static async checkPermissions(...args: any[]) {
return await checkPermissions(...(args as any))
static async checkPermissions(...args: Parameters<typeof checkPermissions>) {
return await checkPermissions(...args)
}
static async onPageLoad(...args: any[]) {
return await onPageLoad(...(args as any))
static async onPageLoad(...args: Parameters<typeof onPageLoad>) {
return await onPageLoad(...args)
}
static async onPageUnload(...args: any[]) {
return await onPageUnload(...(args as any))
static async onPageUnload(...args: Parameters<typeof onPageUnload>) {
return await onPageUnload(...args)
}
static getPageRenderer(...args: any[]) {
return getPageRenderer(...(args as any))
static getPageRenderer(...args: Parameters<typeof getPageRenderer>) {
return getPageRenderer(...args)
}
}

View File

@@ -3,9 +3,9 @@ import { createLuaEngine } from '@/lib/lua/engine/core/create-lua-engine'
import { executeLuaCode } from '@/lib/lua/functions/execution/execute-lua-code'
import { normalizeLuaStructure } from '@/lib/lua/ui/normalize-lua-structure'
import type { UIPageRecord } from '@/lib/seed/import-ui-pages'
import type { JsonObject } from '@/types/utility-types'
import type { JsonObject, JsonValue } from '@/types/utility-types'
export type LuaActionHandler = (payload?: Record<string, unknown>) => Promise<unknown>
export type LuaActionHandler = (payload?: JsonObject) => Promise<JsonValue | null>
/**
* Load a UI page from database and optionally process with Lua
@@ -25,7 +25,7 @@ export async function loadPageFromDB(path: string): Promise<UIPageData | null> {
const page = pages[0]
// 2. Get associated Lua scripts if any (from actions field)
let processedLayout = page.layout
let processedLayout: JsonValue = page.layout
const actionHandlers: Record<string, LuaActionHandler> = {}
if (page.actions) {
@@ -81,7 +81,7 @@ export async function loadPageFromDB(path: string): Promise<UIPageData | null> {
* Create a JavaScript wrapper for a Lua action handler
*/
function createLuaActionHandler(luaCode: string, actionName: string): LuaActionHandler {
return async (payload: Record<string, unknown> = {}) => {
return async (payload: JsonObject = {}) => {
const engine = createLuaEngine()
try {
@@ -97,7 +97,7 @@ function createLuaActionHandler(luaCode: string, actionName: string): LuaActionH
throw new Error(result.error)
}
return result.result
return (result.result ?? null) as JsonValue | null
} finally {
engine.destroy()
}

View File

@@ -1,3 +1,4 @@
import type { ClassValue } from 'clsx'
import { describe, expect, it } from 'vitest'
import { cn } from '@/lib/utils'
@@ -5,17 +6,22 @@ import { cn } from '@/lib/utils'
describe('utils', () => {
describe('cn', () => {
// Note: cn() uses clsx, which concatenates classes without smart Tailwind merging
it.each([
const cases: Array<{
input: ClassValue[]
shouldContain: string[]
shouldNotContain: string[]
description: string
}> = [
{
input: ['px-2 py-1', 'px-3'],
shouldContain: ['px-2', 'py-1', 'px-3'],
shouldNotContain: [] as string[],
shouldNotContain: [],
description: 'concatenate classes (no tailwind deduplication)',
},
{
input: ['px-2', 'py-2'],
shouldContain: ['px-2', 'py-2'],
shouldNotContain: [] as string[],
shouldNotContain: [],
description: 'handle simple classes',
},
{
@@ -27,7 +33,7 @@ describe('utils', () => {
{
input: ['px-2 py-1 bg-red-500', 'px-3 bg-blue-500'],
shouldContain: ['px-2', 'px-3', 'py-1', 'bg-red-500', 'bg-blue-500'],
shouldNotContain: [] as string[],
shouldNotContain: [],
description: 'concatenate all classes (no tailwind deduplication)',
},
{
@@ -42,8 +48,10 @@ describe('utils', () => {
shouldNotContain: [],
description: 'handle arrays of classes',
},
])('should $description', ({ input, shouldContain, shouldNotContain }) => {
const result = cn(...(input as any))
]
it.each(cases)('should $description', ({ input, shouldContain, shouldNotContain }) => {
const result = cn(...input)
shouldContain.forEach(cls => {
expect(result).toContain(cls)
})

View File

@@ -1,12 +1,14 @@
import { describe, expect, it } from 'vitest'
import type { WorkflowNode } from '../../../types/level-types'
import { WorkflowEngine } from '../workflow-engine'
import { createContext, createNode, createWorkflow } from './workflow-engine.fixtures'
describe('workflow-engine errors', () => {
it('fails unknown node types with a clear error', async () => {
const invalidType = 'unknown' as WorkflowNode['type']
const workflow = createWorkflow('err-1', 'Unknown node', [
createNode('mystery', 'unknown' as any, 'Mystery node'),
createNode('mystery', invalidType, 'Mystery node'),
])
const result = await WorkflowEngine.execute(workflow, createContext({}))

View File

@@ -1,7 +1,11 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import * as SandboxFactory from '../../../lua/engine/sandbox/create-sandboxed-lua-engine'
import type { SandboxedLuaResult } from '../../../lua/engine/sandbox/sandboxed-lua-engine'
import type {
SandboxedLuaEngine,
SandboxedLuaResult,
} from '../../../lua/engine/sandbox/sandboxed-lua-engine'
import type { SecurityScanResult } from '../../../security/scanner/types'
import { WorkflowEngine } from '../workflow-engine'
import { createContext, createNode, createWorkflow } from './workflow-engine.fixtures'
@@ -29,17 +33,30 @@ describe('workflow-engine persistence', () => {
})
it('persists security warnings from Lua execution', async () => {
const securityResult: SecurityScanResult = {
safe: false,
severity: 'high',
issues: [
{
type: 'dangerous',
severity: 'high',
message: 'uses os',
pattern: 'os',
},
],
}
const sandboxResult: SandboxedLuaResult = {
execution: { success: true, result: 99, logs: ['lua log'] },
security: { severity: 'high', issues: [{ message: 'uses os' }] } as any,
security: securityResult,
}
const mockEngine = {
const mockEngine: SandboxedLuaEngine = {
executeWithSandbox: vi.fn(async () => sandboxResult),
destroy: vi.fn(),
}
} as SandboxedLuaEngine
vi.spyOn(SandboxFactory, 'createSandboxedLuaEngine').mockReturnValue(mockEngine as any)
vi.spyOn(SandboxFactory, 'createSandboxedLuaEngine').mockReturnValue(mockEngine)
const workflow = createWorkflow('persist-2', 'Lua security', [
createNode('lua', 'lua', 'Sandboxed', { code: 'return 1' }),

View File

@@ -48,13 +48,13 @@ export interface LuaTable {
/**
* Lua function representation
*/
export type LuaFunction = (...args: unknown[]) => unknown
export type LuaFunction = (...args: JsonValue[]) => JsonValue
/**
* Generic property bag for metadata and dynamic properties
* Prefer specific interfaces when structure is known
*/
export type PropertyBag = Record<string, unknown>
export type PropertyBag = Record<string, JsonValue>
/**
* Type-safe record with specific value type
@@ -64,4 +64,4 @@ export type TypedRecord<T> = Record<string, T>
/**
* Unknown error type (for catch blocks)
*/
export type UnknownError = unknown
export type UnknownError = Error | string

View File

@@ -1,6 +1,11 @@
import { describe, it, expect } from 'vitest'
import components from '../seed/components.json'
interface ComponentDefinition {
id: string
type: string
}
describe('Admin Dialog Components', () => {
it('should be a valid array', () => {
expect(components).toBeInstanceOf(Array)
@@ -8,7 +13,7 @@ describe('Admin Dialog Components', () => {
it('should have valid component structure if components exist', () => {
if (components.length > 0) {
components.forEach((component: any) => {
components.forEach((component: ComponentDefinition) => {
expect(component.id).toBeDefined()
expect(component.type).toBeDefined()
expect(typeof component.id).toBe('string')

View File

@@ -1,6 +1,12 @@
import { describe, it, expect } from 'vitest'
import components from '../seed/components.json'
interface ComponentDefinition {
id: string
type: string
children: unknown[]
}
describe('Arcade Lobby Components', () => {
it('should be a valid array', () => {
expect(components).toBeInstanceOf(Array)
@@ -8,7 +14,7 @@ describe('Arcade Lobby Components', () => {
it('should have valid component structure if components exist', () => {
if (components.length > 0) {
components.forEach((component: any) => {
components.forEach((component: ComponentDefinition) => {
expect(component.id).toBeDefined()
expect(component.type).toBeDefined()
expect(typeof component.id).toBe('string')

View File

@@ -1,6 +1,12 @@
import { describe, it, expect } from 'vitest'
import components from '../seed/components.json'
interface ComponentDefinition {
id: string
type: string
children: unknown[]
}
describe('Codegen Studio Components', () => {
it('should be a valid array', () => {
expect(components).toBeInstanceOf(Array)
@@ -8,7 +14,7 @@ describe('Codegen Studio Components', () => {
it('should have valid component structure if components exist', () => {
if (components.length > 0) {
components.forEach((component: any) => {
components.forEach((component: ComponentDefinition) => {
expect(component.id).toBeDefined()
expect(component.type).toBeDefined()
expect(typeof component.id).toBe('string')

View File

@@ -1,6 +1,12 @@
import { describe, it, expect } from 'vitest'
import components from '../seed/components.json'
interface ComponentDefinition {
id: string
type: string
children: unknown[]
}
describe('Dashboard Components', () => {
it('should be a valid array', () => {
expect(components).toBeInstanceOf(Array)
@@ -8,7 +14,7 @@ describe('Dashboard Components', () => {
it('should have valid component structure if components exist', () => {
if (components.length > 0) {
components.forEach((component: any) => {
components.forEach((component: ComponentDefinition) => {
expect(component.id).toBeDefined()
expect(component.type).toBeDefined()
expect(typeof component.id).toBe('string')

View File

@@ -1,6 +1,12 @@
import { describe, it, expect } from 'vitest'
import components from '../seed/components.json'
interface ComponentDefinition {
id: string
type: string
children: unknown[]
}
describe('Data Table Components', () => {
it('should be a valid array', () => {
expect(components).toBeInstanceOf(Array)
@@ -8,7 +14,7 @@ describe('Data Table Components', () => {
it('should have valid component structure if components exist', () => {
if (components.length > 0) {
components.forEach((component: any) => {
components.forEach((component: ComponentDefinition) => {
expect(component.id).toBeDefined()
expect(component.type).toBeDefined()
expect(typeof component.id).toBe('string')

View File

@@ -1,6 +1,12 @@
import { describe, it, expect } from 'vitest'
import components from '../seed/components.json'
interface ComponentDefinition {
id: string
type: string
children: unknown[]
}
describe('Form Builder Components', () => {
it('should be a valid array', () => {
expect(components).toBeInstanceOf(Array)
@@ -8,7 +14,7 @@ describe('Form Builder Components', () => {
it('should have valid component structure if components exist', () => {
if (components.length > 0) {
components.forEach((component: any) => {
components.forEach((component: ComponentDefinition) => {
expect(component.id).toBeDefined()
expect(component.type).toBeDefined()
expect(typeof component.id).toBe('string')

View File

@@ -1,6 +1,12 @@
import { describe, it, expect } from 'vitest'
import components from '../seed/components.json'
interface ComponentDefinition {
id: string
type: string
children: unknown[]
}
describe('Forum Forge Components', () => {
it('should be a valid array', () => {
expect(components).toBeInstanceOf(Array)
@@ -8,7 +14,7 @@ describe('Forum Forge Components', () => {
it('should have valid component structure if components exist', () => {
if (components.length > 0) {
components.forEach((component: any) => {
components.forEach((component: ComponentDefinition) => {
expect(component.id).toBeDefined()
expect(component.type).toBeDefined()
expect(typeof component.id).toBe('string')

View File

@@ -1,6 +1,11 @@
import { describe, it, expect } from 'vitest'
import components from '../seed/components.json'
interface ComponentDefinition {
id: string
type: string
}
describe('Nav Menu Components', () => {
it('should be a valid array', () => {
expect(components).toBeInstanceOf(Array)
@@ -8,7 +13,7 @@ describe('Nav Menu Components', () => {
it('should have valid component structure if components exist', () => {
if (components.length > 0) {
components.forEach((component: any) => {
components.forEach((component: ComponentDefinition) => {
expect(component.id).toBeDefined()
expect(component.type).toBeDefined()
expect(typeof component.id).toBe('string')

View File

@@ -1,6 +1,11 @@
import { describe, it, expect } from 'vitest'
import components from '../seed/components.json'
interface ComponentDefinition {
id: string
type: string
}
describe('Notification Center Components', () => {
it('should be a valid array', () => {
expect(components).toBeInstanceOf(Array)
@@ -8,7 +13,7 @@ describe('Notification Center Components', () => {
it('should have valid component structure if components exist', () => {
if (components.length > 0) {
components.forEach((component: any) => {
components.forEach((component: ComponentDefinition) => {
expect(component.id).toBeDefined()
expect(component.type).toBeDefined()
expect(typeof component.id).toBe('string')

View File

@@ -1,6 +1,12 @@
import { describe, it, expect } from 'vitest'
import components from '../seed/components.json'
interface ComponentDefinition {
id: string
type: string
children: unknown[]
}
describe('Social Hub Components', () => {
it('should be a valid array', () => {
expect(components).toBeInstanceOf(Array)
@@ -8,7 +14,7 @@ describe('Social Hub Components', () => {
it('should have valid component structure if components exist', () => {
if (components.length > 0) {
components.forEach((component: any) => {
components.forEach((component: ComponentDefinition) => {
expect(component.id).toBeDefined()
expect(component.type).toBeDefined()
expect(typeof component.id).toBe('string')

View File

@@ -1,6 +1,12 @@
import { describe, it, expect } from 'vitest'
import components from '../seed/components.json'
interface ComponentDefinition {
id: string
type: string
children: unknown[]
}
describe('Stream Cast Components', () => {
it('should be a valid array', () => {
expect(components).toBeInstanceOf(Array)
@@ -8,7 +14,7 @@ describe('Stream Cast Components', () => {
it('should have valid component structure if components exist', () => {
if (components.length > 0) {
components.forEach((component: any) => {
components.forEach((component: ComponentDefinition) => {
expect(component.id).toBeDefined()
expect(component.type).toBeDefined()
expect(typeof component.id).toBe('string')

View File

@@ -6,7 +6,14 @@ type OverviewStatus = {
details: string
}
const selectDetails = (data: any): OverviewStatus | undefined => {
interface ReportData {
coverage?: number
totalIssues?: number
critical?: number
averageCoverage?: number
}
const selectDetails = (data: ReportData | null): OverviewStatus | undefined => {
if (data?.coverage !== undefined) {
return {
status: data.coverage >= 80 ? '✅ Pass' : '⚠️ Warning',

View File

@@ -1,10 +1,31 @@
import { existsSync, readFileSync } from 'fs'
interface StubInfo {
name: string
file: string
line: number
type: string
flags: string[]
summary: string
}
interface CompletenessAnalysis {
averageCompleteness: number
bySeverity: {
critical: number
high: number
medium: number
low: number
}
flagTypes: Record<string, number>
criticalStubs?: StubInfo[]
}
export const buildCompletenessSection = (): string => {
if (!existsSync('implementation-analysis.json')) return ''
try {
const completeness = JSON.parse(readFileSync('implementation-analysis.json', 'utf8'))
const completeness: CompletenessAnalysis = JSON.parse(readFileSync('implementation-analysis.json', 'utf8'))
let section = '## Implementation Completeness Analysis\n\n'
section += `**Average Completeness Score**: ${completeness.averageCompleteness}%\n\n`
@@ -27,7 +48,15 @@ export const buildCompletenessSection = (): string => {
if (completeness.criticalStubs && completeness.criticalStubs.length > 0) {
section += '### 🔴 Incomplete Implementations (0% Completeness)\n\n'
section += '<details><summary>Click to expand</summary>\n\n'
completeness.criticalStubs.forEach((stub: any) => {
interface CriticalStub {
name: string
file: string
line: number
type: string
flags: string[]
summary: string
}
completeness.criticalStubs.forEach((stub: CriticalStub) => {
section += `#### \`${stub.name}\` in \`${stub.file}:${stub.line}\`\n`
section += `**Type**: ${stub.type}\n`
section += `**Flags**: ${stub.flags.join(', ')}\n`

View File

@@ -1,12 +1,24 @@
import { readFileSync } from 'fs'
interface ESLintMessage {
severity: number
message: string
line: number
column: number
}
interface ESLintFileReport {
filePath: string
messages: ESLintMessage[]
}
try {
const report = JSON.parse(readFileSync('eslint-report.json', 'utf8'))
const report: ESLintFileReport[] = JSON.parse(readFileSync('eslint-report.json', 'utf8'))
const summary = {
totalFiles: report.length,
filesWithIssues: report.filter((f: any) => f.messages.length > 0).length,
errors: report.reduce((sum: number, f: any) => sum + f.messages.filter((m: any) => m.severity === 2).length, 0),
warnings: report.reduce((sum: number, f: any) => sum + f.messages.filter((m: any) => m.severity === 1).length, 0),
filesWithIssues: report.filter((f) => f.messages.length > 0).length,
errors: report.reduce((sum: number, f) => sum + f.messages.filter((m) => m.severity === 2).length, 0),
warnings: report.reduce((sum: number, f) => sum + f.messages.filter((m) => m.severity === 1).length, 0),
timestamp: new Date().toISOString()
}
console.log(JSON.stringify(summary, null, 2))

View File

@@ -3,6 +3,13 @@
// Parse npm audit JSON and format results
import { execSync } from 'child_process'
interface NpmVulnerability {
name: string
severity: string
version: string
fixAvailable: boolean
}
try {
const audit = JSON.parse(execSync('npm audit --json 2>/dev/null || echo "{}"', { encoding: 'utf8' }))
@@ -15,12 +22,15 @@ try {
info: audit.metadata?.vulnerabilities?.info || 0
},
totalVulnerabilities: audit.metadata?.totalVulnerabilities || 0,
advisories: Object.values(audit.vulnerabilities || {}).map((v: any) => ({
name: v.name,
severity: v.severity,
version: v.version,
fixAvailable: v.fixAvailable
})),
advisories: Object.values(audit.vulnerabilities || {}).map((v) => {
const vuln = v as NpmVulnerability
return {
name: vuln.name,
severity: vuln.severity,
version: vuln.version,
fixAvailable: vuln.fixAvailable
}
}),
timestamp: new Date().toISOString()
}