Skip to content

Comments

feat: lvalue template expansion, project-wide lint, universal file input, multi-DB fromdb#98

Merged
benbernard merged 3 commits intomasterfrom
feature/lvalue-template-expansion
Feb 25, 2026
Merged

feat: lvalue template expansion, project-wide lint, universal file input, multi-DB fromdb#98
benbernard merged 3 commits intomasterfrom
feature/lvalue-template-expansion

Conversation

@benbernard
Copy link
Owner

Summary

  • Replace regex-based {{}} template expansion with lvalue/Proxy approach for reliable assignment support
  • Expand no-private lint to all src/tests and refactor # private fields to plain properties project-wide
  • Refactor dispatcher so all commands transparently support both stdin and file args (e.g. recs grep -e '...' file1.json file2.json)
  • Add PostgreSQL, MySQL, and Oracle backends to fromdb with dynamic imports and live integration tests (auto-skip on CI when DBs unavailable)

Test plan

  • 1689 tests pass, 12 skip, 0 fail
  • PostgreSQL/MySQL integration tests run live locally, skip gracefully in CI
  • All lint, typecheck, and pre-commit hooks pass

🤖 Generated with Claude Code

benbernard and others added 3 commits February 24, 2026 17:11
… fields

Refactor all JS private class fields (#) to plain properties and remove
TypeScript visibility modifiers (private/protected/public) project-wide.
Expand check-no-private.ts to scan all of src/ and tests/ instead of
just the explorer subdirectory.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All commands now transparently support both stdin and file arguments.
The dispatcher handles file reading so operations don't need to.
Transform ops (grep, eval, sort, etc.) now accept file args.
Removed duplicated file-reading logic from individual from* operations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add --type pg/mysql/oracle with dynamic imports for pg, mysql2, and
oracledb packages. PostgreSQL uses Unix socket by default when --host
is omitted (matching Perl PR #84). Add live integration tests for pg
and mysql that auto-skip when databases are unavailable (CI-safe).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions
Copy link

Performance Benchmark Results

⚠️ 8 regressions detected out of 103 benchmarks (threshold: 25%)

Benchmark Median Baseline Delta
Record.fromJSON — 10K lines 22.06ms 12.80ms +72.4% 🔴
KeySpec — nested key (address/zip) 914.5µs 558.1µs +63.9% 🔴
Compiled KeySpec.resolveValue — nested (address/zip) 296.0µs 163.1µs +81.5% 🔴
Compiled KeySpec.resolveValue — deep (address/coords/lat) 259.0µs 128.4µs +101.7% 🔴
Compiled KeySpec.resolveValue — array (tags/#0) 291.0µs 151.8µs +91.7% 🔴
chain — 5 ops (grep eval grep eval
Record.toString — 10K records 8.89ms 6.86ms +29.6% 🔴
bulk text + split — 100 lines 103.1µs 78.3µs +31.6% 🔴

103 benchmarks: 24 faster, 16 slower, 63 within noise (10%)

ℹ️ Note: Benchmarks are advisory-only. GitHub Actions shared runners have variable performance, so results may fluctuate ±25% between runs. For reliable benchmarking, run locally with bun run bench.

Full benchmark results

JSON Parsing

Benchmark Median Baseline Delta Throughput
Record.fromJSON — 100 lines 150.6µs 141.3µs +6.6% 663.88K rec/s
Record.fromJSON — 10K lines 22.06ms 12.80ms +72.4% 🔴 453.40K rec/s, 134.3 MB/s
InputStream.fromString — 100 records 229.8µs 209.7µs +9.6% 435.10K rec/s
InputStream.fromString — 10K records 18.38ms 17.88ms +2.8% 544.15K rec/s, 161.2 MB/s
JSON.parse baseline — 10K lines (no Record) 12.86ms 12.69ms +1.3% 777.70K rec/s, 230.3 MB/s
JSON.parse single array — 10K records 12.19ms 12.40ms -1.6% 820.26K rec/s, 242.9 MB/s

JSON Serialization

Benchmark Median Baseline Delta Throughput
Record.toString — 100 records 67.8µs 85.1µs -20.3% 🟢 1.47M rec/s
Record.toString — 10K records 8.29ms 6.86ms +20.7% 🔴 1.21M rec/s, 357.4 MB/s
Record.toJSON — 10K records 286.3µs 283.0µs +1.2% 34.93M rec/s
JSON.stringify baseline — 10K objects (no Record) 7.80ms 7.86ms -0.7% 1.28M rec/s, 379.5 MB/s
Batch join — 10K records (map+join) 8.20ms 8.53ms -3.9% 1.22M rec/s, 361.0 MB/s

KeySpec Access

Benchmark Median Baseline Delta Throughput
KeySpec — simple key (name) 259.0µs 214.5µs +20.8% 🔴 38.61M rec/s
KeySpec — nested key (address/zip) 914.5µs 558.1µs +63.9% 🔴 10.93M rec/s
KeySpec — deep nested (address/coords/lat) 1.07ms 1.08ms -1.3% 9.37M rec/s
KeySpec — array index (tags/#0) 900.3µs 906.0µs -0.6% 11.11M rec/s
Direct property access baseline (rec['name']) 71.6µs 75.6µs -5.3% 139.70M rec/s
Direct nested access baseline (rec.address.coords.lat) 98.7µs 93.2µs +5.9% 101.28M rec/s
KeySpec construction — cached (same spec 10K times) 289.3µs 338.7µs -14.6% 🟢 34.56M rec/s
KeySpec construction — unique specs (10K different) 2.35ms 2.33ms +0.8% 4.25M rec/s
Compiled KeySpec.resolveValue — nested (address/zip) 296.0µs 163.1µs +81.5% 🔴 33.78M rec/s
Compiled KeySpec.resolveValue — deep (address/coords/lat) 259.0µs 128.4µs +101.7% 🔴 38.61M rec/s
Compiled KeySpec.resolveValue — array (tags/#0) 291.0µs 151.8µs +91.7% 🔴 34.36M rec/s
Compiled KeySpec.setValue — nested (address/zip) 212.8µs 233.2µs -8.7% 46.99M rec/s

Core Operations

Benchmark Median Baseline Delta Throughput
grep — 10K records (r.age > 50) 409.3µs 442.6µs -7.5% 24.43M rec/s
grep — 10K records (string match) 366.3µs 430.1µs -14.8% 🟢 27.30M rec/s
eval — 10K records (add computed field) 1.39ms 2.04ms -31.9% 🟢 7.21M rec/s
xform — 10K records (push each record) 1.10ms 2.56ms -57.0% 🟢 9.07M rec/s
sort — 100 records (by score, numeric) 184.0µs 201.3µs -8.6% 543.61K rec/s
sort — 10K records (by score, numeric) 18.33ms 17.71ms +3.5% 545.53K rec/s
sort — 10K records (by name, lexical) 11.84ms 13.75ms -13.9% 🟢 844.76K rec/s
collate — 100 records (count by city) 405.4µs 456.1µs -11.1% 🟢 246.69K rec/s
collate — 10K records (count by city) 12.45ms 12.01ms +3.7% 803.05K rec/s
fromcsv — 10K rows (parse CSV to records) 14.46ms 14.31ms +1.1% 691.61K rec/s, 45.5 MB/s

Pipeline Overhead

Benchmark Median Baseline Delta Throughput
chain — single op (grep), 10K records 7.31ms 7.81ms -6.4% 1.37M rec/s
chain — 3 ops (grep eval grep), 10K records 8.30ms 7.58ms
chain — 5 ops (grep eval grep eval grep), 10K records
passthrough baseline — 10K records (direct collector) 5.96ms 6.07ms -1.8% 1.68M rec/s

Record Creation & Serialization

Benchmark Median Baseline Delta Throughput
new Record() — 10K objects 69.9µs 96.9µs -27.9% 🟢 143.10M rec/s
new Record() empty — 10K 94.8µs 135.5µs -30.1% 🟢 105.52M rec/s
Record.get — 10K records × 3 fields 57.2µs 52.1µs +9.9% 524.50M rec/s
Record.set — 10K records × 1 field 62.2µs 60.3µs +3.1% 160.87M rec/s
Record.toJSON — 10K records 296.5µs 283.0µs +4.8% 33.72M rec/s
Record.toString — 10K records 8.89ms 6.86ms +29.6% 🔴 1.12M rec/s
Record.clone — 10K records 6.18ms 6.36ms -2.8% 1.62M rec/s
Record.fromJSON — 10K lines 12.78ms 12.80ms -0.1% 782.41K rec/s, 231.7 MB/s
Record.dataRef — 10K records (zero-copy) 37.7µs 37.9µs -0.3% 264.94M rec/s
Record.sort — 10K records (numeric field) 11.20ms 11.46ms -2.3% 892.65K rec/s
Record.sort — 10K records (lexical field) 5.93ms 5.93ms +0.1% 1.68M rec/s
Record.cmp — 1M comparisons (single field) 102.21ms 110.17ms -7.2% 9.78M rec/s
Record.sort — 10K records (nested field numeric) 15.23ms 16.20ms -6.0% 656.71K rec/s
Record.cmp — 1M comparisons (multi-field cached) 86.77ms 88.00ms -1.4% 11.52M rec/s
Record.sort — 10K records (cached comparator reuse) 12.04ms 11.81ms +1.9% 830.85K rec/s

Chain vs Pipe

Benchmark Median Baseline Delta Throughput
chain — 2 ops (grep eval), 100 records 58.6µs 141.6µs -58.6% 🟢
pipe — 2 ops (grep eval), 100 records 501.13ms 507.30ms -1.2%
implicit — 2 ops (grep eval), 100 records 81.2µs 128.8µs -37.0% 🟢
chain — 2 ops (grep eval), 1K records 108.1µs 177.1µs -39.0% 🟢
pipe — 2 ops (grep eval), 1K records 501.76ms 507.46ms -1.1%
implicit — 2 ops (grep eval), 1K records 200.6µs 178.9µs +12.1% 🔴
chain — 2 ops (grep eval), 10K records 414.0µs 1.03ms -59.7% 🟢
pipe — 2 ops (grep eval), 10K records 506.79ms 510.16ms -0.7%
implicit — 2 ops (grep eval), 10K records 475.0µs 1.06ms -55.2% 🟢
chain — 3 ops (grep eval grep), 100 records 87.3µs 145.6µs
pipe — 3 ops (grep eval grep), 100 records 754.98ms 756.37ms
implicit — 3 ops (grep eval grep), 100 records 78.5µs 82.4µs
chain — 3 ops (grep eval grep), 1K records 205.8µs 177.0µs
pipe — 3 ops (grep eval grep), 1K records 750.32ms 759.28ms
implicit — 3 ops (grep eval grep), 1K records 122.9µs 184.7µs
chain — 3 ops (grep eval grep), 10K records 417.6µs 1.05ms
pipe — 3 ops (grep eval grep), 10K records 773.19ms 761.95ms
implicit — 3 ops (grep eval grep), 10K records 643.9µs 1.04ms
chain — 5 ops (grep eval grep eval grep), 100 records
pipe — 5 ops (grep eval grep eval grep), 100 records
implicit — 5 ops (grep eval grep eval grep), 100 records
chain — 5 ops (grep eval grep eval grep), 1K records
pipe — 5 ops (grep eval grep eval grep), 1K records
implicit — 5 ops (grep eval grep eval grep), 1K records
chain — 5 ops (grep eval grep eval grep), 10K records
pipe — 5 ops (grep eval grep eval grep), 10K records
implicit — 5 ops (grep eval grep eval grep), 10K records

Line Reading

Benchmark Median Baseline Delta Throughput
InputStream.fromFile — 100 lines 486.2µs 491.3µs -1.0% 205.68K rec/s, 60.8 MB/s
InputStream.fromString — 100 lines 185.1µs 187.3µs -1.2% 540.35K rec/s, 159.6 MB/s
manual buffer (isolated) — 100 lines 289.0µs 304.3µs -5.0% 345.97K rec/s, 102.2 MB/s
bulk text + split — 100 lines 103.1µs 78.3µs +31.6% 🔴 970.05K rec/s, 286.5 MB/s
node readline — 100 lines 504.7µs 415.5µs +21.4% 🔴 198.16K rec/s, 58.5 MB/s
TextDecoderStream — 100 lines 250.8µs 261.4µs -4.0% 398.67K rec/s, 117.8 MB/s
binary newline scan — 100 lines 268.2µs 369.7µs -27.4% 🟢 372.83K rec/s, 110.1 MB/s
bun native stdin — 100 lines 24.88ms 24.70ms +0.7% 4.02K rec/s, 1.2 MB/s
InputStream.fromFile — 10K lines 23.52ms 23.27ms +1.1% 425.09K rec/s, 125.9 MB/s
InputStream.fromString — 10K lines 17.92ms 17.68ms +1.3% 558.15K rec/s, 165.3 MB/s
manual buffer (isolated) — 10K lines 5.79ms 5.84ms -0.7% 1.73M rec/s, 511.1 MB/s
bulk text + split — 10K lines 2.35ms 2.40ms -2.2% 4.26M rec/s, 1260.7 MB/s
node readline — 10K lines 9.31ms 9.31ms +0.1% 1.07M rec/s, 318.0 MB/s
TextDecoderStream — 10K lines 4.83ms 4.66ms +3.6% 2.07M rec/s, 612.9 MB/s
binary newline scan — 10K lines 9.25ms 8.18ms +13.1% 🔴 1.08M rec/s, 320.1 MB/s
bun native stdin — 10K lines 43.58ms 40.38ms +7.9% 229.44K rec/s, 67.9 MB/s
InputStream.fromFile — 100K lines 251.31ms 256.85ms -2.2% 397.92K rec/s, 118.2 MB/s
InputStream.fromString — 100K lines 209.53ms 216.81ms -3.4% 477.26K rec/s, 141.8 MB/s
manual buffer (isolated) — 100K lines 35.94ms 32.55ms +10.4% 🔴 2.78M rec/s, 826.7 MB/s
bulk text + split — 100K lines 26.64ms 30.35ms -12.2% 🟢 3.75M rec/s, 1115.3 MB/s
node readline — 100K lines 84.58ms 82.83ms +2.1% 1.18M rec/s, 351.3 MB/s
TextDecoderStream — 100K lines 37.56ms 39.23ms -4.3% 2.66M rec/s, 791.2 MB/s
binary newline scan — 100K lines 84.70ms 75.64ms +12.0% 🔴 1.18M rec/s, 350.8 MB/s
bun native stdin — 100K lines 114.55ms 117.70ms -2.7% 872.95K rec/s, 259.4 MB/s

@benbernard benbernard merged commit b6b10c1 into master Feb 25, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant