tjs-lang 0.5.3 → 0.5.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
@@ -306,7 +306,7 @@ function greet(name = 'Alice') { } // name is optional, defaults to 'Alice
306
306
  function createUser(user: { name: '', age: 0 }) { }
307
307
 
308
308
  // Nullable type
309
- function find(id: 0 || null) { } // integer or null
309
+ function find(id: 0 | null) { } // integer or null
310
310
 
311
311
  // Optional TS-style
312
312
  function greet(name?: '') { } // same as name = ''
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\nYou can also use `?:` syntax:\n\n```typescript\nfunction greet(name?: '') {} // same as name = ''\n```\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- 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` |\n| `private foo` | `#foo` |\n| `interface User` | `Type User` |\n| `type Status = 'a' \\| 'b'` | `Union(['a', 'b'])` |\n| `enum Color` | `Enum(...)` |\n\n> **Lossy boolean conversion:** TypeScript `x?: boolean` becomes TJS `x = false`.\n> This collapses \"not passed\" (`undefined`) and \"passed as `false`\" into the same\n> default value. Code that distinguishes the three states (`true` / `false` /\n> `undefined`) may break. A future version may emit `x: false || null` for\n> optional booleans to preserve the `undefined` state.\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## 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"
155
155
  },
156
156
  {
157
157
  "title": "Starfield",
@@ -539,7 +539,7 @@
539
539
  "group": "docs",
540
540
  "order": 0,
541
541
  "navTitle": "Documentation",
542
- "text": "<!--{\"section\": \"ajs\", \"group\": \"docs\", \"order\": 0, \"navTitle\": \"Documentation\"}-->\n\n# AJS: The Agent Language\n\n_Code as Data. Safe. Async. Sandboxed._\n\n---\n\n## What is AJS?\n\nAJS (AsyncJS) is a JavaScript subset that compiles to a **JSON AST**. It's designed for untrusted code—user scripts, LLM-generated agents, remote logic.\n\n```javascript\nfunction searchAndSummarize({ query }) {\n let results = httpFetch({ url: `https://api.example.com/search?q=${query}` })\n let summary = llmPredict({ prompt: `Summarize: ${JSON.stringify(results)}` })\n return { query, summary }\n}\n```\n\nThis compiles to JSON that can be:\n\n- Stored in a database\n- Sent over the network\n- Executed in a sandboxed VM\n- Audited before running\n\n---\n\n## The VM\n\nAJS runs in a gas-limited, isolated VM with strict resource controls.\n\n```typescript\nimport { ajs, AgentVM } from 'tjs-lang'\n\nconst agent = ajs`\n function process({ url }) {\n let data = httpFetch({ url })\n return { fetched: data }\n }\n`\n\nconst vm = new AgentVM()\nconst result = await vm.run(\n agent,\n { url: 'https://api.example.com' },\n {\n fuel: 1000, // CPU budget\n timeoutMs: 5000, // Wall-clock limit\n }\n)\n```\n\n### Fuel Metering\n\nEvery operation costs fuel:\n\n| Operation | Cost |\n| ------------------------ | ---- |\n| Expression evaluation | 0.01 |\n| Variable set/get | 0.1 |\n| Control flow (if, while) | 0.5 |\n| HTTP fetch | 10 |\n| LLM predict | 100 |\n\nWhen fuel runs out, execution stops safely:\n\n```typescript\nif (result.fuelExhausted) {\n // Agent tried to run forever - stopped safely\n}\n```\n\n### Timeout Enforcement\n\nFuel protects against CPU abuse. Timeouts protect against I/O abuse:\n\n```typescript\nawait vm.run(agent, args, {\n fuel: 1000,\n timeoutMs: 5000, // Hard 5-second limit\n})\n```\n\nSlow network calls can't hang your servers.\n\n### Capability Injection\n\nThe VM starts with **zero capabilities**. You grant what each agent needs:\n\n```typescript\nconst capabilities = {\n fetch: createFetchCapability({\n allowedHosts: ['api.example.com'],\n }),\n store: createReadOnlyStore(),\n // No llm - this agent can't call AI\n}\n\nawait vm.run(agent, args, { capabilities })\n```\n\n---\n\n## Input/Output Contract\n\nAJS agents are composable — one agent's output feeds into another's input. To ensure this works reliably:\n\n- **Functions take a single destructured object parameter:** `function process({ input })`\n- **Functions must return a plain object:** `return { result }`, `return { summary, count }`\n- **Non-object returns produce an AgentError:** `return 42` or `return 'hello'` will fail\n- **Bare `return` is allowed** for void functions (no output)\n\n```javascript\n// CORRECT — object in, object out\nfunction add({ a, b }) {\n return { sum: a + b }\n}\n\n// WRONG — non-object returns are errors\nfunction add({ a, b }) {\n return a + b // AgentError: must return an object\n}\n```\n\n---\n\n## Syntax\n\nAJS is a JavaScript subset. Familiar syntax, restricted features.\n\n### What's Allowed\n\n```javascript\n// Functions\nfunction process({ input }) {\n return { output: input * 2 }\n}\n\n// Variables\nlet x = 10\nconst y = 'hello'\n\n// Conditionals\nif (x > 5) {\n return { size: 'big' }\n} else {\n return { size: 'small' }\n}\n\n// Loops\nfor (let i = 0; i < 10; i++) {\n total = total + i\n}\n\nfor (let item of items) {\n results.push(item.name)\n}\n\nwhile (count > 0) {\n count = count - 1\n}\n\n// Try/catch\ntry {\n riskyOperation()\n} catch (e) {\n return { error: e.message }\n}\n\n// Template literals\nlet message = `Hello, ${name}!`\n\n// Object/array literals\nlet obj = { a: 1, b: 2 }\nlet arr = [1, 2, 3]\n\n// Destructuring\nlet { name, age } = user\nlet [first, second] = items\n\n// Spread\nlet merged = { ...defaults, ...overrides }\nlet combined = [...arr1, ...arr2]\n\n// Ternary\nlet result = x > 0 ? 'positive' : 'non-positive'\n\n// Logical operators\nlet value = a && b\nlet fallback = a || defaultValue\nlet nullish = a ?? defaultValue\n```\n\n### What's Forbidden\n\n| Feature | Why Forbidden |\n| -------------------------- | ------------------------------------------------- |\n| `class` | Too complex for LLMs, enables prototype pollution |\n| `new` | Arbitrary object construction |\n| `this` | Implicit context, hard to sandbox |\n| Closures | State escapes the sandbox |\n| `async`/`await` | VM handles async internally |\n| `eval`, `Function` | Code injection |\n| `__proto__`, `constructor` | Prototype pollution |\n| `import`/`export` | Module system handled by host |\n\nAJS is intentionally simple—simple enough for 4B parameter LLMs to generate correctly.\n\n### Differences from JavaScript\n\nAJS expressions differ from standard JavaScript in a few important ways:\n\n**Null-safe member access.** All member access uses optional chaining internally. Accessing a property on `null` or `undefined` returns `undefined` instead of throwing `TypeError`:\n\n```javascript\nlet x = null\nlet y = x.foo.bar // undefined (no error)\n```\n\nThis is a deliberate safety choice — agents shouldn't crash on missing data.\n\n**No computed member access with variables.** You can use literal indices (`items[0]`, `obj[\"key\"]`) but not variable indices (`items[i]`). This is rejected at transpile time:\n\n```javascript\n// Works\nlet first = items[0]\nlet name = user['name']\n\n// Fails: \"Computed member access with variables not yet supported\"\nlet item = items[i]\n```\n\nWorkaround: use array atoms like `map`, `reduce`, or `for...of` loops instead of index-based access.\n\n**Structural equality.** `==` and `!=` perform deep structural comparison, not reference or coerced equality. No type coercion: `'1' == 1` is `false`. Use `===` and `!==` for identity (reference) checks:\n\n```javascript\n[1, 2] == [1, 2] // true (structural)\n[1, 2] === [1, 2] // false (different objects)\n{ a: 1 } == { a: 1 } // true (structural)\n'1' == 1 // false (no coercion, unlike JS)\nnull == undefined // true (nullish equality preserved)\n```\n\nObjects with a `.Equals` method or `[Symbol.for('tjs.equals')]` handler get custom comparison behavior.\n\n---\n\n## Atoms\n\nAtoms are the built-in operations. Each atom has a defined cost, input schema, and output schema.\n\n### Flow Control\n\n| Atom | Description |\n| -------- | ------------------------------ |\n| `seq` | Execute operations in sequence |\n| `if` | Conditional branching |\n| `while` | Loop with condition |\n| `return` | Return a value |\n| `try` | Error handling |\n\n### State Management\n\n| Atom | Description |\n| ------------ | -------------------------- |\n| `varSet` | Set a variable |\n| `varGet` | Get a variable |\n| `varsLet` | Batch variable declaration |\n| `varsImport` | Import from arguments |\n| `varsExport` | Export as result |\n| `scope` | Create a local scope |\n\n### I/O\n\n| Atom | Description |\n| ----------- | ------------------------------------------- |\n| `httpFetch` | HTTP requests (requires `fetch` capability) |\n\n### Storage (Core)\n\n| Atom | Description |\n| ------------- | ------------------------ |\n| `storeGet` | Get from key-value store |\n| `storeSet` | Set in key-value store |\n| `storeSearch` | Vector similarity search |\n\n### Storage (Battery)\n\n| Atom | Description |\n| ----------------------- | ------------------------------------- |\n| `storeVectorize` | Generate embeddings from text |\n| `storeCreateCollection` | Create a vector store collection |\n| `storeVectorAdd` | Add a document to a vector collection |\n\n### AI (Core)\n\n| Atom | Description |\n| ------------ | ------------------------------------------ |\n| `llmPredict` | Simple LLM inference (`prompt` → `string`) |\n| `agentRun` | Run a sub-agent |\n\n### AI (Battery)\n\n| Atom | Description |\n| ------------------- | ---------------------------------------------- |\n| `llmPredictBattery` | Chat completion (system/user → message object) |\n| `llmVision` | Analyze images using a vision-capable model |\n\n### Procedures\n\n| Atom | Description |\n| ------------------------ | ------------------------------ |\n| `storeProcedure` | Store an AST as callable token |\n| `releaseProcedure` | Delete a stored procedure |\n| `clearExpiredProcedures` | Clean up expired tokens |\n\n### Utilities\n\n| Atom | Description |\n| --------- | ------------------------ |\n| `random` | Random number generation |\n| `uuid` | Generate UUIDs |\n| `hash` | Compute hashes |\n| `memoize` | In-memory memoization |\n| `cache` | Persistent caching |\n\n---\n\n## Battery Atoms Reference\n\nBattery atoms provide LLM, embedding, and vector store capabilities. They\nrequire a separate import and capability setup.\n\n### Setup\n\n```javascript\nimport { AgentVM } from 'tjs-lang'\nimport { batteryAtoms, getBatteries } from 'tjs-lang'\n\nconst vm = new AgentVM(batteryAtoms)\nconst batteries = await getBatteries() // auto-detects LM Studio models\n\nconst { result } = await vm.run(agent, args, {\n fuel: 1000,\n capabilities: batteries,\n})\n```\n\nThe `getBatteries()` function auto-detects LM Studio and returns:\n\n```javascript\n{\n vector: { embed }, // embedding function (undefined if no LM Studio)\n store: { ... }, // key-value + vector store (always present)\n llmBattery: { predict, embed }, // LLM chat + embeddings (null if no LM Studio)\n models: { ... }, // detected model info (null if no LM Studio)\n}\n```\n\n**Important:** `vector` and `llmBattery` will be `undefined`/`null` if LM Studio\nisn't running or the connection is made over HTTPS (local LLM calls are blocked\nfrom HTTPS contexts for security). Always check for availability or handle\nthe atom's \"missing capability\" error.\n\n### Capability Keys\n\nBattery atoms look up capabilities by specific keys that differ from the base\n`Capabilities` interface:\n\n| Capability key | Used by atoms | Contains |\n| -------------- | -------------------------------------------------------- | ------------------------------- |\n| `llmBattery` | `llmPredictBattery`, `llmVision` | `{ predict, embed }` (full LLM) |\n| `vector` | `storeVectorize` | `{ embed }` only |\n| `store` | `storeSearch`, `storeCreateCollection`, `storeVectorAdd` | KV + vector store operations |\n| `llm` | `llmPredict` (core atom) | `{ predict }` (simple) |\n| `fetch` | `httpFetch` (core atom) | fetch function |\n\nThe split exists because `storeVectorize` only needs the embedding function,\nwhile `llmPredictBattery` needs the full chat API. If you're providing your own\ncapabilities (not using `getBatteries()`), wire the keys accordingly.\n\n### `llmPredict` vs `llmPredictBattery`\n\nThere are two LLM atoms with different interfaces:\n\n| Atom | Input | Output | Capability |\n| ------------------- | ----------------------- | -------------- | ------------------------- |\n| `llmPredict` | `{ prompt }` | `string` | `capabilities.llm` |\n| `llmPredictBattery` | `{ system, user, ... }` | message object | `capabilities.llmBattery` |\n\nUse `llmPredict` for simple prompts. Use `llmPredictBattery` when you need\nsystem prompts, tool calling, or structured output.\n\n### `llmPredictBattery`\n\nChat completion with system prompt, tool calling, and structured output support.\n\n**Input:**\n\n| Field | Type | Required | Description |\n| ---------------- | -------- | -------- | --------------------------------------------- |\n| `system` | `string` | No | System prompt (defaults to helpful assistant) |\n| `user` | `string` | Yes | User message |\n| `tools` | `any[]` | No | Tool definitions (OpenAI format) |\n| `responseFormat` | `any` | No | Structured output format |\n\n**Output:** OpenAI chat message object:\n\n```javascript\n{\n role: 'assistant',\n content: 'The answer is 42.', // null when using tool calls\n tool_calls: [...] // present when tools are invoked\n}\n```\n\n**Example:**\n\n```javascript\nlet response = llmPredictBattery({\n system: 'You are a helpful assistant.',\n user: 'What is the capital of France?',\n})\n// response.content === 'Paris is the capital of France.'\n```\n\n**Cost:** 100 fuel\n\n### `llmVision`\n\nAnalyze images using a vision-capable model.\n\n**Input:**\n\n| Field | Type | Required | Description |\n| ---------------- | ---------- | -------- | ----------------------------------------------- |\n| `system` | `string` | No | System prompt |\n| `prompt` | `string` | Yes | Text prompt describing what to analyze |\n| `images` | `string[]` | Yes | URLs or data URIs (`data:image/...;base64,...`) |\n| `responseFormat` | `any` | No | Structured output format |\n\n**Output:** Same as `llmPredictBattery` (message object with `role`, `content`, `tool_calls`).\n\n**Example:**\n\n```javascript\nlet analysis = llmVision({\n prompt: 'Describe what you see in this image.',\n images: ['https://example.com/photo.jpg'],\n})\n// analysis.content === 'The image shows a sunset over the ocean...'\n```\n\n**Cost:** 150 fuel | **Timeout:** 120 seconds\n\n### `storeVectorize`\n\nGenerate embeddings from text using the vector battery.\n\n**Input:**\n\n| Field | Type | Required | Description |\n| ------- | -------- | -------- | ---------------------- |\n| `text` | `string` | Yes | Text to embed |\n| `model` | `string` | No | Embedding model to use |\n\n**Output:** `number[]` — the embedding vector.\n\n**Example:**\n\n```javascript\nlet embedding = storeVectorize({ text: 'TJS is a typed JavaScript' })\n// embedding === [0.023, -0.412, 0.891, ...]\n```\n\n**Cost:** 20 fuel | **Capability:** `vector`\n\n### `storeCreateCollection`\n\nCreate a vector store collection for similarity search.\n\n**Input:**\n\n| Field | Type | Required | Description |\n| ------------ | -------- | -------- | -------------------------------- |\n| `collection` | `string` | Yes | Collection name |\n| `dimension` | `number` | No | Vector dimension (auto-detected) |\n\n**Output:** None.\n\n**Cost:** 5 fuel | **Capability:** `store`\n\n### `storeVectorAdd`\n\nAdd a document to a vector store collection. The document is automatically\nembedded and indexed.\n\n**Input:**\n\n| Field | Type | Required | Description |\n| ------------ | -------- | -------- | ----------------- |\n| `collection` | `string` | Yes | Collection name |\n| `doc` | `any` | Yes | Document to store |\n\n**Output:** None.\n\n**Example:**\n\n```javascript\nstoreVectorAdd({\n collection: 'articles',\n doc: { title: 'Intro to TJS', content: 'TJS is...', embedding: [...] }\n})\n```\n\n**Cost:** 5 fuel | **Capability:** `store`\n\n### `storeSearch`\n\nSearch a vector store collection by similarity.\n\n**Input:**\n\n| Field | Type | Required | Description |\n| ------------- | ---------- | -------- | ------------------------------ |\n| `collection` | `string` | Yes | Collection name |\n| `queryVector` | `number[]` | Yes | Query embedding vector |\n| `k` | `number` | No | Number of results (default: 5) |\n| `filter` | `object` | No | Metadata filter |\n\n**Output:** `any[]` — array of matching documents, sorted by similarity.\n\n**Example:**\n\n```javascript\nlet query = storeVectorize({ text: 'How does type checking work?' })\nlet results = storeSearch({\n collection: 'articles',\n queryVector: query,\n k: 3,\n})\n// results === [{ title: 'Type System', content: '...' }, ...]\n```\n\n**Cost:** 5 + k fuel (dynamic) | **Capability:** `store`\n\n---\n\n## Expression Builtins\n\nAJS expressions have access to safe built-in objects:\n\n### Math\n\nAll standard math functions:\n\n```javascript\nMath.abs(-5) // 5\nMath.floor(3.7) // 3\nMath.sqrt(16) // 4\nMath.sin(Math.PI) // ~0\nMath.random() // 0-1\nMath.max(1, 2, 3) // 3\nMath.min(1, 2, 3) // 1\n```\n\n### JSON\n\nParse and stringify:\n\n```javascript\nJSON.parse('{\"a\": 1}') // { a: 1 }\nJSON.stringify({ a: 1 }) // '{\"a\": 1}'\n```\n\n### Array\n\nStatic methods:\n\n```javascript\nArray.isArray([1, 2]) // true\nArray.from('abc') // ['a', 'b', 'c']\nArray.of(1, 2, 3) // [1, 2, 3]\n```\n\n### Object\n\nStatic methods:\n\n```javascript\nObject.keys({ a: 1 }) // ['a']\nObject.values({ a: 1 }) // [1]\nObject.entries({ a: 1 }) // [['a', 1]]\nObject.fromEntries([['a', 1]]) // { a: 1 }\nObject.assign({}, a, b) // merged object\n```\n\n### String\n\nStatic methods:\n\n```javascript\nString.fromCharCode(65) // 'A'\nString.fromCodePoint(128512) // emoji\n```\n\n### Number\n\nConstants and checks:\n\n```javascript\nNumber.MAX_VALUE\nNumber.isNaN(NaN) // true\nNumber.isFinite(100) // true\nNumber.parseInt('42') // 42\nNumber.parseFloat('3.14') // 3.14\n```\n\n### Set Operations\n\nSet-like operations:\n\n```javascript\nSet.add([1, 2], 3) // [1, 2, 3]\nSet.remove([1, 2, 3], 2) // [1, 3]\nSet.union([1, 2], [2, 3]) // [1, 2, 3]\nSet.intersection([1, 2], [2, 3]) // [2]\nSet.diff([1, 2, 3], [2]) // [1, 3]\n```\n\n### Date\n\nDate factory with arithmetic:\n\n```javascript\nDate.now() // timestamp\nDate.create('2024-01-15') // Date object\nDate.add(date, 1, 'day') // new Date\nDate.format(date, 'YYYY-MM-DD')\n```\n\n### Schema\n\nBuild JSON schemas for structured LLM outputs:\n\n```javascript\n// From example\nlet schema = Schema.response('person', { name: '', age: 0 })\n\n// With constraints\nlet schema = Schema.response(\n 'user',\n Schema.object({\n email: Schema.string.email,\n age: Schema.number.int.min(0).max(150).optional,\n role: Schema.enum(['admin', 'user', 'guest']),\n })\n)\n```\n\n---\n\n## JSON AST Format\n\nAJS compiles to a JSON AST. Here's what it looks like:\n\n### Sequence\n\n```json\n{\n \"$seq\": [\n { \"$op\": \"varSet\", \"key\": \"x\", \"value\": 10 },\n { \"$op\": \"varSet\", \"key\": \"y\", \"value\": 20 },\n {\n \"$op\": \"return\",\n \"value\": { \"$expr\": \"binary\", \"op\": \"+\", \"left\": \"x\", \"right\": \"y\" }\n }\n ]\n}\n```\n\n### Expressions\n\n```json\n// Literal\n{ \"$expr\": \"literal\", \"value\": 42 }\n\n// Identifier\n{ \"$expr\": \"ident\", \"name\": \"varName\" }\n\n// Binary operation\n{ \"$expr\": \"binary\", \"op\": \"+\", \"left\": {...}, \"right\": {...} }\n\n// Member access\n{ \"$expr\": \"member\", \"object\": {...}, \"property\": \"foo\" }\n\n// Template literal\n{ \"$expr\": \"template\", \"tmpl\": \"Hello, ${name}!\" }\n```\n\n### Conditionals\n\n```json\n{\n \"$op\": \"if\",\n \"cond\": { \"$expr\": \"binary\", \"op\": \">\", \"left\": \"x\", \"right\": 0 },\n \"then\": { \"$seq\": [...] },\n \"else\": { \"$seq\": [...] }\n}\n```\n\n### Loops\n\n```json\n{\n \"$op\": \"while\",\n \"cond\": { \"$expr\": \"binary\", \"op\": \">\", \"left\": \"count\", \"right\": 0 },\n \"body\": { \"$seq\": [...] }\n}\n```\n\n---\n\n## Security Model\n\n### Zero Capabilities by Default\n\nThe VM can't do anything unless you allow it:\n\n```typescript\n// This agent can only compute - no I/O\nawait vm.run(agent, args, { capabilities: {} })\n\n// This agent can fetch from one domain\nawait vm.run(agent, args, {\n capabilities: {\n fetch: createFetchCapability({ allowedHosts: ['api.example.com'] }),\n },\n})\n```\n\n### Forbidden Properties\n\nThese property names are blocked to prevent prototype pollution:\n\n- `__proto__`\n- `constructor`\n- `prototype`\n\n### SSRF Protection\n\nThe `httpFetch` atom can be configured with:\n\n- Allowlisted hosts only\n- Blocked private IP ranges\n- Request signing requirements\n\n### ReDoS Protection\n\nSuspicious regex patterns are rejected before execution.\n\n### Execution Tracing\n\nEvery agent run can produce an audit trail:\n\n```typescript\nconst { result, trace } = await vm.run(agent, args, { trace: true })\n\n// trace: [\n// { op: 'varSet', key: 'x', fuelBefore: 1000, fuelAfter: 999.9 },\n// { op: 'httpFetch', url: '...', fuelBefore: 999.9, fuelAfter: 989.9 },\n// ...\n// ]\n```\n\n---\n\n## Use Cases\n\n### AI Agents\n\n```javascript\nfunction researchAgent({ topic }) {\n let searchResults = httpFetch({\n url: `https://api.search.com?q=${topic}`,\n })\n\n let summary = llmPredict({\n system: 'You are a research assistant.',\n user: `Summarize these results about ${topic}: ${searchResults}`,\n })\n\n return { topic, summary }\n}\n```\n\n### Rule Engines\n\n```javascript\nfunction applyDiscounts({ cart, userTier }) {\n let discount = 0\n\n if (userTier === 'gold') {\n discount = 0.2\n } else if (userTier === 'silver') {\n discount = 0.1\n }\n\n if (cart.total > 100) {\n discount = discount + 0.05\n }\n\n return {\n originalTotal: cart.total,\n discount: discount,\n finalTotal: cart.total * (1 - discount),\n }\n}\n```\n\n### Smart Configuration\n\n```javascript\nfunction routeRequest({ request, config }) {\n for (let rule of config.rules) {\n if (request.path.startsWith(rule.prefix)) {\n return { backend: rule.backend, timeout: rule.timeout }\n }\n }\n return { backend: config.defaultBackend, timeout: 30000 }\n}\n```\n\n### Remote Jobs\n\n```javascript\nfunction processDataBatch({ items, transform }) {\n let results = []\n for (let item of items) {\n let processed = applyTransform(item, transform)\n results.push(processed)\n }\n return { processed: results.length, results }\n}\n```\n\n---\n\n## Custom Atoms\n\nExtend the runtime with your own operations:\n\n```typescript\nimport { defineAtom, AgentVM, s } from 'tjs-lang'\n\nconst myScraper = defineAtom(\n 'scrape', // OpCode\n s.object({ url: s.string }), // Input Schema\n s.string, // Output Schema\n async ({ url }, ctx) => {\n const res = await ctx.capabilities.fetch(url)\n return await res.text()\n },\n { cost: 5 } // Fuel cost\n)\n\nconst myVM = new AgentVM({ scrape: myScraper })\n```\n\nAtoms must:\n\n- Be non-blocking (no synchronous CPU-heavy work)\n- Respect `ctx.signal` for cancellation\n- Access I/O only via `ctx.capabilities`\n\n---\n\n## Builder API\n\nFor programmatic AST construction:\n\n```typescript\nimport { Agent, s } from 'tjs-lang'\n\nconst agent = Agent.take(s.object({ price: s.number, taxRate: s.number }))\n .varSet({ key: 'total', value: Agent.expr('price * (1 + taxRate)') })\n .return(s.object({ total: s.number }))\n\nconst ast = agent.toJSON() // JSON-serializable AST\n```\n\nThe builder is lower-level but gives full control over AST construction.\n\n---\n\n## Limitations\n\n### What AJS Doesn't Do\n\n- **No closures** - functions can't capture outer scope\n- **No classes** - use plain objects\n- **No async/await syntax** - the VM handles async internally\n- **No modules** - logic is self-contained\n- **No direct DOM access** - everything goes through capabilities\n- **No computed member access with variables** - `items[i]` is rejected; use `items[0]` (literal) or `for...of` loops\n\n### What AJS Intentionally Avoids\n\n- Complex language features that enable escape from the sandbox\n- Syntax that LLMs frequently hallucinate incorrectly\n- Patterns that make code hard to audit\n\n---\n\n## Performance\n\n- **100 agents in ~6ms** (torture test benchmark)\n- **~0.01 fuel per expression**\n- **Proportional memory charging** prevents runaway allocations\n\nAJS is interpreted (JSON AST), so it's slower than native JS. But:\n\n- Execution is predictable and bounded\n- I/O dominates most agent workloads\n- Tracing is free (built into the VM)\n\nFor compute-heavy operations in your platform code, use TJS with `wasm {}` blocks.\n\n---\n\n## Learn More\n\n- [TJS Documentation](DOCS-TJS.md) — The host language\n- [Builder's Manifesto](MANIFESTO-BUILDER.md) — Why AJS is fun\n- [Enterprise Guide](MANIFESTO-ENTERPRISE.md) — Why AJS is safe\n- [Technical Context](CONTEXT.md) — Architecture deep dive\n"
542
+ "text": "<!--{\"section\": \"ajs\", \"group\": \"docs\", \"order\": 0, \"navTitle\": \"Documentation\"}-->\n\n# AJS: The Agent Language\n\n_Code as Data. Safe. Async. Sandboxed._\n\n---\n\n## What is AJS?\n\nAJS (AsyncJS) is a JavaScript subset that compiles to a **JSON AST**. It's designed for untrusted code—user scripts, LLM-generated agents, remote logic.\n\n```javascript\nfunction searchAndSummarize({ query }) {\n let results = httpFetch({ url: `https://api.example.com/search?q=${query}` })\n let summary = llmPredict({ prompt: `Summarize: ${JSON.stringify(results)}` })\n return { query, summary }\n}\n```\n\nThis compiles to JSON that can be:\n\n- Stored in a database\n- Sent over the network\n- Executed in a sandboxed VM\n- Audited before running\n\n---\n\n## The VM\n\nAJS runs in a gas-limited, isolated VM with strict resource controls.\n\n```typescript\nimport { ajs, AgentVM } from 'tjs-lang'\n\nconst agent = ajs`\n function process({ url }) {\n let data = httpFetch({ url })\n return { fetched: data }\n }\n`\n\nconst vm = new AgentVM()\nconst result = await vm.run(\n agent,\n { url: 'https://api.example.com' },\n {\n fuel: 1000, // CPU budget\n timeoutMs: 5000, // Wall-clock limit\n }\n)\n```\n\n### Fuel Metering\n\nEvery operation costs fuel:\n\n| Operation | Cost |\n| ------------------------ | ---- |\n| Expression evaluation | 0.01 |\n| Variable set/get | 0.1 |\n| Control flow (if, while) | 0.5 |\n| HTTP fetch | 10 |\n| LLM predict | 100 |\n\nWhen fuel runs out, execution stops safely:\n\n```typescript\nif (result.fuelExhausted) {\n // Agent tried to run forever - stopped safely\n}\n```\n\n### Timeout Enforcement\n\nFuel protects against CPU abuse. Timeouts protect against I/O abuse:\n\n```typescript\nawait vm.run(agent, args, {\n fuel: 1000,\n timeoutMs: 5000, // Hard 5-second limit\n})\n```\n\nSlow network calls can't hang your servers.\n\n### Capability Injection\n\nThe VM starts with **zero capabilities**. You grant what each agent needs:\n\n```typescript\nconst capabilities = {\n fetch: createFetchCapability({\n allowedHosts: ['api.example.com'],\n }),\n store: createReadOnlyStore(),\n // No llm - this agent can't call AI\n}\n\nawait vm.run(agent, args, { capabilities })\n```\n\n---\n\n## Input/Output Contract\n\nAJS agents are composable — one agent's output feeds into another's input. To ensure this works reliably:\n\n- **Functions take a single destructured object parameter:** `function process({ input })`\n- **Functions must return a plain object:** `return { result }`, `return { summary, count }`\n- **Non-object returns produce an AgentError:** `return 42` or `return 'hello'` will fail\n- **Bare `return` is allowed** for void functions (no output)\n\n```javascript\n// CORRECT — object in, object out\nfunction add({ a, b }) {\n return { sum: a + b }\n}\n\n// WRONG — non-object returns are errors\nfunction add({ a, b }) {\n return a + b // AgentError: must return an object\n}\n```\n\n---\n\n## Syntax\n\nAJS is a JavaScript subset. Familiar syntax, restricted features.\n\n### What's Allowed\n\n```javascript\n// Functions\nfunction process({ input }) {\n return { output: input * 2 }\n}\n\n// Variables\nlet x = 10\nconst y = 'hello'\n\n// Conditionals\nif (x > 5) {\n return { size: 'big' }\n} else {\n return { size: 'small' }\n}\n\n// Loops\nfor (let i = 0; i < 10; i++) {\n total = total + i\n}\n\nfor (let item of items) {\n results.push(item.name)\n}\n\nwhile (count > 0) {\n count = count - 1\n}\n\n// Try/catch\ntry {\n riskyOperation()\n} catch (e) {\n return { error: e.message }\n}\n\n// Template literals\nlet message = `Hello, ${name}!`\n\n// Object/array literals\nlet obj = { a: 1, b: 2 }\nlet arr = [1, 2, 3]\n\n// Destructuring\nlet { name, age } = user\nlet [first, second] = items\n\n// Spread\nlet merged = { ...defaults, ...overrides }\nlet combined = [...arr1, ...arr2]\n\n// Ternary\nlet result = x > 0 ? 'positive' : 'non-positive'\n\n// Logical operators\nlet value = a && b\nlet fallback = a || defaultValue\nlet nullish = a ?? defaultValue\n```\n\n### What's Forbidden\n\n| Feature | Why Forbidden |\n| -------------------------- | ------------------------------------------------- |\n| `class` | Too complex for LLMs, enables prototype pollution |\n| `new` | Arbitrary object construction |\n| `this` | Implicit context, hard to sandbox |\n| Closures | State escapes the sandbox |\n| `async`/`await` | VM handles async internally |\n| `eval`, `Function` | Code injection |\n| `__proto__`, `constructor` | Prototype pollution |\n| `import`/`export` | Module system handled by host |\n\nAJS is intentionally simple—simple enough for 4B parameter LLMs to generate correctly.\n\n### Differences from JavaScript\n\nAJS expressions differ from standard JavaScript in a few important ways:\n\n**Null-safe member access.** All member access uses optional chaining internally. Accessing a property on `null` or `undefined` returns `undefined` instead of throwing `TypeError`:\n\n```javascript\nlet x = null\nlet y = x.foo.bar // undefined (no error)\n```\n\nThis is a deliberate safety choice — agents shouldn't crash on missing data.\n\n**No computed member access with variables.** You can use literal indices (`items[0]`, `obj[\"key\"]`) but not variable indices (`items[i]`). This is rejected at transpile time:\n\n```javascript\n// Works\nlet first = items[0]\nlet name = user['name']\n\n// Fails: \"Computed member access with variables not yet supported\"\nlet item = items[i]\n```\n\nWorkaround: use array atoms like `map`, `reduce`, or `for...of` loops instead of index-based access.\n\n**Structural equality.** `==` and `!=` perform deep structural comparison, not reference or coerced equality. No type coercion: `'1' == 1` is `false`. Use `===` and `!==` for identity (reference) checks:\n\n```javascript\n[1, 2] == [1, 2] // true (structural)\n[1, 2] === [1, 2] // false (different objects)\n{ a: 1 } == { a: 1 } // true (structural)\n'1' == 1 // false (no coercion, unlike JS)\nnull == undefined // true (nullish equality preserved)\n```\n\nObjects with a `.Equals` method or `[Symbol.for('tjs.equals')]` handler get custom comparison behavior.\n\n---\n\n## Atoms\n\nAtoms are the built-in operations. Each atom has a defined cost, input schema, and output schema.\n\n### Flow Control\n\n| Atom | Description |\n| -------- | ------------------------------ |\n| `seq` | Execute operations in sequence |\n| `if` | Conditional branching |\n| `while` | Loop with condition |\n| `return` | Return a value |\n| `try` | Error handling |\n\n### State Management\n\n| Atom | Description |\n| ------------ | -------------------------- |\n| `varSet` | Set a variable |\n| `varGet` | Get a variable |\n| `varsLet` | Batch variable declaration |\n| `varsImport` | Import from arguments |\n| `varsExport` | Export as result |\n| `scope` | Create a local scope |\n\n### I/O\n\n| Atom | Description |\n| ----------- | ------------------------------------------- |\n| `httpFetch` | HTTP requests (requires `fetch` capability) |\n\n### Storage (Core)\n\n| Atom | Description |\n| ------------- | ------------------------ |\n| `storeGet` | Get from key-value store |\n| `storeSet` | Set in key-value store |\n| `storeSearch` | Vector similarity search |\n\n### Storage (Battery)\n\n| Atom | Description |\n| ----------------------- | ------------------------------------- |\n| `storeVectorize` | Generate embeddings from text |\n| `storeCreateCollection` | Create a vector store collection |\n| `storeVectorAdd` | Add a document to a vector collection |\n\n### AI (Core)\n\n| Atom | Description |\n| ------------ | ------------------------------------------ |\n| `llmPredict` | Simple LLM inference (`prompt` → `string`) |\n| `agentRun` | Run a sub-agent |\n\n### AI (Battery)\n\n| Atom | Description |\n| ------------------- | ---------------------------------------------- |\n| `llmPredictBattery` | Chat completion (system/user → message object) |\n| `llmVision` | Analyze images using a vision-capable model |\n\n### Procedures\n\n| Atom | Description |\n| ------------------------ | ------------------------------ |\n| `storeProcedure` | Store an AST as callable token |\n| `releaseProcedure` | Delete a stored procedure |\n| `clearExpiredProcedures` | Clean up expired tokens |\n\n### Utilities\n\n| Atom | Description |\n| --------- | ------------------------ |\n| `random` | Random number generation |\n| `uuid` | Generate UUIDs |\n| `hash` | Compute hashes |\n| `memoize` | In-memory memoization |\n| `cache` | Persistent caching |\n\n---\n\n## Battery Atoms Reference\n\nBattery atoms provide LLM, embedding, and vector store capabilities. They\nrequire a separate import and capability setup.\n\n### Setup\n\n```javascript\nimport { AgentVM } from 'tjs-lang'\nimport { batteryAtoms, getBatteries } from 'tjs-lang'\n\nconst vm = new AgentVM(batteryAtoms)\nconst batteries = await getBatteries() // auto-detects LM Studio models\n\nconst { result } = await vm.run(agent, args, {\n fuel: 1000,\n capabilities: batteries,\n})\n```\n\nThe `getBatteries()` function auto-detects LM Studio and returns:\n\n```javascript\n{\n vector: { embed }, // embedding function (undefined if no LM Studio)\n store: { ... }, // key-value + vector store (always present)\n llmBattery: { predict, embed }, // LLM chat + embeddings (null if no LM Studio)\n models: { ... }, // detected model info (null if no LM Studio)\n}\n```\n\n**Important:** `vector` and `llmBattery` will be `undefined`/`null` if LM Studio\nisn't running or the connection is made over HTTPS (local LLM calls are blocked\nfrom HTTPS contexts for security). Always check for availability or handle\nthe atom's \"missing capability\" error.\n\n### Capability Keys\n\nBattery atoms look up capabilities by specific keys that differ from the base\n`Capabilities` interface:\n\n| Capability key | Used by atoms | Contains |\n| -------------- | -------------------------------------------------------- | ------------------------------- |\n| `llmBattery` | `llmPredictBattery`, `llmVision` | `{ predict, embed }` (full LLM) |\n| `vector` | `storeVectorize` | `{ embed }` only |\n| `store` | `storeSearch`, `storeCreateCollection`, `storeVectorAdd` | KV + vector store operations |\n| `llm` | `llmPredict` (core atom) | `{ predict }` (simple) |\n| `fetch` | `httpFetch` (core atom) | fetch function |\n\nThe split exists because `storeVectorize` only needs the embedding function,\nwhile `llmPredictBattery` needs the full chat API. If you're providing your own\ncapabilities (not using `getBatteries()`), wire the keys accordingly.\n\n### `llmPredict` vs `llmPredictBattery`\n\nThere are two LLM atoms with different interfaces:\n\n| Atom | Input | Output | Capability |\n| ------------------- | ----------------------- | -------------- | ------------------------- |\n| `llmPredict` | `{ prompt }` | `string` | `capabilities.llm` |\n| `llmPredictBattery` | `{ system, user, ... }` | message object | `capabilities.llmBattery` |\n\nUse `llmPredict` for simple prompts. Use `llmPredictBattery` when you need\nsystem prompts, tool calling, or structured output.\n\n### `llmPredictBattery`\n\nChat completion with system prompt, tool calling, and structured output support.\n\n**Input:**\n\n| Field | Type | Required | Description |\n| ---------------- | -------- | -------- | --------------------------------------------- |\n| `system` | `string` | No | System prompt (defaults to helpful assistant) |\n| `user` | `string` | Yes | User message |\n| `tools` | `any[]` | No | Tool definitions (OpenAI format) |\n| `responseFormat` | `any` | No | Structured output format |\n\n**Output:** OpenAI chat message object:\n\n```javascript\n{\n role: 'assistant',\n content: 'The answer is 42.', // null when using tool calls\n tool_calls: [...] // present when tools are invoked\n}\n```\n\n**Example:**\n\n```javascript\nlet response = llmPredictBattery({\n system: 'You are a helpful assistant.',\n user: 'What is the capital of France?',\n})\n// response.content === 'Paris is the capital of France.'\n```\n\n**Cost:** 100 fuel\n\n### `llmVision`\n\nAnalyze images using a vision-capable model.\n\n**Input:**\n\n| Field | Type | Required | Description |\n| ---------------- | ---------- | -------- | ----------------------------------------------- |\n| `system` | `string` | No | System prompt |\n| `prompt` | `string` | Yes | Text prompt describing what to analyze |\n| `images` | `string[]` | Yes | URLs or data URIs (`data:image/...;base64,...`) |\n| `responseFormat` | `any` | No | Structured output format |\n\n**Output:** Same as `llmPredictBattery` (message object with `role`, `content`, `tool_calls`).\n\n**Example:**\n\n```javascript\nlet analysis = llmVision({\n prompt: 'Describe what you see in this image.',\n images: ['https://example.com/photo.jpg'],\n})\n// analysis.content === 'The image shows a sunset over the ocean...'\n```\n\n**Cost:** 150 fuel | **Timeout:** 120 seconds\n\n### `storeVectorize`\n\nGenerate embeddings from text using the vector battery.\n\n**Input:**\n\n| Field | Type | Required | Description |\n| ------- | -------- | -------- | ---------------------- |\n| `text` | `string` | Yes | Text to embed |\n| `model` | `string` | No | Embedding model to use |\n\n**Output:** `number[]` — the embedding vector.\n\n**Example:**\n\n```javascript\nlet embedding = storeVectorize({ text: 'TJS is a typed JavaScript' })\n// embedding === [0.023, -0.412, 0.891, ...]\n```\n\n**Cost:** 20 fuel | **Capability:** `vector`\n\n### `storeCreateCollection`\n\nCreate a vector store collection for similarity search.\n\n**Input:**\n\n| Field | Type | Required | Description |\n| ------------ | -------- | -------- | -------------------------------- |\n| `collection` | `string` | Yes | Collection name |\n| `dimension` | `number` | No | Vector dimension (auto-detected) |\n\n**Output:** None.\n\n**Cost:** 5 fuel | **Capability:** `store`\n\n### `storeVectorAdd`\n\nAdd a document to a vector store collection. The document is automatically\nembedded and indexed.\n\n**Input:**\n\n| Field | Type | Required | Description |\n| ------------ | -------- | -------- | ----------------- |\n| `collection` | `string` | Yes | Collection name |\n| `doc` | `any` | Yes | Document to store |\n\n**Output:** None.\n\n**Example:**\n\n```javascript\nstoreVectorAdd({\n collection: 'articles',\n doc: { title: 'Intro to TJS', content: 'TJS is...', embedding: [...] }\n})\n```\n\n**Cost:** 5 fuel | **Capability:** `store`\n\n### `storeSearch`\n\nSearch a vector store collection by similarity.\n\n**Input:**\n\n| Field | Type | Required | Description |\n| ------------- | ---------- | -------- | ------------------------------ |\n| `collection` | `string` | Yes | Collection name |\n| `queryVector` | `number[]` | Yes | Query embedding vector |\n| `k` | `number` | No | Number of results (default: 5) |\n| `filter` | `object` | No | Metadata filter |\n\n**Output:** `any[]` — array of matching documents, sorted by similarity.\n\n**Example:**\n\n```javascript\nlet query = storeVectorize({ text: 'How does type checking work?' })\nlet results = storeSearch({\n collection: 'articles',\n queryVector: query,\n k: 3,\n})\n// results === [{ title: 'Type System', content: '...' }, ...]\n```\n\n**Cost:** 5 + k fuel (dynamic) | **Capability:** `store`\n\n---\n\n## Expression Builtins\n\nAJS expressions have access to safe built-in objects:\n\n### Math\n\nAll standard math functions:\n\n```javascript\nMath.abs(-5) // 5\nMath.floor(3.7) // 3\nMath.sqrt(16) // 4\nMath.sin(Math.PI) // ~0\nMath.random() // 0-1\nMath.max(1, 2, 3) // 3\nMath.min(1, 2, 3) // 1\n```\n\n### JSON\n\nParse and stringify:\n\n```javascript\nJSON.parse('{\"a\": 1}') // { a: 1 }\nJSON.stringify({ a: 1 }) // '{\"a\": 1}'\n```\n\n### Array\n\nStatic methods:\n\n```javascript\nArray.isArray([1, 2]) // true\nArray.from('abc') // ['a', 'b', 'c']\nArray.of(1, 2, 3) // [1, 2, 3]\n```\n\n### Object\n\nStatic methods:\n\n```javascript\nObject.keys({ a: 1 }) // ['a']\nObject.values({ a: 1 }) // [1]\nObject.entries({ a: 1 }) // [['a', 1]]\nObject.fromEntries([['a', 1]]) // { a: 1 }\nObject.assign({}, a, b) // merged object\n```\n\n### String\n\nStatic methods:\n\n```javascript\nString.fromCharCode(65) // 'A'\nString.fromCodePoint(128512) // emoji\n```\n\n### Number\n\nConstants and checks:\n\n```javascript\nNumber.MAX_VALUE\nNumber.isNaN(NaN) // true\nNumber.isFinite(100) // true\nNumber.parseInt('42') // 42\nNumber.parseFloat('3.14') // 3.14\n```\n\n### Set Operations\n\nSet-like operations:\n\n```javascript\nSet.add([1, 2], 3) // [1, 2, 3]\nSet.remove([1, 2, 3], 2) // [1, 3]\nSet.union([1, 2], [2, 3]) // [1, 2, 3]\nSet.intersection([1, 2], [2, 3]) // [2]\nSet.diff([1, 2, 3], [2]) // [1, 3]\n```\n\n### Date\n\nDate factory with arithmetic:\n\n```javascript\nDate.now() // timestamp\nDate.create('2024-01-15') // Date object\nDate.add(date, 1, 'day') // new Date\nDate.format(date, 'YYYY-MM-DD')\n```\n\n### Schema\n\nBuild JSON schemas for structured LLM outputs:\n\n```javascript\n// From example\nlet schema = Schema.response('person', { name: '', age: 0 })\n\n// With constraints\nlet schema = Schema.response(\n 'user',\n Schema.object({\n email: Schema.string.email,\n age: Schema.number.int.min(0).max(150).optional,\n role: Schema.enum(['admin', 'user', 'guest']),\n })\n)\n```\n\n---\n\n## JSON AST Format\n\nAJS compiles to a JSON AST. Here's what it looks like:\n\n### Sequence\n\n```json\n{\n \"$seq\": [\n { \"$op\": \"varSet\", \"key\": \"x\", \"value\": 10 },\n { \"$op\": \"varSet\", \"key\": \"y\", \"value\": 20 },\n {\n \"$op\": \"return\",\n \"value\": { \"$expr\": \"binary\", \"op\": \"+\", \"left\": \"x\", \"right\": \"y\" }\n }\n ]\n}\n```\n\n### Expressions\n\n```json\n// Literal\n{ \"$expr\": \"literal\", \"value\": 42 }\n\n// Identifier\n{ \"$expr\": \"ident\", \"name\": \"varName\" }\n\n// Binary operation\n{ \"$expr\": \"binary\", \"op\": \"+\", \"left\": {...}, \"right\": {...} }\n\n// Member access\n{ \"$expr\": \"member\", \"object\": {...}, \"property\": \"foo\" }\n\n// Template literal\n{ \"$expr\": \"template\", \"tmpl\": \"Hello, ${name}!\" }\n```\n\n### Conditionals\n\n```json\n{\n \"$op\": \"if\",\n \"cond\": { \"$expr\": \"binary\", \"op\": \">\", \"left\": \"x\", \"right\": 0 },\n \"then\": { \"$seq\": [...] },\n \"else\": { \"$seq\": [...] }\n}\n```\n\n### Loops\n\n```json\n{\n \"$op\": \"while\",\n \"cond\": { \"$expr\": \"binary\", \"op\": \">\", \"left\": \"count\", \"right\": 0 },\n \"body\": { \"$seq\": [...] }\n}\n```\n\n---\n\n## Security Model\n\n### Zero Capabilities by Default\n\nThe VM can't do anything unless you allow it:\n\n```typescript\n// This agent can only compute - no I/O\nawait vm.run(agent, args, { capabilities: {} })\n\n// This agent can fetch from one domain\nawait vm.run(agent, args, {\n capabilities: {\n fetch: createFetchCapability({ allowedHosts: ['api.example.com'] }),\n },\n})\n```\n\n### Forbidden Properties\n\nThese property names are blocked to prevent prototype pollution:\n\n- `__proto__`\n- `constructor`\n- `prototype`\n\n### SSRF Protection\n\nThe `httpFetch` atom can be configured with:\n\n- Allowlisted hosts only\n- Blocked private IP ranges\n- Request signing requirements\n\n### ReDoS Protection\n\nSuspicious regex patterns are rejected before execution.\n\n### Execution Tracing\n\nEvery agent run can produce an audit trail:\n\n```typescript\nconst { result, trace } = await vm.run(agent, args, { trace: true })\n\n// trace: [\n// { op: 'varSet', key: 'x', fuelBefore: 1000, fuelAfter: 999.9 },\n// { op: 'httpFetch', url: '...', fuelBefore: 999.9, fuelAfter: 989.9 },\n// ...\n// ]\n```\n\n---\n\n## Use Cases\n\n### AI Agents\n\n```javascript\nfunction researchAgent({ topic }) {\n let searchResults = httpFetch({\n url: `https://api.search.com?q=${topic}`,\n })\n\n let summary = llmPredict({\n system: 'You are a research assistant.',\n user: `Summarize these results about ${topic}: ${searchResults}`,\n })\n\n return { topic, summary }\n}\n```\n\n### Rule Engines\n\n```javascript\nfunction applyDiscounts({ cart, userTier }) {\n let discount = 0\n\n if (userTier === 'gold') {\n discount = 0.2\n } else if (userTier === 'silver') {\n discount = 0.1\n }\n\n if (cart.total > 100) {\n discount = discount + 0.05\n }\n\n return {\n originalTotal: cart.total,\n discount: discount,\n finalTotal: cart.total * (1 - discount),\n }\n}\n```\n\n### Smart Configuration\n\n```javascript\nfunction routeRequest({ request, config }) {\n for (let rule of config.rules) {\n if (request.path.startsWith(rule.prefix)) {\n return { backend: rule.backend, timeout: rule.timeout }\n }\n }\n return { backend: config.defaultBackend, timeout: 30000 }\n}\n```\n\n### Remote Jobs\n\n```javascript\nfunction processDataBatch({ items, transform }) {\n let results = []\n for (let item of items) {\n let processed = applyTransform(item, transform)\n results.push(processed)\n }\n return { processed: results.length, results }\n}\n```\n\n---\n\n## Custom Atoms\n\nExtend the runtime with your own operations:\n\n```typescript\nimport { defineAtom, AgentVM, s } from 'tjs-lang'\n\nconst myScraper = defineAtom(\n 'scrape', // OpCode\n s.object({ url: s.string }), // Input Schema\n s.string, // Output Schema\n async ({ url }, ctx) => {\n const res = await ctx.capabilities.fetch(url)\n return await res.text()\n },\n { cost: 5 } // Fuel cost\n)\n\nconst myVM = new AgentVM({ scrape: myScraper })\n```\n\nAtoms must:\n\n- Be non-blocking (no synchronous CPU-heavy work)\n- Respect `ctx.signal` for cancellation\n- Access I/O only via `ctx.capabilities`\n\n---\n\n## Builder API\n\nFor programmatic AST construction:\n\n```typescript\nimport { Agent, s } from 'tjs-lang'\n\nconst agent = Agent.take(s.object({ price: s.number, taxRate: s.number }))\n .varSet({ key: 'total', value: Agent.expr('price * (1 + taxRate)') })\n .return(s.object({ total: s.number }))\n\nconst ast = agent.toJSON() // JSON-serializable AST\n```\n\nThe builder is lower-level but gives full control over AST construction.\n\n---\n\n## Limitations\n\n### What AJS Doesn't Do\n\n- **No closures** - functions can't capture outer scope\n- **No classes** - use plain objects\n- **No async/await syntax** - the VM handles async internally\n- **No modules** - logic is self-contained\n- **No direct DOM access** - everything goes through capabilities\n- **No computed member access with variables** - `items[i]` is rejected; use `items[0]` (literal) or `for...of` loops\n\n### What AJS Intentionally Avoids\n\n- Complex language features that enable escape from the sandbox\n- Syntax that LLMs frequently hallucinate incorrectly\n- Patterns that make code hard to audit\n\n---\n\n## Performance\n\n- **100 agents in ~6ms** (torture test benchmark)\n- **~0.01 fuel per expression**\n- **Proportional memory charging** prevents runaway allocations\n\nAJS is interpreted (JSON AST), so it's slower than native JS. But:\n\n- Execution is predictable and bounded\n- I/O dominates most agent workloads\n- Tracing is free (built into the VM)\n\nFor compute-heavy operations in your platform code, use TJS with `wasm {}` blocks.\n\n---\n\n## Learn More\n\n- [TJS Documentation](DOCS-TJS.md) — The host language\n- [Builder's Manifesto](MANIFESTO-BUILDER.md) — Why AJS is fun\n- [Enterprise Guide](MANIFESTO-ENTERPRISE.md) — Why AJS is safe\n- [Technical Context](CONTEXT.md) — Architecture deep dive\n"
543
543
  },
544
544
  {
545
545
  "title": "tjs-lang Technical Context",
@@ -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 type-safe virtual machine (~33KB) for safe execution of untrusted code in any JavaScript environment. It compiles logic chains and AI agents to JSON-serializable ASTs that run sandboxed with fuel (gas) limits.\n\nKey concept: Code travels to data (rather than shipping data to code). Agents are defined as data, not deployed code.\n\n**Two languages in one platform:**\n\n- **TJS** — TypeScript-like syntax with runtime type validation for writing your platform\n- **AJS** — Agent language that compiles to JSON AST for safe, sandboxed execution\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 (~2900 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 jsCode = tjs(tjsSource)\n// Generates 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 in `src/lang/lang.test.ts` (~46KB comprehensive)\n\nCoverage targets: 98% lines on `src/vm/runtime.ts` (security-critical), 80%+ overall.\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)\n- `PLAN.md` — Roadmap\n\n### Known Gotcha: Self-Contained Output\n\nTranspiled TJS code currently requires `globalThis.__tjs` to be set up with `createRuntime()` before execution. A `{ 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 type-safe virtual machine (~33KB) for safe execution of untrusted code in any JavaScript environment. It compiles logic chains and AI agents to JSON-serializable ASTs that run sandboxed with fuel (gas) limits.\n\nKey concept: Code travels to data (rather than shipping data to code). Agents are defined as data, not deployed code.\n\n**Two languages in one platform:**\n\n- **TJS** — TypeScript-like syntax with runtime type validation for writing your platform\n- **AJS** — Agent language that compiles to JSON AST for safe, sandboxed execution\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 (~2900 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 jsCode = tjs(tjsSource)\n// Generates 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 in `src/lang/lang.test.ts` (~46KB comprehensive)\n\nCoverage targets: 98% lines on `src/vm/runtime.ts` (security-critical), 80%+ overall.\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)\n- `PLAN.md` — Roadmap\n\n### Known Gotcha: Self-Contained Output\n\nTranspiled TJS code currently requires `globalThis.__tjs` to be set up with `createRuntime()` before execution. A `{ 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",
@@ -681,6 +681,12 @@
681
681
  "filename": "from-ts.ts",
682
682
  "path": "src/lang/emitters/from-ts.ts"
683
683
  },
684
+ {
685
+ "title": "GEMINI.md - Project Context & Instructions",
686
+ "filename": "GEMINI.md",
687
+ "path": "GEMINI.md",
688
+ "text": "# GEMINI.md - Project Context & Instructions\n\nThis file provides foundational mandates and contextual information for Gemini CLI when working in the `tjs-lang` repository.\n\n## Project Overview\n\n**tjs-lang** is a platform for type-safe JavaScript execution and AI agent orchestration. It bridges the gap between static typing and runtime safety by preserving type information and providing a secure execution environment.\n\n### Core Languages\n\n1. **TJS (Typed JavaScript):** A TypeScript-like dialect that transpiles to JavaScript with embedded `__tjs` metadata. This metadata enables runtime type validation, auto-documentation, and introspection.\n2. **AJS (Agent JavaScript):** A safe, sandboxed language for AI agents. It compiles to a JSON-serializable AST and executes in a gas-limited VM with capability-based security.\n\n### Architecture\n\n- **Builder Layer** (`src/builder.ts`): Fluent API for constructing AST nodes.\n- **Runtime Layer** (`src/vm/runtime.ts`): Security-critical core that executes AST nodes and manages fuel/capabilities.\n- **Lang Layer** (`src/lang/`): Contains the parser (based on Acorn), linter, and emitters for TJS and AJS.\n- **Batteries** (`src/batteries/`): Built-in capabilities like LLM integration (LM Studio), vector search, and persistence.\n\n## Development Lifecycle\n\n### Common Commands\n\n- **Install:** `bun install`\n- **Build:** `npm run make` (Format, build grammars, compile TS, and bundle)\n- **Fast Test:** `npm run test:fast` (Runs core tests, skips LLM and benchmarks)\n- **Full Test:** `bun test`\n- **Type Check:** `npm run typecheck`\n- **Lint:** `npm run lint`\n- **Format:** `npm run format` (ESLint fix + Prettier)\n- **Dev Server:** `npm run dev`\n- **CLI Tool:** `bun src/cli/tjs.ts <command>` (Commands: `check`, `run`, `types`, `emit`, `convert`, `test`)\n\n### Testing Strategy\n\n- **Unit Tests:** Located alongside source files (e.g., `src/lang/parser.test.ts`).\n- **Integration Tests:** Located in `src/use-cases/`.\n- **Security Tests:** Critical for the VM, found in `src/use-cases/malicious-actor.test.ts`.\n- **Reproduction:** Always create a reproduction test case before fixing a bug.\n\n## Engineering Standards & Conventions\n\n### Coding Style\n\n- **Formatting:** Prettier (Single quotes, no semicolons, 2-space indentation, 80 char width).\n- **Naming:** Follow existing conventions; prefix unused variables with `_`.\n- **Type Safety:** Prioritize TypeScript for all core logic. `any` is allowed but should be used judiciously.\n- **Security:** Rigorously protect the `src/vm/runtime.ts` file as it is security-critical. Avoid prototype access (`__proto__`, `constructor`, `prototype`) in expression evaluation.\n\n### Language Specifics\n\n- **TJS Types:** In TJS, colons indicate _examples_, not just type annotations (e.g., `name: 'Alice'` means a required string).\n- **AJS Expressions:** Be aware that AJS expressions have differences from JS (e.g., safe null member access, limited computed member access).\n- **Monadic Errors:** Prefer returning error values (Monadic errors) over throwing exceptions to ensure execution stability and security.\n\n### Documentation\n\n- Maintain `DOCS-TJS.md` and `DOCS-AJS.md` for language-specific documentation.\n- Update `CLAUDE.md` for tool-specific guidance if architectural patterns change.\n\n## Contextual Precedence\n\n- **Directives:** Explicit requests for implementation take priority.\n- **Inquiries:** Requests for analysis or advice should not result in code changes without a subsequent Directive.\n- **Security First:** Never compromise the sandboxing or fuel-metering integrity of the VM.\n"
689
+ },
684
690
  {
685
691
  "title": "icebox",
686
692
  "filename": "tasks-f6d70f.md",
@@ -751,7 +757,7 @@
751
757
  "title": "TJS Benchmarks",
752
758
  "filename": "benchmarks.md",
753
759
  "path": "benchmarks.md",
754
- "text": "# TJS Benchmarks\n\nGenerated: 2026-02-17\nRuntime: Bun 1.3.8\nPlatform: darwin arm64\nIterations: 100,000 per test\n\n## Summary\n\n| Benchmark | Baseline | Safe (default) | Unsafe (!) |\n|-----------|----------|----------------|------------|\n| CLI: Bun + TypeScript | 157.6ms | - | - |\n| CLI: tjsx (execute TJS) | 159.2ms | - | - |\n| CLI: tjs emit | 160.6ms | - | - |\n| CLI: tjs check | 159.8ms | - | - |\n| Simple arithmetic (100K iterations) | 0.5ms | 1.0ms (1.9x) | 0.5ms (1.1x) |\n| Object manipulation (100K iterations) | 1.0ms | 1.7ms (1.7x) | 1.1ms (1.1x) |\n| 3-function chain (100K iterations) | 0.6ms | 2.0ms (3.1x) | 0.6ms (~1.0x) |\n\n## Key Findings\n\n### CLI Cold Start\n\n- **Bun + TypeScript**: ~158ms (native, baseline)\n- **tjsx**: ~159ms (includes TJS transpiler load)\n- **Overhead**: 2ms for transpiler initialization\n\nThe ~2ms overhead is from loading the acorn parser and TJS transpiler.\nA compiled binary (via `bun build --compile`) reduces this to ~20ms.\n\n### Safe vs Unsafe Functions\n\nTJS functions are **safe by default** with runtime type validation.\nUse `(!)` to mark functions as unsafe for performance-critical code:\n\n```javascript\n// Safe (default) - validates types at runtime\nfunction add(a: 0, b: 0) -> 0 { return a + b }\n\n// Unsafe - no validation, maximum performance\nfunction fastAdd(! a: 0, b: 0) -> 0 { return a + b }\n```\n\nPerformance comparison:\n- Simple arithmetic: Safe 1.9x vs Unsafe 1.1x\n- Object manipulation: Safe 1.7x vs Unsafe 1.1x\n- 3-function chain: Safe 3.1x vs Unsafe ~1.0x\n\n## Recommendations\n\n1. **Use safe functions at API boundaries** - The default is correct for most code\n2. **Use `(!)` for internal hot paths** - When inputs are already validated\n3. **Consider compiled binary for CLI** - `bun build --compile` for ~20ms startup\n\n## Running Benchmarks\n\n```bash\nbun run bench\n```\n\nOr run the test suite with timing output:\n\n```bash\nbun test src/lang/perf.test.ts\n```\n"
760
+ "text": "# TJS Benchmarks\n\nGenerated: 2026-02-17\nRuntime: Bun 1.3.8\nPlatform: darwin arm64\nIterations: 100,000 per test\n\n## Summary\n\n| Benchmark | Baseline | Safe (default) | Unsafe (!) |\n| ------------------------------------- | -------- | -------------- | ------------- |\n| CLI: Bun + TypeScript | 157.6ms | - | - |\n| CLI: tjsx (execute TJS) | 159.2ms | - | - |\n| CLI: tjs emit | 160.6ms | - | - |\n| CLI: tjs check | 159.8ms | - | - |\n| Simple arithmetic (100K iterations) | 0.5ms | 1.0ms (1.9x) | 0.5ms (1.1x) |\n| Object manipulation (100K iterations) | 1.0ms | 1.7ms (1.7x) | 1.1ms (1.1x) |\n| 3-function chain (100K iterations) | 0.6ms | 2.0ms (3.1x) | 0.6ms (~1.0x) |\n\n## Key Findings\n\n### CLI Cold Start\n\n- **Bun + TypeScript**: ~158ms (native, baseline)\n- **tjsx**: ~159ms (includes TJS transpiler load)\n- **Overhead**: 2ms for transpiler initialization\n\nThe ~2ms overhead is from loading the acorn parser and TJS transpiler.\nA compiled binary (via `bun build --compile`) reduces this to ~20ms.\n\n### Safe vs Unsafe Functions\n\nTJS functions are **safe by default** with runtime type validation.\nUse `(!)` to mark functions as unsafe for performance-critical code:\n\n```javascript\n// Safe (default) - validates types at runtime\nfunction add(a: 0, b: 0) -> 0 { return a + b }\n\n// Unsafe - no validation, maximum performance\nfunction fastAdd(! a: 0, b: 0) -> 0 { return a + b }\n```\n\nPerformance comparison:\n\n- Simple arithmetic: Safe 1.9x vs Unsafe 1.1x\n- Object manipulation: Safe 1.7x vs Unsafe 1.1x\n- 3-function chain: Safe 3.1x vs Unsafe ~1.0x\n\n## Recommendations\n\n1. **Use safe functions at API boundaries** - The default is correct for most code\n2. **Use `(!)` for internal hot paths** - When inputs are already validated\n3. **Consider compiled binary for CLI** - `bun build --compile` for ~20ms startup\n\n## Running Benchmarks\n\n```bash\nbun run bench\n```\n\nOr run the test suite with timing output:\n\n```bash\nbun test src/lang/perf.test.ts\n```\n"
755
761
  },
756
762
  {
757
763
  "title": "TJS Benchmarks",
@@ -775,13 +781,13 @@
775
781
  "title": "TJS-Lang TODO",
776
782
  "filename": "TODO.md",
777
783
  "path": "TODO.md",
778
- "text": "# TJS-Lang TODO\n\n## Playground - Error Navigation\n\n- [ ] Test errors: click should navigate to source location\n- [ ] Console errors: click should navigate to source location\n- [ ] Error in imported module: click through to source\n\n## Playground - Module Management\n\n- [ ] Import one example from another in playground\n- [ ] Save/Load TS examples (consistency with TJS examples)\n- [ ] File name should be linked to example name\n- [ ] New example button in playground\n- [ ] UI for managing stored modules (browse/delete IndexedDB)\n- [ ] Auto-discover and build local dependencies in module resolution\n\n## Language Features\n\n- [ ] Portable Type predicates - expression-only AJS subset (no loops, no async, serializable)\n- [ ] Sync AJS / AJS-to-JS compilation - for type-checked AJS that passes static analysis, transpile to native JS with fuel injection points. Enables both type safety guarantees AND native performance for RBAC rules, predicates, etc.\n- [ ] Self-contained transpiler output (no runtime dependency)\n - Currently transpiled code references `globalThis.__tjs` for pushStack/popStack, typeError, Is/IsNot\n - Requires runtime to be installed or a stub (see playground's manual \\_\\_tjs stub)\n - Goal: TJS produces completely independent code, only needing semantic dependencies\n - Options: inline minimal runtime (~1KB), `{ standalone: true }` option, or tree-shake\n - See: src/lang/emitters/js.ts TODO comment for details\n- [x] WASM compilation at transpile time (not runtime)\n - [x] Compile wasm {} blocks during transpilation\n - [x] Embed base64-encoded WASM bytes in output\n - [x] Include WAT disassembly as comment for debugging/learning\n - [x] Self-contained async instantiation (no separate compileWasmBlocksForIframe)\n- [x] Expand WASM support beyond POC\n - [x] For loops with numeric bounds\n - [x] Conditionals (if/else)\n - [x] Local variables within block\n - [x] Typed array access (Float32Array, Float64Array, Int32Array, Uint8Array)\n - [x] Memory operations\n - [x] Continue/break statements\n - [x] Logical expressions (&& / ||)\n - [x] Math functions (sqrt, abs, floor, ceil, min, max, sin, cos, log, exp, pow)\n- [x] WASM SIMD support (v128/f32x4)\n - 12 f32x4 intrinsics: load, store, splat, extract_lane, replace_lane, add, sub, mul, div, neg, sqrt\n - Explicit intrinsic approach (users call f32x4_* in wasm blocks)\n - Disassembler handles 0xfd prefix with LEB128 sub-opcodes\n - 16-byte aligned memory for v128 loads/stores\n - Demos: starfield SIMD rotation, vector search cosine similarity\n- [ ] WASM SIMD vector search (batteries)\n - Replace JS vectorSearch battery with WASM SIMD implementation\n - SIMD cosine similarity demonstrated in vector search demo\n - TODO: integrate as a battery atom with auto-detect + fallback\n\n## Editor\n\n- [ ] Embedded AJS syntax highlighting\n\n## Documentation / Examples\n\n- [ ] Create an endpoint example\n- [ ] Fold docs and tests into one panel, with passing tests collapsed by default (ts -> tjs inserts test; tjs -> js turns test blocks into documentation along with outcomes).\n- [ ] Dim/hide the preview tab if nothing ever changed it\n- [ ] Single source of truth for version number. I note the badge displayed in console is not matching the version. No hardwired versions -- version number is pulled from package.json and written to version.ts somewhere and that is the single source of truth.\n\n## Infrastructure\n\n- [ ] Make playground components reusable for others\n- [ ] Web worker for transpiles (freezer - not needed yet)\n- [x] Retarget Firebase as host platform (vs GitHub Pages)\n- [ ] Universal LLM endpoint with real LLMs (OpenAI, Anthropic, etc.)\n- [ ] ESM-as-a-service: versioned library endpoints\n- [ ] User accounts (Google sign-in) for API key storage\n- [ ] AJS-based Firestore and Storage security rules\n- [ ] npx tjs-playground - run playground locally with LM Studio\n- [ ] Virtual subdomains for user apps (yourapp.tjs.land)\n - [ ] Wildcard DNS to Firebase\n - [ ] Subdomain routing in Cloud Function\n - [ ] Deploy button in playground\n - [ ] Public/private visibility toggle\n- [ ] Rate limiting / abuse prevention for LLM endpoint\n- [ ] Usage tracking / billing foundation (for future paid tiers)\n\n---\n\n## Completed (this session)\n\n### Project Rename\n\n- [x] Rename from tosijs-agent to tjs-lang\n- [x] Update all references in package.json, docs, scripts\n- [x] Remove bd (beads) issue tracker, replace with TODO.md\n\n### Timestamp & LegalDate Utilities\n\n- [x] Timestamp - pure functions, 1-based months, no Date warts (53 tests)\n - now, from, parse, tryParse\n - addDays/Hours/Minutes/Seconds/Weeks/Months/Years\n - diff, diffSeconds/Minutes/Hours/Days\n - year/month/day/hour/minute/second/millisecond/dayOfWeek\n - toLocal, format, formatDate, formatTime, toDate\n - isBefore/isAfter/isEqual/min/max\n - startOf/endOf Day/Month/Year\n- [x] LegalDate - pure functions, YYYY-MM-DD strings (55 tests)\n - today, todayIn, from, parse, tryParse\n - addDays/Weeks/Months/Years\n - diff, diffMonths, diffYears\n - year/month/day/dayOfWeek/weekOfYear/dayOfYear/quarter\n - isLeapYear, daysInMonth, daysInYear\n - toTimestamp, toUnix, fromUnix\n - format, formatLong, formatShort\n - isBefore/isAfter/isEqual/min/max/isBetween\n - startOf/endOf Month/Quarter/Year/Week\n- [x] Portable predicate helpers: isValidUrl, isValidTimestamp, isValidLegalDate\n\n### TJS Mode System (JS is now the default)\n\n- [x] Invert mode system - JS semantics are default, improvements opt-in\n- [x] TjsEquals directive - structural == and != (null == undefined)\n- [x] TjsClass directive - classes callable without new\n- [x] TjsDate directive - bans Date constructor/methods\n- [x] TjsNoeval directive - bans eval() and new Function()\n- [x] TjsStrict directive - enables all of the above\n- [x] TjsSafeEval directive - includes Eval/SafeFunction for dynamic code execution\n- [x] Updated Is() for nullish equality (null == undefined)\n- [x] Added Is/IsNot tests (structural equality, nullish handling)\n- [x] TjsStandard directive - newlines as statement terminators (prevents ASI footguns)\n- [x] WASM POC - wasm {} blocks with parsing, fallback mechanism, basic numeric compilation\n- [x] Eval/SafeFunction - proper VM-backed implementation with fuel metering and capabilities\n\n### Bundle Size Optimization\n\n- [x] Separated Eval/SafeFunction into standalone module (eval.ts)\n- [x] Created core.ts - AJS transpiler without TypeScript dependency\n- [x] Fixed tjs-transpiler bundle: 4.14MB → 88.9KB (27KB gzipped)\n- [x] Runtime is now ~5KB gzipped (just Is/IsNot, wrap, Type, etc.)\n- [x] Eval adds ~27KB gzipped (VM + AJS transpiler, no TypeScript)\n- [x] TypeScript only bundled in playground (5.8MB) for real-time TS transpilation\n"
784
+ "text": "# TJS-Lang TODO\n\n## Playground - Error Navigation\n\n- [ ] Test errors: click should navigate to source location\n- [ ] Console errors: click should navigate to source location\n- [ ] Error in imported module: click through to source\n\n## Playground - Module Management\n\n- [ ] Import one example from another in playground\n- [ ] Save/Load TS examples (consistency with TJS examples)\n- [ ] File name should be linked to example name\n- [ ] New example button in playground\n- [ ] UI for managing stored modules (browse/delete IndexedDB)\n- [ ] Auto-discover and build local dependencies in module resolution\n\n## Language Features\n\n- [ ] Portable Type predicates - expression-only AJS subset (no loops, no async, serializable)\n- [ ] Sync AJS / AJS-to-JS compilation - for type-checked AJS that passes static analysis, transpile to native JS with fuel injection points. Enables both type safety guarantees AND native performance for RBAC rules, predicates, etc.\n- [ ] Self-contained transpiler output (no runtime dependency)\n - Currently transpiled code references `globalThis.__tjs` for pushStack/popStack, typeError, Is/IsNot\n - Requires runtime to be installed or a stub (see playground's manual \\_\\_tjs stub)\n - Goal: TJS produces completely independent code, only needing semantic dependencies\n - Options: inline minimal runtime (~1KB), `{ standalone: true }` option, or tree-shake\n - See: src/lang/emitters/js.ts TODO comment for details\n- [x] WASM compilation at transpile time (not runtime)\n - [x] Compile wasm {} blocks during transpilation\n - [x] Embed base64-encoded WASM bytes in output\n - [x] Include WAT disassembly as comment for debugging/learning\n - [x] Self-contained async instantiation (no separate compileWasmBlocksForIframe)\n- [x] Expand WASM support beyond POC\n - [x] For loops with numeric bounds\n - [x] Conditionals (if/else)\n - [x] Local variables within block\n - [x] Typed array access (Float32Array, Float64Array, Int32Array, Uint8Array)\n - [x] Memory operations\n - [x] Continue/break statements\n - [x] Logical expressions (&& / ||)\n - [x] Math functions (sqrt, abs, floor, ceil, min, max, sin, cos, log, exp, pow)\n- [x] WASM SIMD support (v128/f32x4)\n - 12 f32x4 intrinsics: load, store, splat, extract_lane, replace_lane, add, sub, mul, div, neg, sqrt\n - Explicit intrinsic approach (users call f32x4\\_\\* in wasm blocks)\n - Disassembler handles 0xfd prefix with LEB128 sub-opcodes\n - 16-byte aligned memory for v128 loads/stores\n - Demos: starfield SIMD rotation, vector search cosine similarity\n- [ ] WASM SIMD vector search (batteries)\n - Replace JS vectorSearch battery with WASM SIMD implementation\n - SIMD cosine similarity demonstrated in vector search demo\n - TODO: integrate as a battery atom with auto-detect + fallback\n\n## Editor\n\n- [ ] Embedded AJS syntax highlighting\n\n## Documentation / Examples\n\n- [ ] Create an endpoint example\n- [ ] Fold docs and tests into one panel, with passing tests collapsed by default (ts -> tjs inserts test; tjs -> js turns test blocks into documentation along with outcomes).\n- [ ] Dim/hide the preview tab if nothing ever changed it\n- [ ] Single source of truth for version number. I note the badge displayed in console is not matching the version. No hardwired versions -- version number is pulled from package.json and written to version.ts somewhere and that is the single source of truth.\n\n## Infrastructure\n\n- [ ] Make playground components reusable for others\n- [ ] Web worker for transpiles (freezer - not needed yet)\n- [x] Retarget Firebase as host platform (vs GitHub Pages)\n- [ ] Universal LLM endpoint with real LLMs (OpenAI, Anthropic, etc.)\n- [ ] ESM-as-a-service: versioned library endpoints\n- [ ] User accounts (Google sign-in) for API key storage\n- [ ] AJS-based Firestore and Storage security rules\n- [ ] npx tjs-playground - run playground locally with LM Studio\n- [ ] Virtual subdomains for user apps (yourapp.tjs.land)\n - [ ] Wildcard DNS to Firebase\n - [ ] Subdomain routing in Cloud Function\n - [ ] Deploy button in playground\n - [ ] Public/private visibility toggle\n- [ ] Rate limiting / abuse prevention for LLM endpoint\n- [ ] Usage tracking / billing foundation (for future paid tiers)\n\n---\n\n## Completed (this session)\n\n### Project Rename\n\n- [x] Rename from tosijs-agent to tjs-lang\n- [x] Update all references in package.json, docs, scripts\n- [x] Remove bd (beads) issue tracker, replace with TODO.md\n\n### Timestamp & LegalDate Utilities\n\n- [x] Timestamp - pure functions, 1-based months, no Date warts (53 tests)\n - now, from, parse, tryParse\n - addDays/Hours/Minutes/Seconds/Weeks/Months/Years\n - diff, diffSeconds/Minutes/Hours/Days\n - year/month/day/hour/minute/second/millisecond/dayOfWeek\n - toLocal, format, formatDate, formatTime, toDate\n - isBefore/isAfter/isEqual/min/max\n - startOf/endOf Day/Month/Year\n- [x] LegalDate - pure functions, YYYY-MM-DD strings (55 tests)\n - today, todayIn, from, parse, tryParse\n - addDays/Weeks/Months/Years\n - diff, diffMonths, diffYears\n - year/month/day/dayOfWeek/weekOfYear/dayOfYear/quarter\n - isLeapYear, daysInMonth, daysInYear\n - toTimestamp, toUnix, fromUnix\n - format, formatLong, formatShort\n - isBefore/isAfter/isEqual/min/max/isBetween\n - startOf/endOf Month/Quarter/Year/Week\n- [x] Portable predicate helpers: isValidUrl, isValidTimestamp, isValidLegalDate\n\n### TJS Mode System (JS is now the default)\n\n- [x] Invert mode system - JS semantics are default, improvements opt-in\n- [x] TjsEquals directive - structural == and != (null == undefined)\n- [x] TjsClass directive - classes callable without new\n- [x] TjsDate directive - bans Date constructor/methods\n- [x] TjsNoeval directive - bans eval() and new Function()\n- [x] TjsStrict directive - enables all of the above\n- [x] TjsSafeEval directive - includes Eval/SafeFunction for dynamic code execution\n- [x] Updated Is() for nullish equality (null == undefined)\n- [x] Added Is/IsNot tests (structural equality, nullish handling)\n- [x] TjsStandard directive - newlines as statement terminators (prevents ASI footguns)\n- [x] WASM POC - wasm {} blocks with parsing, fallback mechanism, basic numeric compilation\n- [x] Eval/SafeFunction - proper VM-backed implementation with fuel metering and capabilities\n\n### Bundle Size Optimization\n\n- [x] Separated Eval/SafeFunction into standalone module (eval.ts)\n- [x] Created core.ts - AJS transpiler without TypeScript dependency\n- [x] Fixed tjs-transpiler bundle: 4.14MB → 88.9KB (27KB gzipped)\n- [x] Runtime is now ~5KB gzipped (just Is/IsNot, wrap, Type, etc.)\n- [x] Eval adds ~27KB gzipped (VM + AJS transpiler, no TypeScript)\n- [x] TypeScript only bundled in playground (5.8MB) for real-time TS transpilation\n"
779
785
  },
780
786
  {
781
787
  "title": "TJS: Typed JavaScript",
782
788
  "filename": "tjs.md",
783
789
  "path": "guides/tjs.md",
784
- "text": "# TJS: Typed JavaScript\n\nTJS is a typed superset of JavaScript where **types are examples**.\n\n```javascript\nfunction greet(name: 'World', times: 3) -> '' {\n let result = ''\n let i = 0\n while (i < times) {\n result = result + `Hello, ${name}! `\n i = i + 1\n }\n return result.trim()\n}\n```\n\n## Philosophy\n\nTJS takes a different approach to typing than TypeScript:\n\n| Aspect | TypeScript | TJS |\n| -------------- | --------------------------------------- | ----------------------------- |\n| Types | Abstract declarations | Concrete examples |\n| Runtime | Erased completely | Preserved as metadata |\n| Validation | Compile-time only | Runtime optional |\n| Learning curve | Learn type syntax | Use values you know |\n| Error messages | \"Type 'string' is not assignable to...\" | \"Expected string, got number\" |\n\n### Why Examples?\n\nConsider how you'd explain a function to another developer:\n\n> \"This function takes a name like 'World' and a count like 3, and returns a greeting string\"\n\nThat's exactly how TJS works. The example _is_ the type:\n\n```javascript\n// TypeScript\nfunction greet(name: string, times: number): string\n\n// TJS - the example IS the documentation\nfunction greet(name: 'World', times: 3) -> ''\n```\n\n## Core Concepts\n\n### 1. Types by Example\n\nInstead of abstract type names, use example values:\n\n```javascript\n// Strings\nname: '' // any string\nname: 'default' // string with default value\n\n// Numbers\ncount: 0 // any number\nport: 8080 // number with default\n\n// Booleans\nenabled: true // boolean (default true)\ndisabled: false // boolean (default false)\n\n// Arrays\nitems: [''] // array of strings\nnumbers: [0] // array of numbers\nmixed: [0, ''] // tuple: number, string\n\n// Objects\nuser: { name: '', age: 0 } // object with shape\n\n// Null/Undefined\nnullable: null\noptional: undefined\n```\n\n### 2. Required vs Optional (`:` vs `=`)\n\nThe colon `:` means required, equals `=` means optional:\n\n```javascript\nfunction createUser(\n name: 'Anonymous', // required string\n email: 'user@example.com', // required string\n age = 0, // optional number (defaults to 0)\n role = 'user' // optional string (defaults to 'user')\n) -> { id: '', name: '', email: '', age: 0, role: '' } {\n return {\n id: crypto.randomUUID(),\n name,\n email,\n age,\n role\n }\n}\n\n// Valid calls:\ncreateUser('Alice', 'alice@example.com')\ncreateUser('Bob', 'bob@example.com', 30)\ncreateUser('Carol', 'carol@example.com', 25, 'admin')\n\n// Invalid - missing required params:\ncreateUser('Dave') // Error: missing required parameter 'email'\n```\n\n### 3. Return Type Annotation\n\nUse `->` to declare the return type:\n\n```javascript\nfunction add(a: 0, b: 0) -> 0 {\n return a + b\n}\n\nfunction getUser(id: '') -> { name: '', email: '' } | null {\n // Returns user object or null\n}\n```\n\n### 4. Union Types\n\nUse `||` for unions (not `|` like TypeScript):\n\n```javascript\nfunction parseInput(value: '' || 0 || null) -> '' {\n if (value === null) return 'null'\n if (typeof value === 'number') return `number: ${value}`\n return `string: ${value}`\n}\n```\n\n### 5. The `any` Type\n\nWhen you genuinely don't know the type:\n\n```javascript\nfunction identity(x: any) -> any {\n return x\n}\n```\n\nGenerics from TypeScript become `any` but preserve metadata:\n\n```javascript\n// TypeScript: function identity<T>(x: T): T\n// TJS: any, but __tjs.typeParams captures the generic info\nfunction identity(x: any) -> any {\n return x\n}\n// identity.__tjs.typeParams = { T: {} }\n```\n\n### 6. Type Declarations\n\nDefine reusable types with the `Type` keyword:\n\n```javascript\n// Type with default value (= syntax)\nType Name = 'Alice'\nType Count = 0\nType Age = +18 // positive number\n\n// Type with description and default\nType Name 'a person name' = 'Alice'\n\n// Type with example (for testing/documentation)\nType User 'registered user' {\n example: { name: '', age: 0 }\n}\n\n// Type with both default and example\nType PositiveAge = +1 {\n example: 30\n}\n\n// Type with predicate (auto-generates type guard from example)\nType EvenNumber {\n example: 2\n predicate(x) { return x % 2 === 0 }\n}\n\n// Complex validation with predicate\nType Email {\n example: 'test@example.com'\n predicate(x) { return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(x) }\n}\n```\n\n**Default vs Example:**\n\n- `= value` sets a **default** for instantiation\n- `example:` in block sets an **example** for testing/documentation\n- When both are present, they serve different purposes\n\nWhen `example` and `predicate` are provided, the type guard auto-checks the example's shape, then your predicate refines it.\n\n### 7. Generic Declarations\n\nDefine parameterized types with the `Generic` keyword:\n\n```javascript\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\nIn the predicate, `T` and `U` are type-checking functions that validate values against the provided type parameters.\n\n### 8. Bare Assignments\n\nUppercase identifiers automatically get `const`:\n\n```javascript\n// These are equivalent:\nFoo = Type('test', 'example')\nconst Foo = Type('test', 'example')\n\n// Works for any uppercase identifier\nMyConfig = { debug: true }\nconst MyConfig = { debug: true }\n```\n\n## Runtime Features\n\n### Monadic Error Handling\n\nTJS functions propagate errors automatically:\n\n```javascript\n// If any input is an error, it passes through\nconst result = processData(maybeError)\n// If maybeError is an error, result is that error (processData not called)\n\n// Check for errors\nif (isError(result)) {\n console.log(result.message)\n}\n```\n\n### Safe by Default\n\nTJS functions are wrapped with runtime type validation by default:\n\n```javascript\nfunction add(a: 0, b: 0) -> 0 {\n return a + b\n}\n\nadd(1, 2) // 3\nadd('1', 2) // Error: expected number, got string\nadd(null, 2) // Error: expected number, got null\n```\n\nThis provides excellent error messages and catches type mismatches at runtime.\n\n### Safety Markers: `(?)` and `(!)`\n\nControl input validation with markers after the opening paren:\n\n```javascript\n// (?) - Safe function: force input validation\nfunction safeAdd(? a: 0, b: 0) -> 0 {\n return a + b\n}\n\n// (!) - Unsafe function: skip input validation\nfunction fastAdd(! a: 0, b: 0) -> 0 {\n return a + b\n}\n\nfastAdd(1, 2) // 3 (fast path, no validation)\nfastAdd('1', 2) // NaN (no validation, garbage in = garbage out)\n```\n\nThe `!` is borrowed from TypeScript's non-null assertion operator - it means \"I know what I'm doing, trust me.\"\n\n### Return Type Safety: `->`, `-?`, `-!`\n\nControl output validation with different arrow styles:\n\n```javascript\n// -> normal return type (validation depends on module settings)\nfunction add(a: 0, b: 0) -> 0 { return a + b }\n\n// -? force output validation (safe return)\nfunction critical(a: 0, b: 0) -? 0 { return a + b }\n\n// -! skip output validation (unsafe return)\nfunction fast(a: 0, b: 0) -! 0 { return a + b }\n```\n\nCombine input and output markers for full control:\n\n```javascript\n// Fully safe: validate inputs AND outputs\nfunction critical(? x: 0) -? 0 { return x * 2 }\n\n// Fully unsafe: skip all validation\nfunction blazingFast(! x: 0) -! 0 { return x * 2 }\n```\n\n### The `unsafe` Block\n\nFor unsafe sections within a safe function, use `unsafe {}`:\n\n```javascript\nfunction sum(numbers: [0]) -> 0 {\n // Parameters are validated, but the inner loop is unsafe\n unsafe {\n let total = 0\n for (let i = 0; i < numbers.length; i++) {\n total += numbers[i]\n }\n return total\n }\n}\n```\n\n### Performance Characteristics\n\n| Mode | Overhead | Use Case |\n| ----------------- | -------- | ---------------------------------------- |\n| Default (safe) | ~50x | API boundaries, user input |\n| `unsafe {}` block | ~1.2x | Hot loops within validated functions |\n| `(!)` function | 0x | Internal utilities, performance critical |\n\nUse `(!)` for internal functions that are called frequently with known-good data. Keep public APIs safe.\n\n### SafeFunction and Eval\n\nSafe replacements for `new Function()` and `eval()` with typed inputs/outputs:\n\n```javascript\n// SafeFunction - create a typed async function from code\nconst add = await SafeFunction({\n inputs: { a: 0, b: 0 }, // typed parameters\n output: 0, // typed return\n body: 'return a + b',\n})\nawait add(1, 2) // 3\nawait add('x', 2) // Error: invalid input 'a'\n\n// Eval - evaluate code once with typed result\nconst result = await Eval({\n code: 'a + b',\n context: { a: 1, b: 2 },\n output: 0,\n}) // 3\n```\n\n**Key safety features:**\n\n- **Typed inputs/outputs** - validated at runtime\n- **Async execution** - can timeout, won't block\n- **Explicit context** - no implicit scope access\n- **Injectable capabilities** - fetch, console, etc. must be provided\n\n```javascript\n// With capabilities and timeout\nconst fetcher = await SafeFunction({\n inputs: { url: '' },\n output: { data: [] },\n body: 'return await fetch(url).then(r => r.json())',\n capabilities: { fetch: globalThis.fetch },\n timeoutMs: 10000,\n})\n\nconst data = await Eval({\n code: 'await fetch(url).then(r => r.json())',\n context: { url: 'https://api.example.com' },\n output: { items: [] },\n capabilities: { fetch: globalThis.fetch },\n})\n```\n\nBoth functions return errors as values (monadic) rather than throwing.\n\n## Testing\n\n### Compile-Time Tests\n\nTests run at **transpile time** and are stripped from output:\n\n```javascript\nType Email {\n example: 'test@example.com'\n predicate(x) { return x.includes('@') }\n}\n\n// This test runs during transpilation\ntest 'email validation' {\n if (!Email.check('user@example.com')) {\n throw new Error('valid email should pass')\n }\n if (Email.check('invalid')) {\n throw new Error('invalid email should fail')\n }\n}\n\nfunction sendEmail(to: Email) {\n // ...\n}\n```\n\nThe transpiled output contains only:\n\n```javascript\nconst Email = Type('Email', ...)\nfunction sendEmail(to) { ... }\n```\n\nThe test code **evaporates** - it verified correctness at build time.\n\n### Implicit Type Tests\n\nTypes with `example` have implicit tests - the example must pass the type check:\n\n```javascript\nType PositiveInt {\n example: 42\n predicate(x) { return Number.isInteger(x) && x > 0 }\n}\n// Implicit test: PositiveInt.check(42) must be true\n```\n\nIf the example fails the predicate, transpilation fails.\n\n### Skip Tests Flag\n\nFor debugging or speed, skip test execution:\n\n```bash\ntjs emit file.tjs --dangerously-skip-tests\n```\n\nTests are still stripped from output, but not executed.\n\n### Legacy Inline Tests\n\nFor runtime tests (e.g., integration tests), use standard test frameworks:\n\n```javascript\ntest('async operations work') {\n const data = await fetchData()\n expect(data).toBeDefined()\n}\n```\n\n## Differences from JavaScript\n\n### Removed/Discouraged\n\n| Feature | Reason |\n| -------------- | --------------------------- |\n| `var` | Use `let` or `const` |\n| `class` | Use functions and objects |\n| `this` | Explicit context passing |\n| `new` (mostly) | Factory functions preferred |\n| `throw` | Return errors as values |\n| `for...in` | Use `Object.keys()` |\n\n### Added\n\n| Feature | Purpose |\n| ----------------- | ------------------------------------------- |\n| `: example` | Required parameter with type |\n| `= example` | Optional parameter with default |\n| `-> Type` | Return type annotation |\n| `-? Type` | Return type with forced output validation |\n| `-! Type` | Return type with skipped output validation |\n| `(?)` | Mark function as safe (force validation) |\n| `(!)` | Mark function as unsafe (skip validation) |\n| `test 'name' {}` | Compile-time test block (evaporates) |\n| `mock {}` | Test setup block |\n| `unsafe {}` | Skip validation for a block |\n| `\\|\\|` in types | Union types |\n| `Type Name = val` | Define runtime type with default |\n| `Generic<T>` | Define a parameterized runtime type |\n| `Foo = ...` | Bare assignment (auto-adds `const`) |\n| `SafeFunction` | Safe typed async replacement for `Function` |\n| `Eval` | Safe typed async replacement for `eval()` |\n\n## Differences from TypeScript\n\n### Types are Values\n\n```typescript\n// TypeScript - abstract type\ninterface User {\n name: string\n age: number\n email?: string\n}\n\n// TJS - concrete example\nconst User = { name: '', age: 0, email = '' }\n```\n\n### Runtime Preservation\n\nTypeScript erases types at compile time. TJS preserves them:\n\n```javascript\nfunction greet(name: 'World') -> '' {\n return `Hello, ${name}!`\n}\n\n// At runtime:\ngreet.__tjs = {\n params: { name: { type: 'string', required: true } },\n returns: { type: 'string' }\n}\n```\n\nThis enables:\n\n- Runtime validation\n- Auto-generated documentation\n- API schema generation\n- Better error messages\n\n### Generics\n\nTypeScript generics become `any` in TJS, but constraints are preserved:\n\n```typescript\n// TypeScript\nfunction process<T extends { id: number }>(item: T): T\n\n// TJS - constraint becomes validatable schema\nfunction process(item: any) -> any\n// process.__tjs.typeParams = { T: { constraint: '{ id: 0 }' } }\n```\n\nThe constraint `{ id: number }` becomes the example `{ id: 0 }` - and can be validated at runtime!\n\n### No Type Gymnastics\n\nTJS doesn't support:\n\n- Conditional types\n- Mapped types\n- Template literal types\n- `infer` keyword\n\nIf you need these, you probably need to rethink your approach. TJS favors simple, explicit types over clever type-level programming.\n\n## The `__tjs` Metadata\n\nEvery TJS function has attached metadata:\n\n```javascript\nfunction createUser(name: 'Anonymous', age = 0) -> { id: '', name: '', age: 0 } {\n return { id: crypto.randomUUID(), name, age }\n}\n\ncreateUser.__tjs = {\n params: {\n name: { type: 'string', required: true, default: 'Anonymous' },\n age: { type: 'number', required: false, default: 0 }\n },\n returns: { type: 'object', shape: { id: 'string', name: 'string', age: 'number' } },\n // For generic functions:\n typeParams: {\n T: { constraint: '{ id: 0 }', default: null }\n }\n}\n```\n\nThis metadata enables:\n\n1. **Runtime validation** via `wrap()`\n2. **Documentation generation** via `generateDocs()`\n3. **API schema export** (OpenAPI, JSON Schema)\n4. **IDE autocompletion**\n5. **Version-safe serialization**\n\n## CLI Tools\n\n### `tjs` - The TJS Compiler\n\n```bash\ntjs check file.tjs # Parse and type check\ntjs emit file.tjs # Output transpiled JavaScript\ntjs run file.tjs # Transpile and execute\ntjs types file.tjs # Output type metadata as JSON\n```\n\n### `tjsx` - Quick Execution\n\n```bash\ntjsx script.tjs # Run a TJS file\ntjsx script.tjs --name=value # Pass arguments\ntjsx -e \"function f() { return 42 }\" # Evaluate inline\necho '{\"x\": 1}' | tjsx script.tjs --json # JSON from stdin\n```\n\n### Bun Plugin - Native `.tjs` Support\n\nRun `.tjs` files directly with Bun using the preload plugin:\n\n```bash\n# Run a single file\nbun --preload ./src/bun-plugin/tjs-plugin.ts script.tjs\n\n# Enable globally in bunfig.toml\n[run]\npreload = [\"./src/bun-plugin/tjs-plugin.ts\"]\n```\n\nThe plugin transpiles `.tjs` files on-the-fly with full runtime support (Type, Generic, Union, etc.).\n\n## Best Practices\n\n### 1. Use Examples That Document\n\n```javascript\n// Bad - meaningless example\nfunction send(to: '', subject: '', body: '') {}\n\n// Good - self-documenting\nfunction send(\n to: 'user@example.com',\n subject: 'Hello!',\n body: 'Message content here...'\n) {}\n```\n\n### 2. Validate at Boundaries\n\n```javascript\n// Public API - safe by default\nexport function createUser(name: '', email: '') -> { id: '', name: '', email: '' } {\n return createUserImpl(name, email)\n}\n\n// Internal - mark as unsafe for speed\nfunction createUserImpl(! name: '', email: '') -> { id: '', name: '', email: '' } {\n return { id: crypto.randomUUID(), name, email }\n}\n```\n\n### 3. Return Errors, Don't Throw\n\n```javascript\n// Bad\nfunction divide(a: 0, b: 0) -> 0 {\n if (b === 0) throw new Error('Division by zero')\n return a / b\n}\n\n// Good\nfunction divide(a: 0, b: 0) -> 0 {\n if (b === 0) return error('Division by zero')\n return a / b\n}\n```\n\n### 4. Keep Types Simple\n\n```javascript\n// Bad - over-engineered\nfunction process(data: {\n items: [{ id: '', meta: { created: 0, tags: [''] } }],\n}) {}\n\n// Good - extract complex types\nconst Item = { id: '', meta: { created: 0, tags: [''] } }\nfunction process(data: { items: [Item] }) {}\n```\n\n## Transpilation\n\nTJS transpiles to standard JavaScript:\n\n```javascript\n// Input (TJS)\nfunction greet(name: 'World') -> '' {\n return `Hello, ${name}!`\n}\n\n// Output (JavaScript)\nfunction greet(name = 'World') {\n return `Hello, ${name}!`\n}\ngreet.__tjs = {\n params: { name: { type: 'string', required: true, default: 'World' } },\n returns: { type: 'string' }\n}\n```\n\nThe output is valid ES modules that work with any bundler (Vite, esbuild, webpack, Bun).\n\n## Further Reading\n\n- [Benchmarks](./benchmarks.md) - Performance characteristics\n- [ajs.md](./ajs.md) - The sandboxed agent language\n- [API Documentation](./docs/) - Generated from source\n"
790
+ "text": "# TJS: Typed JavaScript\n\nTJS is a typed superset of JavaScript where **types are examples**.\n\n```javascript\nfunction greet(name: 'World', times: 3) -> '' {\n let result = ''\n let i = 0\n while (i < times) {\n result = result + `Hello, ${name}! `\n i = i + 1\n }\n return result.trim()\n}\n```\n\n## Philosophy\n\nTJS takes a different approach to typing than TypeScript:\n\n| Aspect | TypeScript | TJS |\n| -------------- | --------------------------------------- | ----------------------------- |\n| Types | Abstract declarations | Concrete examples |\n| Runtime | Erased completely | Preserved as metadata |\n| Validation | Compile-time only | Runtime optional |\n| Learning curve | Learn type syntax | Use values you know |\n| Error messages | \"Type 'string' is not assignable to...\" | \"Expected string, got number\" |\n\n### Why Examples?\n\nConsider how you'd explain a function to another developer:\n\n> \"This function takes a name like 'World' and a count like 3, and returns a greeting string\"\n\nThat's exactly how TJS works. The example _is_ the type:\n\n```javascript\n// TypeScript\nfunction greet(name: string, times: number): string\n\n// TJS - the example IS the documentation\nfunction greet(name: 'World', times: 3) -> ''\n```\n\n## Core Concepts\n\n### 1. Types by Example\n\nInstead of abstract type names, use example values:\n\n```javascript\n// Strings\nname: '' // any string\nname: 'default' // string with default value\n\n// Numbers\ncount: 0 // any number\nport: 8080 // number with default\n\n// Booleans\nenabled: true // boolean (default true)\ndisabled: false // boolean (default false)\n\n// Arrays\nitems: [''] // array of strings\nnumbers: [0] // array of numbers\nmixed: [0, ''] // tuple: number, string\n\n// Objects\nuser: { name: '', age: 0 } // object with shape\n\n// Null/Undefined\nnullable: null\noptional: undefined\n```\n\n### 2. Required vs Optional (`:` vs `=`)\n\nThe colon `:` means required, equals `=` means optional:\n\n```javascript\nfunction createUser(\n name: 'Anonymous', // required string\n email: 'user@example.com', // required string\n age = 0, // optional number (defaults to 0)\n role = 'user' // optional string (defaults to 'user')\n) -> { id: '', name: '', email: '', age: 0, role: '' } {\n return {\n id: crypto.randomUUID(),\n name,\n email,\n age,\n role\n }\n}\n\n// Valid calls:\ncreateUser('Alice', 'alice@example.com')\ncreateUser('Bob', 'bob@example.com', 30)\ncreateUser('Carol', 'carol@example.com', 25, 'admin')\n\n// Invalid - missing required params:\ncreateUser('Dave') // Error: missing required parameter 'email'\n```\n\n### 3. Return Type Annotation\n\nUse `->` to declare the return type:\n\n```javascript\nfunction add(a: 0, b: 0) -> 0 {\n return a + b\n}\n\nfunction getUser(id: '') -> { name: '', email: '' } | null {\n // Returns user object or null\n}\n```\n\n### 4. Union Types\n\nUse `|` for unions (same as TypeScript):\n\n```javascript\nfunction parseInput(value: '' | 0 | null) -> '' {\n if (value === null) return 'null'\n if (typeof value === 'number') return `number: ${value}`\n return `string: ${value}`\n}\n```\n\n### 5. The `any` Type\n\nWhen you genuinely don't know the type:\n\n```javascript\nfunction identity(x: any) -> any {\n return x\n}\n```\n\nGenerics from TypeScript become `any` but preserve metadata:\n\n```javascript\n// TypeScript: function identity<T>(x: T): T\n// TJS: any, but __tjs.typeParams captures the generic info\nfunction identity(x: any) -> any {\n return x\n}\n// identity.__tjs.typeParams = { T: {} }\n```\n\n### 6. Type Declarations\n\nDefine reusable types with the `Type` keyword:\n\n```javascript\n// Type with default value (= syntax)\nType Name = 'Alice'\nType Count = 0\nType Age = +18 // positive number\n\n// Type with description and default\nType Name 'a person name' = 'Alice'\n\n// Type with example (for testing/documentation)\nType User 'registered user' {\n example: { name: '', age: 0 }\n}\n\n// Type with both default and example\nType PositiveAge = +1 {\n example: 30\n}\n\n// Type with predicate (auto-generates type guard from example)\nType EvenNumber {\n example: 2\n predicate(x) { return x % 2 === 0 }\n}\n\n// Complex validation with predicate\nType Email {\n example: 'test@example.com'\n predicate(x) { return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(x) }\n}\n```\n\n**Default vs Example:**\n\n- `= value` sets a **default** for instantiation\n- `example:` in block sets an **example** for testing/documentation\n- When both are present, they serve different purposes\n\nWhen `example` and `predicate` are provided, the type guard auto-checks the example's shape, then your predicate refines it.\n\n### 7. Generic Declarations\n\nDefine parameterized types with the `Generic` keyword:\n\n```javascript\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\nIn the predicate, `T` and `U` are type-checking functions that validate values against the provided type parameters.\n\n### 8. Bare Assignments\n\nUppercase identifiers automatically get `const`:\n\n```javascript\n// These are equivalent:\nFoo = Type('test', 'example')\nconst Foo = Type('test', 'example')\n\n// Works for any uppercase identifier\nMyConfig = { debug: true }\nconst MyConfig = { debug: true }\n```\n\n## Runtime Features\n\n### Monadic Error Handling\n\nTJS functions propagate errors automatically:\n\n```javascript\n// If any input is an error, it passes through\nconst result = processData(maybeError)\n// If maybeError is an error, result is that error (processData not called)\n\n// Check for errors\nif (isError(result)) {\n console.log(result.message)\n}\n```\n\n### Safe by Default\n\nTJS functions are wrapped with runtime type validation by default:\n\n```javascript\nfunction add(a: 0, b: 0) -> 0 {\n return a + b\n}\n\nadd(1, 2) // 3\nadd('1', 2) // Error: expected number, got string\nadd(null, 2) // Error: expected number, got null\n```\n\nThis provides excellent error messages and catches type mismatches at runtime.\n\n### Safety Markers: `(?)` and `(!)`\n\nControl input validation with markers after the opening paren:\n\n```javascript\n// (?) - Safe function: force input validation\nfunction safeAdd(? a: 0, b: 0) -> 0 {\n return a + b\n}\n\n// (!) - Unsafe function: skip input validation\nfunction fastAdd(! a: 0, b: 0) -> 0 {\n return a + b\n}\n\nfastAdd(1, 2) // 3 (fast path, no validation)\nfastAdd('1', 2) // NaN (no validation, garbage in = garbage out)\n```\n\nThe `!` is borrowed from TypeScript's non-null assertion operator - it means \"I know what I'm doing, trust me.\"\n\n### Return Type Safety: `->`, `-?`, `-!`\n\nControl output validation with different arrow styles:\n\n```javascript\n// -> normal return type (validation depends on module settings)\nfunction add(a: 0, b: 0) -> 0 { return a + b }\n\n// -? force output validation (safe return)\nfunction critical(a: 0, b: 0) -? 0 { return a + b }\n\n// -! skip output validation (unsafe return)\nfunction fast(a: 0, b: 0) -! 0 { return a + b }\n```\n\nCombine input and output markers for full control:\n\n```javascript\n// Fully safe: validate inputs AND outputs\nfunction critical(? x: 0) -? 0 { return x * 2 }\n\n// Fully unsafe: skip all validation\nfunction blazingFast(! x: 0) -! 0 { return x * 2 }\n```\n\n### The `unsafe` Block\n\nFor unsafe sections within a safe function, use `unsafe {}`:\n\n```javascript\nfunction sum(numbers: [0]) -> 0 {\n // Parameters are validated, but the inner loop is unsafe\n unsafe {\n let total = 0\n for (let i = 0; i < numbers.length; i++) {\n total += numbers[i]\n }\n return total\n }\n}\n```\n\n### Performance Characteristics\n\n| Mode | Overhead | Use Case |\n| ----------------- | -------- | ---------------------------------------- |\n| Default (safe) | ~50x | API boundaries, user input |\n| `unsafe {}` block | ~1.2x | Hot loops within validated functions |\n| `(!)` function | 0x | Internal utilities, performance critical |\n\nUse `(!)` for internal functions that are called frequently with known-good data. Keep public APIs safe.\n\n### SafeFunction and Eval\n\nSafe replacements for `new Function()` and `eval()` with typed inputs/outputs:\n\n```javascript\n// SafeFunction - create a typed async function from code\nconst add = await SafeFunction({\n inputs: { a: 0, b: 0 }, // typed parameters\n output: 0, // typed return\n body: 'return a + b',\n})\nawait add(1, 2) // 3\nawait add('x', 2) // Error: invalid input 'a'\n\n// Eval - evaluate code once with typed result\nconst result = await Eval({\n code: 'a + b',\n context: { a: 1, b: 2 },\n output: 0,\n}) // 3\n```\n\n**Key safety features:**\n\n- **Typed inputs/outputs** - validated at runtime\n- **Async execution** - can timeout, won't block\n- **Explicit context** - no implicit scope access\n- **Injectable capabilities** - fetch, console, etc. must be provided\n\n```javascript\n// With capabilities and timeout\nconst fetcher = await SafeFunction({\n inputs: { url: '' },\n output: { data: [] },\n body: 'return await fetch(url).then(r => r.json())',\n capabilities: { fetch: globalThis.fetch },\n timeoutMs: 10000,\n})\n\nconst data = await Eval({\n code: 'await fetch(url).then(r => r.json())',\n context: { url: 'https://api.example.com' },\n output: { items: [] },\n capabilities: { fetch: globalThis.fetch },\n})\n```\n\nBoth functions return errors as values (monadic) rather than throwing.\n\n## Testing\n\n### Compile-Time Tests\n\nTests run at **transpile time** and are stripped from output:\n\n```javascript\nType Email {\n example: 'test@example.com'\n predicate(x) { return x.includes('@') }\n}\n\n// This test runs during transpilation\ntest 'email validation' {\n if (!Email.check('user@example.com')) {\n throw new Error('valid email should pass')\n }\n if (Email.check('invalid')) {\n throw new Error('invalid email should fail')\n }\n}\n\nfunction sendEmail(to: Email) {\n // ...\n}\n```\n\nThe transpiled output contains only:\n\n```javascript\nconst Email = Type('Email', ...)\nfunction sendEmail(to) { ... }\n```\n\nThe test code **evaporates** - it verified correctness at build time.\n\n### Implicit Type Tests\n\nTypes with `example` have implicit tests - the example must pass the type check:\n\n```javascript\nType PositiveInt {\n example: 42\n predicate(x) { return Number.isInteger(x) && x > 0 }\n}\n// Implicit test: PositiveInt.check(42) must be true\n```\n\nIf the example fails the predicate, transpilation fails.\n\n### Skip Tests Flag\n\nFor debugging or speed, skip test execution:\n\n```bash\ntjs emit file.tjs --dangerously-skip-tests\n```\n\nTests are still stripped from output, but not executed.\n\n### Legacy Inline Tests\n\nFor runtime tests (e.g., integration tests), use standard test frameworks:\n\n```javascript\ntest('async operations work') {\n const data = await fetchData()\n expect(data).toBeDefined()\n}\n```\n\n## Differences from JavaScript\n\n### Removed/Discouraged\n\n| Feature | Reason |\n| -------------- | --------------------------- |\n| `var` | Use `let` or `const` |\n| `class` | Use functions and objects |\n| `this` | Explicit context passing |\n| `new` (mostly) | Factory functions preferred |\n| `throw` | Return errors as values |\n| `for...in` | Use `Object.keys()` |\n\n### Added\n\n| Feature | Purpose |\n| ----------------- | ------------------------------------------- |\n| `: example` | Required parameter with type |\n| `= example` | Optional parameter with default |\n| `-> Type` | Return type annotation |\n| `-? Type` | Return type with forced output validation |\n| `-! Type` | Return type with skipped output validation |\n| `(?)` | Mark function as safe (force validation) |\n| `(!)` | Mark function as unsafe (skip validation) |\n| `test 'name' {}` | Compile-time test block (evaporates) |\n| `mock {}` | Test setup block |\n| `unsafe {}` | Skip validation for a block |\n| `\\|\\|` in types | Union types |\n| `Type Name = val` | Define runtime type with default |\n| `Generic<T>` | Define a parameterized runtime type |\n| `Foo = ...` | Bare assignment (auto-adds `const`) |\n| `SafeFunction` | Safe typed async replacement for `Function` |\n| `Eval` | Safe typed async replacement for `eval()` |\n\n## Differences from TypeScript\n\n### Types are Values\n\n```typescript\n// TypeScript - abstract type\ninterface User {\n name: string\n age: number\n email?: string\n}\n\n// TJS - concrete example\nconst User = { name: '', age: 0, email = '' }\n```\n\n### Runtime Preservation\n\nTypeScript erases types at compile time. TJS preserves them:\n\n```javascript\nfunction greet(name: 'World') -> '' {\n return `Hello, ${name}!`\n}\n\n// At runtime:\ngreet.__tjs = {\n params: { name: { type: 'string', required: true } },\n returns: { type: 'string' }\n}\n```\n\nThis enables:\n\n- Runtime validation\n- Auto-generated documentation\n- API schema generation\n- Better error messages\n\n### Generics\n\nTypeScript generics become `any` in TJS, but constraints are preserved:\n\n```typescript\n// TypeScript\nfunction process<T extends { id: number }>(item: T): T\n\n// TJS - constraint becomes validatable schema\nfunction process(item: any) -> any\n// process.__tjs.typeParams = { T: { constraint: '{ id: 0 }' } }\n```\n\nThe constraint `{ id: number }` becomes the example `{ id: 0 }` - and can be validated at runtime!\n\n### No Type Gymnastics\n\nTJS doesn't support:\n\n- Conditional types\n- Mapped types\n- Template literal types\n- `infer` keyword\n\nIf you need these, you probably need to rethink your approach. TJS favors simple, explicit types over clever type-level programming.\n\n## The `__tjs` Metadata\n\nEvery TJS function has attached metadata:\n\n```javascript\nfunction createUser(name: 'Anonymous', age = 0) -> { id: '', name: '', age: 0 } {\n return { id: crypto.randomUUID(), name, age }\n}\n\ncreateUser.__tjs = {\n params: {\n name: { type: 'string', required: true, default: 'Anonymous' },\n age: { type: 'number', required: false, default: 0 }\n },\n returns: { type: 'object', shape: { id: 'string', name: 'string', age: 'number' } },\n // For generic functions:\n typeParams: {\n T: { constraint: '{ id: 0 }', default: null }\n }\n}\n```\n\nThis metadata enables:\n\n1. **Runtime validation** via `wrap()`\n2. **Documentation generation** via `generateDocs()`\n3. **API schema export** (OpenAPI, JSON Schema)\n4. **IDE autocompletion**\n5. **Version-safe serialization**\n\n## CLI Tools\n\n### `tjs` - The TJS Compiler\n\n```bash\ntjs check file.tjs # Parse and type check\ntjs emit file.tjs # Output transpiled JavaScript\ntjs run file.tjs # Transpile and execute\ntjs types file.tjs # Output type metadata as JSON\n```\n\n### `tjsx` - Quick Execution\n\n```bash\ntjsx script.tjs # Run a TJS file\ntjsx script.tjs --name=value # Pass arguments\ntjsx -e \"function f() { return 42 }\" # Evaluate inline\necho '{\"x\": 1}' | tjsx script.tjs --json # JSON from stdin\n```\n\n### Bun Plugin - Native `.tjs` Support\n\nRun `.tjs` files directly with Bun using the preload plugin:\n\n```bash\n# Run a single file\nbun --preload ./src/bun-plugin/tjs-plugin.ts script.tjs\n\n# Enable globally in bunfig.toml\n[run]\npreload = [\"./src/bun-plugin/tjs-plugin.ts\"]\n```\n\nThe plugin transpiles `.tjs` files on-the-fly with full runtime support (Type, Generic, Union, etc.).\n\n## Best Practices\n\n### 1. Use Examples That Document\n\n```javascript\n// Bad - meaningless example\nfunction send(to: '', subject: '', body: '') {}\n\n// Good - self-documenting\nfunction send(\n to: 'user@example.com',\n subject: 'Hello!',\n body: 'Message content here...'\n) {}\n```\n\n### 2. Validate at Boundaries\n\n```javascript\n// Public API - safe by default\nexport function createUser(name: '', email: '') -> { id: '', name: '', email: '' } {\n return createUserImpl(name, email)\n}\n\n// Internal - mark as unsafe for speed\nfunction createUserImpl(! name: '', email: '') -> { id: '', name: '', email: '' } {\n return { id: crypto.randomUUID(), name, email }\n}\n```\n\n### 3. Return Errors, Don't Throw\n\n```javascript\n// Bad\nfunction divide(a: 0, b: 0) -> 0 {\n if (b === 0) throw new Error('Division by zero')\n return a / b\n}\n\n// Good\nfunction divide(a: 0, b: 0) -> 0 {\n if (b === 0) return error('Division by zero')\n return a / b\n}\n```\n\n### 4. Keep Types Simple\n\n```javascript\n// Bad - over-engineered\nfunction process(data: {\n items: [{ id: '', meta: { created: 0, tags: [''] } }],\n}) {}\n\n// Good - extract complex types\nconst Item = { id: '', meta: { created: 0, tags: [''] } }\nfunction process(data: { items: [Item] }) {}\n```\n\n## Transpilation\n\nTJS transpiles to standard JavaScript:\n\n```javascript\n// Input (TJS)\nfunction greet(name: 'World') -> '' {\n return `Hello, ${name}!`\n}\n\n// Output (JavaScript)\nfunction greet(name = 'World') {\n return `Hello, ${name}!`\n}\ngreet.__tjs = {\n params: { name: { type: 'string', required: true, default: 'World' } },\n returns: { type: 'string' }\n}\n```\n\nThe output is valid ES modules that work with any bundler (Vite, esbuild, webpack, Bun).\n\n## Further Reading\n\n- [Benchmarks](./benchmarks.md) - Performance characteristics\n- [ajs.md](./ajs.md) - The sandboxed agent language\n- [API Documentation](./docs/) - Generated from source\n"
785
791
  },
786
792
  {
787
793
  "text": "# Example\n\n\\`\\`\\`javascript\nexport function add(a: 0, b: 0) -> 0 {\n return a + b\n}\n\\`\\`\\`",