mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-24 13:54:57 +00:00
fix(types): enhance type safety across various components and utilities
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
}))
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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[]
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
|
||||
@@ -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({}))
|
||||
|
||||
@@ -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' }),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user