From 82e41fbbc9f2a55b5e9cda716f5e8a4b8f209a7c Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 4 Jan 2026 13:16:27 +0000 Subject: [PATCH 1/3] chore: standardize metadata and README --- README.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0dc2a57..5acc2c2 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +> **Part of [Tuulbelt](https://github.com/tuulbelt/tuulbelt)** — A collection of zero-dependency tools. + # Structured Error Handler / `serr` [![Tests](https://github.com/tuulbelt/structured-error-handler/actions/workflows/test.yml/badge.svg)](https://github.com/tuulbelt/structured-error-handler/actions/workflows/test.yml) diff --git a/package.json b/package.json index 17b840c..ae13cee 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ }, "devDependencies": { "@tuulbelt/test-flakiness-detector": "git+https://github.com/tuulbelt/test-flakiness-detector.git", - "@types/node": "^20.0.0", + "@types/node": "^20.19.27", "tsx": "^4.7.0", "typescript": "^5.3.0" } From 83a02e7fbe79386fac6c36fd727052bd2cd00961 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 4 Jan 2026 14:26:02 +0000 Subject: [PATCH 2/3] chore: add benchmark framework with tatami-ng --- benchmarks/index.bench.ts | 93 +++++++++++++++++++++++++++++++++++++++ package.json | 2 + 2 files changed, 95 insertions(+) create mode 100644 benchmarks/index.bench.ts diff --git a/benchmarks/index.bench.ts b/benchmarks/index.bench.ts new file mode 100644 index 0000000..987548c --- /dev/null +++ b/benchmarks/index.bench.ts @@ -0,0 +1,93 @@ +#!/usr/bin/env node --import tsx +/** + * Structured Error Handler Benchmarks + * + * Measures performance of core operations using tatami-ng for statistical rigor. + * + * Run: npm run bench + * + * See: /docs/BENCHMARKING_STANDARDS.md + */ + +import { bench, baseline, group, run } from 'tatami-ng'; +import { StructuredError, ErrorContext, toJSON, fromJSON } from '../src/index.ts'; + +// Prevent dead code elimination +let result: StructuredError | string | ErrorContext; + +// Sample data for benchmarking +const simpleContext: ErrorContext = { operation: 'test' }; +const richContext: ErrorContext = { + operation: 'database.query', + userId: 'user-123', + requestId: 'req-456', + timestamp: Date.now(), + metadata: { table: 'users', query: 'SELECT * FROM users WHERE id = ?' }, +}; + +// ============================================================================ +// Core Operations Benchmarks +// ============================================================================ + +group('Error Creation', () => { + baseline('create: simple error', () => { + result = new StructuredError('Something went wrong', 'GENERIC_ERROR'); + }); + + bench('create: with simple context', () => { + result = new StructuredError('Operation failed', 'OP_FAILED', simpleContext); + }); + + bench('create: with rich context', () => { + result = new StructuredError('Database query failed', 'DB_ERROR', richContext); + }); + + bench('create: with cause chain', () => { + const cause = new Error('Connection timeout'); + result = new StructuredError('Database error', 'DB_ERROR', richContext, cause); + }); +}); + +group('Error Wrapping', () => { + const nativeError = new Error('Native error message'); + + baseline('wrap: native Error', () => { + result = StructuredError.wrap(nativeError); + }); + + bench('wrap: with context', () => { + result = StructuredError.wrap(nativeError, richContext); + }); + + bench('wrap: with code override', () => { + result = StructuredError.wrap(nativeError, richContext, 'WRAPPED_ERROR'); + }); +}); + +group('Serialization', () => { + const error = new StructuredError('Test error', 'TEST', richContext); + + baseline('toJSON: serialize', () => { + result = toJSON(error); + }); + + bench('fromJSON: deserialize', () => { + const json = toJSON(error); + result = fromJSON(json); + }); + + bench('round-trip: serialize + deserialize', () => { + const json = toJSON(error); + result = fromJSON(json); + }); +}); + +// ============================================================================ +// Run Benchmarks +// ============================================================================ + +await run({ + units: false, + silent: false, + json: false, +}); diff --git a/package.json b/package.json index ae13cee..ba77ad9 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "build": "tsc", "test": "node --import tsx --test test/index.test.ts", "test:watch": "node --import tsx --test --watch test/index.test.ts", + "bench": "node --import tsx benchmarks/index.bench.ts", "dogfood": "npm run dogfood:flaky && npm run dogfood:diff", "dogfood:flaky": "flaky --test 'npm test' --runs 10", "dogfood:diff": "npx tsx test/dogfood-diff.ts" @@ -37,6 +38,7 @@ "devDependencies": { "@tuulbelt/test-flakiness-detector": "git+https://github.com/tuulbelt/test-flakiness-detector.git", "@types/node": "^20.19.27", + "tatami-ng": "^0.8.0", "tsx": "^4.7.0", "typescript": "^5.3.0" } From 199b54aeecaf7d3901652c3e37c7afba5cbae5a8 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 4 Jan 2026 16:05:20 +0000 Subject: [PATCH 3/3] fix: update package-lock.json for tatami-ng --- package-lock.json | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index db5e3e8..f8e3341 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,8 @@ }, "devDependencies": { "@tuulbelt/test-flakiness-detector": "git+https://github.com/tuulbelt/test-flakiness-detector.git", - "@types/node": "^20.0.0", + "@types/node": "^20.19.27", + "tatami-ng": "^0.8.0", "tsx": "^4.7.0", "typescript": "^5.3.0" }, @@ -573,6 +574,16 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/peowly": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/peowly/-/peowly-1.3.2.tgz", + "integrity": "sha512-BYIrwr8JCXY49jUZscgw311w9oGEKo7ux/s+BxrhKTQbiQ0iYNdZNJ5LgagaeercQdFHwnR7Z5IxxFWVQ+BasQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.6.0" + } + }, "node_modules/resolve-pkg-maps": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", @@ -583,6 +594,22 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/tatami-ng": { + "version": "0.8.18", + "resolved": "https://registry.npmjs.org/tatami-ng/-/tatami-ng-0.8.18.tgz", + "integrity": "sha512-Q22ZpW/yPXP1Hb4e2s1JQcTtoMaVHZLCt8AjAyBjARiXcorgHyvuWyIPFJOvmrTglXU2qQPLqL+7HEE0tIHdiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "peowly": "^1.3.2" + }, + "bin": { + "tatami": "cli.js" + }, + "peerDependencies": { + "typescript": "^5.4.3" + } + }, "node_modules/tsx": { "version": "4.21.0", "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz",