taist 0.1.0

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/.taistrc.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "include": ["examples/**/*.js", "src/**/*.js", "src/**/*.ts", "lib/**/*.js"],
3
+ "exclude": ["**/node_modules/**", "**/*.test.*", "**/*.spec.*", "**/taist/lib/**"],
4
+ "depth": 3,
5
+ "format": "toon",
6
+ "trace": {
7
+ "enabled": false,
8
+ "depth": 2
9
+ },
10
+ "watch": {
11
+ "ignore": ["node_modules", ".git", "dist", "build"],
12
+ "delay": 500
13
+ },
14
+ "output": {
15
+ "abbreviate": true,
16
+ "maxTokens": 1000
17
+ }
18
+ }
package/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Taist
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,471 @@
1
+ # Taist - AI Test Runner
2
+ ## Token-Optimized Testing Framework for AI-Assisted Development
3
+
4
+ Version: 0.1.0 | January 2025 | [Technical Specification](./SPEC.md)
5
+
6
+ ---
7
+
8
+ ## Table of Contents
9
+ 1. [Why Taist?](#why-taist)
10
+ 2. [Execution Tree Output](#execution-tree-output)
11
+ 3. [Quick Start](#quick-start)
12
+ 4. [Integration Methods](#integration-methods)
13
+ 5. [Test Integration](#test-integration)
14
+ 6. [Configuration Reference](#configuration-reference)
15
+ 7. [Usage Examples](#usage-examples)
16
+
17
+ ---
18
+
19
+ ## Why Taist?
20
+
21
+ Taist solves two critical problems when using LLMs for development and testing:
22
+
23
+ ### 1. Token Reduction (90%)
24
+
25
+ Traditional test output wastes tokens on verbose formatting, redundant stack traces, and decorative elements. Taist compresses output using TOON (Token-Optimized Output Notation):
26
+
27
+ **Traditional Output (450 tokens)**
28
+ ```
29
+ FAIL test/calculator.test.js > Calculator > should add two numbers
30
+ AssertionError: expected 5 to be 6
31
+ Expected: 6
32
+ Received: 5
33
+ at /Users/dev/project/test/calculator.test.js:15:23
34
+ at processTicksAndRejections (node:internal/process/task_queues:95:5)
35
+ at Module._compile (node:internal/modules/cjs/loader:1376:14)
36
+ at Module._extensions..js (node:internal/modules/cjs/loader:1435:10)
37
+ ```
38
+
39
+ **TOON Output (45 tokens)**
40
+ ```
41
+ ✗ calc.add
42
+ @test:15
43
+ exp:6 got:5
44
+ path:add(2,3)→5
45
+ ```
46
+
47
+ ### 2. Execution Visibility Without Code Changes
48
+
49
+ Instead of littering your code with `console.log` statements, Taist automatically traces function calls, arguments, return values, and errors. This gives LLMs the context they need to debug issues.
50
+
51
+ ---
52
+
53
+ ## Execution Tree Output
54
+
55
+ The execution tree is Taist's key debugging feature. It shows the complete call hierarchy with timing, arguments, return values, and errors - all without modifying your source code.
56
+
57
+ ### Example Output
58
+
59
+ ```
60
+ ===TESTS: 5/12===
61
+
62
+ FAILURES:
63
+ ✗ Order Creation > should create order with valid data
64
+ @order.spec.ts:45
65
+ expected 500 to be 200 // Object.is equality
66
+ exp: "200"
67
+ got: "500"
68
+
69
+ TRACE:
70
+ fn:Route.POST /order/create ms:245 args:[{email:"test@..."}] ret:{status:500}
71
+ fn:OrderService.createOrder ms:180 ret:{status:"error"}
72
+ fn:ValidationService.validate ms:10 err:Invalid email format
73
+ fn:AllocationService.allocate ms:45 (not called - previous error)
74
+ fn:StripeService.createPaymentIntent ms:0 (not called)
75
+ ```
76
+
77
+ ### Reading the Trace
78
+
79
+ | Field | Meaning |
80
+ |-------|---------|
81
+ | `fn:` | Function name (Module.method format) |
82
+ | `ms:` | Execution duration in milliseconds |
83
+ | `args:` | Function arguments (truncated for readability) |
84
+ | `ret:` | Return value (truncated) |
85
+ | `err:` | Error message (if function threw) |
86
+
87
+ ### Depth-Based Indentation
88
+
89
+ Indentation reveals the call hierarchy:
90
+ - **No indent**: Entry point (e.g., route handler)
91
+ - **2 spaces**: Called by entry point
92
+ - **4 spaces**: Nested call
93
+ - And so on...
94
+
95
+ In the example above, you can immediately see that:
96
+ 1. The route handler called `OrderService.createOrder`
97
+ 2. Which called `ValidationService.validate`
98
+ 3. Which threw "Invalid email format"
99
+ 4. This caused `AllocationService.allocate` and `StripeService.createPaymentIntent` to never be called
100
+
101
+ **This gives LLMs exactly the context they need to fix the bug.**
102
+
103
+ ---
104
+
105
+ ## Quick Start
106
+
107
+ ### Option 1: ESM Loader (Recommended)
108
+ ```bash
109
+ # Run any Node.js app with automatic tracing
110
+ node --import taist/module-patcher your-app.js
111
+ ```
112
+
113
+ ### Option 2: Manual Instrumentation
114
+ ```javascript
115
+ // Add at the top of your service
116
+ import 'taist/instrument';
117
+ import { instrumentService } from 'taist/instrument';
118
+
119
+ const myService = instrumentService(new MyService(), 'MyService');
120
+ ```
121
+
122
+ ### Option 3: Programmatic API
123
+ ```javascript
124
+ import { ServiceTracer } from 'taist';
125
+
126
+ const tracer = new ServiceTracer({ enabled: true, depth: 3 });
127
+ tracer.instrument(MyClass, 'MyClass');
128
+ ```
129
+
130
+ ---
131
+
132
+ ## Integration Methods
133
+
134
+ | Method | Use Case | Setup |
135
+ |--------|----------|-------|
136
+ | **ESM Loader** | Node.js apps, automatic tracing | `node --import taist/module-patcher app.js` |
137
+ | **Import-based** | Express apps, selective tracing | `import 'taist/instrument'` |
138
+ | **Programmatic** | Full control, multiple tracers | `new ServiceTracer()` |
139
+
140
+ ### ESM Loader Integration (Recommended)
141
+
142
+ The ESM Loader provides automatic instrumentation for Node.js applications without requiring code changes. Configure which modules to trace via `.taistrc.json`.
143
+
144
+ ```bash
145
+ # Run any Node.js app with automatic tracing
146
+ node --import taist/module-patcher your-app.js
147
+
148
+ # With environment variables
149
+ TAIST_ENABLED=true TAIST_DEPTH=3 node --import taist/module-patcher your-app.js
150
+
151
+ # Debug mode (shows what's being instrumented)
152
+ TAIST_DEBUG=1 node --import taist/module-patcher your-app.js
153
+ ```
154
+
155
+ **Configuration (`.taistrc.json`):**
156
+ ```json
157
+ {
158
+ "include": ["src/**/*.js", "services/**/*.js"],
159
+ "exclude": ["**/node_modules/**", "**/*.test.js"],
160
+ "depth": 3
161
+ }
162
+ ```
163
+
164
+ **When to use:**
165
+ - Node.js applications (v18.19+ or v20.6+)
166
+ - Quick debugging without code changes
167
+ - Development and testing environments
168
+
169
+ ### Import-based Instrumentation
170
+
171
+ For Express apps or when you want explicit control without CLI flags:
172
+
173
+ ```javascript
174
+ // Add at the top of your entry point
175
+ import 'taist/instrument';
176
+ import { instrumentExpress, instrumentService } from 'taist/instrument';
177
+ import express from 'express';
178
+
179
+ // Instrument service classes
180
+ class UserService {
181
+ async createUser(data) { /* ... */ }
182
+ async getUser(id) { /* ... */ }
183
+ }
184
+
185
+ const userService = instrumentService(new UserService(), 'UserService');
186
+
187
+ // Instrument Express app
188
+ const app = express();
189
+ instrumentExpress(app);
190
+
191
+ // Routes are automatically traced
192
+ app.get('/users/:id', async (req, res) => {
193
+ const user = await userService.getUser(req.params.id);
194
+ res.json(user);
195
+ });
196
+ ```
197
+
198
+ **Run with tracing:**
199
+ ```bash
200
+ TAIST_ENABLED=true node server.js
201
+ ```
202
+
203
+ **When to use:**
204
+ - Express/Fastify applications
205
+ - Gradual adoption into existing projects
206
+ - When you can't use `--import` flag
207
+
208
+ ### Programmatic API
209
+
210
+ For full control over tracing configuration:
211
+
212
+ ```javascript
213
+ import { ServiceTracer } from 'taist';
214
+
215
+ // Create tracer with explicit configuration
216
+ const tracer = new ServiceTracer({
217
+ enabled: true,
218
+ depth: 3,
219
+ outputFormat: 'toon'
220
+ });
221
+
222
+ // Instrument classes
223
+ class UserService {
224
+ async getUser(id) { /* ... */ }
225
+ }
226
+
227
+ const userService = new UserService();
228
+ tracer.instrument(userService, 'UserService');
229
+
230
+ // Or wrap individual functions
231
+ const tracedFn = tracer.wrapMethod(myFunction, 'myFunction');
232
+ ```
233
+
234
+ **When to use:**
235
+ - Complex scenarios with multiple tracers
236
+ - Custom trace collection logic
237
+ - Maximum flexibility needed
238
+
239
+ ---
240
+
241
+ ## Test Integration
242
+
243
+ Use `TraceSession` to collect and display execution traces in your test suites. This provides visibility into what your code is doing during tests without modifying application code.
244
+
245
+ ### Vitest / Jest Integration
246
+
247
+ ```javascript
248
+ import { describe, it, beforeAll, afterAll } from 'vitest';
249
+ import { spawn } from 'child_process';
250
+ import { TraceSession } from 'taist/testing';
251
+
252
+ let session;
253
+ let serverProcess;
254
+
255
+ beforeAll(async () => {
256
+ // Start trace session
257
+ session = new TraceSession();
258
+ await session.start();
259
+
260
+ // Start your server with tracing enabled
261
+ serverProcess = spawn('node', ['server.js'], {
262
+ env: {
263
+ ...process.env,
264
+ ...session.getEnv(), // Adds TAIST_ENABLED and TAIST_COLLECTOR_SOCKET
265
+ PORT: '3000',
266
+ },
267
+ });
268
+
269
+ await waitForServer();
270
+ });
271
+
272
+ afterAll(async () => {
273
+ // Stop server
274
+ serverProcess?.kill('SIGTERM');
275
+
276
+ // Print collected traces and stop session
277
+ session.printTraces({ maxGroups: 5 });
278
+ await session.stop();
279
+ });
280
+
281
+ describe('API Tests', () => {
282
+ it('should create user', async () => {
283
+ const res = await fetch('http://localhost:3000/users', {
284
+ method: 'POST',
285
+ body: JSON.stringify({ name: 'Alice' }),
286
+ });
287
+ expect(res.status).toBe(201);
288
+ });
289
+ });
290
+ ```
291
+
292
+ ### TraceSession API
293
+
294
+ | Method | Description |
295
+ |--------|-------------|
296
+ | `start()` | Start the trace collector |
297
+ | `getEnv()` | Get environment variables for enabling tracing |
298
+ | `getTraces()` | Get collected trace objects |
299
+ | `printTraces(options)` | Format and print trace tree to console |
300
+ | `formatTraces(options)` | Format traces as string (without printing) |
301
+ | `stop()` | Stop the trace collector |
302
+
303
+ ### Print Options
304
+
305
+ ```javascript
306
+ session.printTraces({
307
+ maxGroups: 10, // Max request groups to show (default: 10)
308
+ showToon: true, // Also show TOON format summary (default: true)
309
+ toonLimit: 30, // Max traces for TOON output (default: 30)
310
+ });
311
+ ```
312
+
313
+ ### Example Output
314
+
315
+ When tests complete, you'll see the execution tree grouped by HTTP request:
316
+
317
+ ```
318
+ ============================================================
319
+ TRACE OUTPUT
320
+ ============================================================
321
+ Traces: 45 | Requests: 12
322
+
323
+ --- Route.POST /users ---
324
+ fn:Route.POST /users depth:0 45ms
325
+ fn:UserService.register depth:1 30ms
326
+ fn:UserService.validateEmail depth:2 5ms
327
+ fn:UserService._hashPassword depth:2 10ms
328
+
329
+ --- Route.GET /users/:id ---
330
+ fn:Route.GET /users/:id depth:0 12ms
331
+ fn:UserService.getUser depth:1 8ms
332
+ fn:Cache.get depth:2 2ms
333
+
334
+ ... and 10 more requests
335
+ ```
336
+
337
+ ---
338
+
339
+ ## Configuration Reference
340
+
341
+ ### Environment Variables
342
+
343
+ | Variable | Description | Default |
344
+ |----------|-------------|---------|
345
+ | `TAIST_ENABLED` | Enable/disable tracing | `true` (when loader used) |
346
+ | `TAIST_DEBUG` | Show internal taist operations | `false` |
347
+ | `TAIST_FORMAT` | Output format: `toon`, `json`, `compact` | `toon` |
348
+ | `TAIST_DEPTH` | Trace depth level (1-5) | `3` |
349
+ | `TAIST_INCLUDE` | Only trace modules matching patterns (comma-separated) | All files |
350
+ | `TAIST_EXCLUDE` | Skip modules matching patterns | `node_modules` |
351
+ | `TAIST_OUTPUT_FILE` | Write traces to file | stdout |
352
+ | `TAIST_OUTPUT_INTERVAL` | Output interval in ms | `30000` |
353
+ | `TAIST_SLOW_THRESHOLD` | Slow operation threshold in ms | `100` |
354
+
355
+ ### CLI Options
356
+
357
+ ```bash
358
+ taist test [options] # Run tests once
359
+ taist watch [options] # Run tests in watch mode
360
+ ```
361
+
362
+ | Option | Short | Description | Default |
363
+ |--------|-------|-------------|---------|
364
+ | `--file` | `-f` | Source file(s) to test | `./src` |
365
+ | `--test` | `-t` | Test file(s) to run | `./test` |
366
+ | `--format` | | Output format | `toon` |
367
+ | `--watch` | `-w` | Enable watch mode | `false` |
368
+ | `--trace` | | Enable execution tracing | `false` |
369
+ | `--depth` | `-d` | Trace depth level (1-5) | `2` |
370
+ | `--output` | `-o` | Output file path | `stdout` |
371
+
372
+ ### Output Formats
373
+
374
+ **TOON (Default)** - Token-optimized for AI consumption
375
+ ```
376
+ [TAIST] up:120s calls:5432 err:3
377
+ [SLOW] 12 ops >100ms
378
+ [TOP] getUser:234 createUser:123
379
+ ```
380
+
381
+ **JSON** - Structured for tooling
382
+ ```json
383
+ {
384
+ "stats": { "totalCalls": 5432, "totalErrors": 3 },
385
+ "traces": { "topFunctions": { "UserService.getUser": 234 } }
386
+ }
387
+ ```
388
+
389
+ **Compact** - One-line summaries for CI/CD
390
+
391
+ ---
392
+
393
+ ## Usage Examples
394
+
395
+ ### Basic Testing
396
+ ```bash
397
+ # Run all tests with TOON output
398
+ taist test
399
+
400
+ # Test specific files
401
+ taist test -f ./src/email.js -t ./test/email.test.js
402
+
403
+ # JSON output for tooling
404
+ taist test --format json > results.json
405
+
406
+ # With execution tracing
407
+ taist test --trace --depth 3
408
+ ```
409
+
410
+ ### With AI Tools
411
+
412
+ ```bash
413
+ # Iterative development with Claude Code
414
+ taist watch -f ./src -t ./test
415
+
416
+ # Pipe to AI tools
417
+ taist test --format toon | gh copilot explain
418
+
419
+ # Generate fix suggestions
420
+ taist test --trace | your-ai-tool analyze
421
+ ```
422
+
423
+ ### CI/CD Integration
424
+
425
+ ```yaml
426
+ # GitHub Actions
427
+ - name: Run AI-friendly tests
428
+ run: |
429
+ npm install -g taist
430
+ taist test --format compact
431
+
432
+ - name: Store detailed results on failure
433
+ if: failure()
434
+ run: taist test --trace --format json > test-results.json
435
+ ```
436
+
437
+ ### Integration with package.json
438
+
439
+ ```json
440
+ {
441
+ "scripts": {
442
+ "test": "vitest",
443
+ "test:ai": "taist test --format toon",
444
+ "test:watch": "taist watch",
445
+ "test:trace": "taist test --trace --depth 3"
446
+ }
447
+ }
448
+ ```
449
+
450
+ ---
451
+
452
+ ## Installation
453
+
454
+ ```bash
455
+ # Global installation
456
+ npm install -g taist
457
+
458
+ # Project installation
459
+ npm install --save-dev taist
460
+ ```
461
+
462
+ ---
463
+
464
+ ## License
465
+
466
+ MIT License - Open source and free for commercial use
467
+
468
+ ## Support
469
+
470
+ - Issues: https://github.com/taist/taist/issues
471
+ - Technical Specification: [SPEC.md](./SPEC.md)
package/index.js ADDED
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Taist - Token-Optimized Testing Framework
3
+ * Main programmatic API
4
+ */
5
+
6
+ import { VitestRunner } from './lib/vitest-runner.js';
7
+ import { OutputFormatter } from './lib/output-formatter.js';
8
+ import { WatchHandler } from './lib/watch-handler.js';
9
+ import { ExecutionTracer } from './lib/execution-tracer.js';
10
+ import { ToonFormatter } from './lib/toon-formatter.js';
11
+
12
+ /**
13
+ * Main Taist class for programmatic usage
14
+ */
15
+ export class Taist {
16
+ constructor(options = {}) {
17
+ this.options = {
18
+ format: options.format || 'toon',
19
+ trace: options.trace !== false,
20
+ depth: options.depth || 2,
21
+ ...options
22
+ };
23
+
24
+ this.tracer = new ExecutionTracer({
25
+ enabled: this.options.trace,
26
+ depth: this.options.depth
27
+ });
28
+
29
+ this.runner = new VitestRunner({
30
+ trace: {
31
+ enabled: this.options.trace,
32
+ depth: this.options.depth
33
+ },
34
+ tracer: this.tracer
35
+ });
36
+
37
+ this.formatter = new OutputFormatter({
38
+ format: this.options.format,
39
+ ...this.options.output
40
+ });
41
+
42
+ this.watchHandler = null;
43
+ }
44
+
45
+ /**
46
+ * Run tests once
47
+ * @param {Object} config - Test configuration
48
+ * @returns {Object} Test results
49
+ */
50
+ async run(config = {}) {
51
+ const results = await this.runner.run({
52
+ files: config.files || this.options.files,
53
+ tests: config.tests || this.options.tests
54
+ });
55
+
56
+ return results;
57
+ }
58
+
59
+ /**
60
+ * Format test results
61
+ * @param {Object} results - Test results
62
+ * @returns {string} Formatted output
63
+ */
64
+ format(results) {
65
+ return this.formatter.format(results);
66
+ }
67
+
68
+ /**
69
+ * Run tests and get formatted output
70
+ * @param {Object} config - Test configuration
71
+ * @returns {string} Formatted test output
72
+ */
73
+ async runAndFormat(config = {}) {
74
+ const results = await this.run(config);
75
+ return this.format(results);
76
+ }
77
+
78
+ /**
79
+ * Start watch mode
80
+ * @param {Object} config - Watch configuration
81
+ */
82
+ async watch(config = {}) {
83
+ if (this.watchHandler) {
84
+ throw new Error('Watch mode already started');
85
+ }
86
+
87
+ this.watchHandler = new WatchHandler({
88
+ ...this.options.watch,
89
+ ...config
90
+ });
91
+
92
+ const watchPaths = config.paths || this.options.paths || ['./src', './test'];
93
+
94
+ // Set up event listeners if callbacks provided
95
+ if (config.onChange) {
96
+ this.watchHandler.on('run-complete', ({ results }) => {
97
+ config.onChange(results);
98
+ });
99
+ }
100
+
101
+ if (config.onError) {
102
+ this.watchHandler.on('run-error', ({ error }) => {
103
+ config.onError(error);
104
+ });
105
+ }
106
+
107
+ await this.watchHandler.start(watchPaths, async () => {
108
+ return await this.run(config);
109
+ });
110
+
111
+ return this.watchHandler;
112
+ }
113
+
114
+ /**
115
+ * Stop watch mode
116
+ */
117
+ async stopWatch() {
118
+ if (this.watchHandler) {
119
+ await this.watchHandler.stop();
120
+ this.watchHandler = null;
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Get execution tracer
126
+ */
127
+ getTracer() {
128
+ return this.tracer;
129
+ }
130
+
131
+ /**
132
+ * Set output format
133
+ */
134
+ setFormat(format) {
135
+ this.formatter.setFormat(format);
136
+ }
137
+ }
138
+
139
+ // Named exports
140
+ export { VitestRunner } from './lib/vitest-runner.js';
141
+ export { OutputFormatter } from './lib/output-formatter.js';
142
+ export { WatchHandler } from './lib/watch-handler.js';
143
+ export { ExecutionTracer } from './lib/execution-tracer.js';
144
+ export { ToonFormatter } from './lib/toon-formatter.js';
145
+
146
+ // Default export
147
+ export default Taist;