Skip to main content

SMRT Project Requirements

Every SMRT project must meet these requirements to work correctly.

Required Files

1. vitest.config.ts (REQUIRED)

import { defineConfig } from 'vitest/config';
import { smrtVitestPlugin } from '@happyvertical/smrt-vitest';

export default defineConfig({
plugins: [smrtVitestPlugin()],
test: {
globals: true,
environment: 'node',
testTimeout: 60000,
fileParallelism: false,
pool: 'forks',
},
});

Why: The vitest plugin automatically generates manifests and loads cross-package dependencies. Without it, tests will fail with "unregistered class" or "No field metadata found" errors.

Note: do not add maxWorkersfileParallelism: false already pins the pool to a single worker and Vitest overrides any user-supplied value. Do not add singleFork either: it was poolOptions.forks.singleFork in Vitest 3 and does not exist in Vitest 4, and unknown keys are ignored rather than rejected.

Keep the default isolate: true. Disabling isolation shares one module registry across test files and makes them order-dependent, which is only worth it for unusually heavy packages and only after verifying that suite under randomized file order (--sequence.shuffle.files --sequence.seed=N). If a heavy package needs relief but depends on per-file isolation, keep isolate: true and raise the per-fork heap with NODE_OPTIONS=--max-old-space-size=... instead.

2. package.json Dependencies

Required:

  • @happyvertical/smrt-core - Core framework
  • @happyvertical/smrt-vitest (devDependency) - Test plugin
  • typescript >= 5.0
  • vitest >= 2.0
  • vite >= 5.0

Example:

{
"type": "module",
"dependencies": {
"@happyvertical/smrt-core": "workspace:*"
},
"devDependencies": {
"@happyvertical/smrt-vitest": "workspace:*",
"typescript": "^5.9.3",
"vitest": "^4.0.0",
"vite": "^6.0.0"
}
}

3. tsconfig.json

Required settings:

{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"composite": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"declaration": true,
"declarationMap": true,
"outDir": "dist"
}
}

Key requirements:

  • "module": "ESNext" or "NodeNext" - ESM is required
  • "moduleResolution": "bundler" or "NodeNext" - For proper module resolution
  • "strict": true - Type safety
  • "composite": true - For monorepo packages (enables project references)
  • "experimentalDecorators": true - Required for @smrt() decorator

For CLI and database configuration:

import type { SmrtConfig } from '@happyvertical/smrt-config';

export default {
packages: {
cli: {
database: {
type: 'sqlite',
url: './data/app.db'
}
}
}
} satisfies SmrtConfig;

Project Structure

my-smrt-project/
├── src/
│ ├── objects/ # SMRT object definitions
│ │ ├── index.ts # Re-exports all objects
│ │ └── *.ts # Classes with @smrt() decorator
│ └── __tests__/ # Test files
│ └── *.test.ts
├── .smrt/ # Generated (gitignored)
│ └── manifest.json # Auto-generated by vitest plugin
├── package.json
├── tsconfig.json
├── vitest.config.ts # REQUIRED - must include smrtVitestPlugin
└── smrt.config.ts # Optional - CLI/database config

Checklist

Before running tests, verify:

  • vitest.config.ts exists with smrtVitestPlugin()
  • @happyvertical/smrt-vitest in devDependencies
  • @happyvertical/smrt-core in dependencies
  • TypeScript strict mode enabled
  • ES modules configured ("type": "module" in package.json)
  • .smrt/ added to .gitignore
  • experimentalDecorators enabled in tsconfig.json

Running Tests

# Correct - vitest plugin handles manifest generation automatically
npx vitest
npx vitest run
npm test

# Deprecated - no longer needed (shows warning but still works)
smrt test

The vitest plugin automatically:

  1. Generates a manifest from your src/**/*.ts files
  2. Discovers and loads manifests from SMRT peer dependencies
  3. Registers all classes with the ObjectRegistry

Plugin Options

smrtVitestPlugin({
// Auto-generate manifest (default: true)
generateManifest: true,

// Source patterns to scan (default: ['src/**/*.ts'])
include: ['src/**/*.ts'],

// Patterns to exclude (default: ['**/*.d.ts', '**/node_modules/**', '**/dist/**'])
exclude: ['**/*.d.ts', '**/node_modules/**'],

// Additional packages to load manifests from
packages: ['@my-org/custom-smrt-package'],

// Enable verbose logging
verbose: true,
})

Common Issues

"No field metadata found" Error

Cause: Manifest not generated or vitest plugin not configured.

Fix: Ensure vitest.config.ts includes smrtVitestPlugin().

// vitest.config.ts
import { smrtVitestPlugin } from '@happyvertical/smrt-vitest';

export default defineConfig({
plugins: [smrtVitestPlugin()], // This line is required!
});

"Unregistered class" Error

Cause: Class not decorated with @smrt() or manifest stale.

Fix:

  1. Add @smrt() decorator to class:
    import { SmrtObject, smrt } from '@happyvertical/smrt-core';

    @smrt() // Required!
    class MyObject extends SmrtObject {
    // ...
    }
  2. Restart vitest (manifest regenerates on startup)

"Cannot find module '@happyvertical/smrt-core'" Error

Cause: Dependencies not installed or package.json misconfigured.

Fix:

  1. Run pnpm install (or npm/yarn)
  2. Verify @happyvertical/smrt-core is in dependencies
  3. Verify "type": "module" in package.json

Tests Pass Locally but Fail in CI

Cause: Different database behavior (SQLite vs PostgreSQL).

Fix: Use createIsolatedTestDb for database isolation:

import { createIsolatedTestDb } from '@happyvertical/smrt-vitest';

let db, cleanup;

beforeEach(async () => {
const result = await createIsolatedTestDb({ schema: '...' });
db = result.db;
cleanup = result.cleanup;
});

afterEach(async () => {
await cleanup();
});

Watch Mode Doesn't Pick Up New Classes

Current behavior: The manifest is generated once at vitest startup.

Fix: Restart vitest after adding new classes or fields.

Future enhancement: Watch mode manifest refresh is planned (see GitHub issues).

Migration from smrt test

If your project previously used smrt test:

  1. Add vitest.config.ts with smrtVitestPlugin() if not already present
  2. Remove smrt test or smrt generate:test from npm scripts (optional - still works)
  3. Update npm test script to use vitest run directly:
    {
    "scripts": {
    "test": "vitest run"
    }
    }
  4. Add .smrt/ to .gitignore if not present

The smrt test command still works but shows a deprecation warning.