tjs-lang 0.6.0 → 0.6.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/CLAUDE.md CHANGED
@@ -77,6 +77,7 @@ npm run functions:serve # Local functions emulator
77
77
  - `src/lang/emitters/ast.ts` - Emits Agent99 AST from parsed source
78
78
  - `src/lang/emitters/js.ts` - Emits JavaScript with `__tjs` metadata
79
79
  - `src/lang/emitters/from-ts.ts` - TypeScript to TJS/JS transpiler with class metadata extraction
80
+ - `src/lang/emitters/dts.ts` - .d.ts declaration file generator from TJS transpilation results
80
81
  - `src/lang/inference.ts` - Type inference from example values
81
82
  - `src/lang/linter.ts` - Static analysis (unused vars, unreachable code, no-explicit-new)
82
83
  - `src/lang/runtime.ts` - TJS runtime (monadic errors, type checking, wrapClass)
@@ -187,6 +188,9 @@ fn('a', 'b') // Returns { error: 'type mismatch', ... }
187
188
  - `fromTS` lives in a separate entry point (`tosijs/lang/from-ts`)
188
189
  - Import only what you need to keep bundle size minimal
189
190
  - Each step is independently testable (see `src/lang/codegen.test.ts`)
191
+ - Constrained generics (`<T extends { id: number }>`) use the constraint as the example value instead of `any`
192
+ - Generic defaults (`<T = string>`) use the default as the example value
193
+ - Unconstrained generics (`<T>`) degrade to `any` — there's no information to use
190
194
 
191
195
  ### Security Model
192
196
 
@@ -377,6 +381,18 @@ Generic Container<T, U = ''> {
377
381
  return T(obj.item) && U(obj.label)
378
382
  }
379
383
  }
384
+
385
+ // Generic with declaration block (for .d.ts emission)
386
+ // The declaration block contains TypeScript syntax emitted verbatim into .d.ts
387
+ // It is stripped from runtime JS output
388
+ Generic BoxedProxy<T> {
389
+ predicate(x, T) { return typeof x === 'object' && T(x.value) }
390
+ declaration {
391
+ value: T
392
+ path: string
393
+ observe(cb: (path: string) => void): void
394
+ }
395
+ }
380
396
  ```
381
397
 
382
398
  #### Bare Assignments
package/demo/docs.json CHANGED
@@ -151,7 +151,7 @@
151
151
  "group": "docs",
152
152
  "order": 0,
153
153
  "navTitle": "Documentation",
154
- "text": "<!--{\"section\": \"tjs\", \"group\": \"docs\", \"order\": 0, \"navTitle\": \"Documentation\"}-->\n\n# TJS: Typed JavaScript\n\n_Types as Examples. Zero Build. Runtime Metadata._\n\n---\n\n## What is TJS?\n\nTJS is a typed superset of JavaScript where **types are concrete values**, not abstract annotations.\n\n```typescript\n// TypeScript: abstract type annotation\nfunction greet(name: string): string\n\n// TJS: concrete example value\nfunction greet(name: 'World') -> '' { return `Hello, ${name}!` }\n```\n\nThe example `'World'` tells TJS that `name` is a string. The example `''` tells TJS the return type is a string. Types are inferred from the examples you provide.\n\nTJS transpiles to JavaScript with embedded `__tjs` metadata, enabling runtime type checking, autocomplete from live objects, and documentation generation.\n\n---\n\n## The Compiler\n\nTJS compiles in the browser. No webpack, no node_modules, no build server.\n\n```typescript\nimport { tjs } from 'tjs-lang'\n\nconst code = tjs`\n function add(a: 0, b: 0) -> 0 {\n return a + b\n }\n`\n\n// Returns transpiled JavaScript with __tjs metadata\n```\n\nYou can also use the CLI:\n\n```bash\nbun src/cli/tjs.ts check file.tjs # Parse and type check\nbun src/cli/tjs.ts run file.tjs # Transpile and execute\nbun src/cli/tjs.ts emit file.tjs # Output transpiled JS\nbun src/cli/tjs.ts types file.tjs # Output type metadata\n```\n\n---\n\n## Syntax\n\n### Parameter Types (Colon Syntax)\n\n> **Not TypeScript.** TJS colon syntax looks like TypeScript but has different\n> semantics. The value after `:` is a **concrete example**, not a type name.\n> Write `name: 'Alice'` (example value), not `name: string` (type name).\n> TJS infers the type from the example: `'Alice'` → string, `0` → integer,\n> `true` → boolean.\n\nRequired parameters use colon syntax with an example value:\n\n```typescript\nfunction greet(name: 'Alice') {} // name is required, type: string\nfunction calculate(value: 0) {} // value is required, type: integer\nfunction measure(rate: 0.0) {} // rate is required, type: number (float)\nfunction count(n: +0) {} // n is required, type: non-negative integer\nfunction toggle(flag: true) {} // flag is required, type: boolean\n```\n\n### Numeric Types\n\nTJS distinguishes three numeric types using valid JavaScript syntax:\n\n```typescript\nfunction process(\n rate: 3.14, // number (float) -- has a decimal point\n count: 42, // integer -- whole number, no decimal\n index: +0 // non-negative integer -- prefixed with +\n) {}\n```\n\n| You Write | Type Inferred | Runtime Validation |\n| --------- | ---------------------- | ------------------------------- |\n| `3.14` | `number` (float) | `typeof x === 'number'` |\n| `0.0` | `number` (float) | `typeof x === 'number'` |\n| `42` | `integer` | `Number.isInteger(x)` |\n| `0` | `integer` | `Number.isInteger(x)` |\n| `+20` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n| `+0` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n| `-5` | `integer` | `Number.isInteger(x)` |\n| `-3.5` | `number` (float) | `typeof x === 'number'` |\n\nAll of these are valid JavaScript expressions. TJS reads the syntax more\ncarefully to give you finer-grained type checking than JS or TypeScript\nprovide natively.\n\n### Optional Parameters (Default Values)\n\nOptional parameters use `=` with a default value:\n\n```typescript\nfunction greet(name = 'World') {} // name is optional, defaults to 'World'\nfunction calculate(value = 0) {} // value is optional, defaults to 0 (integer)\n```\n\n### TypeScript-Style Optional (`?:`)\n\nTJS supports `?:` for compatibility, but consider it a migration aid rather than idiomatic TJS:\n\n```typescript\nfunction greet(name?: '') {} // same as name = ''\n```\n\n**Why `?:` is an antipattern.** In TypeScript, `?:` creates a three-state parameter\n(`value | undefined | missing`) that forces every function body to handle the absent case:\n\n```typescript\n// TypeScript — every caller and callee must reason about undefined\nfunction greet(name?: string) {\n const safeName = name ?? 'World' // defensive check required\n return `Hello, ${safeName}!`\n}\n```\n\nTJS offers two better alternatives:\n\n**1. Safe defaults** — the parameter always has a value, no branching needed:\n\n```typescript\nfunction greet(name = 'World') {\n return `Hello, ${name}!` // name is always a string\n}\n```\n\n**2. Polymorphic functions** — separate signatures for separate behavior:\n\n```typescript\nfunction greet() {\n return 'Hello, World!'\n}\nfunction greet(name: '') {\n return `Hello, ${name}!`\n}\n```\n\nBoth approaches eliminate the `undefined` state entirely. The function body\nnever needs a null check because the type system guarantees a valid value\nat every call site. This is simpler to write, simpler to read, and produces\ntighter runtime validation.\n\n### Object Parameters\n\nObject shapes are defined by example:\n\n```typescript\nfunction createUser(user: { name: ''; age: 0 }) {}\n// user must be an object with string name and number age\n```\n\n### Nullable Types\n\nUse `|` for union with null:\n\n```typescript\nfunction find(id: 0 | null) {} // number or null\n```\n\n### Return Types (Arrow Syntax)\n\nReturn types use `->`:\n\n```typescript\nfunction add(a: 0, b: 0) -> 0 {\n return a + b\n}\n\nfunction getUser(id: 0) -> { name: '', age: 0 } {\n return { name: 'Alice', age: 30 }\n}\n```\n\n### Array Types\n\nArrays use bracket syntax with an example element:\n\n```typescript\nfunction sum(numbers: [0]) -> 0 { // array of numbers\n return numbers.reduce((a, b) => a + b, 0)\n}\n\nfunction names(users: [{ name: '' }]) { // array of objects\n return users.map(u => u.name)\n}\n```\n\n---\n\n## Safety Markers\n\n### Unsafe Functions\n\nSkip validation for hot paths:\n\n```typescript\nfunction fastAdd(! a: 0, b: 0) { return a + b }\n```\n\nThe `!` marker after the function name skips input validation.\n\n### Safe Functions\n\nExplicit validation (for emphasis):\n\n```typescript\nfunction safeAdd(? a: 0, b: 0) { return a + b }\n```\n\n### Unsafe Blocks\n\nSkip validation for a block of code:\n\n```typescript\nunsafe {\n fastPath(data)\n anotherHotFunction(moreData)\n}\n```\n\n### Module Safety Directive\n\nSet the default validation level for an entire file:\n\n```typescript\nsafety none // No validation (metadata only)\nsafety inputs // Validate function inputs (default)\nsafety all // Validate everything (debug mode)\n```\n\n---\n\n## Type System\n\n### Type()\n\nDefine named types with predicates:\n\n```typescript\n// Simple type from example\nType Name 'Alice'\n\n// Type with description\nType User {\n description: 'a user object'\n example: { name: '', age: 0 }\n}\n\n// Type with predicate\nType PositiveNumber {\n description: 'a positive number'\n example: 1\n predicate(x) { return x > 0 }\n}\n```\n\nTypes can be used in function signatures:\n\n```typescript\nfunction greet(name: Name) -> '' {\n return `Hello, ${name}!`\n}\n```\n\n### Generic()\n\nRuntime-checkable generics:\n\n```typescript\nGeneric Box<T> {\n description: 'a boxed value'\n predicate(x, T) {\n return typeof x === 'object' && x !== null && 'value' in x && T(x.value)\n }\n}\n\n// With default type parameter\nGeneric Container<T, U = ''> {\n description: 'container with label'\n predicate(obj, T, U) {\n return T(obj.item) && U(obj.label)\n }\n}\n```\n\n### Union()\n\nDiscriminated unions:\n\n```typescript\nconst Shape = Union('kind', {\n circle: { radius: 0 },\n rectangle: { width: 0, height: 0 }\n})\n\nfunction area(shape: Shape) -> 0 {\n if (shape.kind === 'circle') {\n return Math.PI * shape.radius ** 2\n }\n return shape.width * shape.height\n}\n```\n\n### Enum()\n\nString or numeric enums:\n\n```typescript\nconst Status = Enum(['pending', 'active', 'completed'])\nconst Priority = Enum({ low: 1, medium: 2, high: 3 })\n\nfunction setStatus(status: Status) {}\n```\n\n---\n\n## Structural Equality: Is / IsNot\n\nJavaScript's `==` is broken (type coercion). TJS provides structural equality:\n\n```typescript\n// Structural comparison - no coercion\n[1, 2] Is [1, 2] // true\n5 Is \"5\" // false (different types)\n{ a: 1 } Is { a: 1 } // true\n\n// Arrays compared element-by-element\n[1, [2, 3]] Is [1, [2, 3]] // true\n\n// Negation\n5 IsNot \"5\" // true\n```\n\n### Custom Equality\n\nObjects can define custom equality in two ways:\n\n**1. `[tjsEquals]` symbol protocol** (preferred for Proxies and advanced use):\n\n```typescript\nimport { tjsEquals } from 'tjs-lang/lang'\n\n// A proxy that delegates equality to its target\nconst target = { x: 1, y: 2 }\nconst proxy = new Proxy({\n [tjsEquals](other) { return target Is other }\n}, {})\n\nproxy == { x: 1, y: 2 } // true — delegates to target\n```\n\n**2. `.Equals` method** (simple, works on any object or class):\n\n```typescript\nclass Point {\n constructor(x: 0, y: 0) { this.x = x; this.y = y }\n Equals(other) { return this.x === other.x && this.y === other.y }\n}\n\nPoint(1, 2) Is Point(1, 2) // true (uses .Equals)\n```\n\n**Priority:** `[tjsEquals]` symbol > `.Equals` method > structural comparison.\n\nThe symbol is `Symbol.for('tjs.equals')`, so it works across realms. Access it\nvia `import { tjsEquals } from 'tjs-lang/lang'` or `__tjs.tjsEquals` at runtime.\n\n---\n\n## Classes\n\n### Callable Without `new`\n\nTJS classes are callable without the `new` keyword:\n\n```typescript\nclass User {\n constructor(name: '') {\n this.name = name\n }\n}\n\n// Both work identically:\nconst u1 = User('Alice') // TJS way - clean\nconst u2 = new User('Alice') // Also works (linter warns)\n```\n\n### Private Fields\n\nUse `#` for private fields:\n\n```typescript\nclass Counter {\n #count = 0\n\n increment() {\n this.#count++\n }\n get value() {\n return this.#count\n }\n}\n```\n\nWhen converting from TypeScript, `private foo` becomes `#foo`.\n\n### Getters and Setters\n\nAsymmetric types are captured:\n\n```typescript\nclass Timestamp {\n #value\n\n constructor(initial: '' | 0 | null) {\n this.#value = initial === null ? new Date() : new Date(initial)\n }\n\n set value(v: '' | 0 | null) {\n this.#value = v === null ? new Date() : new Date(v)\n }\n\n get value() {\n return this.#value\n }\n}\n\nconst ts = Timestamp('2024-01-15')\nts.value = 0 // SET accepts: string | number | null\nts.value // GET returns: Date\n```\n\n---\n\n## Polymorphic Functions\n\nMultiple function declarations with the same name are automatically merged into a dispatcher that routes by argument count and type:\n\n```typescript\nfunction describe(value: 0) {\n return 'number: ' + value\n}\nfunction describe(value: '') {\n return 'string: ' + value\n}\nfunction describe(value: { name: '' }) {\n return 'object: ' + value.name\n}\n\ndescribe(42) // 'number: 42'\ndescribe('hello') // 'string: hello'\ndescribe({ name: 'world' }) // 'object: world'\ndescribe(true) // MonadicError: no matching overload\n```\n\n### Dispatch Order\n\n1. **Arity** first (number of arguments)\n2. **Type specificity** within same arity: `integer` > `number` > `any`; objects before primitives\n3. **Declaration order** as tiebreaker\n\n### Polymorphic Constructors\n\nClasses can have multiple constructor signatures. The first becomes the real JS constructor; additional variants become factory functions:\n\n```typescript\nTjsClass\n\nclass Point {\n constructor(x: 0.0, y: 0.0) {\n this.x = x\n this.y = y\n }\n constructor(coords: { x: 0.0; y: 0.0 }) {\n this.x = coords.x\n this.y = coords.y\n }\n}\n\nPoint(3, 4) // variant 1: two numbers\nPoint({ x: 10, y: 20 }) // variant 2: object\n```\n\nAll variants produce correct `instanceof` results.\n\n### Compile-Time Validation\n\nTJS catches these errors at transpile time:\n\n- **Ambiguous signatures**: Two variants with identical types at every position\n- **Rest parameters**: `...args` not supported in polymorphic functions\n- **Mixed async/sync**: All variants must agree\n\n---\n\n## Local Class Extensions\n\nAdd methods to built-in types without polluting prototypes:\n\n```typescript\nextend String {\n capitalize() {\n return this[0].toUpperCase() + this.slice(1)\n }\n words() {\n return this.split(/\\s+/)\n }\n}\n\n'hello world'.capitalize() // 'Hello world'\n'foo bar baz'.words() // ['foo', 'bar', 'baz']\n```\n\n### How It Works\n\nFor known-type receivers (literals, typed variables), calls are rewritten at transpile time to `.call()` — zero runtime overhead:\n\n```javascript\n// TJS source:\n'hello'.capitalize()\n\n// Generated JS:\n__ext_String.capitalize.call('hello')\n```\n\nFor unknown types, a runtime registry (`registerExtension` / `resolveExtension`) provides fallback dispatch.\n\n### Supported Types\n\nExtensions work on any type: `String`, `Number`, `Array`, `Boolean`, custom classes, and DOM classes like `HTMLElement`. Multiple `extend` blocks for the same type merge left-to-right (later declarations can override earlier methods).\n\n### Rules\n\n- Arrow functions are **not allowed** in extend blocks (they don't bind `this`)\n- Extensions are **file-local** — they don't leak across modules\n- Prototypes are **never modified** — `String.prototype.capitalize` remains `undefined`\n\n---\n\n## Runtime Features\n\n### `__tjs` Metadata\n\nEvery TJS function carries its type information:\n\n```typescript\nfunction createUser(input: { name: '', age: 0 }) -> { id: 0 } {\n return { id: 123 }\n}\n\nconsole.log(createUser.__tjs)\n// {\n// params: {\n// input: { type: { kind: 'object', shape: { name: 'string', age: 'number' } } }\n// },\n// returns: { kind: 'object', shape: { id: 'number' } }\n// }\n```\n\nThis enables:\n\n- Autocomplete from live objects\n- Runtime type validation\n- Automatic documentation generation\n\n### Monadic Errors\n\nType validation failures return `MonadicError` instances (extends `Error`),\nnot thrown exceptions:\n\n```typescript\nimport { isMonadicError } from 'tjs-lang/lang'\n\nconst result = createUser({ name: 123 }) // wrong type\n// MonadicError: Expected string for 'createUser.name', got number\n\nif (isMonadicError(result)) {\n console.log(result.message) // \"Expected string for 'createUser.name', got number\"\n console.log(result.path) // \"createUser.name\"\n console.log(result.expected) // \"string\"\n console.log(result.actual) // \"number\"\n}\n```\n\nNo try/catch gambling. The host survives invalid inputs.\n\nFor general-purpose error values (not type errors), use the `error()` helper\nwhich returns plain `{ $error: true, message }` objects checkable with `isError()`.\n\n### Inline Tests\n\nTests live next to code:\n\n```typescript\nfunction double(x: 0) -> 0 { return x * 2 }\n\ntest('doubles numbers') {\n expect(double(5)).toBe(10)\n expect(double(-3)).toBe(-6)\n}\n```\n\nTests are extracted at compile time and can be:\n\n- Run during transpilation\n- Stripped in production builds\n- Used for documentation generation\n\n### WASM Blocks\n\nDrop into WebAssembly for compute-heavy code:\n\n```typescript\nfunction vectorDot(a: [0], b: [0]) -> 0 {\n let sum = 0\n wasm {\n for (let i = 0; i < a.length; i++) {\n sum = sum + a[i] * b[i]\n }\n }\n return sum\n}\n```\n\nVariables are captured automatically. Falls back to JS if WASM unavailable.\n\n#### SIMD Intrinsics (f32x4)\n\nFor compute-heavy workloads, use f32x4 SIMD intrinsics to process 4 float32 values per instruction:\n\n```typescript\nconst scale = wasm (arr: Float32Array, len: 0, factor: 0.0) -> 0 {\n let s = f32x4_splat(factor)\n for (let i = 0; i < len; i += 4) {\n let off = i * 4\n let v = f32x4_load(arr, off)\n f32x4_store(arr, off, f32x4_mul(v, s))\n }\n} fallback {\n for (let i = 0; i < len; i++) arr[i] *= factor\n}\n```\n\nAvailable intrinsics:\n\n| Intrinsic | Description |\n| ----------------------------------- | ------------------------------------ |\n| `f32x4_load(ptr, byteOffset)` | Load 4 floats from memory into v128 |\n| `f32x4_store(ptr, byteOffset, vec)` | Store v128 as 4 floats to memory |\n| `f32x4_splat(scalar)` | Fill all 4 lanes with a scalar value |\n| `f32x4_extract_lane(vec, N)` | Extract float from lane 0-3 |\n| `f32x4_replace_lane(vec, N, val)` | Replace one lane, return new v128 |\n| `f32x4_add(a, b)` | Lane-wise addition |\n| `f32x4_sub(a, b)` | Lane-wise subtraction |\n| `f32x4_mul(a, b)` | Lane-wise multiplication |\n| `f32x4_div(a, b)` | Lane-wise division |\n| `f32x4_neg(v)` | Negate all lanes |\n| `f32x4_sqrt(v)` | Square root of all lanes |\n\nThis mirrors C/C++ SIMD intrinsics (`_mm_add_ps`, etc.) — explicit, predictable, no auto-vectorization magic.\n\n#### Zero-Copy Arrays: `wasmBuffer()`\n\nBy default, typed arrays passed to WASM blocks are copied into WASM memory before the call and copied back out after. For large arrays called frequently, this overhead can negate WASM's speed advantage.\n\n`wasmBuffer(Constructor, length)` allocates typed arrays directly in WASM linear memory. These arrays work like normal typed arrays from JavaScript, but when passed to a `wasm {}` block, they're zero-copy — the data is already there.\n\n```typescript\n// Allocate particle positions in WASM memory\nconst starX = wasmBuffer(Float32Array, 50000)\nconst starY = wasmBuffer(Float32Array, 50000)\n\n// Use from JS like normal arrays\nfor (let i = 0; i < 50000; i++) {\n starX[i] = (Math.random() - 0.5) * 2000\n starY[i] = (Math.random() - 0.5) * 2000\n}\n\n// Zero-copy SIMD processing\nfunction moveParticles(! xs: Float32Array, ys: Float32Array, len: 0, dx: 0.0, dy: 0.0) {\n wasm {\n let vdx = f32x4_splat(dx)\n let vdy = f32x4_splat(dy)\n for (let i = 0; i < len; i += 4) {\n let off = i * 4\n f32x4_store(xs, off, f32x4_add(f32x4_load(xs, off), vdx))\n f32x4_store(ys, off, f32x4_add(f32x4_load(ys, off), vdy))\n }\n } fallback {\n for (let i = 0; i < len; i++) { xs[i] += dx; ys[i] += dy }\n }\n}\n\n// After WASM runs, JS sees the mutations immediately\nmoveParticles(starX, starY, 50000, 1.0, 0.5)\nconsole.log(starX[0]) // updated in place, no copy\n```\n\nKey points:\n\n- Supported constructors: `Float32Array`, `Float64Array`, `Int32Array`, `Uint8Array`\n- Uses a bump allocator — allocations persist for program lifetime\n- All WASM blocks in a file share one 64MB memory\n- Regular typed arrays still work (copy in/out as before)\n- Use `!` (unsafe) on hot-path functions to skip runtime type checks\n\n---\n\n## Module System\n\n### Imports\n\nTJS supports URL imports:\n\n```typescript\nimport { Button } from 'https://cdn.example.com/ui-kit.tjs'\nimport { validate } from './utils/validation.tjs'\n```\n\nModules are:\n\n- Fetched on demand\n- Transpiled in the browser\n- Cached independently (IndexedDB + service worker)\n\n### CDN Integration\n\nExternal packages from esm.sh with pinned versions:\n\n```typescript\nimport lodash from 'https://esm.sh/lodash@4.17.21'\n```\n\n---\n\n## TypeScript Compatibility\n\n### TS → TJS Converter\n\nConvert existing TypeScript:\n\n```bash\nbun src/cli/tjs.ts convert file.ts\n```\n\n```typescript\n// TypeScript\nfunction greet(name: string, age?: number): string { ... }\n\n// Converts to TJS\nfunction greet(name: '', age = 0) -> '' { ... }\n```\n\n### What Gets Converted\n\n| TypeScript | TJS |\n| -------------------------- | ----------------------- |\n| `name: string` | `name: ''` |\n| `age: number` | `age: 0.0` |\n| `flag: boolean` | `flag: false` |\n| `items: string[]` | `items: ['']` |\n| `age?: number` | `age: 0.0 \\| undefined` |\n| `private foo` | `#foo` |\n| `interface User` | `Type User` |\n| `type Status = 'a' \\| 'b'` | `Union(['a', 'b'])` |\n| `enum Color` | `Enum(...)` |\n\n> **Optional params:** TypeScript `x?: boolean` becomes TJS `x: false | undefined`.\n> This preserves the three-state semantics (`true` / `false` / `undefined`)\n> using a union type. The param is required but explicitly accepts `undefined`.\n\n---\n\n## Performance\n\n| Mode | Overhead | Use Case |\n| --------------- | --------- | ------------------------------- |\n| `safety none` | **1.0x** | Metadata only, no validation |\n| `safety inputs` | **~1.5x** | Production (single-arg objects) |\n| `(!) unsafe` | **1.0x** | Hot paths |\n| `wasm {}` | **<1.0x** | Compute-heavy code |\n\n### Why 1.5x, Not 25x\n\nMost validators interpret schemas at runtime (~25x overhead). TJS generates inline checks at transpile time:\n\n```typescript\n// Generated (JIT-friendly)\nif (\n typeof input !== 'object' ||\n input === null ||\n typeof input.name !== 'string' ||\n typeof input.age !== 'number'\n) {\n return { $error: true, message: 'Invalid input', path: 'fn.input' }\n}\n```\n\nNo schema interpretation. No object iteration. The JIT inlines these completely.\n\n---\n\n## Bare Assignments\n\nUppercase identifiers automatically get `const`:\n\n```typescript\nFoo = Type('test', 'example') // becomes: const Foo = Type(...)\nMyConfig = { debug: true } // becomes: const MyConfig = { ... }\n```\n\n---\n\n## Limitations\n\n### What TJS Doesn't Do\n\n- **No gradual typing** - types are all-or-nothing per function\n- **No complex type inference** - you provide examples, not constraints\n- **No declaration files** - types live in the code, not `.d.ts`\n- **No type-level computation** - no conditional types, mapped types, etc.\n\n### What TJS Intentionally Avoids\n\n- Build steps\n- External type checkers\n- Complex tooling configuration\n- Separation of types from runtime\n\n---\n\n## Learn More\n\n- [AJS Documentation](DOCS-AJS.md) — The agent runtime\n- [Builder's Manifesto](MANIFESTO-BUILDER.md) — Why TJS is fun\n- [Enterprise Guide](MANIFESTO-ENTERPRISE.md) — Why TJS is safe\n- [Technical Context](CONTEXT.md) — Architecture deep dive\n"
154
+ "text": "<!--{\"section\": \"tjs\", \"group\": \"docs\", \"order\": 0, \"navTitle\": \"Documentation\"}-->\n\n# TJS: Typed JavaScript\n\n_Types as Examples. Zero Build. Runtime Metadata._\n\n---\n\n## What is TJS?\n\nTJS is a typed superset of JavaScript where **types are concrete values**, not abstract annotations.\n\n```typescript\n// TypeScript: abstract type annotation\nfunction greet(name: string): string\n\n// TJS: concrete example value\nfunction greet(name: 'World') -> '' { return `Hello, ${name}!` }\n```\n\nThe example `'World'` tells TJS that `name` is a string. The example `''` tells TJS the return type is a string. Types are inferred from the examples you provide.\n\nTJS transpiles to JavaScript with embedded `__tjs` metadata, enabling runtime type checking, autocomplete from live objects, and documentation generation.\n\n---\n\n## TJS is JavaScript\n\nTJS is **purely additive**. It adds type annotations, runtime validation, and metadata on top of standard JavaScript. It does not replace, intercept, or modify any existing JavaScript semantics.\n\n**Everything you know about JavaScript still works:**\n\n- **Proxies** — fully supported. TJS never intercepts property access. `__tjs` metadata is a plain property assignment on the function object; it doesn't interfere with Proxy traps. The `[tjsEquals]` symbol protocol is specifically designed for Proxy-friendly custom equality.\n- **WeakMap, WeakSet, Map, Set** — all unchanged. TJS doesn't wrap or validate collection internals.\n- **Closures, Promises, async/await** — work identically to JS.\n- **Prototype chains** — preserved. `wrapClass()` uses a Proxy only on the class constructor (to allow calling without `new`), not on instances.\n- **Module semantics** — TJS preserves ES module `import`/`export` exactly. Lazy getters, circular dependencies, and re-exports work the same as in JS.\n- **`this` binding** — unchanged. Arrow functions, `.bind()`, `.call()`, `.apply()` all work normally.\n- **Regular expressions, JSON, Math, Date** — all standard built-ins are available and unmodified (though `TjsDate` directive can optionally ban `Date` in favor of safer alternatives).\n\n**What TJS adds (and when):**\n\n| Addition | When | Overhead |\n| ---------------------- | ----------------------------------------- | ----------------------------- |\n| Parameter validation | Function entry (unless `!` unsafe) | ~1.5x on that function |\n| Return type validation | Function exit (only with `safety all`) | ~1.5x on that function |\n| `__tjs` metadata | Transpile time | Zero runtime cost |\n| `wrapClass` Proxy | Class declaration (with `TjsClass`) | One-time, on constructor only |\n| Structural equality | Only when `==`/`!=` used with `TjsEquals` | Per-comparison |\n\nIf TJS doesn't understand something in your code, it passes it through unchanged. There is no \"TJS runtime\" that interposes between your code and the JS engine — just the inline checks you can see in the transpiled output.\n\n---\n\n## The Compiler\n\nTJS compiles in the browser. No webpack, no node_modules, no build server.\n\n```typescript\nimport { tjs } from 'tjs-lang'\n\nconst code = tjs`\n function add(a: 0, b: 0) -> 0 {\n return a + b\n }\n`\n\n// Returns transpiled JavaScript with __tjs metadata\n```\n\nYou can also use the CLI:\n\n```bash\nbun src/cli/tjs.ts check file.tjs # Parse and type check\nbun src/cli/tjs.ts run file.tjs # Transpile and execute\nbun src/cli/tjs.ts emit file.tjs # Output transpiled JS\nbun src/cli/tjs.ts types file.tjs # Output type metadata\n```\n\n---\n\n## Syntax\n\n### Parameter Types (Colon Syntax)\n\n> **Not TypeScript.** TJS colon syntax looks like TypeScript but has different\n> semantics. The value after `:` is a **concrete example**, not a type name.\n> Write `name: 'Alice'` (example value), not `name: string` (type name).\n> TJS infers the type from the example: `'Alice'` → string, `0` → integer,\n> `true` → boolean.\n\nRequired parameters use colon syntax with an example value:\n\n```typescript\nfunction greet(name: 'Alice') {} // name is required, type: string\nfunction calculate(value: 0) {} // value is required, type: integer\nfunction measure(rate: 0.0) {} // rate is required, type: number (float)\nfunction count(n: +0) {} // n is required, type: non-negative integer\nfunction toggle(flag: true) {} // flag is required, type: boolean\n```\n\n### Numeric Types\n\nTJS distinguishes three numeric types using valid JavaScript syntax:\n\n```typescript\nfunction process(\n rate: 3.14, // number (float) -- has a decimal point\n count: 42, // integer -- whole number, no decimal\n index: +0 // non-negative integer -- prefixed with +\n) {}\n```\n\n| You Write | Type Inferred | Runtime Validation |\n| --------- | ---------------------- | ------------------------------- |\n| `3.14` | `number` (float) | `typeof x === 'number'` |\n| `0.0` | `number` (float) | `typeof x === 'number'` |\n| `42` | `integer` | `Number.isInteger(x)` |\n| `0` | `integer` | `Number.isInteger(x)` |\n| `+20` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n| `+0` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n| `-5` | `integer` | `Number.isInteger(x)` |\n| `-3.5` | `number` (float) | `typeof x === 'number'` |\n\nAll of these are valid JavaScript expressions. TJS reads the syntax more\ncarefully to give you finer-grained type checking than JS or TypeScript\nprovide natively.\n\n### Optional Parameters (Default Values)\n\nOptional parameters use `=` with a default value:\n\n```typescript\nfunction greet(name = 'World') {} // name is optional, defaults to 'World'\nfunction calculate(value = 0) {} // value is optional, defaults to 0 (integer)\n```\n\n### TypeScript-Style Optional (`?:`)\n\nTJS supports `?:` for compatibility, but consider it a migration aid rather than idiomatic TJS:\n\n```typescript\nfunction greet(name?: '') {} // same as name = ''\n```\n\n**Why `?:` is an antipattern.** In TypeScript, `?:` creates a three-state parameter\n(`value | undefined | missing`) that forces every function body to handle the absent case:\n\n```typescript\n// TypeScript — every caller and callee must reason about undefined\nfunction greet(name?: string) {\n const safeName = name ?? 'World' // defensive check required\n return `Hello, ${safeName}!`\n}\n```\n\nTJS offers two better alternatives:\n\n**1. Safe defaults** — the parameter always has a value, no branching needed:\n\n```typescript\nfunction greet(name = 'World') {\n return `Hello, ${name}!` // name is always a string\n}\n```\n\n**2. Polymorphic functions** — separate signatures for separate behavior:\n\n```typescript\nfunction greet() {\n return 'Hello, World!'\n}\nfunction greet(name: '') {\n return `Hello, ${name}!`\n}\n```\n\nBoth approaches eliminate the `undefined` state entirely. The function body\nnever needs a null check because the type system guarantees a valid value\nat every call site. This is simpler to write, simpler to read, and produces\ntighter runtime validation.\n\n### Object Parameters\n\nObject shapes are defined by example:\n\n```typescript\nfunction createUser(user: { name: ''; age: 0 }) {}\n// user must be an object with string name and number age\n```\n\n### Nullable Types\n\nUse `|` for union with null:\n\n```typescript\nfunction find(id: 0 | null) {} // number or null\n```\n\n### Return Types (Arrow Syntax)\n\nReturn types use `->`:\n\n```typescript\nfunction add(a: 0, b: 0) -> 0 {\n return a + b\n}\n\nfunction getUser(id: 0) -> { name: '', age: 0 } {\n return { name: 'Alice', age: 30 }\n}\n```\n\n### Array Types\n\nArrays use bracket syntax with an example element:\n\n```typescript\nfunction sum(numbers: [0]) -> 0 { // array of numbers\n return numbers.reduce((a, b) => a + b, 0)\n}\n\nfunction names(users: [{ name: '' }]) { // array of objects\n return users.map(u => u.name)\n}\n```\n\n---\n\n## Safety Markers\n\n### Unsafe Functions\n\nSkip validation for hot paths:\n\n```typescript\nfunction fastAdd(! a: 0, b: 0) { return a + b }\n```\n\nThe `!` marker after the function name skips input validation.\n\n### Safe Functions\n\nExplicit validation (for emphasis):\n\n```typescript\nfunction safeAdd(? a: 0, b: 0) { return a + b }\n```\n\n### Unsafe Blocks\n\nSkip validation for a block of code:\n\n```typescript\nunsafe {\n fastPath(data)\n anotherHotFunction(moreData)\n}\n```\n\n### Module Safety Directive\n\nSet the default validation level for an entire file:\n\n```typescript\nsafety none // No validation (metadata only)\nsafety inputs // Validate function inputs (default)\nsafety all // Validate everything (debug mode)\n```\n\n---\n\n## Type System\n\n### Type()\n\nDefine named types with predicates:\n\n```typescript\n// Simple type from example\nType Name 'Alice'\n\n// Type with description\nType User {\n description: 'a user object'\n example: { name: '', age: 0 }\n}\n\n// Type with predicate\nType PositiveNumber {\n description: 'a positive number'\n example: 1\n predicate(x) { return x > 0 }\n}\n```\n\nTypes can be used in function signatures:\n\n```typescript\nfunction greet(name: Name) -> '' {\n return `Hello, ${name}!`\n}\n```\n\n### Generic()\n\nRuntime-checkable generics:\n\n```typescript\nGeneric Box<T> {\n description: 'a boxed value'\n predicate(x, T) {\n return typeof x === 'object' && x !== null && 'value' in x && T(x.value)\n }\n}\n\n// With default type parameter\nGeneric Container<T, U = ''> {\n description: 'container with label'\n predicate(obj, T, U) {\n return T(obj.item) && U(obj.label)\n }\n}\n```\n\n#### Declaration Blocks (for TypeScript Consumers)\n\nGenerics can include an optional `declaration` block that specifies the\nTypeScript interface to emit in `.d.ts` output. This is metadata for TS\nconsumers — it has no effect on runtime behavior.\n\n```typescript\nGeneric BoxedProxy<T> {\n description: 'typed reactive proxy'\n predicate(x, T) {\n return typeof x === 'object' && 'value' in x && T(x.value)\n }\n declaration {\n value: T\n path: string\n observe(cb: (path: string) => void): void\n touch(): void\n }\n}\n```\n\nWhen emitting `.d.ts` via `tjs emit --dts`, this produces:\n\n```typescript\nexport interface BoxedProxy<T> {\n value: T\n path: string\n observe(cb: (path: string) => void): void\n touch(): void\n}\n```\n\nThe declaration content is raw TypeScript syntax — it's emitted verbatim\ninto the `.d.ts` file. This lets TJS libraries provide proper TypeScript\ninterfaces while keeping the TJS source as the single source of truth.\n\nWithout a `declaration` block, Generics emit an `any`-based factory stub\nthat provides basic IDE hints without false type errors.\n\n### Union()\n\nDiscriminated unions:\n\n```typescript\nconst Shape = Union('kind', {\n circle: { radius: 0 },\n rectangle: { width: 0, height: 0 }\n})\n\nfunction area(shape: Shape) -> 0 {\n if (shape.kind === 'circle') {\n return Math.PI * shape.radius ** 2\n }\n return shape.width * shape.height\n}\n```\n\n### Enum()\n\nString or numeric enums:\n\n```typescript\nconst Status = Enum(['pending', 'active', 'completed'])\nconst Priority = Enum({ low: 1, medium: 2, high: 3 })\n\nfunction setStatus(status: Status) {}\n```\n\n---\n\n## Structural Equality: Is / IsNot\n\nJavaScript's `==` is broken (type coercion). TJS provides structural equality:\n\n```typescript\n// Structural comparison - no coercion\n[1, 2] Is [1, 2] // true\n5 Is \"5\" // false (different types)\n{ a: 1 } Is { a: 1 } // true\n\n// Arrays compared element-by-element\n[1, [2, 3]] Is [1, [2, 3]] // true\n\n// Negation\n5 IsNot \"5\" // true\n```\n\n### Custom Equality\n\nObjects can define custom equality in two ways:\n\n**1. `[tjsEquals]` symbol protocol** (preferred for Proxies and advanced use):\n\n```typescript\nimport { tjsEquals } from 'tjs-lang/lang'\n\n// A proxy that delegates equality to its target\nconst target = { x: 1, y: 2 }\nconst proxy = new Proxy({\n [tjsEquals](other) { return target Is other }\n}, {})\n\nproxy == { x: 1, y: 2 } // true — delegates to target\n```\n\n**2. `.Equals` method** (simple, works on any object or class):\n\n```typescript\nclass Point {\n constructor(x: 0, y: 0) { this.x = x; this.y = y }\n Equals(other) { return this.x === other.x && this.y === other.y }\n}\n\nPoint(1, 2) Is Point(1, 2) // true (uses .Equals)\n```\n\n**Priority:** `[tjsEquals]` symbol > `.Equals` method > structural comparison.\n\nThe symbol is `Symbol.for('tjs.equals')`, so it works across realms. Access it\nvia `import { tjsEquals } from 'tjs-lang/lang'` or `__tjs.tjsEquals` at runtime.\n\n---\n\n## Classes\n\n### Callable Without `new`\n\nTJS classes are callable without the `new` keyword:\n\n```typescript\nclass User {\n constructor(name: '') {\n this.name = name\n }\n}\n\n// Both work identically:\nconst u1 = User('Alice') // TJS way - clean\nconst u2 = new User('Alice') // Also works (linter warns)\n```\n\n### Private Fields\n\nUse `#` for private fields:\n\n```typescript\nclass Counter {\n #count = 0\n\n increment() {\n this.#count++\n }\n get value() {\n return this.#count\n }\n}\n```\n\nWhen converting from TypeScript, `private foo` becomes `#foo`.\n\n### Getters and Setters\n\nAsymmetric types are captured:\n\n```typescript\nclass Timestamp {\n #value\n\n constructor(initial: '' | 0 | null) {\n this.#value = initial === null ? new Date() : new Date(initial)\n }\n\n set value(v: '' | 0 | null) {\n this.#value = v === null ? new Date() : new Date(v)\n }\n\n get value() {\n return this.#value\n }\n}\n\nconst ts = Timestamp('2024-01-15')\nts.value = 0 // SET accepts: string | number | null\nts.value // GET returns: Date\n```\n\n---\n\n## Polymorphic Functions\n\nMultiple function declarations with the same name are automatically merged into a dispatcher that routes by argument count and type:\n\n```typescript\nfunction describe(value: 0) {\n return 'number: ' + value\n}\nfunction describe(value: '') {\n return 'string: ' + value\n}\nfunction describe(value: { name: '' }) {\n return 'object: ' + value.name\n}\n\ndescribe(42) // 'number: 42'\ndescribe('hello') // 'string: hello'\ndescribe({ name: 'world' }) // 'object: world'\ndescribe(true) // MonadicError: no matching overload\n```\n\n### Dispatch Order\n\n1. **Arity** first (number of arguments)\n2. **Type specificity** within same arity: `integer` > `number` > `any`; objects before primitives\n3. **Declaration order** as tiebreaker\n\n### Polymorphic Constructors\n\nClasses can have multiple constructor signatures. The first becomes the real JS constructor; additional variants become factory functions:\n\n```typescript\nTjsClass\n\nclass Point {\n constructor(x: 0.0, y: 0.0) {\n this.x = x\n this.y = y\n }\n constructor(coords: { x: 0.0; y: 0.0 }) {\n this.x = coords.x\n this.y = coords.y\n }\n}\n\nPoint(3, 4) // variant 1: two numbers\nPoint({ x: 10, y: 20 }) // variant 2: object\n```\n\nAll variants produce correct `instanceof` results.\n\n### Compile-Time Validation\n\nTJS catches these errors at transpile time:\n\n- **Ambiguous signatures**: Two variants with identical types at every position\n- **Rest parameters**: `...args` not supported in polymorphic functions\n- **Mixed async/sync**: All variants must agree\n\n---\n\n## Local Class Extensions\n\nAdd methods to built-in types without polluting prototypes:\n\n```typescript\nextend String {\n capitalize() {\n return this[0].toUpperCase() + this.slice(1)\n }\n words() {\n return this.split(/\\s+/)\n }\n}\n\n'hello world'.capitalize() // 'Hello world'\n'foo bar baz'.words() // ['foo', 'bar', 'baz']\n```\n\n### How It Works\n\nFor known-type receivers (literals, typed variables), calls are rewritten at transpile time to `.call()` — zero runtime overhead:\n\n```javascript\n// TJS source:\n'hello'.capitalize()\n\n// Generated JS:\n__ext_String.capitalize.call('hello')\n```\n\nFor unknown types, a runtime registry (`registerExtension` / `resolveExtension`) provides fallback dispatch.\n\n### Supported Types\n\nExtensions work on any type: `String`, `Number`, `Array`, `Boolean`, custom classes, and DOM classes like `HTMLElement`. Multiple `extend` blocks for the same type merge left-to-right (later declarations can override earlier methods).\n\n### Rules\n\n- Arrow functions are **not allowed** in extend blocks (they don't bind `this`)\n- Extensions are **file-local** — they don't leak across modules\n- Prototypes are **never modified** — `String.prototype.capitalize` remains `undefined`\n\n---\n\n## Runtime Features\n\n### `__tjs` Metadata\n\nEvery TJS function carries its type information:\n\n```typescript\nfunction createUser(input: { name: '', age: 0 }) -> { id: 0 } {\n return { id: 123 }\n}\n\nconsole.log(createUser.__tjs)\n// {\n// params: {\n// input: { type: { kind: 'object', shape: { name: 'string', age: 'number' } } }\n// },\n// returns: { kind: 'object', shape: { id: 'number' } }\n// }\n```\n\nThis enables:\n\n- Autocomplete from live objects\n- Runtime type validation\n- Automatic documentation generation\n\n### Monadic Errors\n\nType validation failures return `MonadicError` instances (extends `Error`),\nnot thrown exceptions:\n\n```typescript\nimport { isMonadicError } from 'tjs-lang/lang'\n\nconst result = createUser({ name: 123 }) // wrong type\n// MonadicError: Expected string for 'createUser.name', got number\n\nif (isMonadicError(result)) {\n console.log(result.message) // \"Expected string for 'createUser.name', got number\"\n console.log(result.path) // \"createUser.name\"\n console.log(result.expected) // \"string\"\n console.log(result.actual) // \"number\"\n}\n```\n\nNo try/catch gambling. The host survives invalid inputs.\n\nFor general-purpose error values (not type errors), use the `error()` helper\nwhich returns plain `{ $error: true, message }` objects checkable with `isError()`.\n\n### Inline Tests\n\nTests live next to code:\n\n```typescript\nfunction double(x: 0) -> 0 { return x * 2 }\n\ntest('doubles numbers') {\n expect(double(5)).toBe(10)\n expect(double(-3)).toBe(-6)\n}\n```\n\nTests are extracted at compile time and can be:\n\n- Run during transpilation\n- Stripped in production builds\n- Used for documentation generation\n\n### WASM Blocks\n\nDrop into WebAssembly for compute-heavy code:\n\n```typescript\nfunction vectorDot(a: [0], b: [0]) -> 0 {\n let sum = 0\n wasm {\n for (let i = 0; i < a.length; i++) {\n sum = sum + a[i] * b[i]\n }\n }\n return sum\n}\n```\n\nVariables are captured automatically. Falls back to JS if WASM unavailable.\n\n#### SIMD Intrinsics (f32x4)\n\nFor compute-heavy workloads, use f32x4 SIMD intrinsics to process 4 float32 values per instruction:\n\n```typescript\nconst scale = wasm (arr: Float32Array, len: 0, factor: 0.0) -> 0 {\n let s = f32x4_splat(factor)\n for (let i = 0; i < len; i += 4) {\n let off = i * 4\n let v = f32x4_load(arr, off)\n f32x4_store(arr, off, f32x4_mul(v, s))\n }\n} fallback {\n for (let i = 0; i < len; i++) arr[i] *= factor\n}\n```\n\nAvailable intrinsics:\n\n| Intrinsic | Description |\n| ----------------------------------- | ------------------------------------ |\n| `f32x4_load(ptr, byteOffset)` | Load 4 floats from memory into v128 |\n| `f32x4_store(ptr, byteOffset, vec)` | Store v128 as 4 floats to memory |\n| `f32x4_splat(scalar)` | Fill all 4 lanes with a scalar value |\n| `f32x4_extract_lane(vec, N)` | Extract float from lane 0-3 |\n| `f32x4_replace_lane(vec, N, val)` | Replace one lane, return new v128 |\n| `f32x4_add(a, b)` | Lane-wise addition |\n| `f32x4_sub(a, b)` | Lane-wise subtraction |\n| `f32x4_mul(a, b)` | Lane-wise multiplication |\n| `f32x4_div(a, b)` | Lane-wise division |\n| `f32x4_neg(v)` | Negate all lanes |\n| `f32x4_sqrt(v)` | Square root of all lanes |\n\nThis mirrors C/C++ SIMD intrinsics (`_mm_add_ps`, etc.) — explicit, predictable, no auto-vectorization magic.\n\n#### Zero-Copy Arrays: `wasmBuffer()`\n\nBy default, typed arrays passed to WASM blocks are copied into WASM memory before the call and copied back out after. For large arrays called frequently, this overhead can negate WASM's speed advantage.\n\n`wasmBuffer(Constructor, length)` allocates typed arrays directly in WASM linear memory. These arrays work like normal typed arrays from JavaScript, but when passed to a `wasm {}` block, they're zero-copy — the data is already there.\n\n```typescript\n// Allocate particle positions in WASM memory\nconst starX = wasmBuffer(Float32Array, 50000)\nconst starY = wasmBuffer(Float32Array, 50000)\n\n// Use from JS like normal arrays\nfor (let i = 0; i < 50000; i++) {\n starX[i] = (Math.random() - 0.5) * 2000\n starY[i] = (Math.random() - 0.5) * 2000\n}\n\n// Zero-copy SIMD processing\nfunction moveParticles(! xs: Float32Array, ys: Float32Array, len: 0, dx: 0.0, dy: 0.0) {\n wasm {\n let vdx = f32x4_splat(dx)\n let vdy = f32x4_splat(dy)\n for (let i = 0; i < len; i += 4) {\n let off = i * 4\n f32x4_store(xs, off, f32x4_add(f32x4_load(xs, off), vdx))\n f32x4_store(ys, off, f32x4_add(f32x4_load(ys, off), vdy))\n }\n } fallback {\n for (let i = 0; i < len; i++) { xs[i] += dx; ys[i] += dy }\n }\n}\n\n// After WASM runs, JS sees the mutations immediately\nmoveParticles(starX, starY, 50000, 1.0, 0.5)\nconsole.log(starX[0]) // updated in place, no copy\n```\n\nKey points:\n\n- Supported constructors: `Float32Array`, `Float64Array`, `Int32Array`, `Uint8Array`\n- Uses a bump allocator — allocations persist for program lifetime\n- All WASM blocks in a file share one 64MB memory\n- Regular typed arrays still work (copy in/out as before)\n- Use `!` (unsafe) on hot-path functions to skip runtime type checks\n\n---\n\n## Module System\n\nTJS preserves standard ES module semantics exactly. `import` and `export` statements pass through to the output unchanged — TJS does not have its own module resolver.\n\n### Importing .tjs Files\n\nIn **Bun** (with the TJS plugin from `bunfig.toml`):\n\n```typescript\n// .tjs files are transpiled automatically on import\nimport { processOrder } from './orders.tjs'\nimport { validateUser } from './users.ts' // TS files also work\n```\n\nIn the **browser playground**, local modules are resolved from the playground's module store. Relative imports are looked up by name:\n\n```typescript\nimport { formatDate } from './date-utils' // resolves from saved modules\n```\n\nIn **production builds** (`tjs emit`), TJS transpiles `.tjs` → `.js`. Your bundler (esbuild, Rollup, etc.) handles resolution of the output `.js` files normally.\n\n### Importing JS/TS Libraries\n\nTJS files can import any JavaScript or TypeScript library. The imported code runs without TJS validation — it's just normal JS. Add a TJS wrapper at the boundary if you want type safety:\n\n```typescript\nimport { rawGeocode } from 'legacy-geo-pkg'\n\n// Wrap at the boundary — rawGeocode is unchecked, geocode validates its output\nfunction geocode(addr: '') -> { lat: 0.0, lon: 0.0 } {\n return rawGeocode(addr)\n}\n```\n\n### CDN Imports\n\nURL imports work with any ESM CDN:\n\n```typescript\nimport lodash from 'https://esm.sh/lodash@4.17.21'\n```\n\n### Circular Dependencies\n\nTJS doesn't interfere with JS module loading. Circular imports work the same way as in standard ES modules — use lazy getters or late binding if you need to break cycles, exactly as you would in plain JS.\n\n### TypeScript Declaration Files (.d.ts)\n\nTJS can generate `.d.ts` files so TypeScript consumers can use TJS-authored libraries with autocomplete and tooltips:\n\n```bash\nbun src/cli/tjs.ts emit --dts src/lib.tjs -o dist/lib.js\n# Generates dist/lib.js + dist/lib.d.ts\n```\n\nFrom code:\n\n```typescript\nimport { tjs, generateDTS } from 'tjs-lang'\n\nconst result = tjs(source)\nconst dts = generateDTS(result, source)\n```\n\nFunctions get full type declarations. Classes, generics, and predicate-based types get `any`-based stubs that provide IDE hints (parameter names, object shapes) without generating false lint errors for types that TJS validates at runtime.\n\n---\n\n## TypeScript Compatibility\n\n### TS → TJS Converter\n\nConvert existing TypeScript:\n\n```bash\nbun src/cli/tjs.ts convert file.ts\n```\n\n```typescript\n// TypeScript\nfunction greet(name: string, age?: number): string { ... }\n\n// Converts to TJS\nfunction greet(name: '', age = 0) -> '' { ... }\n```\n\n### What Gets Converted\n\n| TypeScript | TJS |\n| -------------------------- | ----------------------- |\n| `name: string` | `name: ''` |\n| `age: number` | `age: 0.0` |\n| `flag: boolean` | `flag: false` |\n| `items: string[]` | `items: ['']` |\n| `age?: number` | `age: 0.0 \\| undefined` |\n| `private foo` | `#foo` |\n| `interface User` | `Type User` |\n| `type Status = 'a' \\| 'b'` | `Union(['a', 'b'])` |\n| `enum Color` | `Enum(...)` |\n\n> **Optional params:** TypeScript `x?: boolean` becomes TJS `x: false | undefined`.\n> This preserves the three-state semantics (`true` / `false` / `undefined`)\n> using a union type. The param is required but explicitly accepts `undefined`.\n\n---\n\n## Performance\n\n| Mode | Overhead | Use Case |\n| --------------- | --------- | ------------------------------- |\n| `safety none` | **1.0x** | Metadata only, no validation |\n| `safety inputs` | **~1.5x** | Production (single-arg objects) |\n| `(!) unsafe` | **1.0x** | Hot paths |\n| `wasm {}` | **<1.0x** | Compute-heavy code |\n\n### Why 1.5x, Not 25x\n\nMost validators interpret schemas at runtime (~25x overhead). TJS generates inline checks at transpile time:\n\n```typescript\n// Generated (JIT-friendly)\nif (\n typeof input !== 'object' ||\n input === null ||\n typeof input.name !== 'string' ||\n typeof input.age !== 'number'\n) {\n return { $error: true, message: 'Invalid input', path: 'fn.input' }\n}\n```\n\nNo schema interpretation. No object iteration. The JIT inlines these completely.\n\n---\n\n## Bare Assignments\n\nUppercase identifiers automatically get `const`:\n\n```typescript\nFoo = Type('test', 'example') // becomes: const Foo = Type(...)\nMyConfig = { debug: true } // becomes: const MyConfig = { ... }\n```\n\n---\n\n## Limitations\n\n### What TJS Doesn't Do\n\n- **No gradual typing** — types are all-or-nothing per function\n- **No complex type inference** — you provide examples, not constraints\n- **No type-level computation** — no conditional types, mapped types, etc.\n\n### What TJS Intentionally Avoids\n\n- Build steps beyond transpilation\n- External type checkers\n- Complex tooling configuration\n- Separation of types from runtime\n\n---\n\n## Troubleshooting\n\n### Common Transpilation Errors\n\n**\"Unexpected token\"** — Usually means TJS-specific syntax (`:` params, `->` returns, `Type`, `Generic`) wasn't recognized. Check:\n\n- Is the file being parsed as TJS (not plain JS)?\n- Are `Type`/`Generic`/`Union` declarations at the top level (not inside functions)?\n- Is the `->` return type before the function body `{`?\n\n**\"Type is not defined\" / \"Generic is not defined\"** — These become `const Name = Type(...)` / `const Name = Generic(...)` after preprocessing. If you see this at runtime, the TJS runtime (`createRuntime()`) wasn't installed, or the file wasn't transpiled through TJS.\n\n**Signature test failures** — TJS runs your function with its example values at transpile time. If the function fails with its own examples, transpilation reports an error. Fix the function or choose better examples:\n\n```typescript\n// BAD: example 0 causes division by zero\nfunction inverse(x: 0) -> 0.0 { return 1 / x }\n\n// GOOD: example 1 works\nfunction inverse(x: 1) -> 0.0 { return 1 / x }\n```\n\n**Monadic errors instead of exceptions** — TJS validation returns `MonadicError` objects (with `$error: true`), it doesn't throw. Check with `isMonadicError(result)`, not `try/catch`:\n\n```typescript\nimport { isMonadicError } from 'tjs-lang'\n\nconst result = myFunction(badInput)\nif (isMonadicError(result)) {\n console.log(result.message) // \"type mismatch: expected string, got number\"\n}\n```\n\n### Debugging Type Checks\n\nEvery transpiled function has `.__tjs` metadata you can inspect:\n\n```typescript\nconsole.log(myFunction.__tjs)\n// { params: { name: { type: { kind: 'string' }, required: true } },\n// returns: { kind: 'string' } }\n```\n\nThe transpiled JS is readable — look at the generated code to see exactly what checks run:\n\n```bash\nbun src/cli/tjs.ts emit myfile.tjs # see the generated JS\n```\n\n### When to Use `!` (Unsafe)\n\nMark functions unsafe when:\n\n- The data source is already validated (e.g., internal helper called only from a validated wrapper)\n- You're in a hot loop and profiling shows the checks matter\n- You're calling a function millions of times with known-good data\n\nDon't use `!` at system boundaries (API handlers, user input, external data).\n\n---\n\n## Learn More\n\n- [AJS Documentation](DOCS-AJS.md) — The agent runtime\n- [Builder's Manifesto](MANIFESTO-BUILDER.md) — Why TJS is fun\n- [Enterprise Guide](MANIFESTO-ENTERPRISE.md) — Why TJS is safe\n- [Technical Context](CONTEXT.md) — Architecture deep dive\n"
155
155
  },
156
156
  {
157
157
  "title": "Starfield",
@@ -655,7 +655,7 @@
655
655
  "title": "CLAUDE.md",
656
656
  "filename": "CLAUDE.md",
657
657
  "path": "CLAUDE.md",
658
- "text": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Project Overview\n\n**tjs-lang** (npm: `tjs-lang`) is a typed JavaScript platform — a language, runtime, and toolchain that transpiles TypeScript and TJS to JavaScript with runtime type validation, inline WASM, monadic errors, and safe eval. It also includes AJS, a gas-metered VM for executing untrusted agent code in any JavaScript environment.\n\n**Three pillars:**\n\n- **TJS** — TypeScript-like syntax where types are examples that survive to runtime as contracts, documentation, and tests. Transpiles TS → TJS → JS in a single fast pass.\n- **AJS** — Agent language that compiles to JSON AST for safe, sandboxed execution with fuel limits and injected capabilities. Code travels to data.\n- **Toolchain** — Compresses transpilation, linting, testing, and documentation generation into one pass. Includes inline WASM with SIMD, polymorphic dispatch, local class extensions, and a browser-based playground.\n\n## Common Commands\n\n```bash\n# Development\nnpm run format # ESLint fix + Prettier\nnpm run test:fast # Core tests (skips LLM & benchmarks)\nnpm run make # Full build (clean, format, grammars, tsc, esbuild)\nnpm run dev # Development server with file watcher\nnpm run start # Build demo + start dev server\nnpm run latest # Clean reinstall (rm node_modules + bun install)\n\n# Testing (framework: bun:test — describe/it/expect)\nbun test # Full test suite\nbun test src/path/to/file.test.ts # Single test file\nbun test --test-name-pattern \"pattern\" # Run tests matching pattern\nSKIP_LLM_TESTS=1 bun test # Skip LLM integration tests\nbun test --coverage # With coverage report\n\n# Efficient test debugging - capture once, query multiple times\nbun test 2>&1 | tee /tmp/test-results.txt | tail -20 # Run and save\ngrep -E \"^\\(fail\\)\" /tmp/test-results.txt # List failures\ngrep -A10 \"test name\" /tmp/test-results.txt # See specific error\n\n# CLI tools\nbun src/cli/tjs.ts check <file> # Parse and type check TJS file\nbun src/cli/tjs.ts run <file> # Transpile and execute\nbun src/cli/tjs.ts types <file> # Output type metadata as JSON\nbun src/cli/tjs.ts emit <file> # Output transpiled JavaScript\nbun src/cli/tjs.ts convert <file> # Convert TypeScript to TJS (--emit-tjs) or JS\nbun src/cli/tjs.ts test <file> # Run inline tests in a TJS file\n\n# Type checking & other\nnpm run typecheck # tsc --noEmit (type check without emitting)\nnpm run test:llm # LM Studio integration tests\nnpm run bench # Vector search benchmarks\nnpm run docs # Generate documentation\n\n# Build standalone CLI binaries\nnpm run build:cli # Compiles tjs + tjsx to dist/\n\n# Deployment (Firebase)\nnpm run deploy # Build demo + deploy functions + hosting\nnpm run deploy:hosting # Hosting only (serves from .demo/)\nnpm run functions:deploy # Cloud functions only\nnpm run functions:serve # Local functions emulator\n```\n\n## Architecture\n\n### Two-Layer Design\n\n1. **Builder Layer** (`src/builder.ts`): Fluent API that constructs AST nodes. Contains no execution logic.\n2. **Runtime Layer** (`src/vm/runtime.ts`): Executes AST nodes. Contains all atom implementations (~2900 lines, security-critical).\n\n### Key Source Files\n\n- `src/index.ts` - Main entry, re-exports everything\n- `src/vm/runtime.ts` - All atom implementations, expression evaluation, fuel charging (~3000 lines, security-critical)\n- `src/vm/vm.ts` - AgentVM class (~226 lines)\n- `src/vm/atoms/batteries.ts` - Battery atoms (vector search, LLM, store operations)\n- `src/builder.ts` - TypedBuilder fluent API (~19KB)\n- `src/lang/parser.ts` - TJS parser with colon shorthand, unsafe markers, return type extraction\n- `src/lang/emitters/ast.ts` - Emits Agent99 AST from parsed source\n- `src/lang/emitters/js.ts` - Emits JavaScript with `__tjs` metadata\n- `src/lang/emitters/from-ts.ts` - TypeScript to TJS/JS transpiler with class metadata extraction\n- `src/lang/inference.ts` - Type inference from example values\n- `src/lang/linter.ts` - Static analysis (unused vars, unreachable code, no-explicit-new)\n- `src/lang/runtime.ts` - TJS runtime (monadic errors, type checking, wrapClass)\n- `src/lang/wasm.ts` - WASM compiler (opcodes, disassembler, bytecode generation)\n- `src/types/` - Type system definitions (Type.ts, Generic.ts)\n- `src/transpiler/` - AJS transpiler (source → AST)\n- `src/batteries/` - LM Studio integration (lazy init, model audit, vector search)\n- `src/store/` - Store implementations for persistence\n- `src/rbac/` - Role-based access control\n- `src/use-cases/` - Integration tests and real-world examples (28 test files)\n- `src/cli/tjs.ts` - CLI tool for check/run/types/emit/convert/test commands\n- `src/cli/tjsx.ts` - JSX/component runner\n- `src/cli/playground.ts` - Local playground server\n- `src/cli/create-app.ts` - Project scaffolding tool\n\n### Core APIs\n\n```typescript\n// Language\najs`...` // Parse AJS to AST\ntjs`...` // Parse TypeScript variant with type metadata\ntranspile(source, options) // Full transpilation with signature extraction\ncreateAgent(source, vm) // Creates callable agent\n\n// VM\nconst vm = new AgentVM(customAtoms)\nawait vm.run(ast, args, { fuel, capabilities, timeoutMs, trace })\n\n// Builder\nAgent.take(schema).varSet(...).httpFetch(...).return(schema)\nvm.Agent // Builder with custom atoms included\n```\n\n### Package Entry Points\n\n```typescript\nimport { Agent, AgentVM, ajs, tjs } from 'tjs-lang' // Main entry\nimport { Eval, SafeFunction } from 'tjs-lang/eval' // Safe eval utilities\nimport { tjs, transpile } from 'tjs-lang/lang' // Language tools only\nimport { fromTS } from 'tjs-lang/lang/from-ts' // TypeScript transpilation\n```\n\n### Transpiler Chain (TS → TJS → JS)\n\nTJS supports transpiling TypeScript to JavaScript with runtime type validation. The pipeline has two distinct, independently testable steps:\n\n**Step 1: TypeScript → TJS** (`fromTS`)\n\n```typescript\nimport { fromTS } from 'tosijs/lang/from-ts'\n\nconst tsSource = `\nfunction greet(name: string): string {\n return \\`Hello, \\${name}!\\`\n}\n`\n\nconst result = fromTS(tsSource, { emitTJS: true })\n// result.code contains TJS:\n// function greet(name: '') -> '' {\n// return \\`Hello, \\${name}!\\`\n// }\n```\n\n**Step 2: TJS → JavaScript** (`tjs`)\n\n```typescript\nimport { tjs } from 'tosijs/lang'\n\nconst tjsSource = `\nfunction greet(name: '') -> '' {\n return \\`Hello, \\${name}!\\`\n}\n`\n\nconst jsResult = tjs(tjsSource)\n// jsResult.code contains JavaScript with __tjs metadata for runtime validation\n```\n\n**Full Chain Example:**\n\n```typescript\nimport { fromTS } from 'tosijs/lang/from-ts'\nimport { tjs } from 'tosijs/lang'\n\n// TypeScript source with type annotations\nconst tsSource = `\nfunction add(a: number, b: number): number {\n return a + b\n}\n`\n\n// Step 1: TS → TJS\nconst tjsResult = fromTS(tsSource, { emitTJS: true })\n\n// Step 2: TJS → JS (with runtime validation)\nconst jsCode = tjs(tjsResult.code)\n\n// Execute the result\nconst fn = new Function('__tjs', jsCode + '; return add')(__tjs_runtime)\nfn(1, 2) // Returns 3\nfn('a', 'b') // Returns { error: 'type mismatch', ... }\n```\n\n**Design Notes:**\n\n- The two steps are intentionally separate for tree-shaking (TS support is optional)\n- `fromTS` lives in a separate entry point (`tosijs/lang/from-ts`)\n- Import only what you need to keep bundle size minimal\n- Each step is independently testable (see `src/lang/codegen.test.ts`)\n\n### Security Model\n\n- **Capability-based**: VM has zero IO by default; inject `fetch`, `store`, `llm` via capabilities\n- **Fuel metering**: Every atom has a cost; execution stops when fuel exhausted\n- **Timeout enforcement**: Default `fuel × 10ms`; explicit `timeoutMs` overrides\n- **Monadic errors**: Errors wrapped in `AgentError` (VM) / `MonadicError` (TJS), not thrown (prevents exception exploits). Use `isMonadicError()` to check — `isError()` is deprecated\n- **Expression sandboxing**: ExprNode AST evaluation, blocked prototype access\n\n### Expression Evaluation\n\nExpressions use AST nodes (`$expr`), not strings:\n\n```typescript\n{ $expr: 'binary', op: '+', left: {...}, right: {...} }\n{ $expr: 'ident', name: 'varName' }\n{ $expr: 'member', object: {...}, property: 'foo' }\n```\n\nEach node costs 0.01 fuel. Forbidden: function calls, `new`, `this`, `__proto__`, `constructor`.\n\n## AJS Expression Gotchas\n\nAJS expressions behave differently from JavaScript in several important ways:\n\n- **Null member access is safe by default**: `null.foo.bar` returns `undefined` silently (uses `?.` semantics internally). This differs from JavaScript which would throw `TypeError`.\n- **No computed member access with variables**: `items[i]` fails at transpile time with \"Computed member access with variables not yet supported\". Literal indices work (`items[0]`, `obj[\"key\"]`). Workaround: use `.map`/`.reduce` atoms instead.\n- **Unknown atom errors**: When an atom doesn't exist, the error is `\"Unknown Atom: <name>\"` with no listing of available atoms.\n- **TJS parameter syntax is NOT TypeScript**: `function foo(x: 'default')` means \"required param, example value 'default'\" — not a TypeScript string literal type. LLMs consistently generate `function foo(x: string)` which is wrong. The colon value is an _example_, not a _type annotation_.\n\n## Testing Strategy\n\n- Unit tests alongside source files (`*.test.ts`)\n- Integration tests in `src/use-cases/` (RAG, orchestration, malicious actors)\n- Security tests in `src/use-cases/malicious-actor.test.ts`\n- Language tests split across 14 files in `src/lang/` (lang.test.ts, features.test.ts, codegen.test.ts, parser.test.ts, from-ts.test.ts, wasm.test.ts, etc.)\n\nCoverage targets: 98% lines on `src/vm/runtime.ts` (security-critical), 80%+ overall.\n\n**Bug fix rule:** Always create a reproduction test case before fixing a bug.\n\n## Key Patterns\n\n### Adding a New Atom\n\n1. Define with `defineAtom(opCode, inputSchema, outputSchema, implementation, { cost, timeoutMs, docs })`\n2. Add to `src/vm/atoms/` and export from `src/vm/atoms/index.ts`\n3. Add tests\n4. Run `npm run test:fast`\n\n**Atom implementation notes:**\n\n- `cost` can be static number or dynamic: `(input, ctx) => number`\n- `timeoutMs` defaults to 1000ms; use `0` for no timeout (e.g., `seq`)\n- Atoms are always async; fuel deduction is automatic in the `exec` wrapper\n\n### Debugging Agents\n\nEnable tracing: `vm.run(ast, args, { trace: true })` returns `TraceEvent[]` with execution path, fuel consumption, and state changes.\n\n### Custom Atoms Must\n\n- Be non-blocking (no synchronous CPU-heavy work)\n- Respect `ctx.signal` for cancellation\n- Access IO only via `ctx.capabilities`\n\n### Value Resolution\n\nThe `resolveValue()` function handles multiple input patterns:\n\n- `{ $kind: 'arg', path: 'varName' }` → lookup in `ctx.args`\n- `{ $expr: ... }` → evaluate ExprNode via `evaluateExpr()`\n- String with dots `'obj.foo.bar'` → traverse state with forbidden property checks\n- Bare strings → lookup in state, else return literal\n\n### Monadic Error Flow\n\nWhen `ctx.error` is set, subsequent atoms in a `seq` skip execution. Errors are wrapped in `AgentError`, not thrown. This prevents exception-based exploits.\n\n### TJS Parser Syntax Extensions\n\nTJS extends JavaScript with type annotations that survive to runtime.\n\n#### Classes (Callable Without `new`)\n\nTJS classes are wrapped to be callable without the `new` keyword:\n\n```typescript\nclass Point {\n constructor(public x: number, public y: number) {}\n}\n\n// Both work identically:\nconst p1 = Point(10, 20) // TJS way - clean\nconst p2 = new Point(10, 20) // Still works, but linter warns\n\n// The linter flags explicit `new` usage:\n// Warning: Unnecessary 'new' keyword. In TJS, classes are callable without 'new'\n```\n\nThe `wrapClass()` function in the runtime uses a Proxy to intercept calls and auto-construct.\n\n#### Function Parameters\n\n```typescript\n// Required param with example value (colon shorthand)\nfunction greet(name: 'Alice') { } // name is required, type inferred as string\n\n// Numeric type narrowing (all valid JS syntax)\nfunction calc(rate: 3.14) { } // number (float) -- has decimal point\nfunction calc(count: 42) { } // integer -- whole number\nfunction calc(index: +0) { } // non-negative integer -- + prefix\n\n// Optional param with default\nfunction greet(name = 'Alice') { } // name is optional, defaults to 'Alice'\n\n// Object parameter with shape\nfunction createUser(user: { name: '', age: 0 }) { }\n\n// Nullable type\nfunction find(id: 0 | null) { } // integer or null\n\n// Optional TS-style\nfunction greet(name?: '') { } // same as name = ''\n```\n\n#### Return Types\n\n```typescript\n// Return type annotation (arrow syntax)\nfunction add(a: 0, b: 0) -> 0 { return a + b }\n\n// Object return type\nfunction getUser(id: 0) -> { name: '', age: 0 } { ... }\n```\n\n#### Safety Markers\n\n```typescript\n// Unsafe function (skips runtime validation)\nfunction fastAdd(! a: 0, b: 0) { return a + b }\n\n// Safe function (explicit validation)\nfunction safeAdd(? a: 0, b: 0) { return a + b }\n\n// Unsafe block\nunsafe {\n // All calls in here skip validation\n fastPath(data)\n}\n```\n\n#### Type Declarations\n\n```typescript\n// Simple type from example\nType Name 'Alice'\n\n// Type with description and example\nType User {\n description: 'a user object'\n example: { name: '', age: 0 }\n}\n\n// Type with predicate (auto-generates type guard from example)\nType EvenNumber {\n description: 'an even number'\n example: 2\n predicate(x) { return x % 2 === 0 }\n}\n```\n\n#### Generic Declarations\n\n```typescript\n// Simple generic\nGeneric Box<T> {\n description: 'a boxed value'\n predicate(x, T) {\n return typeof x === 'object' && x !== null && 'value' in x && T(x.value)\n }\n}\n\n// Generic with default type parameter\nGeneric Container<T, U = ''> {\n description: 'container with label'\n predicate(obj, T, U) {\n return T(obj.item) && U(obj.label)\n }\n}\n```\n\n#### Bare Assignments\n\n```typescript\n// Uppercase identifiers auto-get const\nFoo = Type('test', 'example') // becomes: const Foo = Type(...)\nMyConfig = { debug: true } // becomes: const MyConfig = { ... }\n```\n\n#### Module Safety Directive\n\n```typescript\n// At top of file - sets default validation level\nsafety none // No validation (metadata only)\nsafety inputs // Validate function inputs (default)\nsafety all // Validate everything (debug mode)\n```\n\n#### TJS Mode Directives\n\nJavaScript semantics are the default. TJS improvements are opt-in via file-level directives:\n\n```typescript\nTjsStrict // Enables ALL modes below at once\n\nTjsEquals // == and != use structural equality (Is/IsNot)\nTjsClass // Classes callable without new, explicit new is banned\nTjsDate // Date is banned, use Timestamp/LegalDate instead\nTjsNoeval // eval() and new Function() are banned\nTjsStandard // Newlines as statement terminators (prevents ASI footguns)\nTjsSafeEval // Include Eval/SafeFunction in runtime for dynamic code\n```\n\nMultiple directives can be combined. Place them at the top of the file before any code.\n\n#### Equality Operators\n\nWith `TjsEquals` (or `TjsStrict`), TJS redefines equality to be structural, fixing JavaScript's confusing `==` vs `===` semantics.\n\n| Operator | Meaning | Example |\n| ----------- | -------------------------------- | --------------------------- |\n| `==` | Structural equality | `{a:1} == {a:1}` is `true` |\n| `!=` | Structural inequality | `{a:1} != {a:2}` is `true` |\n| `===` | Identity (same reference) | `obj === obj` is `true` |\n| `!==` | Not same reference | `{a:1} !== {a:1}` is `true` |\n| `a Is b` | Structural equality (explicit) | Same as `==` |\n| `a IsNot b` | Structural inequality (explicit) | Same as `!=` |\n\n```typescript\n// Structural equality - compares values deeply\nconst a = { x: 1, y: [2, 3] }\nconst b = { x: 1, y: [2, 3] }\na == b // true (same structure)\na === b // false (different objects)\n\n// Works with arrays too\n[1, 2, 3] == [1, 2, 3] // true\n\n// Infix operators for readability\nuser Is expectedUser\nresult IsNot errorValue\n```\n\n**Implementation Notes:**\n\n- **AJS (VM)**: The VM's expression evaluator (`src/vm/runtime.ts`) uses `isStructurallyEqual()` for `==`/`!=`\n- **TJS (browser/Node)**: Source transformation converts `==` to `Is()` and `!=` to `IsNot()` calls\n- **`===` and `!==`**: Always preserved as identity checks, never transformed\n- The `Is()` and `IsNot()` functions are available in `src/lang/runtime.ts` and exposed globally\n\n**Custom Equality Protocol:**\n\n- `[tjsEquals]` symbol (`Symbol.for('tjs.equals')`) — highest priority, ideal for Proxies\n- `.Equals` method — backward-compatible, works on any object/class\n- Priority: symbol → `.Equals` → structural comparison\n- `tjsEquals` is exported from `src/lang/runtime.ts` and available as `__tjs.tjsEquals`\n\n#### Polymorphic Functions\n\nMultiple function declarations with the same name are merged into a dispatcher:\n\n```typescript\nfunction area(radius: 3.14) {\n return Math.PI * radius * radius\n}\nfunction area(w: 0.0, h: 0.0) {\n return w * h\n}\n\narea(5) // dispatches to variant 1 (one number)\narea(3, 4) // dispatches to variant 2 (two numbers)\n```\n\nDispatch order: arity first, then type specificity, then declaration order. Ambiguous signatures (same types at same arity) are caught at transpile time.\n\n#### Polymorphic Constructors\n\nClasses can have multiple constructor signatures (requires `TjsClass` directive):\n\n```typescript\nTjsClass\n\nclass Point {\n constructor(x: 0.0, y: 0.0) {\n this.x = x\n this.y = y\n }\n constructor(coords: { x: 0.0; y: 0.0 }) {\n this.x = coords.x\n this.y = coords.y\n }\n}\n\nPoint(3, 4) // variant 1\nPoint({ x: 10, y: 20 }) // variant 2 (both produce correct instanceof)\n```\n\nThe first constructor becomes the real JS constructor; additional variants become factory functions using `Object.create`.\n\n#### Local Class Extensions\n\nAdd methods to built-in types without prototype pollution:\n\n```typescript\nextend String {\n capitalize() { return this[0].toUpperCase() + this.slice(1) }\n}\n\nextend Array {\n last() { return this[this.length - 1] }\n}\n\n'hello'.capitalize() // 'Hello' — rewritten to __ext_String.capitalize.call('hello')\n[1, 2, 3].last() // 3\n```\n\n- Methods are rewritten to `.call()` at transpile time for known-type receivers (zero overhead)\n- Runtime fallback via `registerExtension()`/`resolveExtension()` for unknown types\n- Arrow functions rejected (need `this` binding)\n- Multiple `extend` blocks for same type merge left-to-right\n- File-local only — no cross-module leaking\n\n## WASM Blocks\n\nTJS supports inline WebAssembly for performance-critical code. WASM blocks are compiled at transpile time and embedded as base64 in the output.\n\n### Syntax\n\n```typescript\nconst add = wasm (a: i32, b: i32) -> i32 {\n local.get $a\n local.get $b\n i32.add\n}\n```\n\n### Features\n\n- **Transpile-time compilation**: WASM bytecode is generated during transpilation, not at runtime\n- **WAT comments**: Human-readable WebAssembly Text format is included as comments above the base64\n- **Type-safe**: Parameters and return types are validated\n- **Self-contained**: Compiled WASM is embedded in output JS, no separate .wasm files needed\n\n### Output Example\n\nThe transpiler generates code like:\n\n```javascript\n/*\n * WASM Block: add\n * WAT (WebAssembly Text):\n * (func $add (param $a i32) (param $b i32) (result i32)\n * local.get 0\n * local.get 1\n * i32.add\n * )\n */\nconst add = await (async () => {\n const bytes = Uint8Array.from(atob('AGFzbQEAAAA...'), (c) => c.charCodeAt(0))\n const { instance } = await WebAssembly.instantiate(bytes)\n return instance.exports.fn\n})()\n```\n\n### SIMD Intrinsics (f32x4)\n\nWASM blocks support explicit SIMD via `f32x4_*` intrinsics:\n\n```typescript\nconst scale = wasm (arr: Float32Array, len: 0, factor: 0.0) -> 0 {\n let s = f32x4_splat(factor)\n for (let i = 0; i < len; i += 4) {\n let off = i * 4\n let v = f32x4_load(arr, off)\n f32x4_store(arr, off, f32x4_mul(v, s))\n }\n} fallback {\n for (let i = 0; i < len; i++) arr[i] *= factor\n}\n```\n\nAvailable: `f32x4_load`, `f32x4_store`, `f32x4_splat`, `f32x4_extract_lane`, `f32x4_replace_lane`, `f32x4_add`, `f32x4_sub`, `f32x4_mul`, `f32x4_div`, `f32x4_neg`, `f32x4_sqrt`.\n\n### Zero-Copy Arrays: `wasmBuffer()`\n\n`wasmBuffer(Constructor, length)` allocates typed arrays directly in WASM linear memory. When passed to a `wasm {}` block, these arrays are zero-copy — no marshalling overhead.\n\n```typescript\n// Allocate in WASM memory (zero-copy when passed to wasm blocks)\nconst xs = wasmBuffer(Float32Array, 50000)\n\n// Works like a normal Float32Array from JS\nxs[0] = 3.14\nfor (let i = 0; i < xs.length; i++) xs[i] = Math.random()\n\n// Zero-copy in WASM blocks — data is already in WASM memory\nfunction process(! xs: Float32Array, len: 0, delta: 0.0) {\n wasm {\n let vd = f32x4_splat(delta)\n for (let i = 0; i < len; i += 4) {\n let off = i * 4\n f32x4_store(xs, off, f32x4_add(f32x4_load(xs, off), vd))\n }\n } fallback {\n for (let i = 0; i < len; i++) xs[i] += delta\n }\n}\n\n// After WASM runs, JS sees mutations immediately (same memory)\n```\n\n- Regular `Float32Array` args are copied in before and out after each WASM call\n- `wasmBuffer` arrays skip both copies (detected via `buffer === wasmMemory.buffer`)\n- Uses a bump allocator — allocations persist for program lifetime (no deallocation)\n- All WASM blocks in a file share one `WebAssembly.Memory` (64MB / 1024 pages)\n- Supports `Float32Array`, `Float64Array`, `Int32Array`, `Uint8Array`\n\n### Current Limitations\n\n- No imports/exports beyond the function itself\n- `wasmBuffer` allocations are permanent (bump allocator, no free)\n\n## Dependencies\n\nRuntime (shipped): `acorn` (JS parser, ~30KB), `tosijs-schema` (validation, ~5KB). Both have zero transitive dependencies.\n\n## Forbidden Properties (Security)\n\nThe following property names are blocked in expression evaluation to prevent prototype pollution:\n\n- `__proto__`, `constructor`, `prototype`\n\nThese are hardcoded in `runtime.ts` and checked during member access in `evaluateExpr()`.\n\n## Batteries System\n\nThe batteries (`src/batteries/`) provide zero-config local AI development:\n\n- **Lazy initialization**: First import audits LM Studio models (cached 24 hours)\n- **HTTPS detection**: Blocks local LLM calls from HTTPS contexts (security)\n- **Capabilities interface**: `fetch`, `store` (KV + vector), `llmBattery` (predict/embed)\n\nRegister battery atoms: `new AgentVM(batteryAtoms)` then pass `{ capabilities: batteries }` to `run()`.\n\n### Capability Key Naming\n\nThe base `Capabilities` interface (`runtime.ts`) uses `llm` with `{ predict, embed? }`, but the battery atoms access capabilities via different keys:\n\n| Capability key | Used by | Contains |\n| -------------- | -------------------------------------------------------- | -------------------------------------------- |\n| `llmBattery` | `llmPredictBattery`, `llmVision` | Full `LLMCapability` (`predict` + `embed`) |\n| `vector` | `storeVectorize` | Just `{ embed }` (extracted from llmBattery) |\n| `store` | `storeSearch`, `storeCreateCollection`, `storeVectorAdd` | KV + vector store ops |\n\nBoth `llmBattery` and `vector` can be `undefined`/`null` if LM Studio isn't available or HTTPS is detected.\n\n### Battery Atom Return Types\n\n- **`llmPredictBattery`**: Returns OpenAI message format `{ role?, content?, tool_calls? }` — NOT a plain string\n- **`storeVectorize`**: Returns `number[]` (embedding vector)\n- **`storeSearch`**: Returns `any[]` (matched documents)\n\n## Development Configuration\n\n### Bun Plugin\n\n`bunfig.toml` preloads `src/bun-plugin/tjs-plugin.ts` which enables importing `.tjs` files directly in bun. It also aliases `tjs-lang` to `./src/index.ts` for local development (monorepo-style resolution).\n\n### Code Style\n\n- **Prettier**: Single quotes, no semicolons, 2-space indentation, 80 char width, es5 trailing commas\n- Prefix unused variables with `_` (enforced by ESLint: `argsIgnorePattern: '^_'`)\n- `any` types are allowed (`@typescript-eslint/no-explicit-any: 0`)\n- Module type is ESM (`\"type\": \"module\"` in package.json)\n- Build output goes to `dist/` (declaration files only via `tsconfig.build.json`, bundles via `scripts/build.ts`)\n- Run `npm run format` before committing (ESLint fix + Prettier)\n\n### Firebase Deployment\n\nThe playground is hosted on Firebase (`tjs-platform.web.app`). Demo build output goes to `.demo/` (gitignored) which is the Firebase hosting root. Cloud Functions live in `functions/` with their own build process (`functions/src/*.tjs` → transpile → bundle). Firebase config: `firebase.json`, `.firebaserc`, `firestore.rules`.\n\nThe `docs/` directory contains real documentation (markdown), not build artifacts. See `docs/README.md` for the documentation index.\n\n### Additional Directories\n\n- `tjs-src/` — TJS runtime written in TJS itself (self-hosting)\n- `guides/` — Usage patterns, benchmarks, examples (`patterns.md`, `benchmarks.md`, `tjs-examples.md`)\n- `examples/` — Standalone TJS example files (`hello.tjs`, `datetime.tjs`, `generic-demo.tjs`)\n- `editors/` — Syntax highlighting for Monaco, CodeMirror, Ace, VSCode\n\n### Additional Documentation\n\n- `DOCS-TJS.md` — TJS language guide\n- `DOCS-AJS.md` — AJS runtime guide\n- `CONTEXT.md` — Architecture deep dive\n- `AGENTS.md` — Agent workflow instructions (issue tracking with `bd`, mandatory push-before-done). **Critical**: work is NOT complete until `git push` succeeds; use `bd ready` to find work, `bd close <id>` to complete\n- `PLAN.md` — Roadmap\n\n### Known Gotcha: `tjs()` Returns an Object, Not a String\n\n`tjs(source)` returns `{ code, types, metadata, testResults, ... }`. Use `.code` to get the transpiled JavaScript string. This is a common mistake.\n\n### Known Gotcha: Running Emitted TJS Code in Tests\n\nTranspiled TJS code requires `globalThis.__tjs` to be set up with `createRuntime()` before execution:\n\n```typescript\nimport { createRuntime } from '../lang/runtime'\n\nconst saved = globalThis.__tjs\nglobalThis.__tjs = createRuntime()\ntry {\n const fn = new Function(result.code + '\\nreturn fnName')()\n // ... test fn\n} finally {\n globalThis.__tjs = saved\n}\n```\n\nA `{ standalone: true }` option to inline the ~1KB runtime is planned but not yet implemented.\n"
658
+ "text": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Project Overview\n\n**tjs-lang** (npm: `tjs-lang`) is a typed JavaScript platform — a language, runtime, and toolchain that transpiles TypeScript and TJS to JavaScript with runtime type validation, inline WASM, monadic errors, and safe eval. It also includes AJS, a gas-metered VM for executing untrusted agent code in any JavaScript environment.\n\n**Three pillars:**\n\n- **TJS** — TypeScript-like syntax where types are examples that survive to runtime as contracts, documentation, and tests. Transpiles TS → TJS → JS in a single fast pass.\n- **AJS** — Agent language that compiles to JSON AST for safe, sandboxed execution with fuel limits and injected capabilities. Code travels to data.\n- **Toolchain** — Compresses transpilation, linting, testing, and documentation generation into one pass. Includes inline WASM with SIMD, polymorphic dispatch, local class extensions, and a browser-based playground.\n\n## Common Commands\n\n```bash\n# Development\nnpm run format # ESLint fix + Prettier\nnpm run test:fast # Core tests (skips LLM & benchmarks)\nnpm run make # Full build (clean, format, grammars, tsc, esbuild)\nnpm run dev # Development server with file watcher\nnpm run start # Build demo + start dev server\nnpm run latest # Clean reinstall (rm node_modules + bun install)\n\n# Testing (framework: bun:test — describe/it/expect)\nbun test # Full test suite\nbun test src/path/to/file.test.ts # Single test file\nbun test --test-name-pattern \"pattern\" # Run tests matching pattern\nSKIP_LLM_TESTS=1 bun test # Skip LLM integration tests\nbun test --coverage # With coverage report\n\n# Efficient test debugging - capture once, query multiple times\nbun test 2>&1 | tee /tmp/test-results.txt | tail -20 # Run and save\ngrep -E \"^\\(fail\\)\" /tmp/test-results.txt # List failures\ngrep -A10 \"test name\" /tmp/test-results.txt # See specific error\n\n# CLI tools\nbun src/cli/tjs.ts check <file> # Parse and type check TJS file\nbun src/cli/tjs.ts run <file> # Transpile and execute\nbun src/cli/tjs.ts types <file> # Output type metadata as JSON\nbun src/cli/tjs.ts emit <file> # Output transpiled JavaScript\nbun src/cli/tjs.ts convert <file> # Convert TypeScript to TJS (--emit-tjs) or JS\nbun src/cli/tjs.ts test <file> # Run inline tests in a TJS file\n\n# Type checking & other\nnpm run typecheck # tsc --noEmit (type check without emitting)\nnpm run test:llm # LM Studio integration tests\nnpm run bench # Vector search benchmarks\nnpm run docs # Generate documentation\n\n# Build standalone CLI binaries\nnpm run build:cli # Compiles tjs + tjsx to dist/\n\n# Deployment (Firebase)\nnpm run deploy # Build demo + deploy functions + hosting\nnpm run deploy:hosting # Hosting only (serves from .demo/)\nnpm run functions:deploy # Cloud functions only\nnpm run functions:serve # Local functions emulator\n```\n\n## Architecture\n\n### Two-Layer Design\n\n1. **Builder Layer** (`src/builder.ts`): Fluent API that constructs AST nodes. Contains no execution logic.\n2. **Runtime Layer** (`src/vm/runtime.ts`): Executes AST nodes. Contains all atom implementations (~2900 lines, security-critical).\n\n### Key Source Files\n\n- `src/index.ts` - Main entry, re-exports everything\n- `src/vm/runtime.ts` - All atom implementations, expression evaluation, fuel charging (~3000 lines, security-critical)\n- `src/vm/vm.ts` - AgentVM class (~226 lines)\n- `src/vm/atoms/batteries.ts` - Battery atoms (vector search, LLM, store operations)\n- `src/builder.ts` - TypedBuilder fluent API (~19KB)\n- `src/lang/parser.ts` - TJS parser with colon shorthand, unsafe markers, return type extraction\n- `src/lang/emitters/ast.ts` - Emits Agent99 AST from parsed source\n- `src/lang/emitters/js.ts` - Emits JavaScript with `__tjs` metadata\n- `src/lang/emitters/from-ts.ts` - TypeScript to TJS/JS transpiler with class metadata extraction\n- `src/lang/emitters/dts.ts` - .d.ts declaration file generator from TJS transpilation results\n- `src/lang/inference.ts` - Type inference from example values\n- `src/lang/linter.ts` - Static analysis (unused vars, unreachable code, no-explicit-new)\n- `src/lang/runtime.ts` - TJS runtime (monadic errors, type checking, wrapClass)\n- `src/lang/wasm.ts` - WASM compiler (opcodes, disassembler, bytecode generation)\n- `src/types/` - Type system definitions (Type.ts, Generic.ts)\n- `src/transpiler/` - AJS transpiler (source → AST)\n- `src/batteries/` - LM Studio integration (lazy init, model audit, vector search)\n- `src/store/` - Store implementations for persistence\n- `src/rbac/` - Role-based access control\n- `src/use-cases/` - Integration tests and real-world examples (28 test files)\n- `src/cli/tjs.ts` - CLI tool for check/run/types/emit/convert/test commands\n- `src/cli/tjsx.ts` - JSX/component runner\n- `src/cli/playground.ts` - Local playground server\n- `src/cli/create-app.ts` - Project scaffolding tool\n\n### Core APIs\n\n```typescript\n// Language\najs`...` // Parse AJS to AST\ntjs`...` // Parse TypeScript variant with type metadata\ntranspile(source, options) // Full transpilation with signature extraction\ncreateAgent(source, vm) // Creates callable agent\n\n// VM\nconst vm = new AgentVM(customAtoms)\nawait vm.run(ast, args, { fuel, capabilities, timeoutMs, trace })\n\n// Builder\nAgent.take(schema).varSet(...).httpFetch(...).return(schema)\nvm.Agent // Builder with custom atoms included\n```\n\n### Package Entry Points\n\n```typescript\nimport { Agent, AgentVM, ajs, tjs } from 'tjs-lang' // Main entry\nimport { Eval, SafeFunction } from 'tjs-lang/eval' // Safe eval utilities\nimport { tjs, transpile } from 'tjs-lang/lang' // Language tools only\nimport { fromTS } from 'tjs-lang/lang/from-ts' // TypeScript transpilation\n```\n\n### Transpiler Chain (TS → TJS → JS)\n\nTJS supports transpiling TypeScript to JavaScript with runtime type validation. The pipeline has two distinct, independently testable steps:\n\n**Step 1: TypeScript → TJS** (`fromTS`)\n\n```typescript\nimport { fromTS } from 'tosijs/lang/from-ts'\n\nconst tsSource = `\nfunction greet(name: string): string {\n return \\`Hello, \\${name}!\\`\n}\n`\n\nconst result = fromTS(tsSource, { emitTJS: true })\n// result.code contains TJS:\n// function greet(name: '') -> '' {\n// return \\`Hello, \\${name}!\\`\n// }\n```\n\n**Step 2: TJS → JavaScript** (`tjs`)\n\n```typescript\nimport { tjs } from 'tosijs/lang'\n\nconst tjsSource = `\nfunction greet(name: '') -> '' {\n return \\`Hello, \\${name}!\\`\n}\n`\n\nconst jsResult = tjs(tjsSource)\n// jsResult.code contains JavaScript with __tjs metadata for runtime validation\n```\n\n**Full Chain Example:**\n\n```typescript\nimport { fromTS } from 'tosijs/lang/from-ts'\nimport { tjs } from 'tosijs/lang'\n\n// TypeScript source with type annotations\nconst tsSource = `\nfunction add(a: number, b: number): number {\n return a + b\n}\n`\n\n// Step 1: TS → TJS\nconst tjsResult = fromTS(tsSource, { emitTJS: true })\n\n// Step 2: TJS → JS (with runtime validation)\nconst jsCode = tjs(tjsResult.code)\n\n// Execute the result\nconst fn = new Function('__tjs', jsCode + '; return add')(__tjs_runtime)\nfn(1, 2) // Returns 3\nfn('a', 'b') // Returns { error: 'type mismatch', ... }\n```\n\n**Design Notes:**\n\n- The two steps are intentionally separate for tree-shaking (TS support is optional)\n- `fromTS` lives in a separate entry point (`tosijs/lang/from-ts`)\n- Import only what you need to keep bundle size minimal\n- Each step is independently testable (see `src/lang/codegen.test.ts`)\n- Constrained generics (`<T extends { id: number }>`) use the constraint as the example value instead of `any`\n- Generic defaults (`<T = string>`) use the default as the example value\n- Unconstrained generics (`<T>`) degrade to `any` — there's no information to use\n\n### Security Model\n\n- **Capability-based**: VM has zero IO by default; inject `fetch`, `store`, `llm` via capabilities\n- **Fuel metering**: Every atom has a cost; execution stops when fuel exhausted\n- **Timeout enforcement**: Default `fuel × 10ms`; explicit `timeoutMs` overrides\n- **Monadic errors**: Errors wrapped in `AgentError` (VM) / `MonadicError` (TJS), not thrown (prevents exception exploits). Use `isMonadicError()` to check — `isError()` is deprecated\n- **Expression sandboxing**: ExprNode AST evaluation, blocked prototype access\n\n### Expression Evaluation\n\nExpressions use AST nodes (`$expr`), not strings:\n\n```typescript\n{ $expr: 'binary', op: '+', left: {...}, right: {...} }\n{ $expr: 'ident', name: 'varName' }\n{ $expr: 'member', object: {...}, property: 'foo' }\n```\n\nEach node costs 0.01 fuel. Forbidden: function calls, `new`, `this`, `__proto__`, `constructor`.\n\n## AJS Expression Gotchas\n\nAJS expressions behave differently from JavaScript in several important ways:\n\n- **Null member access is safe by default**: `null.foo.bar` returns `undefined` silently (uses `?.` semantics internally). This differs from JavaScript which would throw `TypeError`.\n- **No computed member access with variables**: `items[i]` fails at transpile time with \"Computed member access with variables not yet supported\". Literal indices work (`items[0]`, `obj[\"key\"]`). Workaround: use `.map`/`.reduce` atoms instead.\n- **Unknown atom errors**: When an atom doesn't exist, the error is `\"Unknown Atom: <name>\"` with no listing of available atoms.\n- **TJS parameter syntax is NOT TypeScript**: `function foo(x: 'default')` means \"required param, example value 'default'\" — not a TypeScript string literal type. LLMs consistently generate `function foo(x: string)` which is wrong. The colon value is an _example_, not a _type annotation_.\n\n## Testing Strategy\n\n- Unit tests alongside source files (`*.test.ts`)\n- Integration tests in `src/use-cases/` (RAG, orchestration, malicious actors)\n- Security tests in `src/use-cases/malicious-actor.test.ts`\n- Language tests split across 14 files in `src/lang/` (lang.test.ts, features.test.ts, codegen.test.ts, parser.test.ts, from-ts.test.ts, wasm.test.ts, etc.)\n\nCoverage targets: 98% lines on `src/vm/runtime.ts` (security-critical), 80%+ overall.\n\n**Bug fix rule:** Always create a reproduction test case before fixing a bug.\n\n## Key Patterns\n\n### Adding a New Atom\n\n1. Define with `defineAtom(opCode, inputSchema, outputSchema, implementation, { cost, timeoutMs, docs })`\n2. Add to `src/vm/atoms/` and export from `src/vm/atoms/index.ts`\n3. Add tests\n4. Run `npm run test:fast`\n\n**Atom implementation notes:**\n\n- `cost` can be static number or dynamic: `(input, ctx) => number`\n- `timeoutMs` defaults to 1000ms; use `0` for no timeout (e.g., `seq`)\n- Atoms are always async; fuel deduction is automatic in the `exec` wrapper\n\n### Debugging Agents\n\nEnable tracing: `vm.run(ast, args, { trace: true })` returns `TraceEvent[]` with execution path, fuel consumption, and state changes.\n\n### Custom Atoms Must\n\n- Be non-blocking (no synchronous CPU-heavy work)\n- Respect `ctx.signal` for cancellation\n- Access IO only via `ctx.capabilities`\n\n### Value Resolution\n\nThe `resolveValue()` function handles multiple input patterns:\n\n- `{ $kind: 'arg', path: 'varName' }` → lookup in `ctx.args`\n- `{ $expr: ... }` → evaluate ExprNode via `evaluateExpr()`\n- String with dots `'obj.foo.bar'` → traverse state with forbidden property checks\n- Bare strings → lookup in state, else return literal\n\n### Monadic Error Flow\n\nWhen `ctx.error` is set, subsequent atoms in a `seq` skip execution. Errors are wrapped in `AgentError`, not thrown. This prevents exception-based exploits.\n\n### TJS Parser Syntax Extensions\n\nTJS extends JavaScript with type annotations that survive to runtime.\n\n#### Classes (Callable Without `new`)\n\nTJS classes are wrapped to be callable without the `new` keyword:\n\n```typescript\nclass Point {\n constructor(public x: number, public y: number) {}\n}\n\n// Both work identically:\nconst p1 = Point(10, 20) // TJS way - clean\nconst p2 = new Point(10, 20) // Still works, but linter warns\n\n// The linter flags explicit `new` usage:\n// Warning: Unnecessary 'new' keyword. In TJS, classes are callable without 'new'\n```\n\nThe `wrapClass()` function in the runtime uses a Proxy to intercept calls and auto-construct.\n\n#### Function Parameters\n\n```typescript\n// Required param with example value (colon shorthand)\nfunction greet(name: 'Alice') { } // name is required, type inferred as string\n\n// Numeric type narrowing (all valid JS syntax)\nfunction calc(rate: 3.14) { } // number (float) -- has decimal point\nfunction calc(count: 42) { } // integer -- whole number\nfunction calc(index: +0) { } // non-negative integer -- + prefix\n\n// Optional param with default\nfunction greet(name = 'Alice') { } // name is optional, defaults to 'Alice'\n\n// Object parameter with shape\nfunction createUser(user: { name: '', age: 0 }) { }\n\n// Nullable type\nfunction find(id: 0 | null) { } // integer or null\n\n// Optional TS-style\nfunction greet(name?: '') { } // same as name = ''\n```\n\n#### Return Types\n\n```typescript\n// Return type annotation (arrow syntax)\nfunction add(a: 0, b: 0) -> 0 { return a + b }\n\n// Object return type\nfunction getUser(id: 0) -> { name: '', age: 0 } { ... }\n```\n\n#### Safety Markers\n\n```typescript\n// Unsafe function (skips runtime validation)\nfunction fastAdd(! a: 0, b: 0) { return a + b }\n\n// Safe function (explicit validation)\nfunction safeAdd(? a: 0, b: 0) { return a + b }\n\n// Unsafe block\nunsafe {\n // All calls in here skip validation\n fastPath(data)\n}\n```\n\n#### Type Declarations\n\n```typescript\n// Simple type from example\nType Name 'Alice'\n\n// Type with description and example\nType User {\n description: 'a user object'\n example: { name: '', age: 0 }\n}\n\n// Type with predicate (auto-generates type guard from example)\nType EvenNumber {\n description: 'an even number'\n example: 2\n predicate(x) { return x % 2 === 0 }\n}\n```\n\n#### Generic Declarations\n\n```typescript\n// Simple generic\nGeneric Box<T> {\n description: 'a boxed value'\n predicate(x, T) {\n return typeof x === 'object' && x !== null && 'value' in x && T(x.value)\n }\n}\n\n// Generic with default type parameter\nGeneric Container<T, U = ''> {\n description: 'container with label'\n predicate(obj, T, U) {\n return T(obj.item) && U(obj.label)\n }\n}\n\n// Generic with declaration block (for .d.ts emission)\n// The declaration block contains TypeScript syntax emitted verbatim into .d.ts\n// It is stripped from runtime JS output\nGeneric BoxedProxy<T> {\n predicate(x, T) { return typeof x === 'object' && T(x.value) }\n declaration {\n value: T\n path: string\n observe(cb: (path: string) => void): void\n }\n}\n```\n\n#### Bare Assignments\n\n```typescript\n// Uppercase identifiers auto-get const\nFoo = Type('test', 'example') // becomes: const Foo = Type(...)\nMyConfig = { debug: true } // becomes: const MyConfig = { ... }\n```\n\n#### Module Safety Directive\n\n```typescript\n// At top of file - sets default validation level\nsafety none // No validation (metadata only)\nsafety inputs // Validate function inputs (default)\nsafety all // Validate everything (debug mode)\n```\n\n#### TJS Mode Directives\n\nJavaScript semantics are the default. TJS improvements are opt-in via file-level directives:\n\n```typescript\nTjsStrict // Enables ALL modes below at once\n\nTjsEquals // == and != use structural equality (Is/IsNot)\nTjsClass // Classes callable without new, explicit new is banned\nTjsDate // Date is banned, use Timestamp/LegalDate instead\nTjsNoeval // eval() and new Function() are banned\nTjsStandard // Newlines as statement terminators (prevents ASI footguns)\nTjsSafeEval // Include Eval/SafeFunction in runtime for dynamic code\n```\n\nMultiple directives can be combined. Place them at the top of the file before any code.\n\n#### Equality Operators\n\nWith `TjsEquals` (or `TjsStrict`), TJS redefines equality to be structural, fixing JavaScript's confusing `==` vs `===` semantics.\n\n| Operator | Meaning | Example |\n| ----------- | -------------------------------- | --------------------------- |\n| `==` | Structural equality | `{a:1} == {a:1}` is `true` |\n| `!=` | Structural inequality | `{a:1} != {a:2}` is `true` |\n| `===` | Identity (same reference) | `obj === obj` is `true` |\n| `!==` | Not same reference | `{a:1} !== {a:1}` is `true` |\n| `a Is b` | Structural equality (explicit) | Same as `==` |\n| `a IsNot b` | Structural inequality (explicit) | Same as `!=` |\n\n```typescript\n// Structural equality - compares values deeply\nconst a = { x: 1, y: [2, 3] }\nconst b = { x: 1, y: [2, 3] }\na == b // true (same structure)\na === b // false (different objects)\n\n// Works with arrays too\n[1, 2, 3] == [1, 2, 3] // true\n\n// Infix operators for readability\nuser Is expectedUser\nresult IsNot errorValue\n```\n\n**Implementation Notes:**\n\n- **AJS (VM)**: The VM's expression evaluator (`src/vm/runtime.ts`) uses `isStructurallyEqual()` for `==`/`!=`\n- **TJS (browser/Node)**: Source transformation converts `==` to `Is()` and `!=` to `IsNot()` calls\n- **`===` and `!==`**: Always preserved as identity checks, never transformed\n- The `Is()` and `IsNot()` functions are available in `src/lang/runtime.ts` and exposed globally\n\n**Custom Equality Protocol:**\n\n- `[tjsEquals]` symbol (`Symbol.for('tjs.equals')`) — highest priority, ideal for Proxies\n- `.Equals` method — backward-compatible, works on any object/class\n- Priority: symbol → `.Equals` → structural comparison\n- `tjsEquals` is exported from `src/lang/runtime.ts` and available as `__tjs.tjsEquals`\n\n#### Polymorphic Functions\n\nMultiple function declarations with the same name are merged into a dispatcher:\n\n```typescript\nfunction area(radius: 3.14) {\n return Math.PI * radius * radius\n}\nfunction area(w: 0.0, h: 0.0) {\n return w * h\n}\n\narea(5) // dispatches to variant 1 (one number)\narea(3, 4) // dispatches to variant 2 (two numbers)\n```\n\nDispatch order: arity first, then type specificity, then declaration order. Ambiguous signatures (same types at same arity) are caught at transpile time.\n\n#### Polymorphic Constructors\n\nClasses can have multiple constructor signatures (requires `TjsClass` directive):\n\n```typescript\nTjsClass\n\nclass Point {\n constructor(x: 0.0, y: 0.0) {\n this.x = x\n this.y = y\n }\n constructor(coords: { x: 0.0; y: 0.0 }) {\n this.x = coords.x\n this.y = coords.y\n }\n}\n\nPoint(3, 4) // variant 1\nPoint({ x: 10, y: 20 }) // variant 2 (both produce correct instanceof)\n```\n\nThe first constructor becomes the real JS constructor; additional variants become factory functions using `Object.create`.\n\n#### Local Class Extensions\n\nAdd methods to built-in types without prototype pollution:\n\n```typescript\nextend String {\n capitalize() { return this[0].toUpperCase() + this.slice(1) }\n}\n\nextend Array {\n last() { return this[this.length - 1] }\n}\n\n'hello'.capitalize() // 'Hello' — rewritten to __ext_String.capitalize.call('hello')\n[1, 2, 3].last() // 3\n```\n\n- Methods are rewritten to `.call()` at transpile time for known-type receivers (zero overhead)\n- Runtime fallback via `registerExtension()`/`resolveExtension()` for unknown types\n- Arrow functions rejected (need `this` binding)\n- Multiple `extend` blocks for same type merge left-to-right\n- File-local only — no cross-module leaking\n\n## WASM Blocks\n\nTJS supports inline WebAssembly for performance-critical code. WASM blocks are compiled at transpile time and embedded as base64 in the output.\n\n### Syntax\n\n```typescript\nconst add = wasm (a: i32, b: i32) -> i32 {\n local.get $a\n local.get $b\n i32.add\n}\n```\n\n### Features\n\n- **Transpile-time compilation**: WASM bytecode is generated during transpilation, not at runtime\n- **WAT comments**: Human-readable WebAssembly Text format is included as comments above the base64\n- **Type-safe**: Parameters and return types are validated\n- **Self-contained**: Compiled WASM is embedded in output JS, no separate .wasm files needed\n\n### Output Example\n\nThe transpiler generates code like:\n\n```javascript\n/*\n * WASM Block: add\n * WAT (WebAssembly Text):\n * (func $add (param $a i32) (param $b i32) (result i32)\n * local.get 0\n * local.get 1\n * i32.add\n * )\n */\nconst add = await (async () => {\n const bytes = Uint8Array.from(atob('AGFzbQEAAAA...'), (c) => c.charCodeAt(0))\n const { instance } = await WebAssembly.instantiate(bytes)\n return instance.exports.fn\n})()\n```\n\n### SIMD Intrinsics (f32x4)\n\nWASM blocks support explicit SIMD via `f32x4_*` intrinsics:\n\n```typescript\nconst scale = wasm (arr: Float32Array, len: 0, factor: 0.0) -> 0 {\n let s = f32x4_splat(factor)\n for (let i = 0; i < len; i += 4) {\n let off = i * 4\n let v = f32x4_load(arr, off)\n f32x4_store(arr, off, f32x4_mul(v, s))\n }\n} fallback {\n for (let i = 0; i < len; i++) arr[i] *= factor\n}\n```\n\nAvailable: `f32x4_load`, `f32x4_store`, `f32x4_splat`, `f32x4_extract_lane`, `f32x4_replace_lane`, `f32x4_add`, `f32x4_sub`, `f32x4_mul`, `f32x4_div`, `f32x4_neg`, `f32x4_sqrt`.\n\n### Zero-Copy Arrays: `wasmBuffer()`\n\n`wasmBuffer(Constructor, length)` allocates typed arrays directly in WASM linear memory. When passed to a `wasm {}` block, these arrays are zero-copy — no marshalling overhead.\n\n```typescript\n// Allocate in WASM memory (zero-copy when passed to wasm blocks)\nconst xs = wasmBuffer(Float32Array, 50000)\n\n// Works like a normal Float32Array from JS\nxs[0] = 3.14\nfor (let i = 0; i < xs.length; i++) xs[i] = Math.random()\n\n// Zero-copy in WASM blocks — data is already in WASM memory\nfunction process(! xs: Float32Array, len: 0, delta: 0.0) {\n wasm {\n let vd = f32x4_splat(delta)\n for (let i = 0; i < len; i += 4) {\n let off = i * 4\n f32x4_store(xs, off, f32x4_add(f32x4_load(xs, off), vd))\n }\n } fallback {\n for (let i = 0; i < len; i++) xs[i] += delta\n }\n}\n\n// After WASM runs, JS sees mutations immediately (same memory)\n```\n\n- Regular `Float32Array` args are copied in before and out after each WASM call\n- `wasmBuffer` arrays skip both copies (detected via `buffer === wasmMemory.buffer`)\n- Uses a bump allocator — allocations persist for program lifetime (no deallocation)\n- All WASM blocks in a file share one `WebAssembly.Memory` (64MB / 1024 pages)\n- Supports `Float32Array`, `Float64Array`, `Int32Array`, `Uint8Array`\n\n### Current Limitations\n\n- No imports/exports beyond the function itself\n- `wasmBuffer` allocations are permanent (bump allocator, no free)\n\n## Dependencies\n\nRuntime (shipped): `acorn` (JS parser, ~30KB), `tosijs-schema` (validation, ~5KB). Both have zero transitive dependencies.\n\n## Forbidden Properties (Security)\n\nThe following property names are blocked in expression evaluation to prevent prototype pollution:\n\n- `__proto__`, `constructor`, `prototype`\n\nThese are hardcoded in `runtime.ts` and checked during member access in `evaluateExpr()`.\n\n## Batteries System\n\nThe batteries (`src/batteries/`) provide zero-config local AI development:\n\n- **Lazy initialization**: First import audits LM Studio models (cached 24 hours)\n- **HTTPS detection**: Blocks local LLM calls from HTTPS contexts (security)\n- **Capabilities interface**: `fetch`, `store` (KV + vector), `llmBattery` (predict/embed)\n\nRegister battery atoms: `new AgentVM(batteryAtoms)` then pass `{ capabilities: batteries }` to `run()`.\n\n### Capability Key Naming\n\nThe base `Capabilities` interface (`runtime.ts`) uses `llm` with `{ predict, embed? }`, but the battery atoms access capabilities via different keys:\n\n| Capability key | Used by | Contains |\n| -------------- | -------------------------------------------------------- | -------------------------------------------- |\n| `llmBattery` | `llmPredictBattery`, `llmVision` | Full `LLMCapability` (`predict` + `embed`) |\n| `vector` | `storeVectorize` | Just `{ embed }` (extracted from llmBattery) |\n| `store` | `storeSearch`, `storeCreateCollection`, `storeVectorAdd` | KV + vector store ops |\n\nBoth `llmBattery` and `vector` can be `undefined`/`null` if LM Studio isn't available or HTTPS is detected.\n\n### Battery Atom Return Types\n\n- **`llmPredictBattery`**: Returns OpenAI message format `{ role?, content?, tool_calls? }` — NOT a plain string\n- **`storeVectorize`**: Returns `number[]` (embedding vector)\n- **`storeSearch`**: Returns `any[]` (matched documents)\n\n## Development Configuration\n\n### Bun Plugin\n\n`bunfig.toml` preloads `src/bun-plugin/tjs-plugin.ts` which enables importing `.tjs` files directly in bun. It also aliases `tjs-lang` to `./src/index.ts` for local development (monorepo-style resolution).\n\n### Code Style\n\n- **Prettier**: Single quotes, no semicolons, 2-space indentation, 80 char width, es5 trailing commas\n- Prefix unused variables with `_` (enforced by ESLint: `argsIgnorePattern: '^_'`)\n- `any` types are allowed (`@typescript-eslint/no-explicit-any: 0`)\n- Module type is ESM (`\"type\": \"module\"` in package.json)\n- Build output goes to `dist/` (declaration files only via `tsconfig.build.json`, bundles via `scripts/build.ts`)\n- Run `npm run format` before committing (ESLint fix + Prettier)\n\n### Firebase Deployment\n\nThe playground is hosted on Firebase (`tjs-platform.web.app`). Demo build output goes to `.demo/` (gitignored) which is the Firebase hosting root. Cloud Functions live in `functions/` with their own build process (`functions/src/*.tjs` → transpile → bundle). Firebase config: `firebase.json`, `.firebaserc`, `firestore.rules`.\n\nThe `docs/` directory contains real documentation (markdown), not build artifacts. See `docs/README.md` for the documentation index.\n\n### Additional Directories\n\n- `tjs-src/` — TJS runtime written in TJS itself (self-hosting)\n- `guides/` — Usage patterns, benchmarks, examples (`patterns.md`, `benchmarks.md`, `tjs-examples.md`)\n- `examples/` — Standalone TJS example files (`hello.tjs`, `datetime.tjs`, `generic-demo.tjs`)\n- `editors/` — Syntax highlighting for Monaco, CodeMirror, Ace, VSCode\n\n### Additional Documentation\n\n- `DOCS-TJS.md` — TJS language guide\n- `DOCS-AJS.md` — AJS runtime guide\n- `CONTEXT.md` — Architecture deep dive\n- `AGENTS.md` — Agent workflow instructions (issue tracking with `bd`, mandatory push-before-done). **Critical**: work is NOT complete until `git push` succeeds; use `bd ready` to find work, `bd close <id>` to complete\n- `PLAN.md` — Roadmap\n\n### Known Gotcha: `tjs()` Returns an Object, Not a String\n\n`tjs(source)` returns `{ code, types, metadata, testResults, ... }`. Use `.code` to get the transpiled JavaScript string. This is a common mistake.\n\n### Known Gotcha: Running Emitted TJS Code in Tests\n\nTranspiled TJS code requires `globalThis.__tjs` to be set up with `createRuntime()` before execution:\n\n```typescript\nimport { createRuntime } from '../lang/runtime'\n\nconst saved = globalThis.__tjs\nglobalThis.__tjs = createRuntime()\ntry {\n const fn = new Function(result.code + '\\nreturn fnName')()\n // ... test fn\n} finally {\n globalThis.__tjs = saved\n}\n```\n\nA `{ standalone: true }` option to inline the ~1KB runtime is planned but not yet implemented.\n"
659
659
  },
660
660
  {
661
661
  "title": "Context: Working with tosijs-schema",
@@ -815,7 +815,7 @@
815
815
  "group": "docs",
816
816
  "order": 0,
817
817
  "navTitle": "TJS for JS Devs",
818
- "text": "<!--{\"section\": \"tjs-for-js\", \"group\": \"docs\", \"order\": 0, \"navTitle\": \"TJS for JS Devs\"}-->\n\n# TJS for JavaScript Programmers\n\n_Everything you already know, plus the things you always wished JavaScript had._\n\n---\n\n## The One-Sentence Pitch\n\nTJS is JavaScript with types that actually exist at runtime, equality that actually works, and errors that don't crash your program.\n\n---\n\n## What Stays the Same\n\nTJS is a superset of JavaScript. All of this works exactly as you expect:\n\n```javascript\nconst x = 42\nlet name = 'Alice'\nconst items = [1, 2, 3].map((n) => n * 2)\nconst user = { name: 'Bob', age: 30 }\nconst { name: userName, age } = user\nconst merged = { ...defaults, ...overrides }\nconst greeting = `Hello, ${name}!`\n\nfor (const item of items) {\n console.log(item)\n}\nwhile (condition) {\n /* ... */\n}\nif (x > 0) {\n /* ... */\n} else {\n /* ... */\n}\n\ntry {\n riskyThing()\n} catch (e) {\n handleError(e)\n}\n\nclass Dog {\n #name\n constructor(name) {\n this.#name = name\n }\n get name() {\n return this.#name\n }\n}\n\nimport { something } from './module.js'\nexport function myFunction() {\n /* ... */\n}\n```\n\nIf you don't use any TJS features, your code is just JavaScript. No lock-in.\n\n---\n\n## What's Different\n\n### 1. Types Are Example Values\n\nIn JavaScript, there are no types. In TJS, you annotate parameters with\nan example of a valid value:\n\n```javascript\n// JavaScript\nfunction greet(name) {\n return `Hello, ${name}!`\n}\n\n// TJS - name is required and must be a string (like 'World')\nfunction greet(name: 'World') -> '' {\n return `Hello, ${name}!`\n}\n```\n\nThe `: 'World'` means \"required, must be a string, here's an example.\" The `-> ''` means \"returns a string.\" These aren't abstract type annotations -- they're concrete values the system can use for testing, documentation, and validation.\n\n| You Write | TJS Infers |\n| ----------------- | ----------------------------- |\n| `name: 'Alice'` | Required string |\n| `count: 42` | Required integer |\n| `rate: 3.14` | Required number (float) |\n| `age: +20` | Required non-negative integer |\n| `flag: true` | Required boolean |\n| `items: [0]` | Required integer[] |\n| `values: [0.0]` | Required number[] |\n| `user: { n: '' }` | Required object |\n| `id: 0 \\|\\| null` | integer or null |\n\nAll of these are valid JavaScript expressions. `42` vs `42.0` vs `+42` are\nall legal JS -- TJS leverages this to distinguish integer, float, and\nnon-negative integer types at the syntax level.\n\nNote: `|| null` means the value accepts the base type _or_ `null`, but not\n`undefined`. TJS treats `null` and `undefined` as distinct types\n(`typeOf(null) === 'null'`, `typeOf(undefined) === 'undefined'`).\n\n**Optional** parameters use `=` (just like JS default values):\n\n```javascript\nfunction greet(name = 'World') {\n /* ... */\n} // optional, defaults to 'World'\nfunction retry(count = 3) {\n /* ... */\n} // optional, defaults to 3 (integer)\n```\n\n**The difference:** `: value` means required. `= value` means optional with a default. In plain JS, both would be written as `= value`.\n\n### 2. Numeric Type Narrowing\n\nTJS distinguishes three numeric types using valid JavaScript syntax:\n\n```javascript\nfunction process(\n rate: 3.14, // number (float) -- has a decimal point\n count: 42, // integer -- whole number, no decimal\n index: +0 // non-negative integer -- prefixed with +\n) { /* ... */ }\n```\n\n| You Write | Type Inferred | Validates |\n| --------- | ---------------------- | ------------------------------- |\n| `3.14` | `number` (float) | Any number |\n| `0.0` | `number` (float) | Any number |\n| `42` | `integer` | `Number.isInteger(x)` |\n| `0` | `integer` | `Number.isInteger(x)` |\n| `+20` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n| `+0` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n| `-5` | `integer` | `Number.isInteger(x)` |\n| `-3.5` | `number` (float) | Any number |\n\nThese are all valid JavaScript expressions -- TJS just reads the syntax more\ncarefully than JS does. At runtime, passing `3.14` to a parameter typed as\ninteger returns a monadic error.\n\n### 3. Return Type Annotations\n\nTJS uses `->` for return types:\n\n```javascript\n// Returns an integer\nfunction add(a: 0, b: 0) -> 0 {\n return a + b\n}\n\n// Returns an object with specific shape\nfunction getUser(id: 0) -> { name: '', age: 0 } {\n return { name: 'Alice', age: 30 }\n}\n```\n\nThe return type example doubles as an automatic test. When you write `-> 0`, TJS will call `add(0, 0)` at transpile time and verify the result is a number.\n\n### 4. Equality That Works\n\nJavaScript's `==` is notoriously broken (type coercion). Its `===` doesn't do structural comparison. TJS fixes this:\n\n```javascript\n// JavaScript\n[1, 2] === [1, 2] // false (different references)\n{ a: 1 } === { a: 1 } // false (different references)\n\n// TJS (with structural equality enabled)\n[1, 2] == [1, 2] // true (same structure)\n{ a: 1 } == { a: 1 } // true (same structure)\n[1, 2] === [1, 2] // false (different references, identity check)\n```\n\nTJS redefines the operators:\n\n| Operator | JavaScript | TJS |\n| -------- | ------------------- | --------------------- |\n| `==` | Coercive equality | Structural equality |\n| `!=` | Coercive inequality | Structural inequality |\n| `===` | Strict equality | Identity (same ref) |\n| `!==` | Strict inequality | Not same reference |\n\nYou can also use the explicit forms `Is` and `IsNot`:\n\n```javascript\nuser Is expectedUser // structural deep equality\nresult IsNot errorValue // structural deep inequality\n```\n\nClasses can define custom equality:\n\n```javascript\nclass Point {\n constructor(x: 0, y: 0) { this.x = x; this.y = y }\n Equals(other) { return this.x === other.x && this.y === other.y }\n}\n\nPoint(1, 2) Is Point(1, 2) // true, uses .Equals\n```\n\nNote: structural equality does not handle circular references. Use `===`\nfor objects that might be circular, or define an `.Equals` method.\n\n### 5. Errors Are Values, Not Exceptions\n\nIn JavaScript, type errors crash your program. In TJS, they're values:\n\n```javascript\nfunction double(x: 0) -> 0 {\n return x * 2\n}\n\ndouble(5) // 10\ndouble('oops') // { $error: true, message: \"Expected number for 'double.x', got string\" }\n```\n\nNo `try`/`catch`. No crashes. The caller gets a clear error value they can inspect:\n\n```javascript\nconst result = double(input)\nif (result?.$error) {\n console.log(result.message) // handle gracefully\n} else {\n useResult(result)\n}\n```\n\nErrors propagate automatically through function calls. If you pass a monadic error to another TJS function, it passes through without executing:\n\n```javascript\nconst a = step1(badInput) // MonadicError\nconst b = step2(a) // skips execution, returns the same error\nconst c = step3(b) // skips again -- error flows to the surface\n```\n\nThis is sometimes called \"railway-oriented programming.\"\n\n### 6. Classes Without `new`\n\nTJS classes are callable as functions:\n\n```javascript\n// JavaScript\nconst p = new Point(10, 20)\n\n// TJS - both work, but the clean form is preferred\nconst p1 = Point(10, 20) // works\nconst p2 = new Point(10, 20) // also works (linter warns)\n```\n\nThis is cleaner and more consistent with functional style. Under the hood, TJS uses a Proxy to auto-construct when you call a class as a function.\n\n### 7. `const` for Free\n\nUppercase identifiers automatically get `const`:\n\n```javascript\n// TJS\nConfig = { debug: true, version: '1.0' }\nMaxRetries = 3\n\n// Transpiles to\nconst Config = { debug: true, version: '1.0' }\nconst MaxRetries = 3\n```\n\nLowercase identifiers behave normally.\n\n---\n\n## The Type System\n\nJavaScript has no type system. Libraries like Zod and Ajv bolt one on,\nbut they're separate from your function signatures -- a second source of\ntruth that drifts. TJS's `Type()` built-in gives you as much narrowing\nand specificity as you want, in one place, using plain functions you\nalready know how to write.\n\n### From Simple to Sophisticated\n\n```javascript\n// Simple -- infer type from an example value\nType Name 'Alice' // any string\n\n// Descriptive -- add documentation\nType User {\n description: 'a registered user'\n example: { name: '', age: 0, email: '' }\n}\n\n// Constrained -- add a predicate for narrowing\nType PositiveNumber {\n description: 'a number greater than zero'\n example: 1\n predicate(x) { return typeof x === 'number' && x > 0 }\n}\n\n// Domain-specific -- real business rules\nType USZipCode {\n description: '5-digit US zip code'\n example: '90210'\n predicate(v) { return typeof v === 'string' && /^\\d{5}$/.test(v) }\n}\n\nType Email {\n description: 'email address'\n example: 'user@example.com'\n predicate(v) { return typeof v === 'string' && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(v) }\n}\n```\n\nThe key insight: a `predicate` is just a function that returns `true` or\n`false`. If you can express a constraint as a boolean check, you can make\nit a type. No schema DSL to learn, no special syntax -- it's JavaScript\nall the way down.\n\nTypes work in function signatures:\n\n```javascript\nfunction sendWelcome(email: Email, name: Name) -> '' {\n return `Welcome, ${name}! Confirmation sent to ${email}.`\n}\n\nsendWelcome('alice@example.com', 'Alice') // works\nsendWelcome('not-an-email', 'Alice') // MonadicError\n```\n\nTJS also ships common types out of the box: `TString`, `TNumber`,\n`TBoolean`, `TInteger`, `TPositiveInt`, `TNonEmptyString`, `TEmail`,\n`TUrl`, `TUuid`, `Timestamp`, `LegalDate`. No imports from a validation\nlibrary needed.\n\n### Combinators\n\nCompose types from other types:\n\n```javascript\nType OptionalEmail Nullable(Email) // Email | null\nType UserIds TArray(TPositiveInt) // array of positive integers\n```\n\n### Unions and Enums\n\n```javascript\n// Union of string literals\nUnion Status 'task status' 'pending' | 'active' | 'done'\n\n// Enum with named members\nEnum Color 'CSS color' {\n Red = 'red'\n Green = 'green'\n Blue = 'blue'\n}\n\n// Discriminated union -- like tagged unions or sum types\nconst Shape = Union('kind', {\n circle: { radius: 0 },\n rectangle: { width: 0, height: 0 }\n})\n```\n\n### Generics\n\nRuntime-checkable generic types:\n\n```javascript\nGeneric Box<T> {\n description: 'a boxed value'\n predicate(x, T) {\n return typeof x === 'object' && x !== null && 'value' in x && T(x.value)\n }\n}\n\n// Instantiate with a concrete type\nconst NumberBox = Box(TNumber)\nNumberBox.check({ value: 42 }) // true\nNumberBox.check({ value: 'nope' }) // false\n```\n\n---\n\n## Runtime Metadata\n\nEvery TJS function carries its type information at runtime via `__tjs`:\n\n```javascript\nfunction createUser(input: { name: '', age: 0 }) -> { id: 0 } {\n return { id: 123 }\n}\n\ncreateUser.__tjs\n// {\n// params: { input: { type: { kind: 'object', shape: {...} }, required: true } },\n// returns: { type: { kind: 'object', shape: { id: { kind: 'number' } } } }\n// }\n```\n\nThis metadata enables autocomplete from live objects, automatic documentation\ngeneration, and runtime reflection -- things that require build tools and\nexternal libraries in vanilla JavaScript.\n\n---\n\n## Safety Levels\n\nYou control how much validation TJS applies:\n\n```javascript\n// Per-module (top of file)\nsafety none // Metadata only -- no runtime checks (fastest)\nsafety inputs // Validate inputs only (default)\nsafety all // Validate everything (debug mode)\n\n// Per-function\nfunction fastPath(! x: 0) { /* ... */ } // Skip validation\nfunction safePath(? x: 0) { /* ... */ } // Force validation\n```\n\nUse `!` (skip validation) only in hot loops where every microsecond counts\nand the data source is already trusted. In all other cases, the ~1.5x\noverhead of `safety inputs` is negligible compared to the bugs it catches.\n\n### Unsafe Blocks\n\nSkip validation for a hot inner loop:\n\n```javascript\nunsafe {\n for (let i = 0; i < million; i++) {\n hotFunction(data[i])\n }\n}\n```\n\n---\n\n## Inline Tests\n\nTests live next to the code they test and run at transpile time:\n\n```javascript\nfunction isPrime(n: 2) -> true {\n if (n < 2) return false\n for (let i = 2; i * i <= n; i++) {\n if (n % i === 0) return false\n }\n return true\n}\n\ntest 'prime detection' {\n expect(isPrime(2)).toBe(true)\n expect(isPrime(4)).toBe(false)\n expect(isPrime(17)).toBe(true)\n}\n```\n\nTests are stripped from production output. They're also generated\nautomatically from return type annotations -- `-> true` means TJS will call\n`isPrime(2)` and verify it returns a boolean.\n\n---\n\n## WASM Blocks\n\nFor compute-heavy code, drop into WebAssembly:\n\n```javascript\nconst add = wasm (a: i32, b: i32) -> i32 {\n local.get $a\n local.get $b\n i32.add\n}\n\nadd(1, 2) // 3, runs as native WASM\n```\n\nWASM is compiled at transpile time and embedded as base64 in the output.\nNo separate `.wasm` files.\n\n---\n\n## Safe Eval\n\nThe killer feature for many use cases. Run untrusted code safely:\n\n```javascript\nimport { Eval, SafeFunction } from 'tjs-lang/eval'\n\n// One-shot evaluation\nconst { result } = await Eval({\n code: 'return items.filter(x => x > threshold)',\n context: { items: [1, 5, 10, 15], threshold: 7 },\n fuel: 1000,\n})\n// result: [10, 15]\n\n// Reusable safe function\nconst transform = await SafeFunction({\n body: 'return x * multiplier',\n params: ['x'],\n fuel: 500,\n})\n\nawait transform(21) // { result: 42, fuelUsed: 8 }\n```\n\n- Gas-limited (fuel runs out = execution stops, no infinite loops)\n- Capability-based (no I/O unless you grant it)\n- No `eval()`, no CSP violations, no containers\n\n---\n\n## What You Give Up\n\nTJS is opinionated. Here's what changes:\n\n| JavaScript | TJS | Why |\n| -------------------------- | ----------------------------- | ------------------------------- |\n| `==` (type coercion) | `==` (structural equality) | Coercion is a bug factory |\n| Exceptions for type bugs | Monadic error values | Exceptions escape, values don't |\n| `new ClassName()` | `ClassName()` preferred | Cleaner, more functional |\n| No runtime types | `__tjs` metadata on functions | Types should exist at runtime |\n| `typeof null === 'object'` | `typeOf(null) === 'null'` | JS got this wrong in 1995 |\n\nNothing is taken away. `new` still works. `===` still works. You can write\nplain JavaScript in a `.tjs` file and it works. The additions are opt-in via\nsyntax (`:` annotations, `->` returns, `Type` declarations).\n\n---\n\n## Getting Started\n\n```bash\nnpm install tjs-lang\n```\n\n### Try It in the Browser\n\nThe playground runs the full TJS compiler in your browser -- no backend needed:\n\n**[tjs-platform.web.app](https://tjs-platform.web.app)**\n\n### CLI\n\n```bash\nbun src/cli/tjs.ts check file.tjs # Parse and type check\nbun src/cli/tjs.ts run file.tjs # Transpile and execute\nbun src/cli/tjs.ts emit file.tjs # Output transpiled JS\nbun src/cli/tjs.ts test file.tjs # Run inline tests\n```\n\n### From Code\n\n```javascript\nimport { tjs } from 'tjs-lang'\n\nconst output = tjs`\n function add(a: 0, b: 0) -> 0 {\n return a + b\n }\n`\n```\n\n---\n\n## Runtime Traceability\n\nEvery TJS function carries its source identity in `__tjs` metadata.\nWhen validation fails, the error tells you exactly which function and\nparameter failed, in which source file:\n\n```javascript\nfunction add(a: 0, b: 0) -> 0 { return a + b }\n\nadd.__tjs.source // \"mymodule.tjs:3\"\nadd('oops', 1) // MonadicError { path: \"mymodule.tjs:3:add.a\", expected: \"integer\", actual: \"string\" }\n```\n\nNo source maps. No build artifacts. The function _knows where it came from_.\n\n---\n\n## Learn More\n\n- [TJS Language Reference](DOCS-TJS.md) -- Full syntax and features\n- [AJS Agent Language](DOCS-AJS.md) -- The sandboxed agent VM\n- [TJS for TypeScript Programmers](TJS-FOR-TS.md) -- Coming from TypeScript?\n- [Playground](https://tjs-platform.web.app) -- Try it live\n"
818
+ "text": "<!--{\"section\": \"tjs-for-js\", \"group\": \"docs\", \"order\": 0, \"navTitle\": \"TJS for JS Devs\"}-->\n\n# TJS for JavaScript Programmers\n\n_Everything you already know, plus the things you always wished JavaScript had._\n\n---\n\n## The One-Sentence Pitch\n\nTJS is JavaScript with types that actually exist at runtime, equality that actually works, and errors that don't crash your program.\n\n---\n\n## What Stays the Same\n\nTJS is a superset of JavaScript. All of this works exactly as you expect:\n\n```javascript\nconst x = 42\nlet name = 'Alice'\nconst items = [1, 2, 3].map((n) => n * 2)\nconst user = { name: 'Bob', age: 30 }\nconst { name: userName, age } = user\nconst merged = { ...defaults, ...overrides }\nconst greeting = `Hello, ${name}!`\n\nfor (const item of items) {\n console.log(item)\n}\nwhile (condition) {\n /* ... */\n}\nif (x > 0) {\n /* ... */\n} else {\n /* ... */\n}\n\ntry {\n riskyThing()\n} catch (e) {\n handleError(e)\n}\n\nclass Dog {\n #name\n constructor(name) {\n this.#name = name\n }\n get name() {\n return this.#name\n }\n}\n\nimport { something } from './module.js'\nexport function myFunction() {\n /* ... */\n}\n```\n\nIf you don't use any TJS features, your code is just JavaScript. No lock-in.\n\nThis extends to advanced patterns too: **Proxies**, **WeakMap/WeakSet**, **Symbols**, **generators**, **async iterators**, **`Object.defineProperty`** — all work identically. TJS adds type checks at function boundaries; it doesn't wrap or intercept any JS runtime behavior.\n\n---\n\n## What's Different\n\n### 1. Types Are Example Values\n\nIn JavaScript, there are no types. In TJS, you annotate parameters with\nan example of a valid value:\n\n```javascript\n// JavaScript\nfunction greet(name) {\n return `Hello, ${name}!`\n}\n\n// TJS - name is required and must be a string (like 'World')\nfunction greet(name: 'World') -> '' {\n return `Hello, ${name}!`\n}\n```\n\nThe `: 'World'` means \"required, must be a string, here's an example.\" The `-> ''` means \"returns a string.\" These aren't abstract type annotations -- they're concrete values the system can use for testing, documentation, and validation.\n\n| You Write | TJS Infers |\n| ----------------- | ----------------------------- |\n| `name: 'Alice'` | Required string |\n| `count: 42` | Required integer |\n| `rate: 3.14` | Required number (float) |\n| `age: +20` | Required non-negative integer |\n| `flag: true` | Required boolean |\n| `items: [0]` | Required integer[] |\n| `values: [0.0]` | Required number[] |\n| `user: { n: '' }` | Required object |\n| `id: 0 \\|\\| null` | integer or null |\n\nAll of these are valid JavaScript expressions. `42` vs `42.0` vs `+42` are\nall legal JS -- TJS leverages this to distinguish integer, float, and\nnon-negative integer types at the syntax level.\n\nNote: `|| null` means the value accepts the base type _or_ `null`, but not\n`undefined`. TJS treats `null` and `undefined` as distinct types\n(`typeOf(null) === 'null'`, `typeOf(undefined) === 'undefined'`).\n\n**Optional** parameters use `=` (just like JS default values):\n\n```javascript\nfunction greet(name = 'World') {\n /* ... */\n} // optional, defaults to 'World'\nfunction retry(count = 3) {\n /* ... */\n} // optional, defaults to 3 (integer)\n```\n\n**The difference:** `: value` means required. `= value` means optional with a default. In plain JS, both would be written as `= value`.\n\n### 2. Numeric Type Narrowing\n\nTJS distinguishes three numeric types using valid JavaScript syntax:\n\n```javascript\nfunction process(\n rate: 3.14, // number (float) -- has a decimal point\n count: 42, // integer -- whole number, no decimal\n index: +0 // non-negative integer -- prefixed with +\n) { /* ... */ }\n```\n\n| You Write | Type Inferred | Validates |\n| --------- | ---------------------- | ------------------------------- |\n| `3.14` | `number` (float) | Any number |\n| `0.0` | `number` (float) | Any number |\n| `42` | `integer` | `Number.isInteger(x)` |\n| `0` | `integer` | `Number.isInteger(x)` |\n| `+20` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n| `+0` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n| `-5` | `integer` | `Number.isInteger(x)` |\n| `-3.5` | `number` (float) | Any number |\n\nThese are all valid JavaScript expressions -- TJS just reads the syntax more\ncarefully than JS does. At runtime, passing `3.14` to a parameter typed as\ninteger returns a monadic error.\n\n### 3. Return Type Annotations\n\nTJS uses `->` for return types:\n\n```javascript\n// Returns an integer\nfunction add(a: 0, b: 0) -> 0 {\n return a + b\n}\n\n// Returns an object with specific shape\nfunction getUser(id: 0) -> { name: '', age: 0 } {\n return { name: 'Alice', age: 30 }\n}\n```\n\nThe return type example doubles as an automatic test. When you write `-> 0`, TJS will call `add(0, 0)` at transpile time and verify the result is a number.\n\n### 4. Equality That Works\n\nJavaScript's `==` is notoriously broken (type coercion). Its `===` doesn't do structural comparison. TJS fixes this:\n\n```javascript\n// JavaScript\n[1, 2] === [1, 2] // false (different references)\n{ a: 1 } === { a: 1 } // false (different references)\n\n// TJS (with structural equality enabled)\n[1, 2] == [1, 2] // true (same structure)\n{ a: 1 } == { a: 1 } // true (same structure)\n[1, 2] === [1, 2] // false (different references, identity check)\n```\n\nTJS redefines the operators:\n\n| Operator | JavaScript | TJS |\n| -------- | ------------------- | --------------------- |\n| `==` | Coercive equality | Structural equality |\n| `!=` | Coercive inequality | Structural inequality |\n| `===` | Strict equality | Identity (same ref) |\n| `!==` | Strict inequality | Not same reference |\n\nYou can also use the explicit forms `Is` and `IsNot`:\n\n```javascript\nuser Is expectedUser // structural deep equality\nresult IsNot errorValue // structural deep inequality\n```\n\nClasses can define custom equality:\n\n```javascript\nclass Point {\n constructor(x: 0, y: 0) { this.x = x; this.y = y }\n Equals(other) { return this.x === other.x && this.y === other.y }\n}\n\nPoint(1, 2) Is Point(1, 2) // true, uses .Equals\n```\n\nNote: structural equality does not handle circular references. Use `===`\nfor objects that might be circular, or define an `.Equals` method.\n\n### 5. Errors Are Values, Not Exceptions\n\nIn JavaScript, type errors crash your program. In TJS, they're values:\n\n```javascript\nfunction double(x: 0) -> 0 {\n return x * 2\n}\n\ndouble(5) // 10\ndouble('oops') // { $error: true, message: \"Expected number for 'double.x', got string\" }\n```\n\nNo `try`/`catch`. No crashes. The caller gets a clear error value they can inspect:\n\n```javascript\nconst result = double(input)\nif (result?.$error) {\n console.log(result.message) // handle gracefully\n} else {\n useResult(result)\n}\n```\n\nErrors propagate automatically through function calls. If you pass a monadic error to another TJS function, it passes through without executing:\n\n```javascript\nconst a = step1(badInput) // MonadicError\nconst b = step2(a) // skips execution, returns the same error\nconst c = step3(b) // skips again -- error flows to the surface\n```\n\nThis is sometimes called \"railway-oriented programming.\"\n\n### 6. Classes Without `new`\n\nTJS classes are callable as functions:\n\n```javascript\n// JavaScript\nconst p = new Point(10, 20)\n\n// TJS - both work, but the clean form is preferred\nconst p1 = Point(10, 20) // works\nconst p2 = new Point(10, 20) // also works (linter warns)\n```\n\nThis is cleaner and more consistent with functional style. Under the hood, TJS uses a Proxy to auto-construct when you call a class as a function.\n\n### 7. `const` for Free\n\nUppercase identifiers automatically get `const`:\n\n```javascript\n// TJS\nConfig = { debug: true, version: '1.0' }\nMaxRetries = 3\n\n// Transpiles to\nconst Config = { debug: true, version: '1.0' }\nconst MaxRetries = 3\n```\n\nLowercase identifiers behave normally.\n\n---\n\n## The Type System\n\nJavaScript has no type system. Libraries like Zod and Ajv bolt one on,\nbut they're separate from your function signatures -- a second source of\ntruth that drifts. TJS's `Type()` built-in gives you as much narrowing\nand specificity as you want, in one place, using plain functions you\nalready know how to write.\n\n### From Simple to Sophisticated\n\n```javascript\n// Simple -- infer type from an example value\nType Name 'Alice' // any string\n\n// Descriptive -- add documentation\nType User {\n description: 'a registered user'\n example: { name: '', age: 0, email: '' }\n}\n\n// Constrained -- add a predicate for narrowing\nType PositiveNumber {\n description: 'a number greater than zero'\n example: 1\n predicate(x) { return typeof x === 'number' && x > 0 }\n}\n\n// Domain-specific -- real business rules\nType USZipCode {\n description: '5-digit US zip code'\n example: '90210'\n predicate(v) { return typeof v === 'string' && /^\\d{5}$/.test(v) }\n}\n\nType Email {\n description: 'email address'\n example: 'user@example.com'\n predicate(v) { return typeof v === 'string' && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(v) }\n}\n```\n\nThe key insight: a `predicate` is just a function that returns `true` or\n`false`. If you can express a constraint as a boolean check, you can make\nit a type. No schema DSL to learn, no special syntax -- it's JavaScript\nall the way down.\n\nTypes work in function signatures:\n\n```javascript\nfunction sendWelcome(email: Email, name: Name) -> '' {\n return `Welcome, ${name}! Confirmation sent to ${email}.`\n}\n\nsendWelcome('alice@example.com', 'Alice') // works\nsendWelcome('not-an-email', 'Alice') // MonadicError\n```\n\nTJS also ships common types out of the box: `TString`, `TNumber`,\n`TBoolean`, `TInteger`, `TPositiveInt`, `TNonEmptyString`, `TEmail`,\n`TUrl`, `TUuid`, `Timestamp`, `LegalDate`. No imports from a validation\nlibrary needed.\n\n### Combinators\n\nCompose types from other types:\n\n```javascript\nType OptionalEmail Nullable(Email) // Email | null\nType UserIds TArray(TPositiveInt) // array of positive integers\n```\n\n### Unions and Enums\n\n```javascript\n// Union of string literals\nUnion Status 'task status' 'pending' | 'active' | 'done'\n\n// Enum with named members\nEnum Color 'CSS color' {\n Red = 'red'\n Green = 'green'\n Blue = 'blue'\n}\n\n// Discriminated union -- like tagged unions or sum types\nconst Shape = Union('kind', {\n circle: { radius: 0 },\n rectangle: { width: 0, height: 0 }\n})\n```\n\n### Generics\n\nRuntime-checkable generic types:\n\n```javascript\nGeneric Box<T> {\n description: 'a boxed value'\n predicate(x, T) {\n return typeof x === 'object' && x !== null && 'value' in x && T(x.value)\n }\n}\n\n// Instantiate with a concrete type\nconst NumberBox = Box(TNumber)\nNumberBox.check({ value: 42 }) // true\nNumberBox.check({ value: 'nope' }) // false\n```\n\n---\n\n## Runtime Metadata\n\nEvery TJS function carries its type information at runtime via `__tjs`:\n\n```javascript\nfunction createUser(input: { name: '', age: 0 }) -> { id: 0 } {\n return { id: 123 }\n}\n\ncreateUser.__tjs\n// {\n// params: { input: { type: { kind: 'object', shape: {...} }, required: true } },\n// returns: { type: { kind: 'object', shape: { id: { kind: 'number' } } } }\n// }\n```\n\nThis metadata enables autocomplete from live objects, automatic documentation\ngeneration, and runtime reflection -- things that require build tools and\nexternal libraries in vanilla JavaScript.\n\n---\n\n## Safety Levels\n\nYou control how much validation TJS applies:\n\n```javascript\n// Per-module (top of file)\nsafety none // Metadata only -- no runtime checks (fastest)\nsafety inputs // Validate inputs only (default)\nsafety all // Validate everything (debug mode)\n\n// Per-function\nfunction fastPath(! x: 0) { /* ... */ } // Skip validation\nfunction safePath(? x: 0) { /* ... */ } // Force validation\n```\n\nUse `!` (skip validation) only in hot loops where every microsecond counts\nand the data source is already trusted. In all other cases, the ~1.5x\noverhead of `safety inputs` is negligible compared to the bugs it catches.\n\n### Unsafe Blocks\n\nSkip validation for a hot inner loop:\n\n```javascript\nunsafe {\n for (let i = 0; i < million; i++) {\n hotFunction(data[i])\n }\n}\n```\n\n---\n\n## Inline Tests\n\nTests live next to the code they test and run at transpile time:\n\n```javascript\nfunction isPrime(n: 2) -> true {\n if (n < 2) return false\n for (let i = 2; i * i <= n; i++) {\n if (n % i === 0) return false\n }\n return true\n}\n\ntest 'prime detection' {\n expect(isPrime(2)).toBe(true)\n expect(isPrime(4)).toBe(false)\n expect(isPrime(17)).toBe(true)\n}\n```\n\nTests are stripped from production output. They're also generated\nautomatically from return type annotations -- `-> true` means TJS will call\n`isPrime(2)` and verify it returns a boolean.\n\n---\n\n## WASM Blocks\n\nFor compute-heavy code, drop into WebAssembly:\n\n```javascript\nconst add = wasm (a: i32, b: i32) -> i32 {\n local.get $a\n local.get $b\n i32.add\n}\n\nadd(1, 2) // 3, runs as native WASM\n```\n\nWASM is compiled at transpile time and embedded as base64 in the output.\nNo separate `.wasm` files.\n\n---\n\n## Safe Eval\n\nThe killer feature for many use cases. Run untrusted code safely:\n\n```javascript\nimport { Eval, SafeFunction } from 'tjs-lang/eval'\n\n// One-shot evaluation\nconst { result } = await Eval({\n code: 'return items.filter(x => x > threshold)',\n context: { items: [1, 5, 10, 15], threshold: 7 },\n fuel: 1000,\n})\n// result: [10, 15]\n\n// Reusable safe function\nconst transform = await SafeFunction({\n body: 'return x * multiplier',\n params: ['x'],\n fuel: 500,\n})\n\nawait transform(21) // { result: 42, fuelUsed: 8 }\n```\n\n- Gas-limited (fuel runs out = execution stops, no infinite loops)\n- Capability-based (no I/O unless you grant it)\n- No `eval()`, no CSP violations, no containers\n\n---\n\n## What You Give Up\n\nTJS is opinionated. Here's what changes:\n\n| JavaScript | TJS | Why |\n| -------------------------- | ----------------------------- | ------------------------------- |\n| `==` (type coercion) | `==` (structural equality) | Coercion is a bug factory |\n| Exceptions for type bugs | Monadic error values | Exceptions escape, values don't |\n| `new ClassName()` | `ClassName()` preferred | Cleaner, more functional |\n| No runtime types | `__tjs` metadata on functions | Types should exist at runtime |\n| `typeof null === 'object'` | `typeOf(null) === 'null'` | JS got this wrong in 1995 |\n\nNothing is taken away. `new` still works. `===` still works. You can write\nplain JavaScript in a `.tjs` file and it works. The additions are opt-in via\nsyntax (`:` annotations, `->` returns, `Type` declarations).\n\n---\n\n## Getting Started\n\n```bash\nnpm install tjs-lang\n```\n\n### Try It in the Browser\n\nThe playground runs the full TJS compiler in your browser -- no backend needed:\n\n**[tjs-platform.web.app](https://tjs-platform.web.app)**\n\n### CLI\n\n```bash\nbun src/cli/tjs.ts check file.tjs # Parse and type check\nbun src/cli/tjs.ts run file.tjs # Transpile and execute\nbun src/cli/tjs.ts emit file.tjs # Output transpiled JS\nbun src/cli/tjs.ts test file.tjs # Run inline tests\n```\n\n### From Code\n\n```javascript\nimport { tjs } from 'tjs-lang'\n\nconst output = tjs`\n function add(a: 0, b: 0) -> 0 {\n return a + b\n }\n`\n```\n\n---\n\n## Runtime Traceability\n\nEvery TJS function carries its source identity in `__tjs` metadata.\nWhen validation fails, the error tells you exactly which function and\nparameter failed, in which source file:\n\n```javascript\nfunction add(a: 0, b: 0) -> 0 { return a + b }\n\nadd.__tjs.source // \"mymodule.tjs:3\"\nadd('oops', 1) // MonadicError { path: \"mymodule.tjs:3:add.a\", expected: \"integer\", actual: \"string\" }\n```\n\nNo source maps. No build artifacts. The function _knows where it came from_.\n\n---\n\n## Learn More\n\n- [TJS Language Reference](DOCS-TJS.md) -- Full syntax and features\n- [AJS Agent Language](DOCS-AJS.md) -- The sandboxed agent VM\n- [TJS for TypeScript Programmers](TJS-FOR-TS.md) -- Coming from TypeScript?\n- [Playground](https://tjs-platform.web.app) -- Try it live\n"
819
819
  },
820
820
  {
821
821
  "title": "TJS for TypeScript Programmers",
@@ -825,6 +825,6 @@
825
825
  "group": "docs",
826
826
  "order": 0,
827
827
  "navTitle": "TJS for TS Devs",
828
- "text": "<!--{\"section\": \"tjs-for-ts\", \"group\": \"docs\", \"order\": 0, \"navTitle\": \"TJS for TS Devs\"}-->\n\n# TJS for TypeScript Programmers\n\n_What if your types didn't disappear at runtime?_\n\n---\n\nTypeScript is great. It catches bugs at compile time, makes refactoring safer,\nand gives you autocomplete. But it has a fundamental limitation: types are\nfiction. They exist only in your editor, and they vanish completely at runtime.\n\nTJS starts from a different premise: **types are example values that survive\nto runtime**. This gives you everything TypeScript gives you, plus runtime\nvalidation, reflection, documentation, inline tests of private methods, and traceability of errors back to source code -- from a single source of truth.\n\nThis guide is split into two paths:\n\n1. **[Using TJS from TypeScript](#part-1-using-tjs-from-typescript)** -- Keep your TS codebase, use TJS for safe eval and agent execution\n2. **[Migrating to TJS](#part-2-migrating-to-tjs)** -- Convert your codebase from TypeScript to TJS\n\n---\n\n# Part 1: Using TJS from TypeScript\n\nYou don't have to rewrite anything. TJS provides tools you can use directly\nfrom your TypeScript codebase.\n\n## Safe Eval\n\nThe most common reason to reach for TJS from TypeScript: running untrusted\ncode safely.\n\n```typescript\nimport { Eval, SafeFunction } from 'tjs-lang/eval'\n\n// Run user-provided code with a gas limit\nconst { result, fuelUsed } = await Eval({\n code: userCode,\n context: { items: data, threshold: 10 },\n fuel: 1000,\n capabilities: {\n fetch: sandboxedFetch, // your whitelist-wrapped fetch\n },\n})\n\n// Or create a reusable safe function\nconst transform = await SafeFunction({\n body: 'return items.filter(x => x.price < budget)',\n params: ['items', 'budget'],\n fuel: 500,\n})\n\nconst { result } = await transform(products, 100)\n```\n\nNo `eval()`. No CSP violations. No Docker containers. The code runs in a\nfuel-metered sandbox with only the capabilities you inject.\n\n## Agent VM\n\nBuild and execute JSON-serializable agents:\n\n```typescript\nimport { ajs, AgentVM } from 'tjs-lang'\n\n// Parse agent source to JSON AST\nconst agent = ajs`\n function analyze({ data, query }) {\n let filtered = data.filter(x => x.score > 0.5)\n let summary = llmPredict({\n prompt: 'Summarize findings for: ' + query,\n data: filtered\n })\n return { query, summary, count: filtered.length }\n }\n`\n\n// Execute with resource limits\nconst vm = new AgentVM()\nconst { result } = await vm.run(\n agent,\n { data, query },\n {\n fuel: 1000,\n timeoutMs: 5000,\n capabilities: { fetch: myFetch, llm: myLlm },\n }\n)\n```\n\nThe agent AST is JSON. You can store it in a database, send it over the\nnetwork, version it, diff it, audit it.\n\n## Type-Safe Builder\n\nConstruct agents programmatically with full TypeScript support:\n\n```typescript\nimport { Agent, AgentVM, s } from 'tjs-lang'\n\nconst pipeline = Agent.take(s.object({ url: s.string, maxResults: s.number }))\n .httpFetch({ url: { $kind: 'arg', path: 'url' } })\n .as('response')\n .varSet({\n key: 'results',\n value: {\n $expr: 'member',\n object: { $expr: 'ident', name: 'response' },\n property: 'items',\n },\n })\n .return(s.object({ results: s.array(s.any) }))\n\nconst vm = new AgentVM()\nconst { result } = await vm.run(\n pipeline.toJSON(),\n { url, maxResults: 10 },\n {\n fuel: 500,\n capabilities: { fetch },\n }\n)\n```\n\n## TypeScript Entry Points\n\nTJS is tree-shakeable. Import only what you need:\n\n```typescript\nimport { Agent, AgentVM, ajs, tjs } from 'tjs-lang' // Everything\nimport { Eval, SafeFunction } from 'tjs-lang/eval' // Safe eval only\nimport { tjs, transpile } from 'tjs-lang/lang' // Language tools\nimport { fromTS } from 'tjs-lang/lang/from-ts' // TS -> TJS converter\n```\n\n## When to Stay in TypeScript\n\nIf your codebase is TypeScript and you're happy with it, you probably only\nneed TJS for:\n\n- Running user-provided or LLM-generated code safely\n- Building agents that travel over the network\n- Adding runtime type validation at system boundaries\n- Eval without `eval()`\n\nYou don't need to migrate anything. The libraries work from TypeScript.\n\n---\n\n# Part 2: Migrating to TJS\n\nIf you want the full TJS experience -- runtime types, structural equality,\nmonadic errors, inline tests -- here's how to convert.\n\n## The Core Idea: Types as Examples\n\nTypeScript describes types abstractly. TJS describes them concretely:\n\n```typescript\n// TypeScript: what TYPE is this?\nfunction greet(name: string): string { ... }\n\n// TJS: what's an EXAMPLE of this?\nfunction greet(name: 'World') -> '' { ... }\n```\n\n`'World'` tells TJS: this is a string, it's required, and here's a valid\nexample. The example doubles as documentation and test data.\n\n## Conversion Reference\n\n### Primitives\n\n```typescript\n// TypeScript // TJS\nname: string name: ''\ncount: number count: 0.0 // float (any number)\nindex: number index: 0 // integer\nage: number age: +0 // non-negative integer\nflag: boolean flag: true\nitems: string[] items: ['']\nnested: number[][] nested: [[0]]\n```\n\n**Important:** The example value determines the _type_, not a literal\nconstraint. `name: 'World'` means \"required string\" -- not \"must be the\nstring `'World'`.\" Any string passes validation. The example is there for\ndocumentation, testing, and type inference. Think of it as `string` with\na built-in `@example` tag.\n\n**Numeric precision:** TJS distinguishes three numeric types using valid\nJavaScript syntax that JS itself ignores:\n\n| You Write | TJS Type | Runtime Check |\n| --------- | ---------------------- | ------------------------------- |\n| `3.14` | `number` (float) | Any number |\n| `0.0` | `number` (float) | Any number |\n| `42` | `integer` | `Number.isInteger(x)` |\n| `0` | `integer` | `Number.isInteger(x)` |\n| `+20` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n| `+0` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n\nTypeScript's `number` is a single type. TJS gives you three levels of\nprecision -- all using expressions that are already legal JavaScript.\nThe automatic converter maps TypeScript `number` to `0.0` (float) to\npreserve the widest behavior; you can then narrow manually to `0` (integer)\nor `+0` (non-negative integer) where appropriate.\n\n### Optional Parameters\n\n```typescript\n// TypeScript // TJS\nfunction f(x?: string) {}\nfunction f(x = '') {}\nfunction f(x: string = 'hi') {}\nfunction f(x = 'hi') {}\n```\n\nIn TypeScript, `?` means optional with type `string | undefined`.\nIn TJS, `= value` means optional with that default. Same semantics, less syntax.\n\n### Object Shapes\n\n```typescript\n// TypeScript\nfunction createUser(opts: { name: string; age: number; email?: string }) {}\n\n// TJS\nfunction createUser(opts: { name: '', age: 0, email = '' }) {}\n```\n\nRequired properties use `:`, optional ones use `=`.\n\n### Return Types\n\n```typescript\n// TypeScript // TJS\nfunction add(a: number, b: number): number function add(a: 0, b: 0) -> 0\nfunction getUser(): { name: string } function getUser() -> { name: '' }\nfunction fetchData(): Promise<string[]> function fetchData() -> ['']\n```\n\nThe `fromTS` converter unwraps `Promise<T>` in return type annotations --\nyou annotate the resolved type, not the wrapper. This only applies when\nconverting from TypeScript; in native TJS you just write normal\n`async`/`await` and annotate what the function resolves to.\n\nThe return annotation also generates an automatic test: `add(0, 0)` must\nreturn a number. If it doesn't, you get an error at transpile time.\n\n### Interfaces and Type Aliases\n\n```typescript\n// TypeScript\ninterface User {\n name: string\n age: number\n email?: string\n}\n\ntype Status = 'active' | 'inactive' | 'banned'\n\n// TJS\nType User {\n description: 'a registered user'\n example: { name: '', age: 0, email = '' }\n}\n\nUnion Status 'account status' 'active' | 'inactive' | 'banned'\n```\n\n### Enums\n\n```typescript\n// TypeScript\nenum Color {\n Red = 'red',\n Green = 'green',\n Blue = 'blue',\n}\n\n// TJS\nEnum Color 'CSS color' {\n Red = 'red'\n Green = 'green'\n Blue = 'blue'\n}\n```\n\n### Classes\n\n```typescript\n// TypeScript\nclass Point {\n private x: number\n private y: number\n\n constructor(x: number, y: number) {\n this.x = x\n this.y = y\n }\n\n distanceTo(other: Point): number {\n return Math.sqrt((this.x - other.x) ** 2 + (this.y - other.y) ** 2)\n }\n}\n\nconst p = new Point(10, 20)\n\n// TJS\nclass Point {\n #x\n #y\n\n constructor(x: 0, y: 0) {\n this.#x = x\n this.#y = y\n }\n\n distanceTo(other: Point) -> 0 {\n return Math.sqrt((this.#x - other.#x) ** 2 + (this.#y - other.#y) ** 2)\n }\n}\n\nconst p = Point(10, 20) // no 'new' needed\n```\n\nKey differences:\n\n- `private` becomes `#` (native private fields)\n- Type annotations become example values\n- `new` is optional (linter warns against it)\n\n### Generics\n\nTJS takes a different approach to generics. TypeScript has function-level\ntype parameters (`<T>`) that vanish at runtime. TJS has `Generic` declarations\nthat produce runtime-checkable type constructors:\n\n```typescript\n// TypeScript -- compile-time only, gone at runtime\nfunction identity<T>(x: T): T { return x }\ninterface Box<T> { value: T }\n\n// TJS -- no function-level generics; use Generic for container types\nGeneric Box<T> {\n description: 'a boxed value'\n predicate(x, T) {\n return typeof x === 'object' && x !== null && 'value' in x && T(x.value)\n }\n}\n\n// Usage: Box(Number) is a runtime type checker\nconst isNumberBox = Box(Number)\nisNumberBox({ value: 42 }) // true\nisNumberBox({ value: 'nope' }) // false\n```\n\nFor simple generic functions like `identity` or `first`, you don't need\ngenerics at all -- just skip the type parameter. TJS validates the\nconcrete types at call sites, not the abstract relationship between them.\n\n```javascript\n// Simple -- no generics needed, the function just works\nfunction first(arr: [0]) { return arr[0] }\n\n// If you need runtime-checked containers, use Generic\nGeneric Pair<T, U> {\n description: 'a typed pair'\n predicate(x, T, U) { return T(x[0]) && U(x[1]) }\n}\n```\n\nWhen converting from TypeScript, the `fromTS` converter preserves generic\nmetadata but types become `any`. This is a place where manual review helps.\n\n### Nullability\n\n```typescript\n// TypeScript\nfunction find(id: number): User | null { ... }\n\n// TJS\nfunction find(id: 0) -> { name: '', age: 0 } || null { ... }\n```\n\nTJS distinguishes `null` from `undefined` -- they're different types, just\nas `typeOf(null)` returns `'null'` and `typeOf(undefined)` returns\n`'undefined'`. Writing `|| null` means the value can be the base type or\n`null`, but not `undefined`. Optional parameters (using `=`) accept\n`undefined` because that's what you get when the caller omits the argument.\n\n## What TypeScript Has That TJS Doesn't\n\nTJS intentionally skips TypeScript features that don't survive to runtime\nor add complexity without proportional value:\n\n| TypeScript Feature | TJS Equivalent |\n| --------------------------- | --------------------------------------- |\n| `interface` | `Type` with example |\n| `type` aliases | `Type`, `Union`, or `Enum` |\n| Conditional types | Use predicates in `Type` |\n| Mapped types | Not needed (types are values) |\n| `keyof`, `typeof` | Use runtime `Object.keys()`, `typeOf()` |\n| `Partial<T>`, `Pick<T>` | Define the shape you need directly |\n| Declaration files (`.d.ts`) | `__tjs` metadata on live objects |\n| `as` type assertions | Not needed (values are checked) |\n| `any` escape hatch | `safety none` per-module or `!` per-fn |\n| Decorators | Not supported |\n| `namespace` | Use modules |\n\nThe philosophy: if a type feature doesn't do something at runtime, it's\ncomplexity without payoff.\n\n### But What About Narrowing?\n\nTypeScript's type system is Turing-complete. You can express astonishing\nconstraints -- `Pick<Omit<T, K>, Extract<keyof T, string>>` -- but the\nresulting types are often harder to understand than the code they describe.\nAnd they vanish at runtime, so they can't protect you from bad API data.\n\nTJS takes the opposite approach: `Type()` gives you a predicate function.\nIf you can write a boolean expression, you can define a type. No type-level\nprogramming language to learn.\n\n```javascript\n// TypeScript: branded types + manual validation\ntype Email = string & { __brand: 'email' }\nfunction isEmail(s: string): s is Email {\n return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(s)\n}\nfunction validateEmail(input: string): Email {\n if (!isEmail(input)) throw new Error('Invalid email')\n return input\n}\n\n// TJS: one line, works at runtime\nType Email {\n description: 'email address'\n example: 'user@example.com'\n predicate(v) { return typeof v === 'string' && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(v) }\n}\n```\n\nThe TJS `Type()` built-in handles everything from simple shapes to\nsophisticated domain constraints:\n\n```javascript\n// Simple -- infer from example\nType Name 'Alice' // string\n\n// Constrained -- predicate narrows beyond the base type\nType PositiveInt {\n description: 'a positive integer'\n example: 1\n predicate(v) { return typeof v === 'number' && Number.isInteger(v) && v > 0 }\n}\n\n// Domain-specific -- readable business rules\nType USZipCode {\n description: '5-digit US zip code'\n example: '90210'\n predicate(v) { return typeof v === 'string' && /^\\d{5}$/.test(v) }\n}\n\n// Combinators -- compose types\nType OptionalEmail Nullable(Email) // Email | null\n\n// Schema-based -- use tosijs-schema for structured validation\nType AgeRange {\n description: 'valid age'\n example: 25\n predicate(v) { return typeof v === 'number' && v >= 0 && v <= 150 }\n}\n```\n\nCompare the TypeScript equivalents:\n\n| What you want | TypeScript | TJS |\n| ------------------- | ----------------------------------------------------------- | -------------------------------------- |\n| String with format | Branded type + type guard + validation function | `Type Email { predicate(v) {...} }` |\n| Number in range | Branded type + manual check | `Type Age { predicate(v) {...} }` |\n| Non-empty string | Template literal type (compile-only) | `Type NonEmpty { predicate(v) {...} }` |\n| Nullable variant | `T \\| null` (compile-only) | `Nullable(MyType)` (runtime-checked) |\n| Union of literals | `'a' \\| 'b' \\| 'c'` (compile-only) | `Union Status 'a' \\| 'b' \\| 'c'` |\n| Discriminated union | `type Shape = { kind: 'circle' } \\| ...` + manual narrowing | `Union('kind', { circle: {...} })` |\n| Generic container | `interface Box<T>` (compile-only) | `Generic Box<T> { predicate(...) }` |\n\nEvery row in the TypeScript column is compile-time fiction that disappears\nwhen your code runs. Every row in the TJS column is a runtime check that\nactually catches bugs in production. And the TJS versions are shorter,\nbecause a predicate is just a function -- not a type-level program.\n\nTJS also ships common types out of the box: `TString`, `TNumber`,\n`TBoolean`, `TInteger`, `TPositiveInt`, `TNonEmptyString`, `TEmail`,\n`TUrl`, `TUuid`, `Timestamp`, `LegalDate`. No imports from a validation\nlibrary needed.\n\n### Tooling Comparison\n\n| Concern | TypeScript | TJS |\n| ------------------- | ---------------------------------- | ---------------------------------------------------------------- |\n| **Type checking** | `tsc` (compile-time only) | Runtime validation (survives build) |\n| **Runtime schemas** | Zod / io-ts / Ajv (separate) | Built-in (types _are_ schemas) |\n| **Linting** | ESLint + plugins | Built-in linter (unused vars, unreachable code, no-explicit-new) |\n| **Testing** | Vitest / Jest (separate files) | Inline `test` blocks (transpile-time) |\n| **Equality** | Reference-based only | Structural `==`, identity `===` |\n| **Build toolchain** | tsc + bundler (webpack/Vite/etc) | Transpiles in-browser, no build step |\n| **Debugging** | Source maps (brittle, build bloat) | Functions carry source identity via `__tjs` metadata |\n| **Documentation** | JSDoc / TypeDoc (manual) | Generated from `__tjs` metadata |\n| **Editor support** | Mature (VSCode, etc) | Monaco/CodeMirror/Ace + VSCode/Cursor extensions |\n\n## What TJS Has That TypeScript Doesn't\n\n### Runtime Validation\n\nTypeScript:\n\n```typescript\n// Types are a promise. A lie, if the data comes from outside.\nfunction processOrder(order: Order) {\n // If order came from an API, nothing guarantees it matches Order.\n // You need Zod/io-ts/ajv AND the TypeScript type AND keep them in sync.\n}\n```\n\nTJS:\n\n```javascript\n// Types are checked at runtime. One source of truth.\nfunction processOrder(order: { items: [{ id: 0, qty: 0 }], total: 0 }) -> { status: '' } {\n // If order doesn't match, caller gets a MonadicError -- no crash.\n}\n```\n\n### Structural Equality\n\nTypeScript inherits JavaScript's broken equality. TJS fixes it:\n\n```javascript\n// TJS\n[1, 2, 3] == [1, 2, 3] // true (structural)\n{ a: 1 } == { a: 1 } // true (structural)\nobj1 === obj2 // identity check (same reference)\n```\n\nThe implementation is optimized: it short-circuits on reference identity\n(`===` check first), then type comparison, then recursive structural\ncomparison. For performance-critical hot paths comparing large objects,\nuse `===` for identity checks or define an `.Equals` method on your class\nto control comparison logic.\n\n### Monadic Errors\n\nTypeScript uses exceptions. TJS uses values:\n\n```javascript\n// TypeScript -- you have to remember to try/catch\nfunction divide(a: number, b: number): number {\n if (b === 0) throw new Error('Division by zero')\n return a / b\n}\n// Caller forgets try/catch? Crash.\n\n// TJS -- errors flow through the pipeline\nfunction divide(a: 0, b: 0) -> 0 {\n if (b === 0) return MonadicError('Division by zero')\n return a / b\n}\n// Caller gets an error value. No crash. Ever.\n```\n\n**How the caller handles it:**\n\n```javascript\n// Option 1: Check the result\nconst result = divide(10, 0)\nif (result instanceof Error) {\n console.log(result.message) // 'Division by zero'\n} else {\n useResult(result)\n}\n\n// Option 2: Just keep going -- errors propagate automatically\nconst a = divide(10, 0) // MonadicError\nconst b = double(a) // Receives error, returns it immediately (skips execution)\nconst c = format(b) // Same -- error flows through the whole chain\n// c is still the original MonadicError from divide()\n```\n\nIf you've used Rust's `Result<T, E>` or Haskell's `Either`, the pattern\nis familiar. The key difference from TypeScript: you never have to guess\nwhether a function might throw. Type errors and validation failures are\nalways values, never exceptions.\n\n### Inline Tests\n\n```javascript\nfunction fibonacci(n: 0) -> 0 {\n if (n <= 1) return n\n return fibonacci(n - 1) + fibonacci(n - 2)\n}\n\ntest 'fibonacci sequence' {\n expect(fibonacci(0)).toBe(0)\n expect(fibonacci(1)).toBe(1)\n expect(fibonacci(10)).toBe(55)\n}\n```\n\nTests run at transpile time. They're stripped from production output.\nNo separate test files, no test runner configuration.\n\n### Safety Controls\n\n```javascript\nsafety none // This module: skip all validation (performance)\nsafety inputs // This module: validate inputs only (default)\nsafety all // This module: validate everything (debug)\n\n// Per-function overrides\nfunction hot(! x: 0) {} // Skip validation even if module says 'inputs'\nfunction safe(? x: 0) {} // Force validation even if module says 'none'\n```\n\nUse `!` (skip validation) only in hot loops where every microsecond counts\nand the data source is already trusted. In all other cases, the ~1.5x\noverhead of `safety inputs` is negligible compared to the bugs it catches.\n\nTypeScript has no equivalent. You're either all-in on types or you\nuse `as any` to escape.\n\n---\n\n## Automatic Conversion\n\nTJS includes a TypeScript-to-TJS converter:\n\n```bash\n# Convert a TypeScript file to TJS\nbun src/cli/tjs.ts convert input.ts --emit-tjs > output.tjs\n\n# Convert and emit JavaScript directly\nbun src/cli/tjs.ts convert input.ts > output.js\n```\n\nFrom code:\n\n```typescript\nimport { fromTS } from 'tjs-lang/lang/from-ts'\n\nconst result = fromTS(tsSource, { emitTJS: true })\nconsole.log(result.code) // TJS source\n```\n\nThe converter handles:\n\n- Primitive type annotations -> example values\n- Optional parameters -> default values\n- Interfaces -> `Type` declarations\n- String literal unions -> `Union` declarations\n- Enums -> `Enum` declarations\n- `private` -> `#` private fields\n- `Promise<T>` -> unwrapped return types\n\nIt warns on constructs it can't convert cleanly (complex generics,\nutility types, conditional types).\n\n## Migration Strategy\n\n### Incremental Adoption\n\nYou don't have to convert everything at once:\n\n1. **Start at boundaries.** Convert API handlers and validation layers\n first -- these benefit most from runtime types.\n2. **Convert hot modules.** Modules with frequent type-related bugs are\n good candidates.\n3. **Leave internals for last.** Pure computational code that's already\n well-tested benefits least from migration.\n\n### The Bun Plugin\n\nIf you use Bun, `.tjs` files work alongside `.ts` files with zero config:\n\n```javascript\n// bunfig.toml already preloads the TJS plugin\nimport { processOrder } from './orders.tjs' // just works\nimport { validateUser } from './users.ts' // also works\n```\n\n### What to Watch For\n\n**Example values matter.** `count: 0` means \"number, example is 0.\" If\nyour function breaks on 0 (division, array index), the automatic signature\ntest will catch it immediately. Choose examples that exercise the\nhappy path.\n\n**Return types generate tests.** `-> 0` means TJS will call your function\nwith the parameter examples and check the result. If your function has\nside effects or requires setup, use `-! 0` to skip the signature test.\n\n**Structural equality changes behavior.** If your code relies on `==`\nfor type coercion (comparing numbers to strings, etc.), you'll need to\nupdate those comparisons. This is almost always a bug fix.\n\n---\n\n## Side-by-Side: A Complete Example\n\n### TypeScript\n\n```typescript\ninterface Product {\n id: string\n name: string\n price: number\n tags: string[]\n}\n\ninterface CartItem {\n product: Product\n quantity: number\n}\n\nfunction calculateTotal(items: CartItem[], taxRate: number = 0.1): number {\n const subtotal = items.reduce(\n (sum, item) => sum + item.product.price * item.quantity,\n 0\n )\n return Math.round(subtotal * (1 + taxRate) * 100) / 100\n}\n\nfunction applyDiscount(\n total: number,\n code: string | null\n): { final: number; discount: number } {\n const discounts: Record<string, number> = {\n SAVE10: 0.1,\n SAVE20: 0.2,\n }\n const rate = code ? discounts[code] ?? 0 : 0\n return {\n final: Math.round(total * (1 - rate) * 100) / 100,\n discount: rate,\n }\n}\n```\n\n### TJS\n\n```javascript\nType Product {\n description: 'a product in the catalog'\n example: { id: '', name: '', price: 0.0, tags: [''] }\n}\n\nType CartItem {\n description: 'a product with quantity'\n example: { product: { id: '', name: '', price: 0.0, tags: [''] }, quantity: +0 }\n}\n\nfunction calculateTotal(items: [CartItem], taxRate = 0.1) -> 0.0 {\n const subtotal = items.reduce(\n (sum, item) => sum + item.product.price * item.quantity,\n 0\n )\n return Math.round(subtotal * (1 + taxRate) * 100) / 100\n}\n\nfunction applyDiscount(total: 0.0, code: '' || null) -> { final: 0.0, discount: 0.0 } {\n const discounts = {\n SAVE10: 0.1,\n SAVE20: 0.2,\n }\n const rate = code ? discounts[code] ?? 0 : 0\n return {\n final: Math.round(total * (1 - rate) * 100) / 100,\n discount: rate,\n }\n}\n\ntest 'cart calculation' {\n const items = [\n { product: { id: '1', name: 'Widget', price: 10, tags: [] }, quantity: 3 }\n ]\n expect(calculateTotal(items, 0)).toBe(30)\n expect(calculateTotal(items, 0.1)).toBe(33)\n}\n\ntest 'discount codes' {\n expect(applyDiscount(100, 'SAVE10')).toEqual({ final: 90, discount: 0.1 })\n expect(applyDiscount(100, null)).toEqual({ final: 100, discount: 0 })\n expect(applyDiscount(100, 'INVALID')).toEqual({ final: 100, discount: 0 })\n}\n```\n\nThe TJS version is about the same length, but the types exist at runtime,\nthe tests live with the code, and invalid inputs return errors instead\nof crashing.\n\n---\n\n## Traceability: The Death of Source Maps\n\nTypeScript debugging relies on source maps -- external files that try to\nmap minified, transpiled JavaScript back to your original code. They're\nbrittle, often out of sync, and fail entirely in complex build pipelines.\n\nTJS eliminates source maps. Every function carries its source identity\nin `__tjs` metadata:\n\n```javascript\nfunction add(a: 0, b: 0) -> 0 { return a + b }\n\nadd.__tjs.source // \"mymodule.tjs:3\"\n```\n\n- **Zero-config debugging:** If a function fails validation, the error\n points to the exact line in your `.tjs` source, not a generated `.js` file.\n- **Transparent eval:** Even code run via `Eval()` or the `AgentVM`\n provides clear traces because AST and source metadata are preserved.\n- **No build bloat:** You don't ship `.map` files to production just to\n know why your app crashed.\n\n---\n\n## FAQ\n\n### How do I use TJS with existing NPM packages?\n\nTJS is a superset of JavaScript. Import any NPM package as usual:\n\n```javascript\nimport lodash from 'https://esm.sh/lodash@4.17.21'\nimport { z } from 'zod' // works, though you won't need it\n```\n\nWhen you import a vanilla JS library, its exports have no TJS metadata.\nYou can wrap them in a TJS boundary to get runtime safety:\n\n```javascript\nimport { rawGeocode } from 'legacy-geo-pkg'\n\n// Wrap to validate at your system boundary\nfunction geocode(addr: '') -> { lat: 0.0, lon: 0.0 } {\n return rawGeocode(addr)\n}\n```\n\nThe untyped library code runs freely. Your TJS wrapper validates the\nresult before it enters your typed world.\n\n### Does structural equality (`==`) handle circular references?\n\nNo. Circular structures will cause infinite recursion. Use identity\ncomparison (`===`) for objects that might be circular, or define a\ncustom `.Equals` method on the class.\n\n### What happens to TypeScript's `strict` mode checks?\n\nTJS doesn't have `strictNullChecks` or `noImplicitAny` because the\nproblems they solve don't exist:\n\n- **Null safety:** `|| null` explicitly marks nullable parameters.\n Functions without it reject null at runtime.\n- **Implicit any:** Every TJS parameter has an example value that\n determines its type. There's nothing to be implicit about.\n- **Strict property access:** Runtime validation catches missing\n properties with a clear error message instead of `undefined`.\n\n---\n\n## Learn More\n\n- [TJS Language Reference](DOCS-TJS.md) -- Full syntax and features\n- [TJS for JavaScript Programmers](TJS-FOR-JS.md) -- Coming from vanilla JS?\n- [AJS Agent Language](DOCS-AJS.md) -- The sandboxed agent VM\n- [Playground](https://tjs-platform.web.app) -- Try it live\n"
828
+ "text": "<!--{\"section\": \"tjs-for-ts\", \"group\": \"docs\", \"order\": 0, \"navTitle\": \"TJS for TS Devs\"}-->\n\n# TJS for TypeScript Programmers\n\n_What if your types didn't disappear at runtime?_\n\n---\n\nTypeScript is great. It catches bugs at compile time, makes refactoring safer,\nand gives you autocomplete. But it has a fundamental limitation: types are\nfiction. They exist only in your editor, and they vanish completely at runtime.\n\nTJS starts from a different premise: **types are example values that survive\nto runtime**. This gives you everything TypeScript gives you, plus runtime\nvalidation, reflection, documentation, inline tests of private methods, and traceability of errors back to source code -- from a single source of truth.\n\nThis guide is split into two paths:\n\n1. **[Using TJS from TypeScript](#part-1-using-tjs-from-typescript)** -- Keep your TS codebase, use TJS for safe eval and agent execution\n2. **[Migrating to TJS](#part-2-migrating-to-tjs)** -- Convert your codebase from TypeScript to TJS\n\n---\n\n# Part 1: Using TJS from TypeScript\n\nYou don't have to rewrite anything. TJS provides tools you can use directly\nfrom your TypeScript codebase.\n\n## Safe Eval\n\nThe most common reason to reach for TJS from TypeScript: running untrusted\ncode safely.\n\n```typescript\nimport { Eval, SafeFunction } from 'tjs-lang/eval'\n\n// Run user-provided code with a gas limit\nconst { result, fuelUsed } = await Eval({\n code: userCode,\n context: { items: data, threshold: 10 },\n fuel: 1000,\n capabilities: {\n fetch: sandboxedFetch, // your whitelist-wrapped fetch\n },\n})\n\n// Or create a reusable safe function\nconst transform = await SafeFunction({\n body: 'return items.filter(x => x.price < budget)',\n params: ['items', 'budget'],\n fuel: 500,\n})\n\nconst { result } = await transform(products, 100)\n```\n\nNo `eval()`. No CSP violations. No Docker containers. The code runs in a\nfuel-metered sandbox with only the capabilities you inject.\n\n## Agent VM\n\nBuild and execute JSON-serializable agents:\n\n```typescript\nimport { ajs, AgentVM } from 'tjs-lang'\n\n// Parse agent source to JSON AST\nconst agent = ajs`\n function analyze({ data, query }) {\n let filtered = data.filter(x => x.score > 0.5)\n let summary = llmPredict({\n prompt: 'Summarize findings for: ' + query,\n data: filtered\n })\n return { query, summary, count: filtered.length }\n }\n`\n\n// Execute with resource limits\nconst vm = new AgentVM()\nconst { result } = await vm.run(\n agent,\n { data, query },\n {\n fuel: 1000,\n timeoutMs: 5000,\n capabilities: { fetch: myFetch, llm: myLlm },\n }\n)\n```\n\nThe agent AST is JSON. You can store it in a database, send it over the\nnetwork, version it, diff it, audit it.\n\n## Type-Safe Builder\n\nConstruct agents programmatically with full TypeScript support:\n\n```typescript\nimport { Agent, AgentVM, s } from 'tjs-lang'\n\nconst pipeline = Agent.take(s.object({ url: s.string, maxResults: s.number }))\n .httpFetch({ url: { $kind: 'arg', path: 'url' } })\n .as('response')\n .varSet({\n key: 'results',\n value: {\n $expr: 'member',\n object: { $expr: 'ident', name: 'response' },\n property: 'items',\n },\n })\n .return(s.object({ results: s.array(s.any) }))\n\nconst vm = new AgentVM()\nconst { result } = await vm.run(\n pipeline.toJSON(),\n { url, maxResults: 10 },\n {\n fuel: 500,\n capabilities: { fetch },\n }\n)\n```\n\n## TypeScript Entry Points\n\nTJS is tree-shakeable. Import only what you need:\n\n```typescript\nimport { Agent, AgentVM, ajs, tjs } from 'tjs-lang' // Everything\nimport { Eval, SafeFunction } from 'tjs-lang/eval' // Safe eval only\nimport { tjs, transpile } from 'tjs-lang/lang' // Language tools\nimport { fromTS } from 'tjs-lang/lang/from-ts' // TS -> TJS converter\n```\n\n## When to Stay in TypeScript\n\nIf your codebase is TypeScript and you're happy with it, you probably only\nneed TJS for:\n\n- Running user-provided or LLM-generated code safely\n- Building agents that travel over the network\n- Adding runtime type validation at system boundaries\n- Eval without `eval()`\n\nYou don't need to migrate anything. The libraries work from TypeScript.\n\n---\n\n# Part 2: Migrating to TJS\n\nIf you want the full TJS experience -- runtime types, structural equality,\nmonadic errors, inline tests -- here's how to convert.\n\n## The Core Idea: Types as Examples\n\nTypeScript describes types abstractly. TJS describes them concretely:\n\n```typescript\n// TypeScript: what TYPE is this?\nfunction greet(name: string): string { ... }\n\n// TJS: what's an EXAMPLE of this?\nfunction greet(name: 'World') -> '' { ... }\n```\n\n`'World'` tells TJS: this is a string, it's required, and here's a valid\nexample. The example doubles as documentation and test data.\n\n## Conversion Reference\n\n### Primitives\n\n```typescript\n// TypeScript // TJS\nname: string name: ''\ncount: number count: 0.0 // float (any number)\nindex: number index: 0 // integer\nage: number age: +0 // non-negative integer\nflag: boolean flag: true\nitems: string[] items: ['']\nnested: number[][] nested: [[0]]\n```\n\n**Important:** The example value determines the _type_, not a literal\nconstraint. `name: 'World'` means \"required string\" -- not \"must be the\nstring `'World'`.\" Any string passes validation. The example is there for\ndocumentation, testing, and type inference. Think of it as `string` with\na built-in `@example` tag.\n\n**Numeric precision:** TJS distinguishes three numeric types using valid\nJavaScript syntax that JS itself ignores:\n\n| You Write | TJS Type | Runtime Check |\n| --------- | ---------------------- | ------------------------------- |\n| `3.14` | `number` (float) | Any number |\n| `0.0` | `number` (float) | Any number |\n| `42` | `integer` | `Number.isInteger(x)` |\n| `0` | `integer` | `Number.isInteger(x)` |\n| `+20` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n| `+0` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n\nTypeScript's `number` is a single type. TJS gives you three levels of\nprecision -- all using expressions that are already legal JavaScript.\nThe automatic converter maps TypeScript `number` to `0.0` (float) to\npreserve the widest behavior; you can then narrow manually to `0` (integer)\nor `+0` (non-negative integer) where appropriate.\n\n### Optional Parameters\n\n```typescript\n// TypeScript // TJS\nfunction f(x?: string) {}\nfunction f(x = '') {}\nfunction f(x: string = 'hi') {}\nfunction f(x = 'hi') {}\n```\n\nIn TypeScript, `?` means optional with type `string | undefined`.\nIn TJS, `= value` means optional with that default. Same semantics, less syntax.\n\n### Object Shapes\n\n```typescript\n// TypeScript\nfunction createUser(opts: { name: string; age: number; email?: string }) {}\n\n// TJS\nfunction createUser(opts: { name: '', age: 0, email = '' }) {}\n```\n\nRequired properties use `:`, optional ones use `=`.\n\n### Return Types\n\n```typescript\n// TypeScript // TJS\nfunction add(a: number, b: number): number function add(a: 0, b: 0) -> 0\nfunction getUser(): { name: string } function getUser() -> { name: '' }\nfunction fetchData(): Promise<string[]> function fetchData() -> ['']\n```\n\nThe `fromTS` converter unwraps `Promise<T>` in return type annotations --\nyou annotate the resolved type, not the wrapper. This only applies when\nconverting from TypeScript; in native TJS you just write normal\n`async`/`await` and annotate what the function resolves to.\n\nThe return annotation also generates an automatic test: `add(0, 0)` must\nreturn a number. If it doesn't, you get an error at transpile time.\n\n### Interfaces and Type Aliases\n\n```typescript\n// TypeScript\ninterface User {\n name: string\n age: number\n email?: string\n}\n\ntype Status = 'active' | 'inactive' | 'banned'\n\n// TJS\nType User {\n description: 'a registered user'\n example: { name: '', age: 0, email = '' }\n}\n\nUnion Status 'account status' 'active' | 'inactive' | 'banned'\n```\n\n### Enums\n\n```typescript\n// TypeScript\nenum Color {\n Red = 'red',\n Green = 'green',\n Blue = 'blue',\n}\n\n// TJS\nEnum Color 'CSS color' {\n Red = 'red'\n Green = 'green'\n Blue = 'blue'\n}\n```\n\n### Classes\n\n```typescript\n// TypeScript\nclass Point {\n private x: number\n private y: number\n\n constructor(x: number, y: number) {\n this.x = x\n this.y = y\n }\n\n distanceTo(other: Point): number {\n return Math.sqrt((this.x - other.x) ** 2 + (this.y - other.y) ** 2)\n }\n}\n\nconst p = new Point(10, 20)\n\n// TJS\nclass Point {\n #x\n #y\n\n constructor(x: 0, y: 0) {\n this.#x = x\n this.#y = y\n }\n\n distanceTo(other: Point) -> 0 {\n return Math.sqrt((this.#x - other.#x) ** 2 + (this.#y - other.#y) ** 2)\n }\n}\n\nconst p = Point(10, 20) // no 'new' needed\n```\n\nKey differences:\n\n- `private` becomes `#` (native private fields)\n- Type annotations become example values\n- `new` is optional (linter warns against it)\n\n### Generics\n\nTJS takes a different approach to generics. TypeScript has function-level\ntype parameters (`<T>`) that vanish at runtime. TJS has `Generic` declarations\nthat produce runtime-checkable type constructors:\n\n```typescript\n// TypeScript -- compile-time only, gone at runtime\nfunction identity<T>(x: T): T { return x }\ninterface Box<T> { value: T }\n\n// TJS -- no function-level generics; use Generic for container types\nGeneric Box<T> {\n description: 'a boxed value'\n predicate(x, T) {\n return typeof x === 'object' && x !== null && 'value' in x && T(x.value)\n }\n}\n\n// Usage: Box(Number) is a runtime type checker\nconst isNumberBox = Box(Number)\nisNumberBox({ value: 42 }) // true\nisNumberBox({ value: 'nope' }) // false\n```\n\nFor simple generic functions like `identity` or `first`, you don't need\ngenerics at all -- just skip the type parameter. TJS validates the\nconcrete types at call sites, not the abstract relationship between them.\n\n```javascript\n// Simple -- no generics needed, the function just works\nfunction first(arr: [0]) { return arr[0] }\n\n// If you need runtime-checked containers, use Generic\nGeneric Pair<T, U> {\n description: 'a typed pair'\n predicate(x, T, U) { return T(x[0]) && U(x[1]) }\n}\n```\n\nWhen converting from TypeScript, the `fromTS` converter preserves generic\nmetadata but types become `any`. This is a place where manual review helps.\n\n### Nullability\n\n```typescript\n// TypeScript\nfunction find(id: number): User | null { ... }\n\n// TJS\nfunction find(id: 0) -> { name: '', age: 0 } || null { ... }\n```\n\nTJS distinguishes `null` from `undefined` -- they're different types, just\nas `typeOf(null)` returns `'null'` and `typeOf(undefined)` returns\n`'undefined'`. Writing `|| null` means the value can be the base type or\n`null`, but not `undefined`. Optional parameters (using `=`) accept\n`undefined` because that's what you get when the caller omits the argument.\n\n## What TypeScript Has That TJS Doesn't\n\nTJS intentionally skips TypeScript features that don't survive to runtime\nor add complexity without proportional value:\n\n| TypeScript Feature | TJS Equivalent |\n| --------------------------- | ----------------------------------------- |\n| `interface` | `Type` with example |\n| `type` aliases | `Type`, `Union`, or `Enum` |\n| Conditional types | Use predicates in `Type` |\n| Mapped types | Not needed (types are values) |\n| `keyof`, `typeof` | Use runtime `Object.keys()`, `typeOf()` |\n| `Partial<T>`, `Pick<T>` | Define the shape you need directly |\n| Declaration files (`.d.ts`) | Generated from `__tjs` metadata (`--dts`) |\n| `as` type assertions | Not needed (values are checked) |\n| `any` escape hatch | `safety none` per-module or `!` per-fn |\n| Decorators | Not supported |\n| `namespace` | Use modules |\n\nThe philosophy: if a type feature doesn't do something at runtime, it's\ncomplexity without payoff.\n\n### But What About Narrowing?\n\nTypeScript's type system is Turing-complete. You can express astonishing\nconstraints -- `Pick<Omit<T, K>, Extract<keyof T, string>>` -- but the\nresulting types are often harder to understand than the code they describe.\nAnd they vanish at runtime, so they can't protect you from bad API data.\n\nTJS takes the opposite approach: `Type()` gives you a predicate function.\nIf you can write a boolean expression, you can define a type. No type-level\nprogramming language to learn.\n\n```javascript\n// TypeScript: branded types + manual validation\ntype Email = string & { __brand: 'email' }\nfunction isEmail(s: string): s is Email {\n return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(s)\n}\nfunction validateEmail(input: string): Email {\n if (!isEmail(input)) throw new Error('Invalid email')\n return input\n}\n\n// TJS: one line, works at runtime\nType Email {\n description: 'email address'\n example: 'user@example.com'\n predicate(v) { return typeof v === 'string' && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(v) }\n}\n```\n\nThe TJS `Type()` built-in handles everything from simple shapes to\nsophisticated domain constraints:\n\n```javascript\n// Simple -- infer from example\nType Name 'Alice' // string\n\n// Constrained -- predicate narrows beyond the base type\nType PositiveInt {\n description: 'a positive integer'\n example: 1\n predicate(v) { return typeof v === 'number' && Number.isInteger(v) && v > 0 }\n}\n\n// Domain-specific -- readable business rules\nType USZipCode {\n description: '5-digit US zip code'\n example: '90210'\n predicate(v) { return typeof v === 'string' && /^\\d{5}$/.test(v) }\n}\n\n// Combinators -- compose types\nType OptionalEmail Nullable(Email) // Email | null\n\n// Schema-based -- use tosijs-schema for structured validation\nType AgeRange {\n description: 'valid age'\n example: 25\n predicate(v) { return typeof v === 'number' && v >= 0 && v <= 150 }\n}\n```\n\nCompare the TypeScript equivalents:\n\n| What you want | TypeScript | TJS |\n| ------------------- | ----------------------------------------------------------- | -------------------------------------- |\n| String with format | Branded type + type guard + validation function | `Type Email { predicate(v) {...} }` |\n| Number in range | Branded type + manual check | `Type Age { predicate(v) {...} }` |\n| Non-empty string | Template literal type (compile-only) | `Type NonEmpty { predicate(v) {...} }` |\n| Nullable variant | `T \\| null` (compile-only) | `Nullable(MyType)` (runtime-checked) |\n| Union of literals | `'a' \\| 'b' \\| 'c'` (compile-only) | `Union Status 'a' \\| 'b' \\| 'c'` |\n| Discriminated union | `type Shape = { kind: 'circle' } \\| ...` + manual narrowing | `Union('kind', { circle: {...} })` |\n| Generic container | `interface Box<T>` (compile-only) | `Generic Box<T> { predicate(...) }` |\n\nEvery row in the TypeScript column is compile-time fiction that disappears\nwhen your code runs. Every row in the TJS column is a runtime check that\nactually catches bugs in production. And the TJS versions are shorter,\nbecause a predicate is just a function -- not a type-level program.\n\nTJS also ships common types out of the box: `TString`, `TNumber`,\n`TBoolean`, `TInteger`, `TPositiveInt`, `TNonEmptyString`, `TEmail`,\n`TUrl`, `TUuid`, `Timestamp`, `LegalDate`. No imports from a validation\nlibrary needed.\n\n### Tooling Comparison\n\n| Concern | TypeScript | TJS |\n| ------------------- | ---------------------------------- | ---------------------------------------------------------------- |\n| **Type checking** | `tsc` (compile-time only) | Runtime validation (survives build) |\n| **Runtime schemas** | Zod / io-ts / Ajv (separate) | Built-in (types _are_ schemas) |\n| **Linting** | ESLint + plugins | Built-in linter (unused vars, unreachable code, no-explicit-new) |\n| **Testing** | Vitest / Jest (separate files) | Inline `test` blocks (transpile-time) |\n| **Equality** | Reference-based only | Structural `==`, identity `===` |\n| **Build toolchain** | tsc + bundler (webpack/Vite/etc) | Transpiles in-browser, no build step |\n| **Debugging** | Source maps (brittle, build bloat) | Functions carry source identity via `__tjs` metadata |\n| **Documentation** | JSDoc / TypeDoc (manual) | Generated from `__tjs` metadata |\n| **Editor support** | Mature (VSCode, etc) | Monaco/CodeMirror/Ace + VSCode/Cursor extensions |\n\n## What TJS Has That TypeScript Doesn't\n\n### Runtime Validation\n\nTypeScript:\n\n```typescript\n// Types are a promise. A lie, if the data comes from outside.\nfunction processOrder(order: Order) {\n // If order came from an API, nothing guarantees it matches Order.\n // You need Zod/io-ts/ajv AND the TypeScript type AND keep them in sync.\n}\n```\n\nTJS:\n\n```javascript\n// Types are checked at runtime. One source of truth.\nfunction processOrder(order: { items: [{ id: 0, qty: 0 }], total: 0 }) -> { status: '' } {\n // If order doesn't match, caller gets a MonadicError -- no crash.\n}\n```\n\n### Structural Equality\n\nTypeScript inherits JavaScript's broken equality. TJS fixes it:\n\n```javascript\n// TJS\n[1, 2, 3] == [1, 2, 3] // true (structural)\n{ a: 1 } == { a: 1 } // true (structural)\nobj1 === obj2 // identity check (same reference)\n```\n\nThe implementation is optimized: it short-circuits on reference identity\n(`===` check first), then type comparison, then recursive structural\ncomparison. For performance-critical hot paths comparing large objects,\nuse `===` for identity checks or define an `.Equals` method on your class\nto control comparison logic.\n\n### Monadic Errors\n\nTypeScript uses exceptions. TJS uses values:\n\n```javascript\n// TypeScript -- you have to remember to try/catch\nfunction divide(a: number, b: number): number {\n if (b === 0) throw new Error('Division by zero')\n return a / b\n}\n// Caller forgets try/catch? Crash.\n\n// TJS -- errors flow through the pipeline\nfunction divide(a: 0, b: 0) -> 0 {\n if (b === 0) return MonadicError('Division by zero')\n return a / b\n}\n// Caller gets an error value. No crash. Ever.\n```\n\n**How the caller handles it:**\n\n```javascript\n// Option 1: Check the result\nconst result = divide(10, 0)\nif (result instanceof Error) {\n console.log(result.message) // 'Division by zero'\n} else {\n useResult(result)\n}\n\n// Option 2: Just keep going -- errors propagate automatically\nconst a = divide(10, 0) // MonadicError\nconst b = double(a) // Receives error, returns it immediately (skips execution)\nconst c = format(b) // Same -- error flows through the whole chain\n// c is still the original MonadicError from divide()\n```\n\nIf you've used Rust's `Result<T, E>` or Haskell's `Either`, the pattern\nis familiar. The key difference from TypeScript: you never have to guess\nwhether a function might throw. Type errors and validation failures are\nalways values, never exceptions.\n\n### Inline Tests\n\n```javascript\nfunction fibonacci(n: 0) -> 0 {\n if (n <= 1) return n\n return fibonacci(n - 1) + fibonacci(n - 2)\n}\n\ntest 'fibonacci sequence' {\n expect(fibonacci(0)).toBe(0)\n expect(fibonacci(1)).toBe(1)\n expect(fibonacci(10)).toBe(55)\n}\n```\n\nTests run at transpile time. They're stripped from production output.\nNo separate test files, no test runner configuration.\n\n### Safety Controls\n\n```javascript\nsafety none // This module: skip all validation (performance)\nsafety inputs // This module: validate inputs only (default)\nsafety all // This module: validate everything (debug)\n\n// Per-function overrides\nfunction hot(! x: 0) {} // Skip validation even if module says 'inputs'\nfunction safe(? x: 0) {} // Force validation even if module says 'none'\n```\n\nUse `!` (skip validation) only in hot loops where every microsecond counts\nand the data source is already trusted. In all other cases, the ~1.5x\noverhead of `safety inputs` is negligible compared to the bugs it catches.\n\nTypeScript has no equivalent. You're either all-in on types or you\nuse `as any` to escape.\n\n---\n\n## Automatic Conversion\n\nTJS includes a TypeScript-to-TJS converter:\n\n```bash\n# Convert a TypeScript file to TJS\nbun src/cli/tjs.ts convert input.ts --emit-tjs > output.tjs\n\n# Convert and emit JavaScript directly\nbun src/cli/tjs.ts convert input.ts > output.js\n```\n\nFrom code:\n\n```typescript\nimport { fromTS } from 'tjs-lang/lang/from-ts'\n\nconst result = fromTS(tsSource, { emitTJS: true })\nconsole.log(result.code) // TJS source\n```\n\nThe converter handles:\n\n- Primitive type annotations -> example values\n- Optional parameters -> default values\n- Interfaces -> `Type` declarations\n- String literal unions -> `Union` declarations\n- Enums -> `Enum` declarations\n- `private` -> `#` private fields\n- `Promise<T>` -> unwrapped return types\n\nIt warns on constructs it can't convert cleanly (complex generics,\nutility types, conditional types).\n\n### What `fromTS` Handles Well\n\n- Primitive annotations (`string`, `number`, `boolean`) → example values\n- Interfaces and type aliases → `Type` declarations\n- String literal unions → `Union`\n- Enums → `Enum`\n- Optional params, default values, private fields\n- Class declarations with constructor params\n- `Promise<T>` unwrapping\n- JSDoc comments → TDoc comments\n\n### Constrained Generics\n\nWhen the converter encounters a constrained generic like\n`<T extends { id: number }>`, it uses the constraint shape as the\nexample value instead of falling back to `any`. This means:\n\n```typescript\n// TypeScript\nfunction first<T extends { id: number }>(items: T[]): T {\n return items[0]\n}\n\n// Converted TJS — uses constraint shape, not 'any'\nfunction first(items: [{ id: 0.0 }]) -! { id: 0.0 } { ... }\n```\n\nGeneric defaults also work: `<T = string>` uses `string` as the example.\nUnconstrained generics (`<T>` with no `extends` or default) still degrade\nto `any` — there's genuinely no information about what T is.\n\n### What `fromTS` Can't Fully Express\n\nTJS types are example values, not abstract type algebra. Some TypeScript\npatterns have no direct TJS equivalent:\n\n| TypeScript Pattern | What Happens | Workaround |\n| ------------------------------------------ | ------------------------------ | ---------------------------------- |\n| `Partial<T>`, `Required<T>` | Emits warning, uses base shape | Define the shape you need directly |\n| `Pick<T, K>`, `Omit<T, K>` | Emits warning, uses full shape | Define the subset shape explicitly |\n| `ReturnType<T>`, `Parameters<T>` | Drops to `any` | Use a concrete example value |\n| Conditional types (`T extends U ? X : Y`) | Drops to `any` | Use a `Type` with a predicate |\n| Mapped types (`{ [K in keyof T]: ... }`) | Drops to `any` | Define the shape literally |\n| Template literal types (`` `${A}-${B}` ``) | Becomes `string` | Use a `Type` with predicate |\n| Deeply nested generics (`Foo<Bar<U>>`) | Inner params become `any` | Define concrete types at use sites |\n| Intersection types (`A & B`) | Objects merged; else `any` | Merge the shapes manually |\n| `readonly`, `as const` | Stripped (JS doesn't enforce) | Use `Object.freeze()` if needed |\n\nThis isn't a limitation of the converter — it's a design choice. TJS types\nare runtime values, so they can only express things that are checkable at\nruntime with a boolean test. TypeScript's type-level computation is powerful\nbut produces types that vanish after compilation.\n\n**The practical impact:** If you're converting a library with heavy utility\ntypes (like `Pick<Omit<T, K>, Extract<keyof T, string>>`), the converter\nwill emit warnings and fall back to `any` for the complex parts. The\ngenerated code still runs correctly — you just lose some type granularity\nthat you can add back with TJS `Type` predicates where it matters.\n\n## Migration Strategy\n\n### Incremental Adoption\n\nYou don't have to convert everything at once:\n\n1. **Start at boundaries.** Convert API handlers and validation layers\n first -- these benefit most from runtime types.\n2. **Convert hot modules.** Modules with frequent type-related bugs are\n good candidates.\n3. **Leave internals for last.** Pure computational code that's already\n well-tested benefits least from migration.\n\n### The Bun Plugin\n\nIf you use Bun, `.tjs` files work alongside `.ts` files with zero config:\n\n```javascript\n// bunfig.toml already preloads the TJS plugin\nimport { processOrder } from './orders.tjs' // just works\nimport { validateUser } from './users.ts' // also works\n```\n\n### What to Watch For\n\n**Example values matter.** `count: 0` means \"number, example is 0.\" If\nyour function breaks on 0 (division, array index), the automatic signature\ntest will catch it immediately. Choose examples that exercise the\nhappy path.\n\n**Return types generate tests.** `-> 0` means TJS will call your function\nwith the parameter examples and check the result. If your function has\nside effects or requires setup, use `-! 0` to skip the signature test.\n\n**Structural equality changes behavior.** If your code relies on `==`\nfor type coercion (comparing numbers to strings, etc.), you'll need to\nupdate those comparisons. This is almost always a bug fix.\n\n---\n\n## Side-by-Side: A Complete Example\n\n### TypeScript\n\n```typescript\ninterface Product {\n id: string\n name: string\n price: number\n tags: string[]\n}\n\ninterface CartItem {\n product: Product\n quantity: number\n}\n\nfunction calculateTotal(items: CartItem[], taxRate: number = 0.1): number {\n const subtotal = items.reduce(\n (sum, item) => sum + item.product.price * item.quantity,\n 0\n )\n return Math.round(subtotal * (1 + taxRate) * 100) / 100\n}\n\nfunction applyDiscount(\n total: number,\n code: string | null\n): { final: number; discount: number } {\n const discounts: Record<string, number> = {\n SAVE10: 0.1,\n SAVE20: 0.2,\n }\n const rate = code ? discounts[code] ?? 0 : 0\n return {\n final: Math.round(total * (1 - rate) * 100) / 100,\n discount: rate,\n }\n}\n```\n\n### TJS\n\n```javascript\nType Product {\n description: 'a product in the catalog'\n example: { id: '', name: '', price: 0.0, tags: [''] }\n}\n\nType CartItem {\n description: 'a product with quantity'\n example: { product: { id: '', name: '', price: 0.0, tags: [''] }, quantity: +0 }\n}\n\nfunction calculateTotal(items: [CartItem], taxRate = 0.1) -> 0.0 {\n const subtotal = items.reduce(\n (sum, item) => sum + item.product.price * item.quantity,\n 0\n )\n return Math.round(subtotal * (1 + taxRate) * 100) / 100\n}\n\nfunction applyDiscount(total: 0.0, code: '' || null) -> { final: 0.0, discount: 0.0 } {\n const discounts = {\n SAVE10: 0.1,\n SAVE20: 0.2,\n }\n const rate = code ? discounts[code] ?? 0 : 0\n return {\n final: Math.round(total * (1 - rate) * 100) / 100,\n discount: rate,\n }\n}\n\ntest 'cart calculation' {\n const items = [\n { product: { id: '1', name: 'Widget', price: 10, tags: [] }, quantity: 3 }\n ]\n expect(calculateTotal(items, 0)).toBe(30)\n expect(calculateTotal(items, 0.1)).toBe(33)\n}\n\ntest 'discount codes' {\n expect(applyDiscount(100, 'SAVE10')).toEqual({ final: 90, discount: 0.1 })\n expect(applyDiscount(100, null)).toEqual({ final: 100, discount: 0 })\n expect(applyDiscount(100, 'INVALID')).toEqual({ final: 100, discount: 0 })\n}\n```\n\nThe TJS version is about the same length, but the types exist at runtime,\nthe tests live with the code, and invalid inputs return errors instead\nof crashing.\n\n---\n\n## Traceability: The Death of Source Maps\n\nTypeScript debugging relies on source maps -- external files that try to\nmap minified, transpiled JavaScript back to your original code. They're\nbrittle, often out of sync, and fail entirely in complex build pipelines.\n\nTJS eliminates source maps. Every function carries its source identity\nin `__tjs` metadata:\n\n```javascript\nfunction add(a: 0, b: 0) -> 0 { return a + b }\n\nadd.__tjs.source // \"mymodule.tjs:3\"\n```\n\n- **Zero-config debugging:** If a function fails validation, the error\n points to the exact line in your `.tjs` source, not a generated `.js` file.\n- **Transparent eval:** Even code run via `Eval()` or the `AgentVM`\n provides clear traces because AST and source metadata are preserved.\n- **No build bloat:** You don't ship `.map` files to production just to\n know why your app crashed.\n\n---\n\n## FAQ\n\n### How do I use TJS with existing NPM packages?\n\nTJS is a superset of JavaScript. Import any NPM package as usual:\n\n```javascript\nimport lodash from 'https://esm.sh/lodash@4.17.21'\nimport { z } from 'zod' // works, though you won't need it\n```\n\nWhen you import a vanilla JS library, its exports have no TJS metadata.\nYou can wrap them in a TJS boundary to get runtime safety:\n\n```javascript\nimport { rawGeocode } from 'legacy-geo-pkg'\n\n// Wrap to validate at your system boundary\nfunction geocode(addr: '') -> { lat: 0.0, lon: 0.0 } {\n return rawGeocode(addr)\n}\n```\n\nThe untyped library code runs freely. Your TJS wrapper validates the\nresult before it enters your typed world.\n\n### Do Proxies, WeakMaps, and other advanced patterns work?\n\nYes. TJS is purely additive — it adds inline type checks and metadata\nproperties but does not wrap, intercept, or modify JavaScript runtime\nbehavior. Specifically:\n\n- **Proxies** work identically to plain JS. TJS attaches `.__tjs` as a\n plain property on function objects, which doesn't trigger Proxy traps.\n If your Proxy needs custom equality, use the `[tjsEquals]` symbol protocol.\n- **WeakMap/WeakSet** are unaffected. TJS doesn't inspect collection contents.\n- **Symbols** work normally. TJS reserves `Symbol.for('tjs.equals')` for\n custom equality but doesn't interfere with other symbols.\n- **`Object.defineProperty`**, getters/setters, non-enumerable properties\n — all work as expected. TJS validation checks value types, not property\n descriptors.\n- **Prototype chains** are preserved. `instanceof` works correctly with\n TJS-wrapped classes.\n\nIf you're building a Proxy-heavy library (reactive state, ORMs, etc.),\nTJS will not interfere. The transpiled output is plain JavaScript with\nsome `typeof` checks at function entry points.\n\n### Does structural equality (`==`) handle circular references?\n\nNo. Circular structures will cause infinite recursion. Use identity\ncomparison (`===`) for objects that might be circular, or define a\ncustom `.Equals` method on the class.\n\n### What happens to TypeScript's `strict` mode checks?\n\nTJS doesn't have `strictNullChecks` or `noImplicitAny` because the\nproblems they solve don't exist:\n\n- **Null safety:** `|| null` explicitly marks nullable parameters.\n Functions without it reject null at runtime.\n- **Implicit any:** Every TJS parameter has an example value that\n determines its type. There's nothing to be implicit about.\n- **Strict property access:** Runtime validation catches missing\n properties with a clear error message instead of `undefined`.\n\n---\n\n## Learn More\n\n- [TJS Language Reference](DOCS-TJS.md) -- Full syntax and features\n- [TJS for JavaScript Programmers](TJS-FOR-JS.md) -- Coming from vanilla JS?\n- [AJS Agent Language](DOCS-AJS.md) -- The sandboxed agent VM\n- [Playground](https://tjs-platform.web.app) -- Try it live\n"
829
829
  }
830
830
  ]
@@ -1306,7 +1306,7 @@ export class TJSPlayground extends Component<TJSPlaygroundParts> {
1306
1306
  )}`
1307
1307
  },
1308
1308
  onPreviewContent: () => {
1309
- this.parts.outputTabs.value = 1 // Preview is second tab (index 1)
1309
+ this.parts.outputTabs.value = 2 // Preview is third tab (index 2)
1310
1310
  },
1311
1311
  onError: (message) => {
1312
1312
  this.log(`Error: ${message}`)