@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 +4 -0
- package/README.md +17 -2
- package/docs/showcases.md +35 -0
- package/package.json +13 -4
- package/src/artifacts.js +1 -1
- package/src/cli.js +1 -1
- package/src/environment.js +1 -1
- package/src/expect.js +1 -1
- package/src/generate.js +5 -6
- package/src/module-loader.js +3 -3
- package/src/runtime.js +2 -2
- package/src/test-utils.js +1 -1
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
|
|
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
|
|
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.
|
|
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": "^
|
|
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
package/src/cli.js
CHANGED
package/src/environment.js
CHANGED
package/src/expect.js
CHANGED
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
|
|
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
|
|
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,
|
|
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
|
|
5290
|
+
} catch {
|
|
5292
5291
|
return path.resolve(targetPath);
|
|
5293
5292
|
}
|
|
5294
5293
|
}
|
package/src/module-loader.js
CHANGED
|
@@ -161,7 +161,7 @@ function createModuleLoader(options = {}) {
|
|
|
161
161
|
function safeRealpath(targetPath) {
|
|
162
162
|
try {
|
|
163
163
|
return fs.realpathSync.native(targetPath);
|
|
164
|
-
} catch
|
|
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
|
|
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
|
|
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
|
|
287
|
+
} catch {
|
|
288
288
|
return String(value);
|
|
289
289
|
}
|
|
290
290
|
}
|