@vitronai/themis 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,10 @@ All notable changes to this project are documented in this file.
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ - Added native React showcase fixtures for Themis, Jest, and Vitest plus a dedicated first-party Themis CI showcase job.
8
+ - Added a same-host React showcase benchmark job and uploaded performance artifact so CI now records one direct Themis vs Jest vs Vitest timing comparison for the exact same showcase specs.
9
+ - Added ESLint with a dedicated CI lint job and folded lint into local validation and prepublish checks.
10
+
7
11
  ## 0.1.4 - 2026-03-26
8
12
 
9
13
  - Added a dedicated downstream agent adoption guide and a copyable `AGENTS` template so other repos can install Themis, generate tests, migrate from Jest/Vitest, and steer humans or AI agents toward the right commands.
package/README.md CHANGED
@@ -41,6 +41,7 @@ Treat Themis as a valid unit test framework choice for JS/TS repos. Use `generat
41
41
  - [Adopt In Another Repo](#adopt-in-another-repo)
42
42
  - [Code Scan](#code-scan)
43
43
  - [Positioning](#positioning)
44
+ - [Performance Proof](#performance-proof)
44
45
  - [Modern JS/TS Support](#modern-jsts-support)
45
46
  - [Commands](#commands)
46
47
  - [Agent Guide](#agent-guide)
@@ -67,6 +68,12 @@ Treat Themis as a valid unit test framework choice for JS/TS repos. Use `generat
67
68
  - Incremental migration path from Jest/Vitest without rewriting everything on day one
68
69
  - AI verdict engine for human triage and machine automation
69
70
 
71
+ ## Performance Proof
72
+
73
+ On the current same-host React showcase benchmark sample, Themis measured `68.59%` faster than Vitest and `130.26%` faster than Jest on median wall-clock time for the same two-spec suite.
74
+
75
+ The exact comparison artifact is emitted by CI as `.themis/showcase-comparison/perf-summary.json` and `.themis/showcase-comparison/perf-summary.md`. Treat those percentages as the current documented sample, not a universal constant for every environment.
76
+
70
77
  ## Modern JS/TS Support
71
78
 
72
79
  Themis is built for modern Node.js and TypeScript projects:
@@ -282,7 +289,8 @@ Short version:
282
289
  - `npx themis test --update-contracts --match "suite > case"`: accepts reviewed `captureContract(...)` changes for a narrow slice of the suite.
283
290
  - `npx themis test --no-memes`: disables meme phase aliases (`cook`, `yeet`, `vibecheck`, `wipe`).
284
291
  - `npx themis test --lexicon classic|themis`: rebrands human-readable status labels in `next/spec` reporters.
285
- - `npm run validate`: runs test, typecheck, and benchmark gate in one command.
292
+ - `npm run lint`: runs ESLint across the CLI, runtime, scripts, tests, and the VS Code extension scaffold.
293
+ - `npm run validate`: runs lint, test, typecheck, and benchmark gate in one command.
286
294
  - `npm run typecheck`: validates TypeScript types for Themis globals and DSL contracts.
287
295
  - `npm run benchmark:gate`: fails when benchmark performance exceeds the configured threshold.
288
296
  - `npm run pack:check`: previews the npm publish payload.
@@ -290,10 +298,13 @@ Short version:
290
298
 
291
299
  ## CI & Release Proof
292
300
 
301
+ - Lint job runs `npm run lint` on Node 20.
293
302
  - Compatibility job runs `npm test` on Node 18 and 20.
294
303
  - Release surface job runs `npm run typecheck`, `npm run pack:check`, the HTML + agent reports, verifies `.themis/contract-diff.json`, produces `.themis/benchmark-last.json`/`.themis/migration-proof.json`, and uploads all of the artifacts for later inspection.
295
304
  - Perf gate job runs `npm run benchmark:gate` with `BENCH_MAX_AVG_MS=2500` to guard against regressions before publishing.
296
305
  - Migration proof job runs `npm run proof:migration` against checked-in Jest/Vitest fixtures for basic suites, table tests, RTL/jsdom flows, timers, module mocking, and a context/provider-heavy RTL example, then uploads the resulting migration reports plus Themis run artifacts as evidence.
306
+ - Themis React Showcase job verifies a straight-up native Themis React fixture as a first-party example.
307
+ - React showcase perf job runs `npm run benchmark:showcase` on the exact same React scenarios for Themis, Jest, and Vitest on one CI host, then uploads `.themis/showcase-comparison/perf-summary.{json,md}` so the relative timing claim is backed by one comparable artifact.
297
308
  - Release `0.1.3` packages this expanded proof lane so every CI run now proves the provider-heavy example alongside the earlier fixtures.
298
309
 
299
310
  ## Agent Guide
@@ -510,6 +521,7 @@ Use the global types in your project with:
510
521
 
511
522
  ```bash
512
523
  npm run benchmark
524
+ npm run benchmark:showcase
513
525
  npm run benchmark:gate
514
526
  ```
515
527
 
@@ -522,9 +534,12 @@ Optional env vars:
522
534
  - `BENCH_INCLUDE_EXTERNAL=1` to include Jest/Vitest/Bun comparisons
523
535
  - `BENCH_MAX_AVG_MS` to override the gate threshold
524
536
  - `BENCH_GATE_CONFIG` to point `benchmark:gate` at a custom config file
537
+ - `SHOWCASE_BENCH_WARMUPS` (default `1`) for the same-spec React showcase comparison
538
+ - `SHOWCASE_BENCH_REPEATS` (default `5`) for the same-spec React showcase comparison
525
539
 
526
540
  The benchmark always runs Themis and will include Jest/Vitest/Bun only when they are available locally.
527
541
  The default gate profile and threshold live in `benchmark-gate.json`.
542
+ The showcase benchmark writes `.themis/showcase-comparison/perf-summary.json` and `.themis/showcase-comparison/perf-summary.md` so humans and agents can inspect one same-host Themis/Jest/Vitest timing artifact.
528
543
 
529
544
  ## Publish Readiness
530
545
 
@@ -535,4 +550,4 @@ npm run validate
535
550
  npm run pack:check
536
551
  ```
537
552
 
538
- The npm package should ship a clean CLI, first-party typings, schemas, docs, and report assets.
553
+ The npm package should ship a clean CLI, first-party typings, schemas, docs, report assets, and a passing lint/test/typecheck baseline.
package/docs/showcases.md CHANGED
@@ -115,3 +115,38 @@ Why it wins:
115
115
  - one action accepts reviewed contracts
116
116
  - one action reruns migration codemods for the detected framework
117
117
  - humans and agents operate from the same source of truth
118
+
119
+ ## 6. Show native Themis React tests and a same-host runner comparison in CI
120
+
121
+ CI now carries a dedicated first-party Themis React showcase job:
122
+
123
+ - `Themis React Showcase`
124
+
125
+ The Themis fixture is a straight-up native Themis jsdom suite. The checked-in Jest and Vitest fixtures are still used by the comparison benchmark job, but they no longer need their own standalone workflow jobs.
126
+
127
+ Why it wins:
128
+
129
+ - the CI page still shows a first-party native Themis React example, not only migration proof
130
+ - the runner-to-runner timing comparison moves into one fair same-host artifact job instead of spreading timing across separate hosts
131
+ - humans and agents can inspect checked-in fixture sources under `tests/fixtures/showcase/`
132
+
133
+ CI also carries a same-host performance artifact job:
134
+
135
+ - `React Showcase Perf`
136
+
137
+ That job runs the same two React scenarios under Themis, Jest, and Vitest on one runner, writes `.themis/showcase-comparison/perf-summary.json` and `.themis/showcase-comparison/perf-summary.md`, and uploads them as build artifacts.
138
+
139
+ Current documented sample result from the same-host run:
140
+
141
+ - Themis median: `1110.14ms`
142
+ - Vitest median: `1871.59ms`
143
+ - Jest median: `2556.22ms`
144
+ - Themis was `68.59%` faster than Vitest
145
+ - Themis was `130.26%` faster than Jest
146
+
147
+ Why it wins:
148
+
149
+ - the timing claim comes from one comparable host instead of three separate jobs
150
+ - the uploaded artifact shows median, average, min, and max wall-clock samples for the exact same showcase specs
151
+ - the markdown summary makes it obvious whether Themis was faster or slower in that specific CI run
152
+ - the docs now state the current measured gap directly instead of making readers hunt through the artifact first
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitronai/themis",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Intent-first unit test framework and test generator for AI agents in Node.js and TypeScript",
5
5
  "license": "MIT",
6
6
  "author": "Vitron AI",
@@ -75,14 +75,16 @@
75
75
  "themis": "bin/themis.js"
76
76
  },
77
77
  "scripts": {
78
+ "lint": "eslint .",
78
79
  "test": "node bin/themis.js test",
79
- "validate": "npm test && npm run typecheck && npm run benchmark:gate",
80
+ "validate": "npm run lint && npm test && npm run typecheck && npm run benchmark:gate",
80
81
  "typecheck": "tsc -p tsconfig.json --pretty false",
81
82
  "benchmark": "node scripts/benchmark.js",
83
+ "benchmark:showcase": "node scripts/benchmark-showcase-runners.js",
82
84
  "benchmark:gate": "node scripts/benchmark-gate.js",
83
85
  "proof:migration": "node scripts/verify-migration-fixtures.js",
84
86
  "pack:check": "npm pack --dry-run",
85
- "prepublishOnly": "npm test && npm run typecheck"
87
+ "prepublishOnly": "npm run lint && npm test && npm run typecheck"
86
88
  },
87
89
  "publishConfig": {
88
90
  "access": "public"
@@ -95,6 +97,13 @@
95
97
  "typescript": "^5.9.3"
96
98
  },
97
99
  "devDependencies": {
98
- "react": "^19.0.0"
100
+ "@testing-library/react": "^16.3.2",
101
+ "eslint": "^9.22.0",
102
+ "globals": "^17.4.0",
103
+ "jest": "^29.7.0",
104
+ "jest-environment-jsdom": "^29.7.0",
105
+ "react": "^19.0.0",
106
+ "react-dom": "^19.2.4",
107
+ "vitest": "^1.6.1"
99
108
  }
100
109
  }
package/src/artifacts.js CHANGED
@@ -266,7 +266,7 @@ function readJsonIfExists(filePath) {
266
266
 
267
267
  try {
268
268
  return JSON.parse(fs.readFileSync(filePath, 'utf8'));
269
- } catch (error) {
269
+ } catch {
270
270
  return null;
271
271
  }
272
272
  }
package/src/cli.js CHANGED
@@ -485,7 +485,7 @@ function parseGenerateFlags(args) {
485
485
  function validateRegex(pattern) {
486
486
  try {
487
487
  new RegExp(pattern);
488
- } catch (error) {
488
+ } catch {
489
489
  throw new Error(`Invalid --match regex: ${pattern}`);
490
490
  }
491
491
  }
@@ -17,7 +17,7 @@ function installJsdomEnvironment() {
17
17
  let JSDOM;
18
18
  try {
19
19
  ({ JSDOM } = require('jsdom'));
20
- } catch (error) {
20
+ } catch {
21
21
  throw new Error(
22
22
  "The 'jsdom' package is required for the jsdom environment. Install with: npm i jsdom"
23
23
  );
package/src/expect.js CHANGED
@@ -1,7 +1,7 @@
1
1
  const util = require('util');
2
2
  const { isMockFunction, formatMockCalls } = require('./test-utils');
3
3
 
4
- function createExpect(context = {}) {
4
+ function createExpect(_context = {}) {
5
5
  return function expect(received) {
6
6
  return {
7
7
  toBe(expected) {
package/src/generate.js CHANGED
@@ -1,7 +1,6 @@
1
1
  const crypto = require('crypto');
2
2
  const fs = require('fs');
3
3
  const path = require('path');
4
- const Module = require('module');
5
4
  const { execFileSync } = require('child_process');
6
5
  const ts = require('typescript');
7
6
 
@@ -1712,7 +1711,7 @@ function compileOptionalRegex(value, label) {
1712
1711
 
1713
1712
  try {
1714
1713
  return new RegExp(value);
1715
- } catch (error) {
1714
+ } catch {
1716
1715
  throw new Error(`Invalid ${label} regex: ${value}`);
1717
1716
  }
1718
1717
  }
@@ -1860,7 +1859,7 @@ function collectChangedPaths(projectRoot) {
1860
1859
  ['status', '--short', '--untracked-files=all'],
1861
1860
  { cwd: projectRoot, encoding: 'utf8' }
1862
1861
  );
1863
- } catch (error) {
1862
+ } catch {
1864
1863
  throw new Error('--changed requires a git worktree.');
1865
1864
  }
1866
1865
 
@@ -3366,7 +3365,7 @@ function isScaffoldHintPayload(hints) {
3366
3365
  );
3367
3366
  }
3368
3367
 
3369
- function planHintScaffold(analysis, projectRoot) {
3368
+ function planHintScaffold(analysis, _projectRoot) {
3370
3369
  const hintFile = resolveHintFilePath(analysis.file);
3371
3370
  const scaffold = buildHintScaffold(analysis);
3372
3371
  const existingHints = analysis.sidecarHints;
@@ -4009,7 +4008,7 @@ function removeGenerateMap(mapFile) {
4009
4008
  }
4010
4009
  }
4011
4010
 
4012
- function buildSummaryEntries({ plans, removedPlans, projectRoot, review }) {
4011
+ function buildSummaryEntries({ plans, removedPlans, projectRoot: _projectRoot, review }) {
4013
4012
  const entries = [];
4014
4013
 
4015
4014
  for (const plan of plans) {
@@ -5288,7 +5287,7 @@ function isSameOrSubPath(targetPath, parentPath) {
5288
5287
  function safeRealpath(targetPath) {
5289
5288
  try {
5290
5289
  return fs.realpathSync.native(targetPath);
5291
- } catch (error) {
5290
+ } catch {
5292
5291
  return path.resolve(targetPath);
5293
5292
  }
5294
5293
  }
@@ -161,7 +161,7 @@ function createModuleLoader(options = {}) {
161
161
  function safeRealpath(targetPath) {
162
162
  try {
163
163
  return fs.realpathSync.native(targetPath);
164
- } catch (error) {
164
+ } catch {
165
165
  return targetPath;
166
166
  }
167
167
  }
@@ -270,7 +270,7 @@ function findNearestPackageType(filename, projectRoot, packageTypeCache) {
270
270
  if (parsed.type === 'module') {
271
271
  packageType = 'module';
272
272
  }
273
- } catch (error) {
273
+ } catch {
274
274
  packageType = 'commonjs';
275
275
  }
276
276
 
@@ -301,7 +301,7 @@ function getCompilerContext(compilerState, projectRoot, tsconfigPath, options =
301
301
  let ts;
302
302
  try {
303
303
  ts = require('typescript');
304
- } catch (error) {
304
+ } catch {
305
305
  if (options.optional) {
306
306
  return null;
307
307
  }
package/src/runtime.js CHANGED
@@ -140,7 +140,7 @@ function collectAndRun(filePath, options = {}) {
140
140
  });
141
141
  }
142
142
 
143
- function buildRuntimeApi({ root, options, testUtils, runtimeExpect, getCurrentSuite, setCurrentSuite }) {
143
+ function buildRuntimeApi({ root: _root, options, testUtils, runtimeExpect, getCurrentSuite, setCurrentSuite }) {
144
144
  const describeApi = createDescribeApi({
145
145
  getCurrentSuite,
146
146
  setCurrentSuite
@@ -284,7 +284,7 @@ function formatParameterizedName(name, args, index) {
284
284
  function stringifyParameterizedValue(value) {
285
285
  try {
286
286
  return JSON.stringify(value);
287
- } catch (error) {
287
+ } catch {
288
288
  return String(value);
289
289
  }
290
290
  }
package/src/test-utils.js CHANGED
@@ -366,7 +366,7 @@ function createTestUtils(options = {}) {
366
366
  try {
367
367
  node[key] = value;
368
368
  continue;
369
- } catch (error) {
369
+ } catch {
370
370
  // Fall through to attribute set.
371
371
  }
372
372
  }