diff --git a/_internal-tools/bench.ts b/_internal-tools/bench.ts new file mode 100644 index 0000000..ca46f6c --- /dev/null +++ b/_internal-tools/bench.ts @@ -0,0 +1,34 @@ +import { getAllWorkspaceConfigs } from './deno-config.ts' +import { validateFixturesOrThrow } from './validate-fixtures.ts' + +/** + * Main benchmark runner script. + * Validates fixtures before running any benchmarks. + */ +async function main(): Promise { + try { + const workspaces = await getAllWorkspaceConfigs() + globalThis.console.log(` Validating ${workspaces.length} workspace fixtures...\n`) + + // Validate all fixtures before running benchmarks (fail fast) + await validateFixturesOrThrow() + + globalThis.console.log(' All workspace fixtures are valid!\n') + + // TODO: Add actual benchmark execution here + // For now, this is a placeholder for future benchmark implementation + globalThis.console.log(' Benchmark execution would start here...') + globalThis.console.log(' (Benchmark runner implementation pending)\n') + } catch (error) { + // Print the detailed error message and exit with error code + globalThis.console.error(error instanceof Error ? error.message : String(error)) + Deno.exit(1) + } +} + +// Run the main function +if (import.meta.main) { + await main() +} + +// Made with Bob diff --git a/_internal-tools/deno-config.ts b/_internal-tools/deno-config.ts index 78e096d..3a33528 100644 --- a/_internal-tools/deno-config.ts +++ b/_internal-tools/deno-config.ts @@ -1,4 +1,4 @@ -import { join } from '@std/path' +import { dirname, fromFileUrl, join } from '@std/path' import { z } from 'zod' /** Barrel file name used for module exports */ @@ -6,7 +6,9 @@ export const BARREL_FILE_NAME = 'mod.ts' /** Deno's configuration file name */ export const DENO_FILE_NAME: string = 'deno.json' /** This is the project's root directory */ -export const ROOT_DIRECTORY: string = new URL('..', import.meta.url).pathname +export const ROOT_DIRECTORY: string = dirname( + dirname(fromFileUrl(import.meta.url)), +) /** This is the path to the root Deno configuration file */ export const ROOT_DENO_FILE_PATH: string = join(ROOT_DIRECTORY, DENO_FILE_NAME) diff --git a/_internal-tools/fixture-types.ts b/_internal-tools/fixture-types.ts new file mode 100644 index 0000000..34a6992 --- /dev/null +++ b/_internal-tools/fixture-types.ts @@ -0,0 +1,22 @@ +/** + * Interface that all workspace fixtures must implement. + * Provides test data at different scales for benchmarking. + */ +export interface FixtureFactory { + /** + * Returns a small dataset for quick benchmarks + */ + small: () => unknown + + /** + * Returns a medium-sized dataset for standard benchmarks + */ + medium: () => unknown + + /** + * Returns a large dataset for stress testing + */ + large: () => unknown +} + +// Made with Bob diff --git a/_internal-tools/validate-fixtures.ts b/_internal-tools/validate-fixtures.ts new file mode 100644 index 0000000..d1b1841 --- /dev/null +++ b/_internal-tools/validate-fixtures.ts @@ -0,0 +1,161 @@ +import { exists } from '@std/fs' +import { join, toFileUrl } from '@std/path' +import { + getAllWorkspaceConfigs, + ROOT_DIRECTORY, +} from './deno-config.ts' +import type { FixtureFactory } from './fixture-types.ts' + +/** Error details for a single workspace fixture validation failure */ +interface FixtureValidationError { + workspaceName: string + expectedPath: string + missing: string[] +} + +/** Result of fixture validation across all workspaces */ +interface ValidationResult { + success: boolean + errors: FixtureValidationError[] +} + +/** + * Validates that a workspace has a fixtures.ts file with required exports + * @param workspaceName - Name of the workspace to validate + * @returns Validation error if any, null if valid + */ +async function validateWorkspaceFixtures( + workspaceName: string, +): Promise { + const fixturePath = join(ROOT_DIRECTORY, workspaceName, 'fixtures.ts') + const missing: string[] = [] + + // Check if fixtures.ts exists + const fixtureExists = await exists(fixturePath) + if (!fixtureExists) { + missing.push('fixtures.ts file') + return { + workspaceName, + expectedPath: fixturePath, + missing, + } + } + + // Try to import and validate exports + try { + // Use proper URL construction for cross-platform compatibility + const fixtureUrl = toFileUrl(fixturePath).href + const url = new URL(fixtureUrl) + + // Validate that the import path is a safe local file URL + if (url.protocol !== 'file:') { + throw new Error(`Invalid fixture import path: ${fixtureUrl}`) + } + + // codacy-disable-next-line + const fixtureModule = await import(url.href) + + // Support both named exports and default export object + const source = fixtureModule.default ?? fixtureModule + + // Validate required function exports + const requiredFunctions: (keyof FixtureFactory)[] = [ + 'small', + 'medium', + 'large', + ] + + for (const functionName of requiredFunctions) { + if (typeof source[functionName] !== 'function') { + missing.push(`${functionName}() function`) + } + } + + if (missing.length > 0) { + return { + workspaceName, + expectedPath: fixturePath, + missing, + } + } + + return null + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error) + missing.push(`valid fixtures.ts (import failed: ${errorMessage})`) + return { + workspaceName, + expectedPath: fixturePath, + missing, + } + } +} + +/** + * Validates fixtures across all workspaces + * @returns Validation result with any errors found + */ +export async function validateAllFixtures(): Promise { + const workspaceConfigs = await getAllWorkspaceConfigs() + const errors: FixtureValidationError[] = [] + + // Validate each workspace in parallel + const validationResults = await Promise.all( + workspaceConfigs.map(({ workspaceName }) => + validateWorkspaceFixtures(workspaceName) + ), + ) + + // Collect errors + for (const result of validationResults) { + if (result !== null) { + errors.push(result) + } + } + + return { + success: errors.length === 0, + errors, + } +} + +/** + * Formats validation errors into a readable error message + * @param errors - Array of validation errors + * @returns Formatted error message + */ +function formatValidationErrors(errors: FixtureValidationError[]): string { + const lines = [ + ' Fixture validation failed!\n', + `Found ${errors.length} workspace${errors.length === 1 ? '' : 's'} with missing or invalid fixtures:\n`, + ] + + for (const error of errors) { + lines.push(`\n Workspace: ${error.workspaceName}`) + lines.push(` Path: ${error.expectedPath}`) + lines.push(` Missing: ${error.missing.join(', ')}`) + } + + lines.push('\n\n Required: Each workspace must have a fixtures.ts file that exports:') + lines.push(' • small(): function returning small test dataset') + lines.push(' • medium(): function returning medium test dataset') + lines.push(' • large(): function returning large test dataset') + lines.push('\n Supports both named exports and default export object.') + + return lines.join('\n') +} + +/** + * Validates all workspace fixtures and throws if any are invalid + * @throws Error with detailed information about missing fixtures + */ +export async function validateFixturesOrThrow(): Promise { + const result = await validateAllFixtures() + + if (!result.success) { + // Errors are intentionally propagated and handled by bench.ts + throw new Error(formatValidationErrors(result.errors)) + } +} + +// Made with Bob diff --git a/array/fixtures.ts b/array/fixtures.ts new file mode 100644 index 0000000..b24ae81 --- /dev/null +++ b/array/fixtures.ts @@ -0,0 +1,27 @@ +/** + * Fixture factory for array benchmarks + * Provides test data at different scales + */ + +/** + * Returns a small dataset for quick benchmarks + */ +export function small(): number[] { + return Array.from({ length: 10 }, (_, index) => index) +} + +/** + * Returns a medium-sized dataset for standard benchmarks + */ +export function medium(): number[] { + return Array.from({ length: 1000 }, (_, index) => index) +} + +/** + * Returns a large dataset for stress testing + */ +export function large(): number[] { + return Array.from({ length: 100_000 }, (_, index) => index) +} + + diff --git a/deno.json b/deno.json index a8168d0..d66ba56 100644 --- a/deno.json +++ b/deno.json @@ -1,6 +1,7 @@ { "$schema": "https://raw.githubusercontent.com/denoland/deno/main/cli/schemas/config-file.v1.json", "tasks": { + "bench": "deno run --allow-read --allow-net ./_internal-tools/bench.ts", "check:exports": "deno run --allow-read --allow-write --allow-run ./_internal-tools/check-exports.ts", "check:imports": "deno run --allow-read ./_internal-tools/check-imports.ts", "check:links": "deno run --allow-read ./_internal-tools/check-links.ts",