rip-lang 3.6.2 → 3.7.3

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
@@ -7,7 +7,43 @@ All notable changes to Rip will be documented in this file.
7
7
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
8
8
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
9
9
 
10
- ## [3.6.0] - 2026-02-10
10
+ ## [3.7.3] - 2026-02-11
11
+
12
+ ### Fixes & Polish
13
+
14
+ - **Interface `::` fix** — `::` type annotations in interface bodies no longer produce double colons in `.d.ts` output.
15
+ - **`.=` dot chain support** — `obj.name .= toUpperCase()` now works (walks back full property chain).
16
+ - **`*` merge auto-init** — `*foo = {...}` auto-creates `foo` if undefined via `??= {}`.
17
+ - **`loop n` uses `it`** — Loop counter is accessible as `it` inside the body.
18
+ - **Syntax highlighting** — `|>` pipe operator added to VS Code, Vim, Playground, and rip-print.
19
+
20
+ ## [3.7.0] - 2026-02-11
21
+
22
+ ### Pipe Operator (`|>`)
23
+
24
+ - **First-arg insertion pipe** — `x |> fn` compiles to `fn(x)`, and `x |> fn(y)` compiles to `fn(x, y)`. Combines the simplicity of F#-style pipes with multi-arg support like Elixir — no placeholder syntax needed. Left-associative, chains naturally: `5 |> double |> add(1)`.
25
+ - Works with dotted references (`x |> Math.sqrt`, `x |> console.log`) and dotted calls (`x |> obj.method(y)` → `obj.method(x, y)`).
26
+ - Implicit calls close at pipe boundaries — `x |> fn 1 |> bar 2` works correctly without parens.
27
+
28
+ ### Method Assignment (`.=`) — A Rip Original
29
+
30
+ - **Compound method assignment** — `x .= trim()` compiles to `x = x.trim()`. The method-call equivalent of `+=`. No other language has this. Combined with implicit `it`, enables concise transformation pipelines: `items .= filter -> it.active` then `items .= map -> it.name`.
31
+
32
+ ### Prototype Operator (`::`)
33
+
34
+ - **CoffeeScript-style prototype access restored** — `String::trim` compiles to `String.prototype.trim`. Disambiguated from type annotations by spacing: `::` with no space is prototype, `::` with a space is a type annotation. Both coexist cleanly.
35
+
36
+ ### `loop n` — Repeat N Times
37
+
38
+ - **Counted loop** — `loop 5 -> body` compiles to `for (let _i = 0; _i < 5; _i++) { body }`. Works with literals, variables, and expressions.
39
+
40
+ ### Implicit `it` Parameter
41
+
42
+ - **Auto-injected parameter** — Arrow functions with no explicit params that reference `it` in the body automatically inject `it` as the parameter. `arr.filter -> it > 5` compiles to `arr.filter(function(it) { return (it > 5); })`. Works with both `->` and `=>`. Stops at nested function boundaries.
43
+
44
+ ### Negative Indexing
45
+
46
+ - **Python-style negative indexing** — `arr[-1]` compiles to `arr.at(-1)`, `arr[-2]` to `arr.at(-2)`, etc. Works on arrays and strings. Optional variant: `arr?[-1]` → `arr?.at(-1)`. Only literal negative numbers trigger the transform; variable indexes pass through unchanged.
11
47
 
12
48
  ### Browser Runtime — Full Async/Await Support
13
49
 
package/README.md CHANGED
@@ -9,15 +9,15 @@
9
9
  </p>
10
10
 
11
11
  <p align="center">
12
- <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-3.6.0-blue.svg" alt="Version"></a>
12
+ <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-3.7.3-blue.svg" alt="Version"></a>
13
13
  <a href="#zero-dependencies"><img src="https://img.shields.io/badge/dependencies-ZERO-brightgreen.svg" alt="Dependencies"></a>
14
- <a href="#"><img src="https://img.shields.io/badge/tests-1140%2F1140-brightgreen.svg" alt="Tests"></a>
14
+ <a href="#"><img src="https://img.shields.io/badge/tests-1225%2F1225-brightgreen.svg" alt="Tests"></a>
15
15
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License"></a>
16
16
  </p>
17
17
 
18
18
  ---
19
19
 
20
- Rip is a modern language inspired by CoffeeScript. It compiles to **ES2022** (classes, `?.`, `??`, modules), adds about a **dozen new operators**, includes **built-in reactivity**, and sports a self-hosting compiler with **zero dependencies** — all in about 10,300 lines of code.
20
+ Rip is a modern language inspired by CoffeeScript. It compiles to **ES2022** (classes, `?.`, `??`, modules), adds about a **dozen new operators**, includes **built-in reactivity**, and sports a self-hosting compiler with **zero dependencies** — all in about 10,800 lines of code.
21
21
 
22
22
  > **No imports. No hooks. No dependency arrays. Just write code.**
23
23
 
@@ -38,7 +38,7 @@ get '/users/:id' -> # RESTful API endpoint, comma-less
38
38
 
39
39
  **What makes Rip different:**
40
40
  - **Modern output** — ES2022 with native classes, `?.`, `??`, modules
41
- - **New operators** — `!`, `!?`, `//`, `%%`, `=~`, `.new()`, and more
41
+ - **New operators** — `!`, `!?`, `//`, `%%`, `=~`, `|>`, `.new()`, and more
42
42
  - **Reactive operators** — `:=`, `~=`, `~>` as language syntax
43
43
  - **Optional types** — `::` annotations, `::=` aliases, `.d.ts` emission
44
44
  - **Zero dependencies** — everything included, even the parser generator
@@ -122,6 +122,21 @@ for x as iterable # ES6 for-of on any iterable
122
122
 
123
123
  for x as! asyncIterable # Async iteration shorthand
124
124
  console.log x # Equivalent to: for await x as asyncIterable
125
+
126
+ loop # Infinite loop (while true)
127
+ process!
128
+ loop 5 # Repeat N times
129
+ console.log "hi"
130
+ ```
131
+
132
+ ### Implicit `it`
133
+
134
+ Arrow functions with no params that reference `it` auto-inject it as the parameter:
135
+
136
+ ```coffee
137
+ users.filter -> it.active # → users.filter(function(it) { ... })
138
+ names = users.map -> it.name # no need to name a throwaway variable
139
+ orders.filter -> it.total > 100 # works with any expression
125
140
  ```
126
141
 
127
142
  ### Reactivity
@@ -168,6 +183,7 @@ Compiles to `.js` (types erased) + `.d.ts` (types preserved) — full IDE suppor
168
183
  | `!?` (otherwise) | `val !? 5` | Default only if `undefined` |
169
184
  | `?` (existence) | `x?` | True if `x != null` |
170
185
  | `?:` (ternary) | `x > 0 ? 'yes' : 'no'` | JS-style ternary expression |
186
+ | `if...else` (postfix) | `"yes" if cond else "no"` | Python-style ternary expression |
171
187
  | `?.` `?.[]` `?.()` | `a?.b` `a?.[0]` `a?.()` | Optional chaining (ES6) |
172
188
  | `?[]` `?()` | `a?[0]` `a?(x)` | Optional chaining shorthand |
173
189
  | `??` | `a ?? b` | Nullish coalescing |
@@ -177,6 +193,15 @@ Compiles to `.js` (types erased) + `.d.ts` (types preserved) — full IDE suppor
177
193
  | `=~` | `str =~ /Hello, (\w+)/` | Match (captures in `_`) |
178
194
  | `[//, n]` | `str[/Hello, (\w+)/, 1]` | Extract capture n |
179
195
  | `.new()` | `Dog.new()` | Ruby-style constructor |
196
+ | `::` (prototype) | `String::trim` | `String.prototype.trim` |
197
+ | `[-n]` (negative index) | `arr[-1]` | Last element via `.at()` |
198
+ | `*` (string repeat) | `"-" * 40` | String repeat via `.repeat()` |
199
+ | `<` `<=` (chained) | `1 < x < 10` | Chained comparisons |
200
+ | `\|>` (pipe) | `x \|> fn` or `x \|> fn(y)` | Pipe operator (first-arg insertion) |
201
+ | `not in` | `x not in arr` | Negated membership test |
202
+ | `not of` | `k not of obj` | Negated key existence |
203
+ | `.=` (method assign) | `x .= trim()` | `x = x.trim()` — compound method assignment |
204
+ | `*` (merge assign) | `*obj = {a: 1}` | `Object.assign(obj, {a: 1})` |
180
205
  | `or return` | `x = get() or return err` | Guard clause (Ruby-style) |
181
206
  | `?? throw` | `x = get() ?? throw err` | Nullish guard |
182
207
 
@@ -254,9 +279,9 @@ See [@rip-lang/ui](packages/ui/) for the full framework: file-based router, reac
254
279
  | **Reactivity** | None | Built-in |
255
280
  | **Dependencies** | Multiple | Zero |
256
281
  | **Self-hosting** | No | Yes |
257
- | **Lexer** | 3,558 LOC | 1,867 LOC |
258
- | **Compiler** | 10,346 LOC | 3,292 LOC |
259
- | **Total** | 17,760 LOC | ~10,300 LOC |
282
+ | **Lexer** | 3,558 LOC | 1,958 LOC |
283
+ | **Compiler** | 10,346 LOC | 3,378 LOC |
284
+ | **Total** | 17,760 LOC | ~10,800 LOC |
260
285
 
261
286
  Smaller codebase, modern output, built-in reactivity.
262
287
 
@@ -291,25 +316,25 @@ await rip("res = fetch! 'https://api.example.com/todos/1'; res.json!") // → {
291
316
 
292
317
  ```
293
318
  Source -> Lexer -> emitTypes -> Parser -> S-Expressions -> Codegen -> JavaScript
294
- (1,867) (types.js) (357) ["=", "x", 42] (3,292) + source map
319
+ (1,958) (types.js) (359) ["=", "x", 42] (3,378) + source map
295
320
  ```
296
321
 
297
322
  Simple arrays (with `.loc`) instead of AST node classes. The compiler is self-hosting — `bun run parser` rebuilds from source.
298
323
 
299
324
  | Component | File | Lines |
300
325
  |-----------|------|-------|
301
- | Lexer + Rewriter | `src/lexer.js` | 1,867 |
302
- | Compiler + Codegen | `src/compiler.js` | 3,292 |
303
- | Type System | `src/types.js` | 719 |
326
+ | Lexer + Rewriter | `src/lexer.js` | 1,958 |
327
+ | Compiler + Codegen | `src/compiler.js` | 3,378 |
328
+ | Type System | `src/types.js` | 1,099 |
304
329
  | Component System | `src/components.js` | 1,240 |
305
330
  | Source Maps | `src/sourcemaps.js` | 122 |
306
- | Parser (generated) | `src/parser.js` | 357 |
307
- | Grammar | `src/grammar/grammar.rip` | 935 |
331
+ | Parser (generated) | `src/parser.js` | 359 |
332
+ | Grammar | `src/grammar/grammar.rip` | 945 |
308
333
  | Parser Generator | `src/grammar/solar.rip` | 916 |
309
- | REPL | `src/repl.js` | 707 |
334
+ | REPL | `src/repl.js` | 582 |
310
335
  | Browser Entry | `src/browser.js` | 119 |
311
336
  | Tags | `src/tags.js` | 63 |
312
- | **Total** | | **~10,325** |
337
+ | **Total** | | **~10,774** |
313
338
 
314
339
  ---
315
340
 
@@ -319,7 +344,7 @@ Rip includes optional packages for full-stack development:
319
344
 
320
345
  | Package | Version | Purpose |
321
346
  |---------|---------|---------|
322
- | [rip-lang](https://www.npmjs.com/package/rip-lang) | 3.6.0 | Core language compiler |
347
+ | [rip-lang](https://www.npmjs.com/package/rip-lang) | 3.7.3 | Core language compiler |
323
348
  | [@rip-lang/api](packages/api/) | 1.1.4 | HTTP framework (Sinatra-style routing, 37 validators) |
324
349
  | [@rip-lang/server](packages/server/) | 1.1.3 | Multi-worker app server (hot reload, HTTPS, mDNS) |
325
350
  | [@rip-lang/db](packages/db/) | 1.1.2 | DuckDB server with official UI (pure Bun FFI) |
@@ -365,7 +390,7 @@ rip file.rip # Run
365
390
  rip -c file.rip # Compile
366
391
  rip -t file.rip # Tokens
367
392
  rip -s file.rip # S-expressions
368
- bun run test # 1140 tests
393
+ bun run test # 1225 tests
369
394
  bun run parser # Rebuild parser
370
395
  bun run browser # Build browser bundle
371
396
  ```
package/docs/NOTES.md ADDED
@@ -0,0 +1,93 @@
1
+ <img src="https://raw.githubusercontent.com/shreeve/rip-lang/main/docs/rip.png" style="width:50px" /> <br>
2
+
3
+ # Rip — Notes
4
+
5
+ Ideas, future plans, and design thoughts for Rip.
6
+
7
+ ---
8
+
9
+ ## Standard Library (`stdlib`)
10
+
11
+ Rip is a zero-dependency language, but a small standard library of useful
12
+ utilities would save users from writing the same one-liners in every project.
13
+ These are **not** language features — they're plain functions that could ship
14
+ as a prelude or optional import.
15
+
16
+ ### Candidates
17
+
18
+ ```coffee
19
+ # Printing (Ruby's p)
20
+ p = console.log
21
+
22
+ # Exit with optional code (uses implicit `it`)
23
+ exit = -> process.exit(it)
24
+
25
+ # Tap — call a function for side effects, return the original value
26
+ # Useful in pipe chains: data |> tap(console.log) |> process
27
+ tap = (v, fn) -> fn(v); v
28
+
29
+ # Identity — returns its argument unchanged
30
+ # Useful as a default callback: items.filter(id)
31
+ id = -> it
32
+
33
+ # No-op — does nothing
34
+ # Useful as a default handler: onClick ?= noop
35
+ noop = ->
36
+
37
+ # String method aliases (shorter names for common checks)
38
+ String::starts = String::startsWith
39
+ String::ends = String::endsWith
40
+ String::has = String::includes
41
+
42
+ # Clamp a value to a range
43
+ clamp = (v, lo, hi) -> Math.min(Math.max(v, lo), hi)
44
+
45
+ # Sleep for N milliseconds (returns a Promise)
46
+ sleep = (ms) -> new Promise (resolve) -> setTimeout resolve, ms
47
+
48
+ # Times helper — call a function N times, collect results
49
+ times = (n, fn) -> (fn(i) for i in [0...n])
50
+ ```
51
+
52
+ ### Design Questions
53
+
54
+ - **Prelude vs import?** Should these be injected automatically (like Go's
55
+ `fmt` or Rip's reactive runtime), or explicitly imported (`import { p, tap }
56
+ from '@rip-lang/std'`)? Leaning toward explicit — Rip's philosophy is zero
57
+ magic in the output.
58
+
59
+ - **Scope?** Keep it tiny. A stdlib that grows to 500 functions defeats the
60
+ purpose. Each entry should save real keystrokes on something people do
61
+ constantly.
62
+
63
+ - **Node vs Browser?** Some helpers (like `exit`) are Node-only. Others (like
64
+ `p`, `tap`, `sleep`) work everywhere. May want to split into `std` (universal)
65
+ and `std/node` (server-only).
66
+
67
+ ---
68
+
69
+ ## Future Syntax Ideas
70
+
71
+ Ideas that have been discussed but not yet implemented. Each would need
72
+ design discussion before building.
73
+
74
+ - **`defer`** — Go-style cleanup that runs when the function exits. Compiles
75
+ to try/finally. `defer file.close()`.
76
+
77
+ - **`is a` / `isnt a`** — Readable instanceof. `x is a String` →
78
+ `x instanceof String`.
79
+
80
+ - **`.starts?` / `.ends?` / `.has?`** — Ruby-style question-mark methods.
81
+ `url.starts? "https"` → `url.startsWith("https")`.
82
+
83
+ - **Pattern matching** — `match value` with destructuring arms. Big feature,
84
+ needs careful design.
85
+
86
+ - **Reactive resource operator (`~>?`)** — Language-level `createResource`.
87
+ `user ~>? fetch!("/api/users/#{id}").json!` gives `user.loading`,
88
+ `user.error`, `user.data`. Park until real-world usage shows demand.
89
+
90
+ - **Pipe operator (`|>`) — Hack-style placeholder** — Currently Rip uses
91
+ Elixir-style first-arg insertion. A `%` placeholder for arbitrary position
92
+ (`data |> fn(1, %, 3)`) could be added later if needed. Current design
93
+ covers 95%+ of cases.
package/docs/RIP-GUIDE.md CHANGED
@@ -117,6 +117,7 @@ else
117
117
 
118
118
  # Ternary
119
119
  status = active ? "on" : "off"
120
+ label = "big" if x > 5 else "small"
120
121
 
121
122
  # Postfix
122
123
  console.log "hi" if ready
@@ -147,11 +148,26 @@ for key, value of object
147
148
  for i in [1..10] # inclusive (1 to 10)
148
149
  for i in [1...10] # exclusive (1 to 9)
149
150
 
151
+ # Loop N times
152
+ loop 5
153
+ console.log "hi"
154
+
150
155
  # Comprehensions
151
156
  squares = (x * x for x in [1..10])
152
157
  evens = (x for x in items when x % 2 is 0)
153
158
  ```
154
159
 
160
+ ### Implicit `it`
161
+
162
+ Arrow functions with no params that reference `it` auto-inject it as the parameter:
163
+
164
+ ```coffee
165
+ users.filter -> it.active # instead of (u) -> u.active
166
+ names = users.map -> it.name # instead of (u) -> u.name
167
+ orders.filter -> it.total > 100 # works with any expression
168
+ items.map => it.toUpperCase() # works with fat arrows too
169
+ ```
170
+
155
171
  ---
156
172
 
157
173
  ## Operators
@@ -174,6 +190,15 @@ Rip extends JavaScript with powerful operators:
174
190
  | `:=` | State | `count := 0` | Reactive signal |
175
191
  | `~=` | Computed | `doubled ~= x * 2` | Reactive computed |
176
192
  | `~>` | Effect | `~> console.log x` | Reactive side effect |
193
+ | `::` | Prototype | `String::trim` | `String.prototype.trim` |
194
+ | `[-n]` | Negative index | `arr[-1]` | `arr.at(-1)` |
195
+ | `*` | String repeat | `"-" * 40` | `"-".repeat(40)` |
196
+ | `<` `<=` | Chained | `1 < x < 10` | `(1 < x) && (x < 10)` |
197
+ | `.=` | Method assign | `x .= trim()` | `x = x.trim()` |
198
+ | `*` | Merge assign | `*obj = {a: 1}` | `Object.assign(obj, {a: 1})` |
199
+ | `not in` | Not in | `x not in arr` | Negated membership |
200
+ | `not of` | Not of | `k not of obj` | Negated key existence |
201
+ | `if...else` | Postfix ternary | `"a" if x else "b"` | `x ? "a" : "b"` |
177
202
  | `**` | Power | `2 ** 10` | `1024` |
178
203
  | `..` | Range | `[1..5]` | Inclusive range |
179
204
  | `...` | Spread/rest | `[...a, ...b]` | ES6 spread |
@@ -211,6 +236,19 @@ if text =~ /Hello, (\w+)/
211
236
  domain = "user@example.com"[/@(.+)$/, 1] # "example.com"
212
237
  ```
213
238
 
239
+ ### Pipe Operator (`|>`)
240
+
241
+ Pipes a value into a function as the first argument. Chains left-to-right:
242
+
243
+ ```coffee
244
+ 5 |> double # → double(5)
245
+ 5 |> add(3) # → add(5, 3)
246
+ 5 |> double |> add(1) # → add(double(5), 1)
247
+ data |> JSON.stringify(null, 2) # → JSON.stringify(data, null, 2)
248
+ ```
249
+
250
+ If the right side is a bare reference, it's called with the piped value. If the right side is already a call, the piped value is inserted as the first argument. This is the Elixir-style pipe — cleaner than F# (multi-arg works) and simpler than Hack (no placeholder needed).
251
+
214
252
  ### Guard Clauses
215
253
 
216
254
  ```coffee
@@ -633,7 +671,7 @@ rip -d file.rip # Generate .d.ts
633
671
  rip -t file.rip # Show tokens
634
672
  rip -s file.rip # Show S-expressions
635
673
  rip -q -c file.rip # Quiet (no headers)
636
- bun run test # Run test suite (1,140 tests)
674
+ bun run test # Run test suite
637
675
  ```
638
676
 
639
677
  ---
@@ -45,7 +45,7 @@ class BinaryOp {
45
45
  ["+", left, right] // That's it!
46
46
  ```
47
47
 
48
- **Result:** CoffeeScript's compiler is 17,760 LOC. Rip's is ~10,300 LOC — smaller, yet includes a complete reactive runtime, type system, component system, and source maps.
48
+ **Result:** CoffeeScript's compiler is 17,760 LOC. Rip's is ~10,800 LOC — smaller, yet includes a complete reactive runtime, type system, component system, and source maps.
49
49
 
50
50
  > **Transform the IR (s-expressions), not the output (strings).**
51
51
 
@@ -66,7 +66,7 @@ console.log(code);
66
66
  | Feature | CoffeeScript | Rip |
67
67
  |---------|-------------|------|
68
68
  | Optional chaining | 4 soak operators | ES6 `?.` / `?.[]` / `?.()` + shorthand `?[]` / `?()` |
69
- | Ternary | No | `x ? a : b` |
69
+ | Ternary | No | `x ? a : b` and `a if x else b` |
70
70
  | Regex features | Basic | Ruby-style (`=~`, indexing, captures in `_`) |
71
71
  | Async shorthand | No | Dammit operator (`!`) |
72
72
  | Void functions | No | `def fn!` |
@@ -77,7 +77,7 @@ console.log(code);
77
77
  | Dependencies | Multiple | **Zero** |
78
78
  | Parser generator | External (Jison) | **Built-in (Solar)** |
79
79
  | Self-hosting | No | **Yes** |
80
- | Total LOC | 17,760 | ~10,300 |
80
+ | Total LOC | 17,760 | ~10,800 |
81
81
 
82
82
  ## Design Principles
83
83
 
@@ -93,7 +93,7 @@ console.log(code);
93
93
 
94
94
  ```
95
95
  Source Code → Lexer → emitTypes → Parser → S-Expressions → Codegen → JavaScript
96
- (1,867) (types.js) (357) (arrays + .loc) (3,292) + source map
96
+ (1,958) (types.js) (359) (arrays + .loc) (3,378) + source map
97
97
 
98
98
  file.d.ts (when types: "emit")
99
99
  ```
@@ -102,14 +102,14 @@ Source Code → Lexer → emitTypes → Parser → S-Expressions → C
102
102
 
103
103
  | File | Purpose | Lines | Modify? |
104
104
  |------|---------|-------|---------|
105
- | `src/lexer.js` | Lexer + Rewriter | 1,867 | Yes |
106
- | `src/compiler.js` | Compiler + Code Generator | 3,292 | Yes |
107
- | `src/types.js` | Type System (lexer sidecar) | 719 | Yes |
105
+ | `src/lexer.js` | Lexer + Rewriter | 1,958 | Yes |
106
+ | `src/compiler.js` | Compiler + Code Generator | 3,378 | Yes |
107
+ | `src/types.js` | Type System (lexer sidecar) | 1,099 | Yes |
108
108
  | `src/components.js` | Component System (compiler sidecar) | 1,240 | Yes |
109
109
  | `src/sourcemaps.js` | Source Map V3 Generator | 122 | Yes |
110
110
  | `src/tags.js` | HTML Tag Classification | 63 | Yes |
111
111
  | `src/parser.js` | Generated parser | 357 | No (auto-gen) |
112
- | `src/grammar/grammar.rip` | Grammar specification | 935 | Yes (carefully) |
112
+ | `src/grammar/grammar.rip` | Grammar specification | 945 | Yes (carefully) |
113
113
  | `src/grammar/solar.rip` | Parser generator | 916 | No |
114
114
 
115
115
  ## Example Flow
@@ -579,4 +579,4 @@ rip> .js # Toggle JS display
579
579
 
580
580
  ---
581
581
 
582
- *Rip 3.4 — 1,140 tests passing — Zero dependencies — Self-hosting — ~10,300 LOC*
582
+ *Rip 3.7 — 1,219 tests passing — Zero dependencies — Self-hosting — ~10,800 LOC*