tjs-lang 0.8.3 → 0.8.4

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/demo/docs.json CHANGED
@@ -211,7 +211,7 @@
211
211
  "group": "docs",
212
212
  "order": 0,
213
213
  "navTitle": "Documentation",
214
- "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'): '' {\n return `Hello, ${name}!`\n}\n```\n\nThe example `'World'` tells TJS that `name` is a string. The example `''` tells TJS the return type is a string. Types are inferred from the examples you provide.\n\nTJS transpiles to JavaScript with embedded `__tjs` metadata, enabling runtime type checking, autocomplete from live objects, and documentation generation.\n\n---\n\n## TJS is JavaScript\n\nTJS is **purely additive**. It adds type annotations, runtime validation, and metadata on top of standard JavaScript. It does not replace, intercept, or modify any existing JavaScript semantics.\n\n**Everything you know about JavaScript still works:**\n\n- **Proxies** — fully supported. TJS never intercepts property access. `__tjs` metadata is a plain property assignment on the function object; it doesn't interfere with Proxy traps. The `[tjsEquals]` symbol protocol is specifically designed for Proxy-friendly custom equality.\n- **WeakMap, WeakSet, Map, Set** — all unchanged. TJS doesn't wrap or validate collection internals.\n- **Closures, Promises, async/await** — work identically to JS.\n- **Prototype chains** — preserved. `wrapClass()` uses a Proxy only on the class constructor (to allow calling without `new`), not on instances.\n- **Module semantics** — TJS preserves ES module `import`/`export` exactly. Lazy getters, circular dependencies, and re-exports work the same as in JS.\n- **`this` binding** — unchanged. Arrow functions, `.bind()`, `.call()`, `.apply()` all work normally.\n- **Regular expressions, JSON, Math, Date** — all standard built-ins are available and unmodified (though `Date` is banned by default in native TJS via `TjsDate` in favor of safer alternatives like `Timestamp`/`LegalDate`; use `TjsCompat` to restore it).\n\n**What TJS adds (and when): **\n\n| Addition | When | Overhead |\n| ---------------------- | ----------------------------------------------- | ----------------------------- |\n| Parameter validation | Function entry (unless `!` unsafe) | ~1.15-1.3x on that function |\n| Return type validation | Function exit (only with `safety all`) | ~1.15-1.3x on that function |\n| `__tjs` metadata | Transpile time | Zero runtime cost |\n| `wrapClass` Proxy | Class declaration (on by default in native TJS) | One-time, on constructor only |\n| Structural equality | `==`/`!=` (on by default in native TJS) | Per-comparison |\n\nIf TJS doesn't understand something in your code, it passes it through unchanged. There is no \"TJS runtime\" that interposes between your code and the JS engine — just the inline checks you can see in the transpiled output.\n\n---\n\n## The Compiler\n\nTJS compiles in the browser. No webpack, no node_modules, no build server.\n\n```typescript\nimport { tjs } from 'tjs-lang'\n\nconst code = tjs`\n function add(a: 0, b: 0): 0 {\n return a + b\n }\n`\n\n// Returns transpiled JavaScript with __tjs metadata\n```\n\nYou can also use the CLI:\n\n```bash\nbun src/cli/tjs.ts check file.tjs # Parse and type check\nbun src/cli/tjs.ts run file.tjs # Transpile and execute\nbun src/cli/tjs.ts emit file.tjs # Output transpiled JS\nbun src/cli/tjs.ts types file.tjs # Output type metadata\n```\n\n---\n\n## Syntax\n\n### Parameter Types (Colon Syntax)\n\n> **Not TypeScript.** TJS colon syntax looks like TypeScript but has different\n> semantics. The value after `:` is a **concrete example**, not a type name.\n> Write `name: 'Alice'` (example value), not `name: string` (type name).\n> TJS infers the type from the example: `'Alice'` → string, `0` → integer,\n> `true` → boolean.\n\nRequired parameters use colon syntax with an example value:\n\n```typescript\nfunction greet(name: 'Alice') {} // name is required, type: string\nfunction calculate(value: 0) {} // value is required, type: integer\nfunction measure(rate: 0.0) {} // rate is required, type: number (float)\nfunction count(n: +0) {} // n is required, type: non-negative integer\nfunction toggle(flag: true) {} // flag is required, type: boolean\n```\n\n### Numeric Types\n\nTJS distinguishes three numeric types using valid JavaScript syntax:\n\n```typescript\nfunction process(\n rate: 3.14, // number (float) -- has a decimal point\n count: 42, // integer -- whole number, no decimal\n index: +0 // non-negative integer -- prefixed with +\n) {}\n```\n\n| You Write | Type Inferred | Runtime Validation |\n| --------- | ---------------------- | ------------------------------- |\n| `3.14` | `number` (float) | `typeof x === 'number'` |\n| `0.0` | `number` (float) | `typeof x === 'number'` |\n| `42` | `integer` | `Number.isInteger(x)` |\n| `0` | `integer` | `Number.isInteger(x)` |\n| `+20` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n| `+0` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n| `-5` | `integer` | `Number.isInteger(x)` |\n| `-3.5` | `number` (float) | `typeof x === 'number'` |\n\nAll of these are valid JavaScript expressions. TJS reads the syntax more\ncarefully to give you finer-grained type checking than JS or TypeScript\nprovide natively.\n\n### Optional Parameters (Default Values)\n\nOptional parameters use `=` with a default value:\n\n```typescript\nfunction greet(name = 'World') {} // name is optional, defaults to 'World'\nfunction calculate(value = 0) {} // value is optional, defaults to 0 (integer)\n```\n\n### TypeScript-Style Optional (`?:`)\n\nTJS supports `?:` for compatibility, but consider it a migration aid rather than idiomatic TJS:\n\n```typescript\nfunction greet(name?: '') {} // same as name = ''\n```\n\n**Why `?:` is an antipattern.** In TypeScript, `?:` creates a three-state parameter\n(`value | undefined | missing`) that forces every function body to handle the absent case:\n\n```typescript\n// TypeScript — every caller and callee must reason about undefined\nfunction greet(name?: string) {\n const safeName = name ?? 'World' // defensive check required\n return `Hello, ${safeName}!`\n}\n```\n\nTJS offers two better alternatives:\n\n**1. Safe defaults** — the parameter always has a value, no branching needed:\n\n```typescript\nfunction greet(name = 'World') {\n return `Hello, ${name}!` // name is always a string\n}\n```\n\n**2. Polymorphic functions** — separate signatures for separate behavior:\n\n```typescript\nfunction greet() {\n return 'Hello, World!'\n}\nfunction greet(name: '') {\n return `Hello, ${name}!`\n}\n```\n\nBoth approaches eliminate the `undefined` state entirely. The function body\nnever needs a null check because the type system guarantees a valid value\nat every call site. This is simpler to write, simpler to read, and produces\ntighter runtime validation.\n\n### Object Parameters\n\nObject shapes are defined by example:\n\n```typescript\nfunction createUser(user: { name: ''; age: 0 }) {}\n// user must be an object with string name and number age\n```\n\n### Nullable Types\n\nUse `|` for union with null:\n\n```typescript\nfunction find(id: 0 | null) {} // number or null\n```\n\n### Rest Parameters\n\nRest params use `:` with an array example. The annotation is stripped from\nthe JS output (JS doesn't allow defaults on rest params) but captured in\n`__tjs` metadata:\n\n```typescript\nfunction sum(...nums: [1, 2, 3]): 6 {\n return nums.reduce((a = 0, b: 0) => a + b, 0)\n}\n\nfunction mean(...values: [1.0, 2.0, 3.0, 2.0]): 2.0 {\n return values.length\n ? values.reduce((sum = 0.0, x: 1.0) => sum + x) / values.length\n : 0.0\n}\n```\n\nSignature tests work with rest params — the example array elements are\nspread as individual arguments. `mean(1.0, 2.0, 3.0, 2.0)` is called\nand the result is checked against the `: 2.0` expected return using\nexact value comparison (deepEqual).\n\nThe array example tells TJS the element type. `[0]` means \"array of\nintegers\", `[1.0, 2.0]` means \"array of numbers (floats)\".\n\n**Heterogeneous arrays** infer a union item type:\n\n```typescript\nfunction log(...args: ['info', 42, true]) {}\n// args type: array<string | integer | boolean>\n```\n\n### Return Types (Colon 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 {\n // array of numbers\n return numbers.reduce((a, b) => a + b, 0)\n}\n\nfunction names(users: [{ name: '' }]) {\n // array of objects\n return users.map((u) => u.name)\n}\n```\n\n---\n\n## Safety Markers\n\n### Unsafe Functions\n\nSkip validation for hot paths:\n\n```typescript\nfunction fastAdd(! a: 0, b: 0) { return a + b }\n```\n\nThe `!` marker after the function name skips input validation.\n\n### Safe Functions\n\nExplicit validation (for emphasis):\n\n```typescript\nfunction safeAdd(? a: 0, b: 0) { return a + b }\n```\n\n### Unsafe Blocks\n\nSkip validation for a block of code:\n\n```typescript\nunsafe {\n fastPath(data)\n anotherHotFunction(moreData)\n}\n```\n\n### Module Safety Directive\n\nSet the default validation level for an entire file:\n\n```typescript\nsafety none // No validation (metadata only)\nsafety inputs // Validate function inputs (default)\nsafety all // Validate everything (debug mode)\n```\n\n---\n\n## Type System\n\n### Type()\n\nDefine named types with predicates:\n\n```typescript\n// Simple type from example\nType Name 'Alice'\n\n// Type with description\nType User {\n description: 'a user object'\n example: { name: '', age: 0 }\n}\n\n// Type with predicate\nType PositiveNumber {\n description: 'a positive number'\n example: 1\n predicate(x) { return x > 0 }\n}\n```\n\nTypes can be used in function signatures:\n\n```typescript\nfunction greet(name: Name): '' {\n return `Hello, ${name}!`\n}\n```\n\n### Generic()\n\nRuntime-checkable generics:\n\n```typescript\nGeneric Box<T> {\n description: 'a boxed value'\n predicate(x, T) {\n return typeof x === 'object' && x !== null && 'value' in x && T(x.value)\n }\n}\n\n// With default type parameter\nGeneric Container<T, U = ''> {\n description: 'container with label'\n predicate(obj, T, U) {\n return T(obj.item) && U(obj.label)\n }\n}\n```\n\n#### Declaration Blocks (for TypeScript Consumers)\n\nGenerics can include an optional `declaration` block that specifies the\nTypeScript interface to emit in `.d.ts` output. This is metadata for TS\nconsumers — it has no effect on runtime behavior.\n\n```typescript\nGeneric BoxedProxy<T> {\n description: 'typed reactive proxy'\n predicate(x, T) {\n return typeof x === 'object' && 'value' in x && T(x.value)\n }\n declaration {\n value: T\n path: string\n observe(cb: (path: string) => void): void\n touch(): void\n }\n}\n```\n\nWhen emitting `.d.ts` via `tjs emit --dts`, this produces:\n\n```typescript\nexport interface BoxedProxy<T> {\n value: T\n path: string\n observe(cb: (path: string) => void): void\n touch(): void\n}\n```\n\nThe declaration content is raw TypeScript syntax — it's emitted verbatim\ninto the `.d.ts` file. This lets TJS libraries provide proper TypeScript\ninterfaces while keeping the TJS source as the single source of truth.\n\nWithout a `declaration` block, Generics emit an `any`-based factory stub\nthat provides basic IDE hints without false type errors.\n\n### Union()\n\nDiscriminated unions:\n\n```typescript\nconst Shape = Union('kind', {\n circle: { radius: 0 },\n rectangle: { width: 0, height: 0 },\n})\n\nfunction area(shape: Shape): 0 {\n if (shape.kind === 'circle') {\n return Math.PI * shape.radius ** 2\n }\n return shape.width * shape.height\n}\n```\n\n### Enum()\n\nString or numeric enums:\n\n```typescript\nconst Status = Enum(['pending', 'active', 'completed'])\nconst Priority = Enum({ low: 1, medium: 2, high: 3 })\n\nfunction setStatus(status: Status) {}\n```\n\n---\n\n## Structural Equality: Is / IsNot\n\nJavaScript's `==` is broken (type coercion). TJS provides structural equality:\n\n```typescript\n// Structural comparison - no coercion\n[1, 2] Is [1, 2] // true\n5 Is \"5\" // false (different types)\n{ a: 1 } Is { a: 1 } // true\n\n// Arrays compared element-by-element\n[1, [2, 3]] Is [1, [2, 3]] // true\n\n// Negation\n5 IsNot \"5\" // true\n```\n\n### Custom Equality\n\nObjects can define custom equality in two ways:\n\n**1. `[tjsEquals]` symbol protocol** (preferred for Proxies and advanced use):\n\n```typescript\nimport { tjsEquals } from 'tjs-lang/lang'\n\n// A proxy that delegates equality to its target\nconst target = { x: 1, y: 2 }\nconst proxy = new Proxy({\n [tjsEquals](other) { return target Is other }\n}, {})\n\nproxy == { x: 1, y: 2 } // true — delegates to target\n```\n\n**2. `.Equals` method** (simple, works on any object or class):\n\n```typescript\nclass Point {\n constructor(x: 0, y: 0) { this.x = x; this.y = y }\n Equals(other) { return this.x === other.x && this.y === other.y }\n}\n\nPoint(1, 2) Is Point(1, 2) // true (uses .Equals)\n```\n\n**Priority:** `[tjsEquals]` symbol > `.Equals` method > structural comparison.\n\nThe symbol is `Symbol.for('tjs.equals')`, so it works across realms. Access it\nvia `import { tjsEquals } from 'tjs-lang/lang'` or `__tjs.tjsEquals` at runtime.\n\n---\n\n## Classes\n\n### Callable Without `new`\n\nIn native TJS, classes are automatically wrapped so they can be called\nwithout `new` (the `TjsClass` mode is on by default). For TS-originated\ncode, add the `TjsClass` directive to enable this:\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\nThe wrapping uses a Proxy on the constructor that intercepts bare calls\nand forwards them to `Reflect.construct`. This means `User('Alice')`\nand `new User('Alice')` always produce the same result — an instance.\n\n**What gets wrapped:** Only `class` declarations in your `.tjs` file.\nSpecifically:\n\n- `class Foo { }` in native TJS (or with `TjsClass` directive) → wrapped\n- Built-in globals (`Boolean`, `Number`, `String`, `Array`) → **never touched**\n- Old-style constructor functions (`function Foo() { }` with `Foo.prototype`) → **never touched**\n\n**Why not built-ins:** JavaScript's built-in constructors have dual\nbehavior — `Boolean(0)` returns the primitive `false` (type coercion),\nwhile `new Boolean(0)` returns a `Boolean` object wrapping `false`\n(which is truthy!). If TJS wrapped `Boolean`, then `Boolean(0)` would\nsilently become `new Boolean(0)` — a truthy object instead of `false`.\nThe same applies to `Number()`, `String()`, and `Array()`.\n\n**Why not old-style constructors:** If you're using `function` +\n`prototype` to build a class manually, you may intentionally want\n`Foo(x)` to behave differently from `new Foo(x)` — the same dual\nbehavior pattern as the built-ins. TJS respects this by only wrapping\nthe `class` keyword, where calling without `new` has no existing\nmeaning in JavaScript (it throws `TypeError`).\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 (enabled by default in native TJS via `TjsClass`). The first becomes the real JS constructor; additional variants become factory functions:\n\n```typescript\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- JSON Schema generation (see below)\n\n### JSON Schema\n\nTJS types and function signatures can be exported as standard JSON Schema. Instead of writing schemas and inferring types (Zod), you write typed functions and get schemas out.\n\n#### From Types\n\n```typescript\nType User {\n example: { name: '', age: 0, email: '' }\n}\n\nUser.toJSONSchema()\n// {\n// type: 'object',\n// properties: {\n// name: { type: 'string' },\n// age: { type: 'integer' },\n// email: { type: 'string' }\n// },\n// required: ['name', 'age', 'email'],\n// additionalProperties: false\n// }\n\nUser.check({ name: 'Alice', age: 30, email: 'a@b.com' }) // true\nUser.strip({ name: 'Alice', age: 30, secret: 'pw' })\n// { name: 'Alice', age: 30 } — extra fields removed\n```\n\n#### From Function Signatures\n\n```typescript\nimport { functionMetaToJSONSchema } from 'tjs-lang/lang'\n\nfunction createUser(name: '', age: 0): { id: 0; name: '' } {\n return { id: 1, name }\n}\n\nconst { input, output } = functionMetaToJSONSchema(createUser.__tjs)\n// input: { type: 'object', properties: { name: { type: 'string' }, age: { type: 'integer' } }, required: ['name', 'age'] }\n// output: { type: 'object', properties: { id: { type: 'integer' }, name: { type: 'string' } }, ... }\n```\n\nWhen the shared runtime is installed (`installRuntime()`), `.schema()` is also available directly on the metadata:\n\n```typescript\ncreateUser.__tjs.schema() // same { input, output } result\n```\n\n#### Unions and Enums\n\n```typescript\nconst Direction = Union('direction', ['up', 'down', 'left', 'right'])\nDirection.toJSONSchema() // { enum: ['up', 'down', 'left', 'right'] }\n\nconst Status = Enum('status', { Active: 1, Inactive: 0 })\nStatus.toJSONSchema() // { enum: [1, 0] }\n```\n\nThis gives you OpenAPI-ready API contracts from your function signatures — no extra schema definitions needed.\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### Error History\n\nSince monadic errors don't throw, they can silently vanish if nobody checks the return value. TJS tracks recent type errors in a ring buffer so you can catch these:\n\n```typescript\n// Errors are tracked automatically (on by default, zero cost on happy path)\ngreet(42) // returns MonadicError, caller ignores it\nprocessOrder('bad') // same\n\n// Check what failed recently\nconst recent = __tjs.errors() // → recent MonadicErrors (newest last, max 64)\nfor (const err of recent) {\n console.log(err.message, err.path)\n}\n\n// Testing workflow: clear → run → check for surprises\n__tjs.clearErrors()\nrunMyCode()\nexpect(__tjs.errors()).toEqual([]) // no unexpected type errors\n\n// Total count survives ring buffer wrapping\n__tjs.getErrorCount() // → total since last clear\n```\n\n### Runtime Configuration\n\n```typescript\nimport { configure } from 'tjs-lang/lang'\n\n// Log type errors to console when they occur\nconfigure({ logTypeErrors: true })\n\n// Throw type errors instead of returning them (for debugging)\nconfigure({ throwTypeErrors: true })\n\n// Enable call stack tracking (off by default — ~2x overhead)\n// Useful for server-side logging and agent debugging without devtools\nconfigure({ callStacks: true })\n\n// Disable error history tracking (on by default, zero cost on happy path)\nconfigure({ trackErrors: false })\n```\n\nBoth `logTypeErrors` and `throwTypeErrors` work on the shared runtime and isolated `createRuntime()` instances.\n\n### Inline Tests\n\nTests live next to code:\n\n```typescript\nfunction double(x: 0): 0 { return x * 2 }\n\ntest('doubles numbers') {\n expect(double(5)).toBe(10)\n expect(double(-3)).toBe(-6)\n}\n```\n\nTests are extracted at compile time and can be:\n\n- Run during transpilation\n- Stripped in production builds\n- Used for documentation generation\n\n### WASM Blocks\n\nDrop into WebAssembly for compute-heavy code:\n\n```typescript\nfunction vectorDot(a: [0], b: [0]): 0 {\n let sum = 0\n wasm {\n for (let i = 0; i < a.length; i++) {\n sum = sum + a[i] * b[i]\n }\n }\n return sum\n}\n```\n\nVariables are captured automatically. Falls back to JS if WASM unavailable.\n\n#### SIMD Intrinsics (f32x4)\n\nFor compute-heavy workloads, use f32x4 SIMD intrinsics to process 4 float32 values per instruction:\n\n```typescript\nconst scale = wasm (arr: Float32Array, len: 0, factor: 0.0): 0 {\n let s = f32x4_splat(factor)\n for (let i = 0; i < len; i += 4) {\n let off = i * 4\n let v = f32x4_load(arr, off)\n f32x4_store(arr, off, f32x4_mul(v, s))\n }\n} fallback {\n for (let i = 0; i < len; i++) arr[i] *= factor\n}\n```\n\nAvailable intrinsics:\n\n| Intrinsic | Description |\n| ----------------------------------- | ------------------------------------ |\n| `f32x4_load(ptr, byteOffset)` | Load 4 floats from memory into v128 |\n| `f32x4_store(ptr, byteOffset, vec)` | Store v128 as 4 floats to memory |\n| `f32x4_splat(scalar)` | Fill all 4 lanes with a scalar value |\n| `f32x4_extract_lane(vec, N)` | Extract float from lane 0-3 |\n| `f32x4_replace_lane(vec, N, val)` | Replace one lane, return new v128 |\n| `f32x4_add(a, b)` | Lane-wise addition |\n| `f32x4_sub(a, b)` | Lane-wise subtraction |\n| `f32x4_mul(a, b)` | Lane-wise multiplication |\n| `f32x4_div(a, b)` | Lane-wise division |\n| `f32x4_neg(v)` | Negate all lanes |\n| `f32x4_sqrt(v)` | Square root of all lanes |\n\nThis mirrors C/C++ SIMD intrinsics (`_mm_add_ps`, etc.) — explicit, predictable, no auto-vectorization magic.\n\n#### Zero-Copy Arrays: `wasmBuffer()`\n\nBy default, typed arrays passed to WASM blocks are copied into WASM memory before the call and copied back out after. For large arrays called frequently, this overhead can negate WASM's speed advantage.\n\n`wasmBuffer(Constructor, length)` allocates typed arrays directly in WASM linear memory. These arrays work like normal typed arrays from JavaScript, but when passed to a `wasm {}` block, they're zero-copy — the data is already there.\n\n```typescript\n// Allocate particle positions in WASM memory\nconst starX = wasmBuffer(Float32Array, 50000)\nconst starY = wasmBuffer(Float32Array, 50000)\n\n// Use from JS like normal arrays\nfor (let i = 0; i < 50000; i++) {\n starX[i] = (Math.random() - 0.5) * 2000\n starY[i] = (Math.random() - 0.5) * 2000\n}\n\n// Zero-copy SIMD processing\nfunction moveParticles(! xs: Float32Array, ys: Float32Array, len: 0, dx: 0.0, dy: 0.0) {\n wasm {\n let vdx = f32x4_splat(dx)\n let vdy = f32x4_splat(dy)\n for (let i = 0; i < len; i += 4) {\n let off = i * 4\n f32x4_store(xs, off, f32x4_add(f32x4_load(xs, off), vdx))\n f32x4_store(ys, off, f32x4_add(f32x4_load(ys, off), vdy))\n }\n } fallback {\n for (let i = 0; i < len; i++) { xs[i] += dx; ys[i] += dy }\n }\n}\n\n// After WASM runs, JS sees the mutations immediately\nmoveParticles(starX, starY, 50000, 1.0, 0.5)\nconsole.log(starX[0]) // updated in place, no copy\n```\n\nKey points:\n\n- Supported constructors: `Float32Array`, `Float64Array`, `Int32Array`, `Uint8Array`\n- Uses a bump allocator — allocations persist for program lifetime\n- All WASM blocks in a file share one 64MB memory\n- Regular typed arrays still work (copy in/out as before)\n- Use `!` (unsafe) on hot-path functions to skip runtime type checks\n\n---\n\n## Module System\n\nTJS preserves standard ES module semantics exactly. `import` and `export` statements pass through to the output unchanged — TJS does not have its own module resolver.\n\n### Importing .tjs Files\n\nIn **Bun** (with the TJS plugin from `bunfig.toml`):\n\n```typescript\n// .tjs files are transpiled automatically on import\nimport { processOrder } from './orders.tjs'\nimport { validateUser } from './users.ts' // TS files also work\n```\n\nIn the **browser playground**, local modules are resolved from the playground's module store. Relative imports are looked up by name:\n\n```typescript\nimport { formatDate } from './date-utils' // resolves from saved modules\n```\n\nIn **production builds** (`tjs emit`), TJS transpiles `.tjs` → `.js`. Your bundler (esbuild, Rollup, etc.) handles resolution of the output `.js` files normally.\n\n### Importing JS/TS Libraries\n\nTJS files can import any JavaScript or TypeScript library. The imported code runs without TJS validation — it's just normal JS. Add a TJS wrapper at the boundary if you want type safety:\n\n```typescript\nimport { rawGeocode } from 'legacy-geo-pkg'\n\n// Wrap at the boundary — rawGeocode is unchecked, geocode validates its output\nfunction geocode(addr: ''): { lat: 0.0; lon: 0.0 } {\n return rawGeocode(addr)\n}\n```\n\n### CDN Imports\n\nURL imports work with any ESM CDN:\n\n```typescript\nimport lodash from 'https://esm.sh/lodash@4.17.21'\n```\n\n### Circular Dependencies\n\nTJS doesn't interfere with JS module loading. Circular imports work the same way as in standard ES modules — use lazy getters or late binding if you need to break cycles, exactly as you would in plain JS.\n\n### TypeScript Declaration Files (.d.ts)\n\nTJS can generate `.d.ts` files so TypeScript consumers can use TJS-authored libraries with autocomplete and tooltips:\n\n```bash\nbun src/cli/tjs.ts emit --dts src/lib.tjs -o dist/lib.js\n# Generates dist/lib.js + dist/lib.d.ts\n```\n\nFrom code:\n\n```typescript\nimport { tjs, generateDTS } from 'tjs-lang'\n\nconst result = tjs(source)\nconst dts = generateDTS(result, source)\n```\n\nFunctions get full type declarations. Classes, generics, and predicate-based types get `any`-based stubs that provide IDE hints (parameter names, object shapes) without generating false lint errors for types that TJS validates at runtime.\n\n---\n\n## TypeScript Compatibility\n\n### TS → TJS Converter\n\nConvert existing TypeScript:\n\n```bash\nbun src/cli/tjs.ts convert file.ts\n```\n\n```typescript\n// TypeScript\nfunction greet(name: string, age?: number): string { ... }\n\n// Converts to TJS\nfunction greet(name: '', age = 0): '' { ... }\n```\n\n### What Gets Converted\n\n| TypeScript | TJS |\n| -------------------------- | ----------------------- |\n| `name: string` | `name: ''` |\n| `age: number` | `age: 0.0` |\n| `flag: boolean` | `flag: false` |\n| `items: string[]` | `items: ['']` |\n| `age?: number` | `age: 0.0 \\| undefined` |\n| `private foo` | `#foo` |\n| `interface User` | `Type User` |\n| `type Status = 'a' \\| 'b'` | `Union(['a', 'b'])` |\n| `enum Color` | `Enum(...)` |\n\n> **Optional params:** TypeScript `x?: boolean` becomes TJS `x: false | undefined`.\n> This preserves the three-state semantics (`true` / `false` / `undefined`)\n> using a union type. The param is required but explicitly accepts `undefined`.\n\n---\n\n## Performance\n\n| Mode | Overhead | Use Case |\n| --------------- | -------------- | ---------------------------- |\n| `safety none` | **1.0x** | Metadata only, no validation |\n| `safety inputs` | **~1.15-1.3x** | Production |\n| `(!) unsafe` | **1.0x** | Hot paths |\n| `wasm {}` | **<1.0x** | Compute-heavy code |\n\n### Why ~1.15x, Not 25x\n\nMost validators interpret schemas at runtime (~25x overhead). TJS generates inline checks at transpile time:\n\n```typescript\n// Generated (JIT-friendly)\nif (\n typeof input !== 'object' ||\n input === null ||\n typeof input.name !== 'string' ||\n typeof input.age !== 'number'\n) {\n return { $error: true, message: 'Invalid input', path: 'fn.input' }\n}\n```\n\nNo schema interpretation. No object iteration. The JIT inlines these completely.\n\n---\n\n## Bare Assignments\n\nUppercase identifiers automatically get `const`:\n\n```typescript\nFoo = Type('test', 'example') // becomes: const Foo = Type(...)\nMyConfig = { debug: true } // becomes: const MyConfig = { ... }\n```\n\n---\n\n## Limitations\n\n### What TJS Doesn't Do\n\n- **No gradual typing** — types are all-or-nothing per function\n- **No complex type inference** — you provide examples, not constraints\n- **No type-level computation** — no conditional types, mapped types, etc.\n\n### What TJS Intentionally Avoids\n\n- Build steps beyond transpilation\n- External type checkers\n- Complex tooling configuration\n- Separation of types from runtime\n\n---\n\n## Troubleshooting\n\n### Common Transpilation Errors\n\n**\"Unexpected token\"** — Usually means TJS-specific syntax (`:` params, `:` returns, `Type`, `Generic`) wasn't recognized. Check:\n\n- Is the file being parsed as TJS (not plain JS)?\n- Are `Type`/`Generic`/`Union` declarations at the top level (not inside functions)?\n- Is the `: returnType` before the function body `{`?\n\n**\"Type is not defined\" / \"Generic is not defined\"** — These become `const Name = Type(...)` / `const Name = Generic(...)` after preprocessing. If you see this at runtime, the TJS runtime (`createRuntime()`) wasn't installed, or the file wasn't transpiled through TJS.\n\n**Signature test failures** — TJS runs your function with its example values at transpile time. If the function fails with its own examples, transpilation reports an error. Fix the function or choose better examples:\n\n```typescript\n// BAD: example 0 causes division by zero\nfunction inverse(x: 0): 0.0 {\n return 1 / x\n}\n\n// GOOD: example 1 works\nfunction inverse(x: 1): 0.0 {\n return 1 / x\n}\n```\n\n**Monadic errors instead of exceptions** — TJS validation returns `MonadicError` objects (with `$error: true`), it doesn't throw. Check with `isMonadicError(result)`, not `try/catch`:\n\n```typescript\nimport { isMonadicError } from 'tjs-lang'\n\nconst result = myFunction(badInput)\nif (isMonadicError(result)) {\n console.log(result.message) // \"type mismatch: expected string, got number\"\n}\n```\n\n### Debugging Type Checks\n\nEvery transpiled function has `.__tjs` metadata you can inspect:\n\n```typescript\nconsole.log(myFunction.__tjs)\n// { params: { name: { type: { kind: 'string' }, required: true } },\n// returns: { kind: 'string' } }\n```\n\nThe transpiled JS is readable — look at the generated code to see exactly what checks run:\n\n```bash\nbun src/cli/tjs.ts emit myfile.tjs # see the generated JS\n```\n\n### When to Use `!` (Unsafe)\n\nMark functions unsafe when:\n\n- The data source is already validated (e.g., internal helper called only from a validated wrapper)\n- You're in a hot loop and profiling shows the checks matter\n- You're calling a function millions of times with known-good data\n\nDon't use `!` at system boundaries (API handlers, user input, external data).\n\n---\n\n## Learn More\n\n- [AJS Documentation](DOCS-AJS.md) — The agent runtime\n- [Builder's Manifesto](MANIFESTO-BUILDER.md) — Why TJS is fun\n- [Enterprise Guide](MANIFESTO-ENTERPRISE.md) — Why TJS is safe\n- [Technical Context](CONTEXT.md) — Architecture deep dive\n"
214
+ "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'): '' {\n return `Hello, ${name}!`\n}\n```\n\nThe example `'World'` tells TJS that `name` is a string. The example `''` tells TJS the return type is a string. Types are inferred from the examples you provide.\n\nTJS transpiles to JavaScript with embedded `__tjs` metadata, enabling runtime type checking, autocomplete from live objects, and documentation generation.\n\n---\n\n## TJS is JavaScript\n\nTJS is **purely additive**. It adds type annotations, runtime validation, and metadata on top of standard JavaScript. It does not replace, intercept, or modify any existing JavaScript semantics.\n\n**Everything you know about JavaScript still works:**\n\n- **Proxies** — fully supported. TJS never intercepts property access. `__tjs` metadata is a plain property assignment on the function object; it doesn't interfere with Proxy traps. The `[tjsEquals]` symbol protocol is specifically designed for Proxy-friendly custom equality.\n- **WeakMap, WeakSet, Map, Set** — all unchanged. TJS doesn't wrap or validate collection internals.\n- **Closures, Promises, async/await** — work identically to JS.\n- **Prototype chains** — preserved. `wrapClass()` uses a Proxy only on the class constructor (to allow calling without `new`), not on instances.\n- **Module semantics** — TJS preserves ES module `import`/`export` exactly. Lazy getters, circular dependencies, and re-exports work the same as in JS.\n- **`this` binding** — unchanged. Arrow functions, `.bind()`, `.call()`, `.apply()` all work normally.\n- **Regular expressions, JSON, Math, Date** — all standard built-ins are available and unmodified (though `Date` is banned by default in native TJS via `TjsDate` in favor of safer alternatives like `Timestamp`/`LegalDate`; use `TjsCompat` to restore it).\n\n**What TJS adds (and when): **\n\n| Addition | When | Overhead |\n| ---------------------- | ----------------------------------------------- | ----------------------------- |\n| Parameter validation | Function entry (unless `!` unsafe) | ~1.15-1.3x on that function |\n| Return type validation | Function exit (only with `safety all`) | ~1.15-1.3x on that function |\n| `__tjs` metadata | Transpile time | Zero runtime cost |\n| `wrapClass` Proxy | Class declaration (on by default in native TJS) | One-time, on constructor only |\n| Footgun-free equality | `==`/`!=` (on by default in native TJS) | Per-comparison |\n\nIf TJS doesn't understand something in your code, it passes it through unchanged. There is no \"TJS runtime\" that interposes between your code and the JS engine — just the inline checks you can see in the transpiled output.\n\n---\n\n## The Compiler\n\nTJS compiles in the browser. No webpack, no node_modules, no build server.\n\n```typescript\nimport { tjs } from 'tjs-lang'\n\nconst code = tjs`\n function add(a: 0, b: 0): 0 {\n return a + b\n }\n`\n\n// Returns transpiled JavaScript with __tjs metadata\n```\n\nYou can also use the CLI:\n\n```bash\nbun src/cli/tjs.ts check file.tjs # Parse and type check\nbun src/cli/tjs.ts run file.tjs # Transpile and execute\nbun src/cli/tjs.ts emit file.tjs # Output transpiled JS\nbun src/cli/tjs.ts types file.tjs # Output type metadata\n```\n\n---\n\n## Syntax\n\n### Parameter Types (Colon Syntax)\n\n> **Not TypeScript.** TJS colon syntax looks like TypeScript but has different\n> semantics. The value after `:` is a **concrete example**, not a type name.\n> Write `name: 'Alice'` (example value), not `name: string` (type name).\n> TJS infers the type from the example: `'Alice'` → string, `0` → integer,\n> `true` → boolean.\n\nRequired parameters use colon syntax with an example value:\n\n```typescript\nfunction greet(name: 'Alice') {} // name is required, type: string\nfunction calculate(value: 0) {} // value is required, type: integer\nfunction measure(rate: 0.0) {} // rate is required, type: number (float)\nfunction count(n: +0) {} // n is required, type: non-negative integer\nfunction toggle(flag: true) {} // flag is required, type: boolean\n```\n\n### Numeric Types\n\nTJS distinguishes three numeric types using valid JavaScript syntax:\n\n```typescript\nfunction process(\n rate: 3.14, // number (float) -- has a decimal point\n count: 42, // integer -- whole number, no decimal\n index: +0 // non-negative integer -- prefixed with +\n) {}\n```\n\n| You Write | Type Inferred | Runtime Validation |\n| --------- | ---------------------- | ------------------------------- |\n| `3.14` | `number` (float) | `typeof x === 'number'` |\n| `0.0` | `number` (float) | `typeof x === 'number'` |\n| `42` | `integer` | `Number.isInteger(x)` |\n| `0` | `integer` | `Number.isInteger(x)` |\n| `+20` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n| `+0` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n| `-5` | `integer` | `Number.isInteger(x)` |\n| `-3.5` | `number` (float) | `typeof x === 'number'` |\n\nAll of these are valid JavaScript expressions. TJS reads the syntax more\ncarefully to give you finer-grained type checking than JS or TypeScript\nprovide natively.\n\n### Optional Parameters (Default Values)\n\nOptional parameters use `=` with a default value:\n\n```typescript\nfunction greet(name = 'World') {} // name is optional, defaults to 'World'\nfunction calculate(value = 0) {} // value is optional, defaults to 0 (integer)\n```\n\n### TypeScript-Style Optional (`?:`)\n\nTJS supports `?:` for compatibility, but consider it a migration aid rather than idiomatic TJS:\n\n```typescript\nfunction greet(name?: '') {} // same as name = ''\n```\n\n**Why `?:` is an antipattern.** In TypeScript, `?:` creates a three-state parameter\n(`value | undefined | missing`) that forces every function body to handle the absent case:\n\n```typescript\n// TypeScript — every caller and callee must reason about undefined\nfunction greet(name?: string) {\n const safeName = name ?? 'World' // defensive check required\n return `Hello, ${safeName}!`\n}\n```\n\nTJS offers two better alternatives:\n\n**1. Safe defaults** — the parameter always has a value, no branching needed:\n\n```typescript\nfunction greet(name = 'World') {\n return `Hello, ${name}!` // name is always a string\n}\n```\n\n**2. Polymorphic functions** — separate signatures for separate behavior:\n\n```typescript\nfunction greet() {\n return 'Hello, World!'\n}\nfunction greet(name: '') {\n return `Hello, ${name}!`\n}\n```\n\nBoth approaches eliminate the `undefined` state entirely. The function body\nnever needs a null check because the type system guarantees a valid value\nat every call site. This is simpler to write, simpler to read, and produces\ntighter runtime validation.\n\n### Object Parameters\n\nObject shapes are defined by example:\n\n```typescript\nfunction createUser(user: { name: ''; age: 0 }) {}\n// user must be an object with string name and number age\n```\n\n### Nullable Types\n\nUse `|` for union with null:\n\n```typescript\nfunction find(id: 0 | null) {} // number or null\n```\n\n### Rest Parameters\n\nRest params use `:` with an array example. The annotation is stripped from\nthe JS output (JS doesn't allow defaults on rest params) but captured in\n`__tjs` metadata:\n\n```typescript\nfunction sum(...nums: [1, 2, 3]): 6 {\n return nums.reduce((a = 0, b: 0) => a + b, 0)\n}\n\nfunction mean(...values: [1.0, 2.0, 3.0, 2.0]): 2.0 {\n return values.length\n ? values.reduce((sum = 0.0, x: 1.0) => sum + x) / values.length\n : 0.0\n}\n```\n\nSignature tests work with rest params — the example array elements are\nspread as individual arguments. `mean(1.0, 2.0, 3.0, 2.0)` is called\nand the result is checked against the `: 2.0` expected return using\nexact value comparison (deepEqual).\n\nThe array example tells TJS the element type. `[0]` means \"array of\nintegers\", `[1.0, 2.0]` means \"array of numbers (floats)\".\n\n**Heterogeneous arrays** infer a union item type:\n\n```typescript\nfunction log(...args: ['info', 42, true]) {}\n// args type: array<string | integer | boolean>\n```\n\n### Return Types (Colon 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 {\n // array of numbers\n return numbers.reduce((a, b) => a + b, 0)\n}\n\nfunction names(users: [{ name: '' }]) {\n // array of objects\n return users.map((u) => u.name)\n}\n```\n\n---\n\n## Safety Markers\n\n### Unsafe Functions\n\nSkip validation for hot paths:\n\n```typescript\nfunction fastAdd(! a: 0, b: 0) { return a + b }\n```\n\nThe `!` marker after the function name skips input validation.\n\n### Safe Functions\n\nExplicit validation (for emphasis):\n\n```typescript\nfunction safeAdd(? a: 0, b: 0) { return a + b }\n```\n\n### Unsafe Blocks\n\nSkip validation for a block of code:\n\n```typescript\nunsafe {\n fastPath(data)\n anotherHotFunction(moreData)\n}\n```\n\n### Module Safety Directive\n\nSet the default validation level for an entire file:\n\n```typescript\nsafety none // No validation (metadata only)\nsafety inputs // Validate function inputs (default)\nsafety all // Validate everything (debug mode)\n```\n\n---\n\n## Type System\n\n### Type()\n\nDefine named types with predicates:\n\n```typescript\n// Simple type from example\nType Name 'Alice'\n\n// Type with description\nType User {\n description: 'a user object'\n example: { name: '', age: 0 }\n}\n\n// Type with predicate\nType PositiveNumber {\n description: 'a positive number'\n example: 1\n predicate(x) { return x > 0 }\n}\n```\n\nTypes can be used in function signatures:\n\n```typescript\nfunction greet(name: Name): '' {\n return `Hello, ${name}!`\n}\n```\n\n### Generic()\n\nRuntime-checkable generics:\n\n```typescript\nGeneric Box<T> {\n description: 'a boxed value'\n predicate(x, T) {\n return typeof x === 'object' && x !== null && 'value' in x && T(x.value)\n }\n}\n\n// With default type parameter\nGeneric Container<T, U = ''> {\n description: 'container with label'\n predicate(obj, T, U) {\n return T(obj.item) && U(obj.label)\n }\n}\n```\n\n#### Declaration Blocks (for TypeScript Consumers)\n\nGenerics can include an optional `declaration` block that specifies the\nTypeScript interface to emit in `.d.ts` output. This is metadata for TS\nconsumers — it has no effect on runtime behavior.\n\n```typescript\nGeneric BoxedProxy<T> {\n description: 'typed reactive proxy'\n predicate(x, T) {\n return typeof x === 'object' && 'value' in x && T(x.value)\n }\n declaration {\n value: T\n path: string\n observe(cb: (path: string) => void): void\n touch(): void\n }\n}\n```\n\nWhen emitting `.d.ts` via `tjs emit --dts`, this produces:\n\n```typescript\nexport interface BoxedProxy<T> {\n value: T\n path: string\n observe(cb: (path: string) => void): void\n touch(): void\n}\n```\n\nThe declaration content is raw TypeScript syntax — it's emitted verbatim\ninto the `.d.ts` file. This lets TJS libraries provide proper TypeScript\ninterfaces while keeping the TJS source as the single source of truth.\n\nWithout a `declaration` block, Generics emit an `any`-based factory stub\nthat provides basic IDE hints without false type errors.\n\n### Union()\n\nDiscriminated unions:\n\n```typescript\nconst Shape = Union('kind', {\n circle: { radius: 0 },\n rectangle: { width: 0, height: 0 },\n})\n\nfunction area(shape: Shape): 0 {\n if (shape.kind === 'circle') {\n return Math.PI * shape.radius ** 2\n }\n return shape.width * shape.height\n}\n```\n\n### Enum()\n\nString or numeric enums:\n\n```typescript\nconst Status = Enum(['pending', 'active', 'completed'])\nconst Priority = Enum({ low: 1, medium: 2, high: 3 })\n\nfunction setStatus(status: Status) {}\n```\n\n---\n\n## Structural Equality: Is / IsNot\n\nJavaScript's `==` is broken (type coercion) and `===` is identity-only. For\ndeep structural comparison, TJS provides `Is` / `IsNot`:\n\n> Note: TJS's `==` / `!=` are **not** structural — they are footgun-free `===`\n> (no coercion, unwraps boxed primitives, `null == undefined`). Distinct objects\n> are genuinely distinct: `{ a: 1 } == { a: 1 }` is `false`. Use `Is` (below)\n> for deep structural comparison.\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 Is { 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\nIn native TJS, classes are automatically wrapped so they can be called\nwithout `new` (the `TjsClass` mode is on by default). For TS-originated\ncode, add the `TjsClass` directive to enable this:\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\nThe wrapping uses a Proxy on the constructor that intercepts bare calls\nand forwards them to `Reflect.construct`. This means `User('Alice')`\nand `new User('Alice')` always produce the same result — an instance.\n\n**What gets wrapped:** Only `class` declarations in your `.tjs` file.\nSpecifically:\n\n- `class Foo { }` in native TJS (or with `TjsClass` directive) → wrapped\n- Built-in globals (`Boolean`, `Number`, `String`, `Array`) → **never touched**\n- Old-style constructor functions (`function Foo() { }` with `Foo.prototype`) → **never touched**\n\n**Why not built-ins:** JavaScript's built-in constructors have dual\nbehavior — `Boolean(0)` returns the primitive `false` (type coercion),\nwhile `new Boolean(0)` returns a `Boolean` object wrapping `false`\n(which is truthy!). If TJS wrapped `Boolean`, then `Boolean(0)` would\nsilently become `new Boolean(0)` — a truthy object instead of `false`.\nThe same applies to `Number()`, `String()`, and `Array()`.\n\n**Why not old-style constructors:** If you're using `function` +\n`prototype` to build a class manually, you may intentionally want\n`Foo(x)` to behave differently from `new Foo(x)` — the same dual\nbehavior pattern as the built-ins. TJS respects this by only wrapping\nthe `class` keyword, where calling without `new` has no existing\nmeaning in JavaScript (it throws `TypeError`).\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 (enabled by default in native TJS via `TjsClass`). The first becomes the real JS constructor; additional variants become factory functions:\n\n```typescript\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- JSON Schema generation (see below)\n\n### JSON Schema\n\nTJS types and function signatures can be exported as standard JSON Schema. Instead of writing schemas and inferring types (Zod), you write typed functions and get schemas out.\n\n#### From Types\n\n```typescript\nType User {\n example: { name: '', age: 0, email: '' }\n}\n\nUser.toJSONSchema()\n// {\n// type: 'object',\n// properties: {\n// name: { type: 'string' },\n// age: { type: 'integer' },\n// email: { type: 'string' }\n// },\n// required: ['name', 'age', 'email'],\n// additionalProperties: false\n// }\n\nUser.check({ name: 'Alice', age: 30, email: 'a@b.com' }) // true\nUser.strip({ name: 'Alice', age: 30, secret: 'pw' })\n// { name: 'Alice', age: 30 } — extra fields removed\n```\n\n#### From Function Signatures\n\n```typescript\nimport { functionMetaToJSONSchema } from 'tjs-lang/lang'\n\nfunction createUser(name: '', age: 0): { id: 0; name: '' } {\n return { id: 1, name }\n}\n\nconst { input, output } = functionMetaToJSONSchema(createUser.__tjs)\n// input: { type: 'object', properties: { name: { type: 'string' }, age: { type: 'integer' } }, required: ['name', 'age'] }\n// output: { type: 'object', properties: { id: { type: 'integer' }, name: { type: 'string' } }, ... }\n```\n\nWhen the shared runtime is installed (`installRuntime()`), `.schema()` is also available directly on the metadata:\n\n```typescript\ncreateUser.__tjs.schema() // same { input, output } result\n```\n\n#### Unions and Enums\n\n```typescript\nconst Direction = Union('direction', ['up', 'down', 'left', 'right'])\nDirection.toJSONSchema() // { enum: ['up', 'down', 'left', 'right'] }\n\nconst Status = Enum('status', { Active: 1, Inactive: 0 })\nStatus.toJSONSchema() // { enum: [1, 0] }\n```\n\nThis gives you OpenAPI-ready API contracts from your function signatures — no extra schema definitions needed.\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### Error History\n\nSince monadic errors don't throw, they can silently vanish if nobody checks the return value. TJS tracks recent type errors in a ring buffer so you can catch these:\n\n```typescript\n// Errors are tracked automatically (on by default, zero cost on happy path)\ngreet(42) // returns MonadicError, caller ignores it\nprocessOrder('bad') // same\n\n// Check what failed recently\nconst recent = __tjs.errors() // → recent MonadicErrors (newest last, max 64)\nfor (const err of recent) {\n console.log(err.message, err.path)\n}\n\n// Testing workflow: clear → run → check for surprises\n__tjs.clearErrors()\nrunMyCode()\nexpect(__tjs.errors()).toEqual([]) // no unexpected type errors\n\n// Total count survives ring buffer wrapping\n__tjs.getErrorCount() // → total since last clear\n```\n\n### Runtime Configuration\n\n```typescript\nimport { configure } from 'tjs-lang/lang'\n\n// Log type errors to console when they occur\nconfigure({ logTypeErrors: true })\n\n// Throw type errors instead of returning them (for debugging)\nconfigure({ throwTypeErrors: true })\n\n// Enable call stack tracking (off by default — ~2x overhead)\n// Useful for server-side logging and agent debugging without devtools\nconfigure({ callStacks: true })\n\n// Disable error history tracking (on by default, zero cost on happy path)\nconfigure({ trackErrors: false })\n```\n\nBoth `logTypeErrors` and `throwTypeErrors` work on the shared runtime and isolated `createRuntime()` instances.\n\n### Inline Tests\n\nTests live next to code:\n\n```typescript\nfunction double(x: 0): 0 { return x * 2 }\n\ntest('doubles numbers') {\n expect(double(5)).toBe(10)\n expect(double(-3)).toBe(-6)\n}\n```\n\nTests are extracted at compile time and can be:\n\n- Run during transpilation\n- Stripped in production builds\n- Used for documentation generation\n\n### WASM Blocks\n\nDrop into WebAssembly for compute-heavy code:\n\n```typescript\nfunction vectorDot(a: [0], b: [0]): 0 {\n let sum = 0\n wasm {\n for (let i = 0; i < a.length; i++) {\n sum = sum + a[i] * b[i]\n }\n }\n return sum\n}\n```\n\nVariables are captured automatically. Falls back to JS if WASM unavailable.\n\n#### SIMD Intrinsics (f32x4)\n\nFor compute-heavy workloads, use f32x4 SIMD intrinsics to process 4 float32 values per instruction:\n\n```typescript\nconst scale = wasm (arr: Float32Array, len: 0, factor: 0.0): 0 {\n let s = f32x4_splat(factor)\n for (let i = 0; i < len; i += 4) {\n let off = i * 4\n let v = f32x4_load(arr, off)\n f32x4_store(arr, off, f32x4_mul(v, s))\n }\n} fallback {\n for (let i = 0; i < len; i++) arr[i] *= factor\n}\n```\n\nAvailable intrinsics:\n\n| Intrinsic | Description |\n| ----------------------------------- | ------------------------------------ |\n| `f32x4_load(ptr, byteOffset)` | Load 4 floats from memory into v128 |\n| `f32x4_store(ptr, byteOffset, vec)` | Store v128 as 4 floats to memory |\n| `f32x4_splat(scalar)` | Fill all 4 lanes with a scalar value |\n| `f32x4_extract_lane(vec, N)` | Extract float from lane 0-3 |\n| `f32x4_replace_lane(vec, N, val)` | Replace one lane, return new v128 |\n| `f32x4_add(a, b)` | Lane-wise addition |\n| `f32x4_sub(a, b)` | Lane-wise subtraction |\n| `f32x4_mul(a, b)` | Lane-wise multiplication |\n| `f32x4_div(a, b)` | Lane-wise division |\n| `f32x4_neg(v)` | Negate all lanes |\n| `f32x4_sqrt(v)` | Square root of all lanes |\n\nThis mirrors C/C++ SIMD intrinsics (`_mm_add_ps`, etc.) — explicit, predictable, no auto-vectorization magic.\n\n#### Zero-Copy Arrays: `wasmBuffer()`\n\nBy default, typed arrays passed to WASM blocks are copied into WASM memory before the call and copied back out after. For large arrays called frequently, this overhead can negate WASM's speed advantage.\n\n`wasmBuffer(Constructor, length)` allocates typed arrays directly in WASM linear memory. These arrays work like normal typed arrays from JavaScript, but when passed to a `wasm {}` block, they're zero-copy — the data is already there.\n\n```typescript\n// Allocate particle positions in WASM memory\nconst starX = wasmBuffer(Float32Array, 50000)\nconst starY = wasmBuffer(Float32Array, 50000)\n\n// Use from JS like normal arrays\nfor (let i = 0; i < 50000; i++) {\n starX[i] = (Math.random() - 0.5) * 2000\n starY[i] = (Math.random() - 0.5) * 2000\n}\n\n// Zero-copy SIMD processing\nfunction moveParticles(! xs: Float32Array, ys: Float32Array, len: 0, dx: 0.0, dy: 0.0) {\n wasm {\n let vdx = f32x4_splat(dx)\n let vdy = f32x4_splat(dy)\n for (let i = 0; i < len; i += 4) {\n let off = i * 4\n f32x4_store(xs, off, f32x4_add(f32x4_load(xs, off), vdx))\n f32x4_store(ys, off, f32x4_add(f32x4_load(ys, off), vdy))\n }\n } fallback {\n for (let i = 0; i < len; i++) { xs[i] += dx; ys[i] += dy }\n }\n}\n\n// After WASM runs, JS sees the mutations immediately\nmoveParticles(starX, starY, 50000, 1.0, 0.5)\nconsole.log(starX[0]) // updated in place, no copy\n```\n\nKey points:\n\n- Supported constructors: `Float32Array`, `Float64Array`, `Int32Array`, `Uint8Array`\n- Uses a bump allocator — allocations persist for program lifetime\n- All WASM blocks in a file share one 64MB memory\n- Regular typed arrays still work (copy in/out as before)\n- Use `!` (unsafe) on hot-path functions to skip runtime type checks\n\n---\n\n## Module System\n\nTJS preserves standard ES module semantics exactly. `import` and `export` statements pass through to the output unchanged — TJS does not have its own module resolver.\n\n### Importing .tjs Files\n\nIn **Bun** (with the TJS plugin from `bunfig.toml`):\n\n```typescript\n// .tjs files are transpiled automatically on import\nimport { processOrder } from './orders.tjs'\nimport { validateUser } from './users.ts' // TS files also work\n```\n\nIn the **browser playground**, local modules are resolved from the playground's module store. Relative imports are looked up by name:\n\n```typescript\nimport { formatDate } from './date-utils' // resolves from saved modules\n```\n\nIn **production builds** (`tjs emit`), TJS transpiles `.tjs` → `.js`. Your bundler (esbuild, Rollup, etc.) handles resolution of the output `.js` files normally.\n\n### Importing JS/TS Libraries\n\nTJS files can import any JavaScript or TypeScript library. The imported code runs without TJS validation — it's just normal JS. Add a TJS wrapper at the boundary if you want type safety:\n\n```typescript\nimport { rawGeocode } from 'legacy-geo-pkg'\n\n// Wrap at the boundary — rawGeocode is unchecked, geocode validates its output\nfunction geocode(addr: ''): { lat: 0.0; lon: 0.0 } {\n return rawGeocode(addr)\n}\n```\n\n### CDN Imports\n\nURL imports work with any ESM CDN:\n\n```typescript\nimport lodash from 'https://esm.sh/lodash@4.17.21'\n```\n\n### Circular Dependencies\n\nTJS doesn't interfere with JS module loading. Circular imports work the same way as in standard ES modules — use lazy getters or late binding if you need to break cycles, exactly as you would in plain JS.\n\n### TypeScript Declaration Files (.d.ts)\n\nTJS can generate `.d.ts` files so TypeScript consumers can use TJS-authored libraries with autocomplete and tooltips:\n\n```bash\nbun src/cli/tjs.ts emit --dts src/lib.tjs -o dist/lib.js\n# Generates dist/lib.js + dist/lib.d.ts\n```\n\nFrom code:\n\n```typescript\nimport { tjs, generateDTS } from 'tjs-lang'\n\nconst result = tjs(source)\nconst dts = generateDTS(result, source)\n```\n\nFunctions get full type declarations. Classes, generics, and predicate-based types get `any`-based stubs that provide IDE hints (parameter names, object shapes) without generating false lint errors for types that TJS validates at runtime.\n\n---\n\n## TypeScript Compatibility\n\n### TS → TJS Converter\n\nConvert existing TypeScript:\n\n```bash\nbun src/cli/tjs.ts convert file.ts\n```\n\n```typescript\n// TypeScript\nfunction greet(name: string, age?: number): string { ... }\n\n// Converts to TJS\nfunction greet(name: '', age = 0): '' { ... }\n```\n\n### What Gets Converted\n\n| TypeScript | TJS |\n| -------------------------- | ----------------------- |\n| `name: string` | `name: ''` |\n| `age: number` | `age: 0.0` |\n| `flag: boolean` | `flag: false` |\n| `items: string[]` | `items: ['']` |\n| `age?: number` | `age: 0.0 \\| undefined` |\n| `private foo` | `#foo` |\n| `interface User` | `Type User` |\n| `type Status = 'a' \\| 'b'` | `Union(['a', 'b'])` |\n| `enum Color` | `Enum(...)` |\n\n> **Optional params:** TypeScript `x?: boolean` becomes TJS `x: false | undefined`.\n> This preserves the three-state semantics (`true` / `false` / `undefined`)\n> using a union type. The param is required but explicitly accepts `undefined`.\n\n---\n\n## Performance\n\n| Mode | Overhead | Use Case |\n| --------------- | -------------- | ---------------------------- |\n| `safety none` | **1.0x** | Metadata only, no validation |\n| `safety inputs` | **~1.15-1.3x** | Production |\n| `(!) unsafe` | **1.0x** | Hot paths |\n| `wasm {}` | **<1.0x** | Compute-heavy code |\n\n### Why ~1.15x, Not 25x\n\nMost validators interpret schemas at runtime (~25x overhead). TJS generates inline checks at transpile time:\n\n```typescript\n// Generated (JIT-friendly)\nif (\n typeof input !== 'object' ||\n input === null ||\n typeof input.name !== 'string' ||\n typeof input.age !== 'number'\n) {\n return { $error: true, message: 'Invalid input', path: 'fn.input' }\n}\n```\n\nNo schema interpretation. No object iteration. The JIT inlines these completely.\n\n---\n\n## Bare Assignments\n\nUppercase identifiers automatically get `const`:\n\n```typescript\nFoo = Type('test', 'example') // becomes: const Foo = Type(...)\nMyConfig = { debug: true } // becomes: const MyConfig = { ... }\n```\n\n---\n\n## Limitations\n\n### What TJS Doesn't Do\n\n- **No gradual typing** — types are all-or-nothing per function\n- **No complex type inference** — you provide examples, not constraints\n- **No type-level computation** — no conditional types, mapped types, etc.\n\n### What TJS Intentionally Avoids\n\n- Build steps beyond transpilation\n- External type checkers\n- Complex tooling configuration\n- Separation of types from runtime\n\n---\n\n## Troubleshooting\n\n### Common Transpilation Errors\n\n**\"Unexpected token\"** — Usually means TJS-specific syntax (`:` params, `:` returns, `Type`, `Generic`) wasn't recognized. Check:\n\n- Is the file being parsed as TJS (not plain JS)?\n- Are `Type`/`Generic`/`Union` declarations at the top level (not inside functions)?\n- Is the `: returnType` before the function body `{`?\n\n**\"Type is not defined\" / \"Generic is not defined\"** — These become `const Name = Type(...)` / `const Name = Generic(...)` after preprocessing. If you see this at runtime, the TJS runtime (`createRuntime()`) wasn't installed, or the file wasn't transpiled through TJS.\n\n**Signature test failures** — TJS runs your function with its example values at transpile time. If the function fails with its own examples, transpilation reports an error. Fix the function or choose better examples:\n\n```typescript\n// BAD: example 0 causes division by zero\nfunction inverse(x: 0): 0.0 {\n return 1 / x\n}\n\n// GOOD: example 1 works\nfunction inverse(x: 1): 0.0 {\n return 1 / x\n}\n```\n\n**Monadic errors instead of exceptions** — TJS validation returns `MonadicError` objects (with `$error: true`), it doesn't throw. Check with `isMonadicError(result)`, not `try/catch`:\n\n```typescript\nimport { isMonadicError } from 'tjs-lang'\n\nconst result = myFunction(badInput)\nif (isMonadicError(result)) {\n console.log(result.message) // \"type mismatch: expected string, got number\"\n}\n```\n\n### Debugging Type Checks\n\nEvery transpiled function has `.__tjs` metadata you can inspect:\n\n```typescript\nconsole.log(myFunction.__tjs)\n// { params: { name: { type: { kind: 'string' }, required: true } },\n// returns: { kind: 'string' } }\n```\n\nThe transpiled JS is readable — look at the generated code to see exactly what checks run:\n\n```bash\nbun src/cli/tjs.ts emit myfile.tjs # see the generated JS\n```\n\n### When to Use `!` (Unsafe)\n\nMark functions unsafe when:\n\n- The data source is already validated (e.g., internal helper called only from a validated wrapper)\n- You're in a hot loop and profiling shows the checks matter\n- You're calling a function millions of times with known-good data\n\nDon't use `!` at system boundaries (API handlers, user input, external data).\n\n---\n\n## Learn More\n\n- [AJS Documentation](DOCS-AJS.md) — The agent runtime\n- [Builder's Manifesto](MANIFESTO-BUILDER.md) — Why TJS is fun\n- [Enterprise Guide](MANIFESTO-ENTERPRISE.md) — Why TJS is safe\n- [Technical Context](CONTEXT.md) — Architecture deep dive\n"
215
215
  },
216
216
  {
217
217
  "title": "Playground Imports",
@@ -717,7 +717,7 @@
717
717
  "group": "docs",
718
718
  "order": 0,
719
719
  "navTitle": "Documentation",
720
- "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### Local helper functions\n\nAn agent source file may declare **multiple** top-level functions. The **last**\ndeclaration is the entry point; the ones before it are **helpers** the entry (or\nother helpers) can call by name:\n\n```javascript\nfunction double(x) {\n return x * 2\n}\n\nfunction addOne(x) {\n const d = double(x) // helpers can call earlier helpers\n return d + 1\n}\n\nfunction main(n) {\n const a = double(n)\n const b = addOne(n)\n return { a, b } // only the entry must return an object\n}\n```\n\nHelpers behave like ordinary functions, with a few deliberate rules:\n\n- **Top-level siblings, not closures.** A helper sees only its own parameters —\n never the caller's locals. This keeps them predictable and reusable.\n- **They may return any value** (number, string, array, object). Only the\n _entry_ function is held to the object-return contract.\n- **Recursion is allowed.** Helpers may call themselves or each other. Runaway\n recursion is bounded by fuel/timeout, with a hard call-depth cap (256) that\n surfaces as a normal monadic error — never a host crash.\n- **Call them at statement level.** Like template literals, a helper call cannot\n be nested inside a larger expression — lift it to a variable first:\n\n ```javascript\n // Fails at transpile time:\n return { v: double(n) + 1 }\n\n // Do this instead:\n const d = double(n)\n return { v: d + 1 }\n ```\n\nHelper bodies are compiled once and dispatched by name, so calling a helper many\ntimes (or in a loop) doesn't bloat the agent's AST.\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"
720
+ "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### Local helper functions\n\nAn agent source file may declare **multiple** top-level functions. The **last**\ndeclaration is the entry point; the ones before it are **helpers** the entry (or\nother helpers) can call by name:\n\n```javascript\nfunction double(x) {\n return x * 2\n}\n\nfunction addOne(x) {\n const d = double(x) // helpers can call earlier helpers\n return d + 1\n}\n\nfunction main(n) {\n const a = double(n)\n const b = addOne(n)\n return { a, b } // only the entry must return an object\n}\n```\n\nHelpers behave like ordinary functions, with a few deliberate rules:\n\n- **Top-level siblings, not closures.** A helper sees only its own parameters —\n never the caller's locals. This keeps them predictable and reusable.\n- **They may return any value** (number, string, array, object). Only the\n _entry_ function is held to the object-return contract.\n- **Recursion is allowed.** Helpers may call themselves or each other. Runaway\n recursion is bounded by fuel/timeout, with a hard call-depth cap (256) that\n surfaces as a normal monadic error — never a host crash.\n- **Call them at statement level.** Like template literals, a helper call cannot\n be nested inside a larger expression — lift it to a variable first:\n\n ```javascript\n // Fails at transpile time:\n return { v: double(n) + 1 }\n\n // Do this instead:\n const d = double(n)\n return { v: d + 1 }\n ```\n\nHelper bodies are compiled once and dispatched by name, so calling a helper many\ntimes (or in a loop) doesn't bloat the agent's AST.\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**Footgun-free equality.** `==` and `!=` are footgun-free `===` (matching TJS) — no type coercion, but NOT structural. They unwrap boxed primitives and treat `null`/`undefined` (and `NaN`) as equal; distinct objects/arrays are distinct. Use `===`/`!==` for strict identity:\n\n```javascript\n'1' == 1 // false (no coercion, unlike JS)\nnull == undefined // true (nullish equality)\n[1, 2] == [1, 2] // false (distinct objects — NOT structural)\n{ a: 1 } == { a: 1 } // false (distinct objects)\n[1, 2] === [1, 2] // false (strict identity)\n```\n\nStructural (deep) comparison is an explicit operation, never `==` — consistent with TJS, where it's the `Is`/`IsNot` function.\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"
721
721
  },
722
722
  {
723
723
  "title": "tjs-lang Technical Context",
@@ -1289,7 +1289,7 @@
1289
1289
  "title": "CLAUDE.md",
1290
1290
  "filename": "CLAUDE.md",
1291
1291
  "path": "CLAUDE.md",
1292
- "text": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Project Overview\n\n**tjs-lang** (npm: `tjs-lang`) is a typed JavaScript platform — a language, runtime, and toolchain that transpiles TypeScript and TJS to JavaScript with runtime type validation, inline WASM, monadic errors, and safe eval. It also includes AJS, a gas-metered VM for executing untrusted agent code in any JavaScript environment.\n\n**Three pillars:**\n\n- **TJS** — TypeScript-like syntax where types are examples that survive to runtime as contracts, documentation, and tests. Transpiles TS → TJS → JS in a single fast pass.\n- **AJS** — Agent language that compiles to JSON AST for safe, sandboxed execution with fuel limits and injected capabilities. Code travels to data.\n- **Toolchain** — Compresses transpilation, linting, testing, and documentation generation into one pass. Includes inline WASM with SIMD, polymorphic dispatch, local class extensions, and a browser-based playground.\n\n> **TJS syntax is NOT TypeScript.** Full reference: [`CLAUDE-TJS-SYNTAX.md`](CLAUDE-TJS-SYNTAX.md). The single most common LLM mistake is treating `function foo(x: 'default')` as a TypeScript string-literal type. It is _not_ — the colon value is an **example**, and `'default'` widens to `string`. See the syntax doc before writing or modifying TJS source.\n\n## Common Commands\n\n```bash\n# Development\nbun run format # ESLint fix + Prettier\nbun run test:fast # Core tests (skips LLM & benchmarks)\nbun run make # Full build (clean, format, grammars, tsc, esbuild)\nbun run dev # Development server with file watcher\nbun run start # Build demo + start dev server\nbun 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\nbun run typecheck # tsc --noEmit (type check without emitting)\nbun run test:llm # LM Studio integration tests\nbun run bench # Vector search benchmarks\nbun run docs # Generate documentation\n\n# Build standalone CLI binaries\nbun run build:cli # Compiles tjs + tjsx to dist/\n\n# Compatibility testing — see scripts/compat-*.ts (zod, effect, radash, superstruct, ts-pattern, kysely)\n\n# Deployment (Firebase)\nbun run deploy # Build demo + deploy functions + hosting\nbun run deploy:hosting # Hosting only (serves from .demo/)\nbun run functions:deploy # Cloud functions only\nbun 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 (~3024 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 (~3024 lines, security-critical)\n- `src/vm/vm.ts` - AgentVM class (~247 lines)\n- `src/vm/atoms/batteries.ts` - Battery atoms (vector search, LLM, store operations)\n- `src/builder.ts` - TypedBuilder fluent API (~757 lines / ~19KB)\n- `src/lang/parser.ts` - TJS parser with colon shorthand, unsafe markers, return type extraction\n- `src/lang/parser-transforms.ts` - Type, Generic, and FunctionPredicate block/function form transforms\n- `src/lang/emitters/ast.ts` - Emits Agent99 AST from parsed source\n- `src/lang/emitters/js.ts` - Emits JavaScript with `__tjs` metadata\n- `src/lang/emitters/from-ts.ts` - TypeScript to TJS/JS transpiler with class metadata extraction\n- `src/lang/emitters/dts.ts` - .d.ts declaration file generator from TJS transpilation results\n- `src/lang/inference.ts` - Type inference from example values\n- `src/lang/json-schema.ts` - JSON Schema generation from TypeDescriptors and example values\n- `src/lang/linter.ts` - Static analysis (unused vars, unreachable code, no-explicit-new)\n- `src/lang/predicate.ts` - Predicate-safety verifier: certifies a cluster of pure, synchronous, composable predicates (reads the atom `effects` tag via `effectfulFromAtoms`); verified predicates compile to native JS. Also `suggest()` — mines a cluster for autocomplete completions (keyword sets → `value`s, `startsWith` guards → open-ended `stub`s like `var(--`; mined values run through the compiled predicate so they're guaranteed valid). Exported from `tjs-lang/lang`. See `experiments/predicates/` (CSS torture set + perf + suggest demo)\n- `src/lang/predicate-schema.ts` - Predicate-aware JSON-Schema: the `$predicate` keyword (computational types). `compilePredicateSchema`/`validatePredicateSchema` — structure for naive validators, `$predicate` for aware ones (progressive enhancement). The serializable-into-JSON-Schema endgame; exported from `tjs-lang/lang`\n- `src/lang/runtime.ts` - TJS runtime (monadic errors, type checking, wrapClass)\n- `src/lang/wasm.ts` - WASM compiler (opcodes, disassembler, bytecode generation; multi-function module composition; wasm-to-wasm `call <index>` resolution)\n- `src/lang/emitters/js-wasm.ts` - JS bootstrap emitter for compiled wasm modules (one `WebAssembly.compile` per file, name→export-index table, type-aware wrappers)\n- `src/lang/module-loader.ts` - Transpile-time `.tjs`/`.ts`/`.js` module loader (Phase 0.75); used by cross-file `wasm function` composition\n- `src/linalg/` - `tjs-lang/linalg` stdlib subpath (f32x4 SIMD vector kernels)\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 (30 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, {\n fuel, capabilities, timeoutMs, trace,\n costOverrides: { atomOp: 5 }, // per-atom fuel cost override\n timeoutOverrides: { atomOp: 60_000 }, // per-atom wall-clock override (ms; 0 disables)\n})\n\n// Builder\nAgent.take(schema).varSet(...).httpFetch(...).return(schema)\nvm.Agent // Builder with custom atoms included\n\n// JSON Schema\nType('user', { name: '', age: 0 }).toJSONSchema() // → JSON Schema object\nType('user', { name: '', age: 0 }).strip(value) // → strip extra fields\nfunctionMetaToJSONSchema(fn.__tjs) // → { input, output } schemas\n\n// Error History (on by default, zero cost on happy path)\n__tjs.errors() // → recent MonadicErrors (ring buffer, max 64)\n__tjs.clearErrors() // → returns and clears\n__tjs.getErrorCount() // → total since last clear\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\nimport { AgentVM } from 'tjs-lang/vm' // VM only (smaller bundle)\nimport { batteryAtoms } from 'tjs-lang/batteries' // LM Studio batteries\nimport { dot, norm_sq } from 'tjs-lang/linalg' // SIMD linear-algebra kernels\n// Editor integrations: 'tjs-lang/editors/monaco', '/codemirror', '/ace'\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 'tjs-lang/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 'tjs-lang/lang'\n\nconst tjsSource = `\nfunction greet(name: ''): '' {\n return \\`Hello, \\${name}!\\`\n}\n`\n\nconst jsResult = tjs(tjsSource)\n// jsResult.code contains JavaScript with __tjs metadata for runtime validation\n```\n\n**Full Chain Example:**\n\n```typescript\nimport { fromTS } from 'tjs-lang/lang/from-ts'\nimport { tjs } from 'tjs-lang/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 (`tjs-lang/lang/from-ts`)\n- Import only what you need to keep bundle size minimal\n- Each step is independently testable (see `src/lang/codegen.test.ts`)\n- Constrained generics (`<T extends { id: number }>`) use the constraint as the example value instead of `any`\n- Generic defaults (`<T = string>`) use the default as the example value\n- Unconstrained generics (`<T>`) degrade to `any` — there's no information to use\n\n### Security Model\n\n- **Capability-based**: VM has zero IO by default; inject `fetch`, `store`, `llm` via capabilities\n- **Fuel metering**: Every atom has a cost; execution stops when fuel exhausted\n- **Timeout enforcement**: Default `fuel × 10ms`; explicit `timeoutMs` overrides\n- **Monadic errors**: Errors wrapped in `AgentError` (VM) / `MonadicError` (TJS), not thrown (prevents exception exploits). Use `isMonadicError()` to check — `isError()` is deprecated\n- **Expression sandboxing**: ExprNode AST evaluation, blocked prototype access\n\n### Expression Evaluation\n\nExpressions use AST nodes (`$expr`), not strings:\n\n```typescript\n{ $expr: 'binary', op: '+', left: {...}, right: {...} }\n{ $expr: 'ident', name: 'varName' }\n{ $expr: 'member', object: {...}, property: 'foo' }\n```\n\nEach node costs 0.01 fuel. Forbidden: function calls, `new`, `this`, `__proto__`, `constructor`.\n\n## AJS Expression Gotchas\n\nAJS expressions behave differently from JavaScript in several important ways:\n\n- **Null member access is safe by default**: `null.foo.bar` returns `undefined` silently (uses `?.` semantics internally). This differs from JavaScript which would throw `TypeError`.\n- **No computed member access with variables**: `items[i]` fails at transpile time with \"Computed member access with variables not yet supported\". Literal indices work (`items[0]`, `obj[\"key\"]`). Workaround: use `.map`/`.reduce` atoms instead.\n- **Unknown atom errors**: When an atom doesn't exist, the error is `\"Unknown Atom: <name>\"` with no listing of available atoms.\n- **TJS parameter syntax is NOT TypeScript**: `function foo(x: 'default')` means \"required param, example value 'default'\" — not a TypeScript string literal type. LLMs consistently generate `function foo(x: string)` which is wrong. The colon value is an _example_, not a _type annotation_.\n\n## Testing Strategy\n\n- Unit tests alongside source files (`*.test.ts`)\n- Integration tests in `src/use-cases/` (RAG, orchestration, malicious actors)\n- Security tests in `src/use-cases/malicious-actor.test.ts`\n- Language tests split across 18 files in `src/lang/` (lang.test.ts, features.test.ts, codegen.test.ts, parser.test.ts, from-ts.test.ts, wasm.test.ts, etc.)\n- LLM integration tests (run via full `bun test`, skipped by `SKIP_LLM_TESTS`) need a local **LM Studio** server with a chat + embedding model loaded. Setup and the hard-won gotchas (model load failures, leaked-VRAM stray `node` worker, updating runtimes, CORS, the audit-cache parallel race) are in [`docs/lm-studio-setup.md`](docs/lm-studio-setup.md).\n\nCoverage targets: 98% lines on `src/vm/runtime.ts` (security-critical), 80%+ overall.\n\n**Bug fix rule:** Always create a reproduction test case before fixing a bug.\n\n## Key Patterns\n\n### Adding a New Atom\n\n1. Define with `defineAtom(opCode, inputSchema, outputSchema, implementation, { cost, timeoutMs, docs, effects })`\n2. Add to `src/vm/atoms/` and export from `src/vm/atoms/index.ts`\n3. Add tests\n4. Run `bun 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- `effects` defaults to `'pure'`; **set `'io'` for any atom that touches `ctx.capabilities` (fetch/store/llm/agent/code), is nondeterministic (random/uuid), or has side effects (console)**. This drives predicate-safety (a predicate may only call pure atoms — see `experiments/predicates/`). Core IO atoms are tagged centrally via `EFFECTFUL_CORE_OPS` in `runtime.ts`; the invariant is guarded by `src/vm/atom-effects.test.ts`.\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 Syntax Reference\n\nFull syntax documentation is in [`CLAUDE-TJS-SYNTAX.md`](CLAUDE-TJS-SYNTAX.md). Key concepts:\n\n- **Colon shorthand**: `function foo(x: 'hello')` — colon value is an _example_, not a type. This is the most common LLM mistake.\n- **Numeric narrowing**: `3.14` = float, `42` = integer, `+0` = non-negative integer\n- **Return types**: `function add(a: 0, b: 0): 0 { ... }` (colon syntax, same as TypeScript)\n- **Safety markers**: `!` = unsafe (skip validation), `?` = safe (explicit validation)\n- **Mode defaults**: Native TJS has all modes ON by default (`TjsEquals`, `TjsClass`, `TjsDate`, `TjsNoeval`, `TjsNoVar`, `TjsStandard`). TS-originated code (`fromTS`) and AJS/VM code get modes OFF. `TjsCompat` directive explicitly disables all modes. `TjsStrict` enables all modes (useful for TS-originated code opting in).\n- **Source dialect (`dialect: 'js' | 'tjs'`)**: the public, programmatic way to set the modes-on/off default. `tjs(src, { dialect: 'js' })` preserves plain-JS semantics (modes OFF, `safety: 'none'`); `'tjs'` (or a bare string) is native TJS (modes ON). `dialect` is authoritative; otherwise inferred from the `fromTS` annotation / `vmTarget`. For file-based tooling, use the canonical extension→dialect helpers `dialectForFilename(filename)` / `sourceKindForFilename(filename)` from `tjs-lang/lang` (`.js`/`.mjs`/`.cjs` → `'js'`, `.tjs` → `'tjs'`, `.ts` → use `fromTS`). This upholds the **TJS ⊇ JS** invariant — see `PRINCIPLES.md`. (`.tjs` is a _better_ language, not just JS; choosing it is the opt-in to the modes.)\n- **Bang access**: `x!.foo` — returns MonadicError if `x` is null/undefined, otherwise bare `x.foo`. Chains propagate: `x!.foo!.bar`.\n- **Type/Generic/FunctionPredicate**: Three declaration forms for runtime type predicates\n- **`const!`**: Compile-time immutability, zero runtime cost\n- **Equality**: `==`/`!=` = structural equality by default in native TJS (via `Is`/`IsNot`), `===`/`!==` = identity\n- **Polymorphic functions**: Multiple same-name declarations merge into arity/type dispatcher\n- **`extend` blocks**: Local class extensions without prototype pollution\n- **WASM blocks**: Inline WebAssembly compiled at transpile time, with SIMD intrinsics and `wasmBuffer()` zero-copy arrays\n- **`@tjs` annotations**: `/* @tjs ... */` comments in TS files enrich TJS output\n\n#### Runtime Configuration\n\n```typescript\nimport { configure } from 'tjs-lang/lang'\n\nconfigure({ logTypeErrors: true }) // Log type errors to console\nconfigure({ throwTypeErrors: true }) // Throw instead of return (debugging)\nconfigure({ callStacks: true }) // Track call stacks in errors (~2x overhead)\nconfigure({ trackErrors: false }) // Disable error history (on by default)\n```\n\n#### Error History\n\nType errors are tracked in a ring buffer (on by default, zero cost on happy path):\n\n```typescript\n__tjs.errors() // → recent MonadicErrors (newest last, max 64)\n__tjs.clearErrors() // → returns and clears the buffer\n__tjs.getErrorCount() // → total since last clear (survives buffer wrap)\n```\n\nUse for debugging (find silent failures), testing (`clearErrors()` → run → check), and monitoring.\n\n#### Standalone JS Output\n\nEmitted `.js` files work without any runtime setup. Each file includes an inline\nminimal runtime as fallback — only the functions actually used are included (~500\nbytes for a basic validated function). If `globalThis.__tjs` exists (shared runtime),\nit's used instead.\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 (transpiled on-the-fly). It also aliases `tjs-lang` to `./src/index.ts` for local development, so `import { tjs } from 'tjs-lang'` resolves to the source tree without needing `npm link` or a published package.\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 `bun 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### Playground Examples\n\nThe playground (https://tjs-platform.web.app) shows interactive TJS and AJS examples in a navigable sidebar. Examples live as markdown files with embedded code blocks, NOT as raw `.tjs` files.\n\n**Where they live:**\n\n- TJS playground examples: `guides/examples/tjs/<slug>.md`\n- AJS playground examples: `guides/examples/ajs/<slug>.md` (assumed parallel structure)\n\n**File format:**\n\n<!-- prettier-ignore -->\n```markdown\n<!--{\"section\":\"tjs\",\"type\":\"example\",\"group\":\"basics\",\"order\":16}-->\n\n# Example Title\n\nShort intro paragraph (plain markdown).\n\n​```tjs\n/*#\n## Optional H2 — markdown rendered above the code in the playground\nExplain the concept here. Use markdown freely.\n*/\n\n// Then the actual TJS code\nfunction demo() { ... }\n\n/**\n * JSDoc-style /** ... *​/ blocks are also extracted as docs.\n * Leading ` * ` is stripped from each line; the rest renders as markdown.\n * Use this when porting TS source where JSDoc is already idiomatic.\n */\n\ntest 'a description' {\n expect(...).toBe(...)\n}\n​```\n```\n\nFrontmatter fields: `section` (`tjs`/`ajs`), `type: \"example\"`, `group` (`basics`/`advanced`/etc.), `order` (numeric, controls sidebar position). The H1 becomes the example title in the nav.\n\n**Registration:**\n\nExamples are auto-discovered by `bin/docs.js` (run via `bun run docs`), which walks the markdown tree, parses frontmatter, extracts the `tjs`/`ajs` code block, and writes the result to `demo/docs.json`. The demo loads `docs.json` at runtime — no other registration step.\n\n**After adding/editing an example:** run `bun run docs` and commit the regenerated `demo/docs.json` alongside the `.md` file. (The docs builder also runs as part of `bun run build:demo` and `bun run deploy`.)\n\n**Testing playground examples:**\n\nThe CLI (`bun src/cli/tjs.ts run`) does NOT inject the test-block `expect` harness — that's a playground-only thing. So running an extracted code block via the CLI prints \"expect is not defined\" for any `test { expect(...) }` blocks even though they pass in the playground. To verify an example:\n\n1. **Console-log behavior** (works via CLI): extract the `tjs` code block and run it.\n\n ````bash\n awk '/^```tjs$/{flag=1; next} /^```$/{flag=0} flag' \\\n guides/examples/tjs/<slug>.md > /tmp/example.tjs\n bun src/cli/tjs.ts run /tmp/example.tjs\n ````\n\n Verify the printed output matches the expected behavior shown in the example's comments.\n\n2. **Test blocks**: spin up the dev server (`bun run start`) and load the example in the playground UI to confirm tests pass under the real `expect` harness.\n\n3. **Frontmatter / registration**: after `bun run docs`, grep `demo/docs.json` for the slug to confirm it was picked up with the right `section`/`group`/`order`.\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- `PRINCIPLES.md` — **non-negotiable language invariants**: options-off TJS ⊇ JS, and TJS ⊇ AJS. A richer layer may do _more_ with the same source but must never make subset-legal code _illegal_ (e.g. un-runnable signature tests are inconclusive, not errors). Read before changing parser/transpiler acceptance or signature-test behavior; a subset violation is a bug.\n- `llms.txt` — agent-facing navigation index (ships in npm bundle); points to docs and source entry points\n- `guides/footguns.md` — JS footguns TJS fixes (boxed-primitive truthiness, `==` coercion, `typeof null`, uninitialized `let`, etc.). Demo: `examples/js-footguns-fixed.tjs`.\n- `guides/playground-imports.md` — how the playground/dev-server resolves bare imports: TFS service worker, default JSDelivr `/+esm` routing, esm.sh allowlist for peer-dep packages (React), CDN hints (`jsdelivr/`, `esmsh/`, `unpkg/`, `github/`), and full-URL passthrough.\n- `README.md` — Project intro, install, quick start\n- `DOCS-TJS.md` — TJS language guide\n- `DOCS-AJS.md` — AJS runtime guide\n- `TJS-FOR-JS.md` — TJS guide for JavaScript developers (syntax differences, gotchas)\n- `TJS-FOR-TS.md` — TJS guide for TypeScript developers (migration, interop)\n- `CONTEXT.md` — Architecture deep dive\n- `AGENTS.md` — Agent workflow instructions (session-completion checklist, push-before-done rule)\n- `TODO.md` — Open work, organized by area; move items to the **Completed** section when done\n- `PLAN.md` — Roadmap\n- `DOCS-WASM.md` — Canonical WASM reference: inline blocks, `wasm function` declarations, memory model, cross-file composition, `tjs-lang/linalg`, current limitations\n- `wasm-library-plan.md` — Cross-file WASM library design (composable `wasm function` declarations, transpile-time module composition, linalg stdlib). **Shipped in v0.8.0** — all phases (0.5, 0.75, 1, 1.5, 2, 3, 4, 5 MVP, 6) complete. See the plan for what's deferred (linalg expansion, i32/f32/v128 return types, etc.).\n- `MANIFESTO-BUILDER.md` / `MANIFESTO-ENTERPRISE.md` — Positioning docs (audience-targeted pitches)\n- `benchmarks.md` — Top-level benchmark results (separate from `guides/benchmarks.md`)\n\n### Keeping This File and `llms.txt` Current\n\nUpdate both files when you change something an agent needs to discover:\n\n- **New top-level markdown doc** → add to \"Additional Documentation\" here AND to the appropriate section of `llms.txt`.\n- **New package entry point** (subpath export in `package.json`) → add to \"Package Entry Points\" here AND to \"Package entry points\" in `llms.txt`.\n- **New CLI command or `bun run` script** → add to \"Common Commands\".\n- **Renamed or moved key source file** → update \"Key Source Files\" here AND \"Source map\" in `llms.txt`.\n- **New language mode / safety directive** → add to the TJS Syntax Reference section.\n- **New playground example** → add to `guides/examples/{tjs,ajs}/<slug>.md`, then `bun run docs` to regenerate `demo/docs.json`. See \"Playground Examples\" above.\n\nSkip stale-prone precision (exact line counts, file sizes) for new entries — they drift silently. The existing `~3024` etc. are kept current opportunistically, not on every commit.\n\n### Tracking Work\n\nWork is tracked in plain markdown — no external issue tracker. Open items live in `TODO.md` (organized by area). When you start a task, find or add the relevant entry; when you finish, check the box and (for substantial work) move it to the Completed section with a short note.\n\n### Landing the Plane (Session Completion Checklist)\n\nSee `AGENTS.md` for the canonical session-completion checklist. Hard rule: work is not complete until `git push` succeeds — never stop before pushing, never `--no-verify` to bypass hooks.\n\n### Common Gotchas\n\n- **`tjs(source)` returns an object, not a string.** It returns `{ code, types, metadata, testResults, ... }` — use `.code` for the transpiled JS string.\n- **Prettier mangles bare-expression code blocks in markdown.** Code blocks tagged ` ```js` get reformatted; bare expressions like `'5' == 5` and `[1] == 1` on consecutive lines collapse into one expression with ASI guards. Use `<!-- prettier-ignore -->` directly above the code fence to preserve hand-formatted JS examples (or tag the block as `text`/`tjs`/`ts` instead — Prettier ignores those).\n- **`tjs-lang` package alias only works inside the project** (set in `bunfig.toml`). Test scripts written in `/tmp` won't resolve `import { tjs } from 'tjs-lang/lang'` to the local source — they'll resolve to whatever's in `node_modules`. For ad-hoc experiments outside the repo, use absolute paths: `import { tjs } from '/Users/.../tjs-lang/src/lang/index'`.\n\n### Running Emitted TJS Code\n\nEmitted JS works standalone — no setup required. Each file includes an inline\nruntime fallback. If you want the shared runtime (e.g. for `isMonadicError` to\nwork across files), install it first:\n\n```typescript\nimport { installRuntime, createRuntime } from '../lang/runtime'\ninstallRuntime() // or: globalThis.__tjs = createRuntime()\n\nconst fn = new Function(result.code + '\\nreturn fnName')()\nfn('valid') // works\nfn(42) // returns MonadicError (not thrown)\n```\n"
1292
+ "text": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Project Overview\n\n**tjs-lang** (npm: `tjs-lang`) is a typed JavaScript platform — a language, runtime, and toolchain that transpiles TypeScript and TJS to JavaScript with runtime type validation, inline WASM, monadic errors, and safe eval. It also includes AJS, a gas-metered VM for executing untrusted agent code in any JavaScript environment.\n\n**Three pillars:**\n\n- **TJS** — TypeScript-like syntax where types are examples that survive to runtime as contracts, documentation, and tests. Transpiles TS → TJS → JS in a single fast pass.\n- **AJS** — Agent language that compiles to JSON AST for safe, sandboxed execution with fuel limits and injected capabilities. Code travels to data.\n- **Toolchain** — Compresses transpilation, linting, testing, and documentation generation into one pass. Includes inline WASM with SIMD, polymorphic dispatch, local class extensions, and a browser-based playground.\n\n> **TJS syntax is NOT TypeScript.** Full reference: [`CLAUDE-TJS-SYNTAX.md`](CLAUDE-TJS-SYNTAX.md). The single most common LLM mistake is treating `function foo(x: 'default')` as a TypeScript string-literal type. It is _not_ — the colon value is an **example**, and `'default'` widens to `string`. See the syntax doc before writing or modifying TJS source.\n\n## Common Commands\n\n```bash\n# Development\nbun run format # ESLint fix + Prettier\nbun run test:fast # Core tests (skips LLM & benchmarks)\nbun run make # Full build (clean, format, grammars, tsc, esbuild)\nbun run dev # Development server with file watcher\nbun run start # Build demo + start dev server\nbun 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\nbun run typecheck # tsc --noEmit (type check without emitting)\nbun run test:llm # LM Studio integration tests\nbun run bench # Vector search benchmarks\nbun run docs # Generate documentation\n\n# Build standalone CLI binaries\nbun run build:cli # Compiles tjs + tjsx to dist/\n\n# Compatibility testing — see scripts/compat-*.ts (zod, effect, radash, superstruct, ts-pattern, kysely)\n\n# Deployment (Firebase)\nbun run deploy # Build demo + deploy functions + hosting\nbun run deploy:hosting # Hosting only (serves from .demo/)\nbun run functions:deploy # Cloud functions only\nbun 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 (~3024 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 (~3024 lines, security-critical)\n- `src/vm/vm.ts` - AgentVM class (~247 lines)\n- `src/vm/atoms/batteries.ts` - Battery atoms (vector search, LLM, store operations)\n- `src/builder.ts` - TypedBuilder fluent API (~757 lines / ~19KB)\n- `src/lang/parser.ts` - TJS parser with colon shorthand, unsafe markers, return type extraction\n- `src/lang/parser-transforms.ts` - Type, Generic, and FunctionPredicate block/function form transforms\n- `src/lang/emitters/ast.ts` - Emits Agent99 AST from parsed source\n- `src/lang/emitters/js.ts` - Emits JavaScript with `__tjs` metadata\n- `src/lang/emitters/from-ts.ts` - TypeScript to TJS/JS transpiler with class metadata extraction\n- `src/lang/emitters/dts.ts` - .d.ts declaration file generator from TJS transpilation results\n- `src/lang/inference.ts` - Type inference from example values\n- `src/lang/json-schema.ts` - JSON Schema generation from TypeDescriptors and example values\n- `src/lang/linter.ts` - Static analysis (unused vars, unreachable code, no-explicit-new)\n- `src/lang/predicate.ts` - Predicate-safety verifier: certifies a cluster of pure, synchronous, composable predicates (reads the atom `effects` tag via `effectfulFromAtoms`); verified predicates compile to native JS. Also `suggest()` — mines a cluster for autocomplete completions (keyword sets → `value`s, `startsWith` guards → open-ended `stub`s like `var(--`; mined values run through the compiled predicate so they're guaranteed valid). Exported from `tjs-lang/lang`. See `experiments/predicates/` (CSS torture set + perf + suggest demo)\n- `src/lang/predicate-schema.ts` - Predicate-aware JSON-Schema: the `$predicate` keyword (computational types). `compilePredicateSchema`/`validatePredicateSchema` — structure for naive validators, `$predicate` for aware ones (progressive enhancement). The serializable-into-JSON-Schema endgame; exported from `tjs-lang/lang`\n- `src/lang/runtime.ts` - TJS runtime (monadic errors, type checking, wrapClass)\n- `src/lang/wasm.ts` - WASM compiler (opcodes, disassembler, bytecode generation; multi-function module composition; wasm-to-wasm `call <index>` resolution)\n- `src/lang/emitters/js-wasm.ts` - JS bootstrap emitter for compiled wasm modules (one `WebAssembly.compile` per file, name→export-index table, type-aware wrappers)\n- `src/lang/module-loader.ts` - Transpile-time `.tjs`/`.ts`/`.js` module loader (Phase 0.75); used by cross-file `wasm function` composition\n- `src/linalg/` - `tjs-lang/linalg` stdlib subpath (f32x4 SIMD vector kernels)\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 (30 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, {\n fuel, capabilities, timeoutMs, trace,\n costOverrides: { atomOp: 5 }, // per-atom fuel cost override\n timeoutOverrides: { atomOp: 60_000 }, // per-atom wall-clock override (ms; 0 disables)\n})\n\n// Builder\nAgent.take(schema).varSet(...).httpFetch(...).return(schema)\nvm.Agent // Builder with custom atoms included\n\n// JSON Schema\nType('user', { name: '', age: 0 }).toJSONSchema() // → JSON Schema object\nType('user', { name: '', age: 0 }).strip(value) // → strip extra fields\nfunctionMetaToJSONSchema(fn.__tjs) // → { input, output } schemas\n\n// Error History (on by default, zero cost on happy path)\n__tjs.errors() // → recent MonadicErrors (ring buffer, max 64)\n__tjs.clearErrors() // → returns and clears\n__tjs.getErrorCount() // → total since last clear\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\nimport { AgentVM } from 'tjs-lang/vm' // VM only (smaller bundle)\nimport { batteryAtoms } from 'tjs-lang/batteries' // LM Studio batteries\nimport { dot, norm_sq } from 'tjs-lang/linalg' // SIMD linear-algebra kernels\n// Editor integrations: 'tjs-lang/editors/monaco', '/codemirror', '/ace'\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 'tjs-lang/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 'tjs-lang/lang'\n\nconst tjsSource = `\nfunction greet(name: ''): '' {\n return \\`Hello, \\${name}!\\`\n}\n`\n\nconst jsResult = tjs(tjsSource)\n// jsResult.code contains JavaScript with __tjs metadata for runtime validation\n```\n\n**Full Chain Example:**\n\n```typescript\nimport { fromTS } from 'tjs-lang/lang/from-ts'\nimport { tjs } from 'tjs-lang/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 (`tjs-lang/lang/from-ts`)\n- Import only what you need to keep bundle size minimal\n- Each step is independently testable (see `src/lang/codegen.test.ts`)\n- Constrained generics (`<T extends { id: number }>`) use the constraint as the example value instead of `any`\n- Generic defaults (`<T = string>`) use the default as the example value\n- Unconstrained generics (`<T>`) degrade to `any` — there's no information to use\n\n### Security Model\n\n- **Capability-based**: VM has zero IO by default; inject `fetch`, `store`, `llm` via capabilities\n- **Fuel metering**: Every atom has a cost; execution stops when fuel exhausted\n- **Timeout enforcement**: Default `fuel × 10ms`; explicit `timeoutMs` overrides\n- **Monadic errors**: Errors wrapped in `AgentError` (VM) / `MonadicError` (TJS), not thrown (prevents exception exploits). Use `isMonadicError()` to check — `isError()` is deprecated\n- **Expression sandboxing**: ExprNode AST evaluation, blocked prototype access\n\n### Expression Evaluation\n\nExpressions use AST nodes (`$expr`), not strings:\n\n```typescript\n{ $expr: 'binary', op: '+', left: {...}, right: {...} }\n{ $expr: 'ident', name: 'varName' }\n{ $expr: 'member', object: {...}, property: 'foo' }\n```\n\nEach node costs 0.01 fuel. Forbidden: function calls, `new`, `this`, `__proto__`, `constructor`.\n\n## AJS Expression Gotchas\n\nAJS expressions behave differently from JavaScript in several important ways:\n\n- **Null member access is safe by default**: `null.foo.bar` returns `undefined` silently (uses `?.` semantics internally). This differs from JavaScript which would throw `TypeError`.\n- **No computed member access with variables**: `items[i]` fails at transpile time with \"Computed member access with variables not yet supported\". Literal indices work (`items[0]`, `obj[\"key\"]`). Workaround: use `.map`/`.reduce` atoms instead.\n- **Unknown atom errors**: When an atom doesn't exist, the error is `\"Unknown Atom: <name>\"` with no listing of available atoms.\n- **TJS parameter syntax is NOT TypeScript**: `function foo(x: 'default')` means \"required param, example value 'default'\" — not a TypeScript string literal type. LLMs consistently generate `function foo(x: string)` which is wrong. The colon value is an _example_, not a _type annotation_.\n\n## Testing Strategy\n\n- Unit tests alongside source files (`*.test.ts`)\n- Integration tests in `src/use-cases/` (RAG, orchestration, malicious actors)\n- Security tests in `src/use-cases/malicious-actor.test.ts`\n- Language tests split across 18 files in `src/lang/` (lang.test.ts, features.test.ts, codegen.test.ts, parser.test.ts, from-ts.test.ts, wasm.test.ts, etc.)\n- LLM integration tests (run via full `bun test`, skipped by `SKIP_LLM_TESTS`) need a local **LM Studio** server with a chat + embedding model loaded. Setup and the hard-won gotchas (model load failures, leaked-VRAM stray `node` worker, updating runtimes, CORS, the audit-cache parallel race) are in [`docs/lm-studio-setup.md`](docs/lm-studio-setup.md).\n\nCoverage targets: 98% lines on `src/vm/runtime.ts` (security-critical), 80%+ overall.\n\n**Bug fix rule:** Always create a reproduction test case before fixing a bug.\n\n## Key Patterns\n\n### Adding a New Atom\n\n1. Define with `defineAtom(opCode, inputSchema, outputSchema, implementation, { cost, timeoutMs, docs, effects })`\n2. Add to `src/vm/atoms/` and export from `src/vm/atoms/index.ts`\n3. Add tests\n4. Run `bun 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- `effects` defaults to `'pure'`; **set `'io'` for any atom that touches `ctx.capabilities` (fetch/store/llm/agent/code), is nondeterministic (random/uuid), or has side effects (console)**. This drives predicate-safety (a predicate may only call pure atoms — see `experiments/predicates/`). Core IO atoms are tagged centrally via `EFFECTFUL_CORE_OPS` in `runtime.ts`; the invariant is guarded by `src/vm/atom-effects.test.ts`.\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 Syntax Reference\n\nFull syntax documentation is in [`CLAUDE-TJS-SYNTAX.md`](CLAUDE-TJS-SYNTAX.md). Key concepts:\n\n- **Colon shorthand**: `function foo(x: 'hello')` — colon value is an _example_, not a type. This is the most common LLM mistake.\n- **Numeric narrowing**: `3.14` = float, `42` = integer, `+0` = non-negative integer\n- **Return types**: `function add(a: 0, b: 0): 0 { ... }` (colon syntax, same as TypeScript)\n- **Safety markers**: `!` = unsafe (skip validation), `?` = safe (explicit validation)\n- **Mode defaults**: Native TJS has all modes ON by default (`TjsEquals`, `TjsClass`, `TjsDate`, `TjsNoeval`, `TjsNoVar`, `TjsStandard`). TS-originated code (`fromTS`) and AJS/VM code get modes OFF. `TjsCompat` directive explicitly disables all modes. `TjsStrict` enables all modes (useful for TS-originated code opting in).\n- **Source dialect (`dialect: 'js' | 'tjs'`)**: the public, programmatic way to set the modes-on/off default. `tjs(src, { dialect: 'js' })` preserves plain-JS semantics (modes OFF, `safety: 'none'`); `'tjs'` (or a bare string) is native TJS (modes ON). `dialect` is authoritative; otherwise inferred from the `fromTS` annotation / `vmTarget`. For file-based tooling, use the canonical extension→dialect helpers `dialectForFilename(filename)` / `sourceKindForFilename(filename)` from `tjs-lang/lang` (`.js`/`.mjs`/`.cjs` → `'js'`, `.tjs` → `'tjs'`, `.ts` → use `fromTS`). This upholds the **TJS ⊇ JS** invariant — see `PRINCIPLES.md`. (`.tjs` is a _better_ language, not just JS; choosing it is the opt-in to the modes.)\n- **Bang access**: `x!.foo` — returns MonadicError if `x` is null/undefined, otherwise bare `x.foo`. Chains propagate: `x!.foo!.bar`.\n- **Type/Generic/FunctionPredicate**: Three declaration forms for runtime type predicates\n- **`const!`**: Compile-time immutability, zero runtime cost\n- **Equality**: `==`/`!=` in native TJS (via `Eq`/`NotEq` under `TjsEquals`) are **footgun-free `===`** — they unwrap boxed primitives (`new Boolean(false) == false`) and treat `null`/`undefined` as equal, but do **NOT** coerce types (`'5' != 5`, `'' != false`) and are **NOT structural** (distinct objects/arrays are distinct: `{a:1} != {a:1}` — a real distinction, and structural `==` would be a silent O(n) hit). For deep structural comparison use the `Is`/`IsNot` function (or a type's `.Equals` hook). `===`/`!==` = strict identity.\n- **Polymorphic functions**: Multiple same-name declarations merge into arity/type dispatcher\n- **`extend` blocks**: Local class extensions without prototype pollution\n- **WASM blocks**: Inline WebAssembly compiled at transpile time, with SIMD intrinsics and `wasmBuffer()` zero-copy arrays\n- **`@tjs` annotations**: `/* @tjs ... */` comments in TS files enrich TJS output\n\n#### Runtime Configuration\n\n```typescript\nimport { configure } from 'tjs-lang/lang'\n\nconfigure({ logTypeErrors: true }) // Log type errors to console\nconfigure({ throwTypeErrors: true }) // Throw instead of return (debugging)\nconfigure({ callStacks: true }) // Track call stacks in errors (~2x overhead)\nconfigure({ trackErrors: false }) // Disable error history (on by default)\n```\n\n#### Error History\n\nType errors are tracked in a ring buffer (on by default, zero cost on happy path):\n\n```typescript\n__tjs.errors() // → recent MonadicErrors (newest last, max 64)\n__tjs.clearErrors() // → returns and clears the buffer\n__tjs.getErrorCount() // → total since last clear (survives buffer wrap)\n```\n\nUse for debugging (find silent failures), testing (`clearErrors()` → run → check), and monitoring.\n\n#### Standalone JS Output\n\nEmitted `.js` files work without any runtime setup. Each file includes an inline\nminimal runtime as fallback — only the functions actually used are included (~500\nbytes for a basic validated function). If `globalThis.__tjs` exists (shared runtime),\nit's used instead.\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 (transpiled on-the-fly). It also aliases `tjs-lang` to `./src/index.ts` for local development, so `import { tjs } from 'tjs-lang'` resolves to the source tree without needing `npm link` or a published package.\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 `bun 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### Playground Examples\n\nThe playground (https://tjs-platform.web.app) shows interactive TJS and AJS examples in a navigable sidebar. Examples live as markdown files with embedded code blocks, NOT as raw `.tjs` files.\n\n**Where they live:**\n\n- TJS playground examples: `guides/examples/tjs/<slug>.md`\n- AJS playground examples: `guides/examples/ajs/<slug>.md` (assumed parallel structure)\n\n**File format:**\n\n<!-- prettier-ignore -->\n```markdown\n<!--{\"section\":\"tjs\",\"type\":\"example\",\"group\":\"basics\",\"order\":16}-->\n\n# Example Title\n\nShort intro paragraph (plain markdown).\n\n​```tjs\n/*#\n## Optional H2 — markdown rendered above the code in the playground\nExplain the concept here. Use markdown freely.\n*/\n\n// Then the actual TJS code\nfunction demo() { ... }\n\n/**\n * JSDoc-style /** ... *​/ blocks are also extracted as docs.\n * Leading ` * ` is stripped from each line; the rest renders as markdown.\n * Use this when porting TS source where JSDoc is already idiomatic.\n */\n\ntest 'a description' {\n expect(...).toBe(...)\n}\n​```\n```\n\nFrontmatter fields: `section` (`tjs`/`ajs`), `type: \"example\"`, `group` (`basics`/`advanced`/etc.), `order` (numeric, controls sidebar position). The H1 becomes the example title in the nav.\n\n**Registration:**\n\nExamples are auto-discovered by `bin/docs.js` (run via `bun run docs`), which walks the markdown tree, parses frontmatter, extracts the `tjs`/`ajs` code block, and writes the result to `demo/docs.json`. The demo loads `docs.json` at runtime — no other registration step.\n\n**After adding/editing an example:** run `bun run docs` and commit the regenerated `demo/docs.json` alongside the `.md` file. (The docs builder also runs as part of `bun run build:demo` and `bun run deploy`.)\n\n**Testing playground examples:**\n\nThe CLI (`bun src/cli/tjs.ts run`) does NOT inject the test-block `expect` harness — that's a playground-only thing. So running an extracted code block via the CLI prints \"expect is not defined\" for any `test { expect(...) }` blocks even though they pass in the playground. To verify an example:\n\n1. **Console-log behavior** (works via CLI): extract the `tjs` code block and run it.\n\n ````bash\n awk '/^```tjs$/{flag=1; next} /^```$/{flag=0} flag' \\\n guides/examples/tjs/<slug>.md > /tmp/example.tjs\n bun src/cli/tjs.ts run /tmp/example.tjs\n ````\n\n Verify the printed output matches the expected behavior shown in the example's comments.\n\n2. **Test blocks**: spin up the dev server (`bun run start`) and load the example in the playground UI to confirm tests pass under the real `expect` harness.\n\n3. **Frontmatter / registration**: after `bun run docs`, grep `demo/docs.json` for the slug to confirm it was picked up with the right `section`/`group`/`order`.\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- `PRINCIPLES.md` — **non-negotiable language invariants**: options-off TJS ⊇ JS, and TJS ⊇ AJS. A richer layer may do _more_ with the same source but must never make subset-legal code _illegal_ (e.g. un-runnable signature tests are inconclusive, not errors). Read before changing parser/transpiler acceptance or signature-test behavior; a subset violation is a bug.\n- `llms.txt` — agent-facing navigation index (ships in npm bundle); points to docs and source entry points\n- `guides/footguns.md` — JS footguns TJS fixes (boxed-primitive truthiness, `==` coercion, `typeof null`, uninitialized `let`, etc.). Demo: `examples/js-footguns-fixed.tjs`.\n- `guides/playground-imports.md` — how the playground/dev-server resolves bare imports: TFS service worker, default JSDelivr `/+esm` routing, esm.sh allowlist for peer-dep packages (React), CDN hints (`jsdelivr/`, `esmsh/`, `unpkg/`, `github/`), and full-URL passthrough.\n- `README.md` — Project intro, install, quick start\n- `DOCS-TJS.md` — TJS language guide\n- `DOCS-AJS.md` — AJS runtime guide\n- `TJS-FOR-JS.md` — TJS guide for JavaScript developers (syntax differences, gotchas)\n- `TJS-FOR-TS.md` — TJS guide for TypeScript developers (migration, interop)\n- `CONTEXT.md` — Architecture deep dive\n- `AGENTS.md` — Agent workflow instructions (session-completion checklist, push-before-done rule)\n- `TODO.md` — Open work, organized by area; move items to the **Completed** section when done\n- `PLAN.md` — Roadmap\n- `DOCS-WASM.md` — Canonical WASM reference: inline blocks, `wasm function` declarations, memory model, cross-file composition, `tjs-lang/linalg`, current limitations\n- `wasm-library-plan.md` — Cross-file WASM library design (composable `wasm function` declarations, transpile-time module composition, linalg stdlib). **Shipped in v0.8.0** — all phases (0.5, 0.75, 1, 1.5, 2, 3, 4, 5 MVP, 6) complete. See the plan for what's deferred (linalg expansion, i32/f32/v128 return types, etc.).\n- `MANIFESTO-BUILDER.md` / `MANIFESTO-ENTERPRISE.md` — Positioning docs (audience-targeted pitches)\n- `benchmarks.md` — Top-level benchmark results (separate from `guides/benchmarks.md`)\n\n### Keeping This File and `llms.txt` Current\n\nUpdate both files when you change something an agent needs to discover:\n\n- **New top-level markdown doc** → add to \"Additional Documentation\" here AND to the appropriate section of `llms.txt`.\n- **New package entry point** (subpath export in `package.json`) → add to \"Package Entry Points\" here AND to \"Package entry points\" in `llms.txt`.\n- **New CLI command or `bun run` script** → add to \"Common Commands\".\n- **Renamed or moved key source file** → update \"Key Source Files\" here AND \"Source map\" in `llms.txt`.\n- **New language mode / safety directive** → add to the TJS Syntax Reference section.\n- **New playground example** → add to `guides/examples/{tjs,ajs}/<slug>.md`, then `bun run docs` to regenerate `demo/docs.json`. See \"Playground Examples\" above.\n\nSkip stale-prone precision (exact line counts, file sizes) for new entries — they drift silently. The existing `~3024` etc. are kept current opportunistically, not on every commit.\n\n### Tracking Work\n\nWork is tracked in plain markdown — no external issue tracker. Open items live in `TODO.md` (organized by area). When you start a task, find or add the relevant entry; when you finish, check the box and (for substantial work) move it to the Completed section with a short note.\n\n### Landing the Plane (Session Completion Checklist)\n\nSee `AGENTS.md` for the canonical session-completion checklist. Hard rule: work is not complete until `git push` succeeds — never stop before pushing, never `--no-verify` to bypass hooks.\n\n### Common Gotchas\n\n- **`tjs(source)` returns an object, not a string.** It returns `{ code, types, metadata, testResults, ... }` — use `.code` for the transpiled JS string.\n- **Prettier mangles bare-expression code blocks in markdown.** Code blocks tagged ` ```js` get reformatted; bare expressions like `'5' == 5` and `[1] == 1` on consecutive lines collapse into one expression with ASI guards. Use `<!-- prettier-ignore -->` directly above the code fence to preserve hand-formatted JS examples (or tag the block as `text`/`tjs`/`ts` instead — Prettier ignores those).\n- **`tjs-lang` package alias only works inside the project** (set in `bunfig.toml`). Test scripts written in `/tmp` won't resolve `import { tjs } from 'tjs-lang/lang'` to the local source — they'll resolve to whatever's in `node_modules`. For ad-hoc experiments outside the repo, use absolute paths: `import { tjs } from '/Users/.../tjs-lang/src/lang/index'`.\n\n### Running Emitted TJS Code\n\nEmitted JS works standalone — no setup required. Each file includes an inline\nruntime fallback. If you want the shared runtime (e.g. for `isMonadicError` to\nwork across files), install it first:\n\n```typescript\nimport { installRuntime, createRuntime } from '../lang/runtime'\ninstallRuntime() // or: globalThis.__tjs = createRuntime()\n\nconst fn = new Function(result.code + '\\nreturn fnName')()\nfn('valid') // works\nfn(42) // returns MonadicError (not thrown)\n```\n"
1293
1293
  },
1294
1294
  {
1295
1295
  "title": "Context: Working with tosijs-schema",
@@ -1613,7 +1613,7 @@
1613
1613
  "title": "TJS Syntax Reference",
1614
1614
  "filename": "CLAUDE-TJS-SYNTAX.md",
1615
1615
  "path": "CLAUDE-TJS-SYNTAX.md",
1616
- "text": "# TJS Syntax Reference\n\nThis file is the detailed TJS syntax reference, extracted from CLAUDE.md for readability.\nSee CLAUDE.md for commands, architecture, and development patterns.\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. In native TJS, `TjsClass` is on by default, so all `class` declarations are wrapped. TS-originated code requires an explicit `TjsClass` directive. Built-in constructors (`Boolean`, `Number`, `String`, etc.) and old-style `function` + `prototype` constructors are never touched because they may have intentional dual behavior (e.g., `Boolean(0)` returns `false` but `new Boolean(0)` returns a truthy wrapper object).\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// Rest parameters — array example is the type (annotation stripped in JS output)\nfunction sum(...nums: [0]) { } // nums: array of integers\nfunction log(...args: ['', 0, true]) { } // args: array<string | integer | boolean>\n```\n\n## Return Types\n\n```typescript\n// Return type annotation (colon 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## Bang Access (`!.`)\n\nAsserted non-null member access. Returns a MonadicError if the target is null or undefined, and propagates existing MonadicErrors through chains.\n\n```typescript\nx!.foo // MonadicError if x is null/undefined, otherwise bare x.foo\nx!.foo!.bar // propagates — if x!.foo is a MonadicError, x!.foo!.bar returns it\nobj!.a!.b!.c // safe deep access, first null/error short-circuits the chain\n```\n\nUnlike optional chaining (`?.`), which silently returns `undefined`, bang access produces a trackable MonadicError on null/undefined. On other errors (e.g., accessing a property that throws), it throws as usual.\n\n## Type Declarations\n\n```typescript\n// Simple type from example\nType Name 'Alice'\n\n// Type with description and example\nType User {\n description: 'a user object'\n example: { name: '', age: 0 }\n}\n\n// Type with predicate (auto-generates type guard from example)\nType EvenNumber {\n description: 'an even number'\n example: 2\n predicate(x) { return x % 2 === 0 }\n}\n```\n\n## Generic Declarations\n\n```typescript\n// Simple generic\nGeneric Box<T> {\n description: 'a boxed value'\n predicate(x, T) {\n return typeof x === 'object' && x !== null && 'value' in x && T(x.value)\n }\n}\n\n// Generic with default type parameter\nGeneric Container<T, U = ''> {\n description: 'container with label'\n predicate(obj, T, U) {\n return T(obj.item) && U(obj.label)\n }\n}\n\n// Generic with declaration block (for .d.ts emission)\n// The declaration block contains TypeScript syntax emitted verbatim into .d.ts\n// It is stripped from runtime JS output\nGeneric BoxedProxy<T> {\n predicate(x, T) { return typeof x === 'object' && T(x.value) }\n declaration {\n value: T\n path: string\n observe(cb: (path: string) => void): void\n }\n}\n```\n\n## FunctionPredicate Declarations\n\nFirst-class function types, completing the Type/Generic/FunctionPredicate triad:\n\n```typescript\n// Block form — declare a function type shape\nFunctionPredicate Callback {\n params: { x: 0, y: 0 }\n returns: ''\n}\n\n// Function form — extract signature from existing function\nFunctionPredicate Handler(existingFn, 'description')\n\n// Return contracts:\n// : returns (standard)\n// :! assertReturns (throws on mismatch)\n// :? checkedReturns (wraps in MonadicError)\n```\n\nRuntime creates a `RuntimeType` that checks `typeof === 'function'`. The spec includes params, returns, and returnContract. In `fromTS`, TS function type aliases (`type Cb = (x: number) => void`) emit FunctionPredicate declarations automatically.\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\nNative TJS (`.tjs` files) has all modes ON by default: `TjsEquals`, `TjsClass`, `TjsDate`, `TjsNoeval`, `TjsNoVar`, `TjsStandard`. The default safety level is `inputs`.\n\nTS-originated code (from `fromTS`, detected by the `/* tjs <- */` annotation) and AJS/VM code get all modes OFF with safety `none`, matching plain JavaScript semantics.\n\n```typescript\n// Individual modes (on by default in native TJS, off by default in TS-originated/AJS)\nTjsEquals // == and != use honest equality (Eq/NotEq) — no coercion, unwraps boxed primitives\nTjsClass // Classes callable without new, explicit new is banned\nTjsDate // Date is banned, use Timestamp/LegalDate instead\nTjsNoeval // eval() and new Function() are banned\nTjsNoVar // var declarations are syntax errors — use const or let\nTjsStandard // Newlines as statement terminators (prevents ASI footguns)\nTjsSafeEval // Include Eval/SafeFunction in runtime for dynamic code (always opt-in, adds an import)\n\n// Meta-directives\nTjsStrict // Enables ALL modes (useful for TS-originated code opting in to TJS semantics)\nTjsCompat // Disables ALL modes (for gradual migration or JS interop in native TJS files)\n```\n\n`TjsSafeEval` is always opt-in regardless of file origin because it adds a runtime import.\n\nIndividual directives still work for selective enable/disable. Multiple directives can be combined. Place them at the top of the file before any code.\n\n## Compile-Time Immutability (`const!`)\n\n`const!` declares bindings whose properties cannot be mutated. Enforced at transpile time with zero runtime cost — emits as plain `const`.\n\n```typescript\nconst! config = { debug: false, port: 8080 }\nconsole.log(config.port) // OK — reads are fine\nconfig.debug = true // ERROR at transpile time\n\nconst! items = [1, 2, 3]\nitems.map(x => x * 2) // OK — non-mutating methods\nitems.push(4) // ERROR — mutating method\n```\n\nCatches: property assignment, compound assignment (`+=`), increment/decrement, `delete`, and mutating array methods (`push`, `pop`, `splice`, `shift`, `unshift`, `sort`, `reverse`, `fill`).\n\nWhen runtimes support records/tuples, `const!` can emit those instead.\n\n## Equality Operators\n\nWith `TjsEquals` (or `TjsStrict`), TJS fixes JavaScript's confusing `==` coercion without the performance cost of deep structural comparison.\n\n| Operator | Meaning | Example |\n| ----------- | -------------------------------------------- | ---------------------------------- |\n| `==` | Honest equality (no coercion, unwraps boxed) | `new String('x') == 'x'` is `true` |\n| `!=` | Honest inequality | `0 != ''` is `true` (no coercion) |\n| `===` | Identity (same reference) | `obj === obj` is `true` |\n| `!==` | Not same reference | `{a:1} !== {a:1}` is `true` |\n| `a Is b` | Deep structural equality (explicit) | `{a:1} Is {a:1}` is `true` |\n| `a IsNot b` | Deep structural inequality (explicit) | `[1,2] IsNot [2,1]` is `true` |\n\n```typescript\n// == is honest: no coercion, unwraps boxed primitives\n'foo' == 'foo' // true\nnew String('foo') == 'foo' // true (unwraps)\nnew Boolean(false) == false // true (unwraps)\nnull == undefined // true (nullish equality preserved)\n0 == '' // false (no coercion!)\nfalse == [] // false (no coercion!)\n\n// == is fast: objects/arrays use reference equality (O(1))\n{a:1} == {a:1} // false (different refs)\n[1,2] == [1,2] // false (different refs)\n\n// Is/IsNot for explicit deep structural comparison (O(n))\n{a:1} Is {a:1} // true\n[1,2,3] Is [1,2,3] // true\nnew Set([1,2]) Is new Set([2,1]) // true (Sets are order-independent)\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 `Eq()` and `!=` to `NotEq()` calls\n- **`===` and `!==`**: Always preserved as identity checks, never transformed\n- `Eq()`/`NotEq()` — fast honest equality (unwraps boxed primitives, nullish equality, reference for objects)\n- `Is()`/`IsNot()` — deep structural comparison (arrays, objects, Sets, Maps, Dates, RegExps)\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## Honest typeof\n\nWith `TjsEquals`, `typeof null` returns `'null'` instead of `'object'` (JS's oldest bug). All other typeof results are unchanged. Transforms `typeof expr` to `TypeOf(expr)`.\n\n## Honest Boolean Coercion (TjsStandard)\n\nRaw JS: `Boolean(new Boolean(false)) === true` (a boxed primitive is an Object → truthy). Same trap for `if`, `!`, `&&`, `||`, `?:`, `while`, `for`, `do/while`. The spec's `ToBoolean` operation has no override hook (`Symbol.toPrimitive` doesn't fire for boolean coercion).\n\nNative TJS rewrites every truthiness context to `__tjs.toBool(x)`, which unwraps boxed primitives before coercing. Always-on under `TjsStandard`.\n\n```typescript\nBoolean(new Boolean(false)) // false ✓\nif (new Boolean(false)) ... // does not enter ✓\n!new Boolean(false) // true ✓\nnew Boolean(false) || 'x' // 'x' ✓\nnew Boolean(false) ? 'a' : 'b' // 'b' ✓\n```\n\n`&&` / `||` rewrites preserve JS's value-returning semantics (`a && b` returns `a` when falsy, else `b`). `??` is intentionally not touched (it checks null/undefined, not truthiness). `===` / `!==` are not touched (use `Is` for structural).\n\nSee [`guides/footguns.md`](guides/footguns.md) for the broader list of JS footguns TJS fixes, with a runnable example at [`examples/js-footguns-fixed.tjs`](examples/js-footguns-fixed.tjs).\n\n## `@tjs` Annotations in TypeScript Source\n\nTypeScript files can include `/* @tjs ... */` comments that `fromTS` uses to enrich\nthe TJS output. The TS compiler ignores them as regular comments.\n\n```typescript\n/* @tjs TjsClass TjsEquals */ // Enable TJS modes in TS-originated code\n/* @tjs-skip */ // Skip this declaration entirely\n/* @tjs example: { name: 'Alice' } */ // Custom example value for Type\n/* @tjs predicate(x) { return x > 0 } */ // Custom runtime predicate\n/* @tjs declaration { value: T } */ // Declaration block for Generic .d.ts\n```\n\nMode directives (`TjsClass`, `TjsEquals`, etc.) are emitted at the top of the `.tjs`\noutput. These are mainly useful for TS-originated code (where modes are off by\ndefault) to opt in to TJS features like `private → #` conversion (`TjsClass`).\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 (`TjsClass` is on by default in native TJS):\n\n```typescript\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"
1616
+ "text": "# TJS Syntax Reference\n\nThis file is the detailed TJS syntax reference, extracted from CLAUDE.md for readability.\nSee CLAUDE.md for commands, architecture, and development patterns.\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. In native TJS, `TjsClass` is on by default, so all `class` declarations are wrapped. TS-originated code requires an explicit `TjsClass` directive. Built-in constructors (`Boolean`, `Number`, `String`, etc.) and old-style `function` + `prototype` constructors are never touched because they may have intentional dual behavior (e.g., `Boolean(0)` returns `false` but `new Boolean(0)` returns a truthy wrapper object).\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// Rest parameters — array example is the type (annotation stripped in JS output)\nfunction sum(...nums: [0]) { } // nums: array of integers\nfunction log(...args: ['', 0, true]) { } // args: array<string | integer | boolean>\n```\n\n## Return Types\n\n```typescript\n// Return type annotation (colon 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## Bang Access (`!.`)\n\nAsserted non-null member access. Returns a MonadicError if the target is null or undefined, and propagates existing MonadicErrors through chains.\n\n```typescript\nx!.foo // MonadicError if x is null/undefined, otherwise bare x.foo\nx!.foo!.bar // propagates — if x!.foo is a MonadicError, x!.foo!.bar returns it\nobj!.a!.b!.c // safe deep access, first null/error short-circuits the chain\n```\n\nUnlike optional chaining (`?.`), which silently returns `undefined`, bang access produces a trackable MonadicError on null/undefined. On other errors (e.g., accessing a property that throws), it throws as usual.\n\n## Type Declarations\n\n```typescript\n// Simple type from example\nType Name 'Alice'\n\n// Type with description and example\nType User {\n description: 'a user object'\n example: { name: '', age: 0 }\n}\n\n// Type with predicate (auto-generates type guard from example)\nType EvenNumber {\n description: 'an even number'\n example: 2\n predicate(x) { return x % 2 === 0 }\n}\n```\n\n## Generic Declarations\n\n```typescript\n// Simple generic\nGeneric Box<T> {\n description: 'a boxed value'\n predicate(x, T) {\n return typeof x === 'object' && x !== null && 'value' in x && T(x.value)\n }\n}\n\n// Generic with default type parameter\nGeneric Container<T, U = ''> {\n description: 'container with label'\n predicate(obj, T, U) {\n return T(obj.item) && U(obj.label)\n }\n}\n\n// Generic with declaration block (for .d.ts emission)\n// The declaration block contains TypeScript syntax emitted verbatim into .d.ts\n// It is stripped from runtime JS output\nGeneric BoxedProxy<T> {\n predicate(x, T) { return typeof x === 'object' && T(x.value) }\n declaration {\n value: T\n path: string\n observe(cb: (path: string) => void): void\n }\n}\n```\n\n## FunctionPredicate Declarations\n\nFirst-class function types, completing the Type/Generic/FunctionPredicate triad:\n\n```typescript\n// Block form — declare a function type shape\nFunctionPredicate Callback {\n params: { x: 0, y: 0 }\n returns: ''\n}\n\n// Function form — extract signature from existing function\nFunctionPredicate Handler(existingFn, 'description')\n\n// Return contracts:\n// : returns (standard)\n// :! assertReturns (throws on mismatch)\n// :? checkedReturns (wraps in MonadicError)\n```\n\nRuntime creates a `RuntimeType` that checks `typeof === 'function'`. The spec includes params, returns, and returnContract. In `fromTS`, TS function type aliases (`type Cb = (x: number) => void`) emit FunctionPredicate declarations automatically.\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\nNative TJS (`.tjs` files) has all modes ON by default: `TjsEquals`, `TjsClass`, `TjsDate`, `TjsNoeval`, `TjsNoVar`, `TjsStandard`. The default safety level is `inputs`.\n\nTS-originated code (from `fromTS`, detected by the `/* tjs <- */` annotation) and AJS/VM code get all modes OFF with safety `none`, matching plain JavaScript semantics.\n\n```typescript\n// Individual modes (on by default in native TJS, off by default in TS-originated/AJS)\nTjsEquals // == and != use honest equality (Eq/NotEq) — no coercion, unwraps boxed primitives\nTjsClass // Classes callable without new, explicit new is banned\nTjsDate // Date is banned, use Timestamp/LegalDate instead\nTjsNoeval // eval() and new Function() are banned\nTjsNoVar // var declarations are syntax errors — use const or let\nTjsStandard // Newlines as statement terminators (prevents ASI footguns)\nTjsSafeEval // Include Eval/SafeFunction in runtime for dynamic code (always opt-in, adds an import)\n\n// Meta-directives\nTjsStrict // Enables ALL modes (useful for TS-originated code opting in to TJS semantics)\nTjsCompat // Disables ALL modes (for gradual migration or JS interop in native TJS files)\n```\n\n`TjsSafeEval` is always opt-in regardless of file origin because it adds a runtime import.\n\nIndividual directives still work for selective enable/disable. Multiple directives can be combined. Place them at the top of the file before any code.\n\n## Compile-Time Immutability (`const!`)\n\n`const!` declares bindings whose properties cannot be mutated. Enforced at transpile time with zero runtime cost — emits as plain `const`.\n\n```typescript\nconst! config = { debug: false, port: 8080 }\nconsole.log(config.port) // OK — reads are fine\nconfig.debug = true // ERROR at transpile time\n\nconst! items = [1, 2, 3]\nitems.map(x => x * 2) // OK — non-mutating methods\nitems.push(4) // ERROR — mutating method\n```\n\nCatches: property assignment, compound assignment (`+=`), increment/decrement, `delete`, and mutating array methods (`push`, `pop`, `splice`, `shift`, `unshift`, `sort`, `reverse`, `fill`).\n\nWhen runtimes support records/tuples, `const!` can emit those instead.\n\n## Equality Operators\n\nWith `TjsEquals` (or `TjsStrict`), TJS fixes JavaScript's confusing `==` coercion without the performance cost of deep structural comparison.\n\n| Operator | Meaning | Example |\n| ----------- | -------------------------------------------- | ---------------------------------- |\n| `==` | Honest equality (no coercion, unwraps boxed) | `new String('x') == 'x'` is `true` |\n| `!=` | Honest inequality | `0 != ''` is `true` (no coercion) |\n| `===` | Identity (same reference) | `obj === obj` is `true` |\n| `!==` | Not same reference | `{a:1} !== {a:1}` is `true` |\n| `a Is b` | Deep structural equality (explicit) | `{a:1} Is {a:1}` is `true` |\n| `a IsNot b` | Deep structural inequality (explicit) | `[1,2] IsNot [2,1]` is `true` |\n\n```typescript\n// == is honest: no coercion, unwraps boxed primitives\n'foo' == 'foo' // true\nnew String('foo') == 'foo' // true (unwraps)\nnew Boolean(false) == false // true (unwraps)\nnull == undefined // true (nullish equality preserved)\n0 == '' // false (no coercion!)\nfalse == [] // false (no coercion!)\n\n// == is fast: objects/arrays use reference equality (O(1))\n{a:1} == {a:1} // false (different refs)\n[1,2] == [1,2] // false (different refs)\n\n// Is/IsNot for explicit deep structural comparison (O(n))\n{a:1} Is {a:1} // true\n[1,2,3] Is [1,2,3] // true\nnew Set([1,2]) Is new Set([2,1]) // true (Sets are order-independent)\n```\n\n**Implementation Notes:**\n\n- **AJS (VM)**: The VM's expression evaluator (`src/vm/runtime.ts`) uses footgun-free `eqValue()` for `==`/`!=` — same semantics as TJS `Eq` (NOT structural). (Earlier the VM did deep structural comparison here; that early divergence was removed so AJS `==` matches TJS `==`.)\n- **TJS (browser/Node)**: Source transformation converts `==` to `Eq()` and `!=` to `NotEq()` calls\n- **`===` and `!==`**: Always preserved as identity checks, never transformed\n- `Eq()`/`NotEq()` — fast honest equality (unwraps boxed primitives, nullish equality, reference for objects)\n- `Is()`/`IsNot()` — deep structural comparison (arrays, objects, Sets, Maps, Dates, RegExps)\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## Honest typeof\n\nWith `TjsEquals`, `typeof null` returns `'null'` instead of `'object'` (JS's oldest bug). All other typeof results are unchanged. Transforms `typeof expr` to `TypeOf(expr)`.\n\n## Honest Boolean Coercion (TjsStandard)\n\nRaw JS: `Boolean(new Boolean(false)) === true` (a boxed primitive is an Object → truthy). Same trap for `if`, `!`, `&&`, `||`, `?:`, `while`, `for`, `do/while`. The spec's `ToBoolean` operation has no override hook (`Symbol.toPrimitive` doesn't fire for boolean coercion).\n\nNative TJS rewrites every truthiness context to `__tjs.toBool(x)`, which unwraps boxed primitives before coercing. Always-on under `TjsStandard`.\n\n```typescript\nBoolean(new Boolean(false)) // false ✓\nif (new Boolean(false)) ... // does not enter ✓\n!new Boolean(false) // true ✓\nnew Boolean(false) || 'x' // 'x' ✓\nnew Boolean(false) ? 'a' : 'b' // 'b' ✓\n```\n\n`&&` / `||` rewrites preserve JS's value-returning semantics (`a && b` returns `a` when falsy, else `b`). `??` is intentionally not touched (it checks null/undefined, not truthiness). `===` / `!==` are not touched (use `Is` for structural).\n\nSee [`guides/footguns.md`](guides/footguns.md) for the broader list of JS footguns TJS fixes, with a runnable example at [`examples/js-footguns-fixed.tjs`](examples/js-footguns-fixed.tjs).\n\n## `@tjs` Annotations in TypeScript Source\n\nTypeScript files can include `/* @tjs ... */` comments that `fromTS` uses to enrich\nthe TJS output. The TS compiler ignores them as regular comments.\n\n```typescript\n/* @tjs TjsClass TjsEquals */ // Enable TJS modes in TS-originated code\n/* @tjs-skip */ // Skip this declaration entirely\n/* @tjs example: { name: 'Alice' } */ // Custom example value for Type\n/* @tjs predicate(x) { return x > 0 } */ // Custom runtime predicate\n/* @tjs declaration { value: T } */ // Declaration block for Generic .d.ts\n```\n\nMode directives (`TjsClass`, `TjsEquals`, etc.) are emitted at the top of the `.tjs`\noutput. These are mainly useful for TS-originated code (where modes are off by\ndefault) to opt in to TJS features like `private → #` conversion (`TjsClass`).\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 (`TjsClass` is on by default in native TJS):\n\n```typescript\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"
1617
1617
  },
1618
1618
  {
1619
1619
  "title": "tjs-lang Guides",
@@ -1625,7 +1625,7 @@
1625
1625
  "title": "TJS-Lang TODO",
1626
1626
  "filename": "TODO.md",
1627
1627
  "path": "TODO.md",
1628
- "text": "# TJS-Lang TODO\n\n## Predicate types — \"AJS is JSON-Schema's missing piece\"\n\nThe thesis (see the blog draft): JSON-Schema / TS can't express types that need\n**computation**; verified-pure, composable AJS predicates can — serializable\n(the AJS AST), safe (no IO, fuel-bounded), and compiled to native JS so they're\nfast. CSS is the torture-test proof. The engine is built and green on `main`;\nwhat remains is **delivery, measurement, and reach** — not invention.\n\n**Done (engine):**\n\n- [x] Atom `effects: 'pure' | 'io'` keystone — classified, guarded (`src/vm/atom-effects.test.ts`).\n- [x] `verifyPredicate` / `compilePredicate` — `src/lang/predicate.ts`, exported from `tjs-lang/lang`. Transitive closure check, pure-method whitelist, registry-driven effects.\n- [x] Fuel-bounded, global-shadowed native compiler (loops rejected; `__fuel()` at function entry; per-call budget; stack-overflow normalized to `PredicateFuelExhausted`). Zero measurable perf cost.\n- [x] PoC + CSS torture set + perf ballpark in `experiments/predicates/` (theme ~0.13ms, ReDoS-linear).\n\n**Remaining (delivery / north star):**\n\n- [x] **#4 Autocomplete `suggest()` companion** — `src/lang/predicate.ts` (`suggest`, exported from `tjs-lang/lang`). Mines a cluster for completions: keyword sets (array literals + `==` literals) → `value` suggestions, `startsWith(...)` guards → open-ended `stub`s (`var(--`/`calc(`). Mined values are run through the compiled entry predicate so suggestions are _guaranteed valid_, not just enumerated. Beats both TS modes: a `string` fallback offers nothing, a finite union can't offer the open-ended stubs. Prefix-filtered + limited. Tests: `src/lang/suggest.test.ts`, demo `experiments/predicates/suggest.demo.test.ts`.\n- [ ] **#5 Wire into `FunctionPredicate` / `Type`** — predicate bodies authored in this verified-safe substrate; the real consumer.\n- [x] **#6 (tjs-lang side) the `$predicate` keyword + reference evaluator** — `src/lang/predicate-schema.ts` (`compilePredicateSchema` / `validatePredicateSchema`, exported from `tjs-lang/lang`). A JSON-Schema node carries `$predicate` (predicate-cluster _source_; trivially serializable, the verifier makes it safe to run). Structural keywords (type/properties/required/items) validate for everyone; `$predicate` runs only for aware validators → progressive enhancement. Demoed on CSS (`experiments/predicates/css-schema.demo.test.ts`): same JSON, naive sees `string`, aware validates var()/calc()/!important + recursion. Gotcha noted: embed predicate source via `String.raw` (regex backslashes) — moot in real JSON.\n- [ ] **#6 (production) wire `$predicate` into tosijs-schema** — the \"incoming version\" from the blog: tosijs-schema (separate repo) evaluates `$predicate` via this engine. The tjs-lang format + evaluator are ready to consume. **The remaining north-star step.**\n- [ ] **Real CSS predicate library** — productionize beyond the PoC corpus (the tosijs CSS replacement). Recursive structure is plain `$ref` JSON-Schema; only leaf value-grammar needs `$predicate` (progressive enhancement). Schema validates the _serialized/data_ form; `Color` instances + bare-number→`px` are runtime conveniences (duck-typeable via `.toString()` where wanted).\n- [ ] **Regex-linting in the verifier** — the ReDoS path-forward the blog commits to: reject catastrophic-backtracking patterns (the one unbounded primitive fuel can't interrupt). Until then, predicates are \"no worse than `pattern`, with the bounded-tokenizer option.\"\n\n## \"Safe is fast\" — the campaign (measurement + propagation, not invention)\n\nThe architecture already makes the safe path the fast path: boundary-level checks\n(a few comparisons per _call_, not per-op), verify→native for validation, inline\nWASM/SIMD for hot loops, zero-cost happy-path errors. The strip-the-safety\ntranspiler option is the Obj-C-`IMP`-cache / Rust-`unsafe` move — it wins the perf\nargument precisely because, in practice, you leave the safety on. What's left is\nto **prove it and spread it**:\n\n- [ ] **Systematic overhead benchmark** — TJS-checked function call vs raw call across representative code (not just predicates), so \"safe is fast\" is backed by numbers, not just architecture. (Doubles as the CSS-post perf data — re-run on the _real_ tosijs theme with the full predicate set, confirming the ~0.1ms claim on real data.)\n- [ ] **Propagate verify→native** — weave it under the type system / tosijs so the capability is pervasive, not just an engine + PoC.\n- [ ] Frame the announcement around data + a real framework running it, not a promise. The blog draft is the spec: its present-tense claims (#6, the CSS lib, the real-theme number) must be true before publishing.\n\n## Playground - Introspection-driven autocomplete\n\nThe current completion provider was regex-based and useless on real examples\n(`extractVariables` matched only `const NAME =`, missing ALL destructuring — and\nthe tosijs examples bind everything via destructuring). Direction: introspection,\nChrome-console style — run the user's actual code and read real values; predicates\nfill the value-grammar leaf (a runtime string can't reveal valid CSS colors). See\nthe `introspection-autocomplete` memory.\n\n- [x] **Increment 1a — scope-aware symbol model** — `demo/src/scope-symbols.ts`\n (`collectScopeSymbols`, acorn + acorn-loose fallback, destructuring-aware,\n position-scoped, origin-tracking). Wired into `demo/src/autocomplete.ts`\n (replaces the regex extractors, with regex as never-go-blank fallback).\n `todoApp`, `h1`…`button` now complete; `h1` shows `∈ elements`. Tests:\n `demo/src/scope-symbols.test.ts` (11) + provider regression in\n `demo/autocomplete.test.ts`.\n- [x] **Increment 1b — introspection bridge** — done. (i) path-aware member\n resolution in `ajs-language.ts` (`getPathBeforeDot`/`resolvePath`/\n `getCompletionsFromPath`) so `todoApp.items.` resolves, not just `todoApp.`.\n (ii) `editors/introspect-value.ts` (serializable, self-contained, injectable)\n + async `AutocompleteConfig.getMembers` + `demo/src/introspection-bridge.ts`\n (hidden disposable iframe, reuses the run pipeline, direct-`eval` handle into\n module scope, caches last good sandbox) wired via `getMembers` in the\n playground. Tested headlessly through the real `tjsCompletionSource`; the\n iframe round-trip is browser-only (live-verify). **NEEDS LIVE VERIFICATION\n in the running playground.**\n- [ ] **Increment 2 — richer hints from real values** — function arity, `__tjs`\n metadata when present, signature help from the live function.\n- [ ] **Increment 3 — the `elementParts`/`style` CSS leaf** — once a symbol is\n known as an element factory, complete its `style` keys + per-property values\n via the predicate-schema + `suggest()` work (`src/lang/predicate.ts`).\n- [ ] **Increment 4 — completions-as-functions** — let a value/type carry a\n `suggest` hook (annotation / `__suggest`) the bridge calls; transpiles away\n under build options (dev-only, like the strip-safety pattern). The third leg\n of \"a language, not a type system.\"\n\n## Playground - Leverage tjs documentation system\n\n- [ ] tosijs-ui essentially encapsulates most of what we've done with playgrounds in a more reusable way\n- [ ] where necessary identify shortcomings in tosijs-ui's build / doc system\n- [ ] fold in anything we add / need beyond the new build / doc system\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- [ ] **Wire `ModuleLoader` into the playground's `tjs()` invocation** for transpile-time cross-file `wasm function` composition (Phase 3 of the wasm-library plan). Today the playground resolves imports at runtime via the local-module store — correct but uses the \"boundary form\" with a JS↔wasm crossing per call. With a ModuleLoader, imported `wasm function`s would be composed into the consumer's own `WebAssembly.Module` at transpile time, enabling wasm-to-wasm calls (single-digit nanosecond per-call cost). The `wasm-library-consumer.md` example flags this as a known gap. See `src/lang/module-loader.ts` (already shipped) and `wasm-library-plan.md` § Phase 3.\n\n## Language Features\n\n- [x] Honest boolean coercion (TjsStandard) — `Boolean(new Boolean(false))` and friends now return false. Source rewriter wraps every truthiness context (`if`/`while`/`for`/`do`/`!`/`&&`/`||`/`?:`, `Boolean(x)` calls) with `__tjs.toBool` which unwraps boxed primitives. Always-on under `TjsStandard`. Demo: `examples/js-footguns-fixed.tjs`. Doc: `guides/footguns.md`.\n- [ ] Intra-function type safety — bring TJS to parity with TS / good linters\n - [ ] **Tier 1 (lint):** `TjsTypedLet` mode — warn/error on `let` without type annotation. Follows the `TjsNoVar` precedent (`src/lang/parser.ts:214`). Severity gated by mode (info under `TjsStandard`, error under `TjsStrict`). ~30 lines in `src/lang/linter.ts`.\n - [ ] **Tier 2 (compile-time inference):** infer `TypeDescriptor` from initializer (already have `src/lang/inference.ts`), store per-decl in scope, walk subsequent `AssignmentExpression` nodes, warn on type-incompatible reassignment. ~200–300 lines, linter-only, no codegen changes.\n - [ ] **Tier 3 (runtime checks, long-term):** rewrite `let x = e` / `x = e` in the JS emitter to `__tjs.checkType(...)` so out-of-band assignments return MonadicError. Open design questions: closed-over `let`s, uninitialized `let x`, perf cost of per-assignment call. Defer until we see how Tier 1+2 land.\n- [ ] Audit monadic-error propagation when an error is nested inside a parameter (esp. arrays)\n - Rule: a MonadicError reaching a checked boundary should surface as ONE error, not as data containing an error (e.g. `[5, <error>, 7]`).\n - Caveat: if the function never inspects the param, no error needs to fire — propagation is on-check, not eager.\n - Partial coverage today: input-validation in emitted JS scans top-level array params for an embedded MonadicError and re-propagates it (commit `3db372d`). Other paths likely miss this — return values, deeper nesting (object fields, arrays-of-arrays), function-typed params whose callbacks return arrays containing errors, etc.\n - Investigate: where does a MonadicError survive past a boundary as data? Audit `checkType` in `src/lang/runtime.ts`, the emitted-JS validation prefix in `src/lang/emitters/js.ts`, and `checkFnShape` interaction with array returns.\n- [x] Portable Type predicates — expression-only AJS subset (no loops/async, serializable). **Done** as the predicate engine — see the \"Predicate types\" section above (`src/lang/predicate.ts`).\n- [x] Sync AJS / AJS-to-JS compilation — verified-pure predicates compile to native JS with fuel-injection points. **Done** (`compilePredicate`); see \"Predicate types\" above. (Generalizing this to arbitrary type-checked AJS beyond predicates is the future \"propagate verify→native\" item.)\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## Cross-file WASM Libraries (v0.8.0)\n\nShipped in v0.8.0 — design + history in `wasm-library-plan.md`, user-facing reference in `DOCS-WASM.md`.\n\n- [x] Module consolidation: one `WebAssembly.Module` per file with N exports (was N separate modules sharing memory)\n- [x] Transpile-time `ModuleLoader` (`src/lang/module-loader.ts`) — opt-in `.tjs`/`.ts`/`.js` resolution\n- [x] `(export)? wasm function NAME(params): RetType { body }` declaration syntax\n- [x] Purity enforcement (backend already rejects host imports) + `(!)` unsafe marker reserved\n- [x] Cross-file composition: `import { dot } from 'tjs-lang/linalg'` resolves at transpile time\n- [x] Wasm-to-wasm `call <index>` instructions (Phase 1.5) — no JS↔wasm boundary on intra-module calls\n- [x] Tree-shaking + transitive dep walking in cross-file composition (only reached functions get pulled in)\n- [x] Boundary distribution form: same source → self-contained `.js` for non-tjs consumers\n- [x] `tjs-lang/linalg` MVP — `dot`, `norm_sq`, `dot_at`, `norm_sq_at` (f32x4 SIMD)\n- [x] Canonical 3-way vector-search benchmark proves composed-WASM matches inline perf\n- [x] DOCS-WASM.md + TJS-FOR-JS.md additions + playground examples (`wasm-functions.md`, `wasm-library-author.md`, `wasm-library-consumer.md`)\n- [x] JSDoc `/** */` blocks extracted by playground docs renderer\n\n### Deferred follow-ups\n\n- [ ] Wire `ModuleLoader` into the playground's `tjs()` invocation so cross-file composition works inside the playground (today the playground resolves imports at runtime — works but uses the boundary form). See `Playground - Module Management` section above for the full note. **High priority — the canonical wasm-library demo runs at boundary-form perf in the playground until this lands.**\n- [ ] `i32` / `f32` / `v128` return types in wasm bytecode emitter (currently all returns are f64-or-void). Parsed today via `: RetType` annotation but not driving emission. Needed for top-K (i32 indices) and any wasm function that naturally returns f32 from SIMD.\n- [ ] `tjs-lang/linalg` expansion beyond MVP:\n - Vector: `norm`, `normalize`, `add`, `sub`, `scale`, `lerp` (use `out` parameter for buffer results)\n - Matrix: `matmul`, `transpose`, `identity`, `inverse_3x3`, `inverse_4x4`\n - 3D: `cross`, `quat_mul`, `mat4_from_quat`, `look_at`, `perspective`\n - Batched kernels: `cosine_search(corpus, query, count, dim) → bestIdx`, `top_k_cosine(corpus, query, count, dim, k, outIdx, outScores)` (one boundary crossing for the whole workload regardless of K)\n- [ ] gl-matrix benchmark — measure linalg vs the standard JS vector library at realistic scale\n- [ ] Production `dist/tjs-linalg.js` bundle wired into `scripts/build.ts` (currently `bun` resolves the `.tjs` source directly; production consumers need the pre-transpiled `.js`)\n- [ ] SIMD tail-loop for `n` not a multiple of 4 (today callers must pad)\n- [ ] Inline `wasm{}` blocks still subject to `==` → `Eq()` rewrite (the inline-block extractor runs after `transformEqualityToStructural`; the new `wasm function` extractor runs before it). Fix: move `extractWasmBlocks` earlier in `preprocess()` too. Pre-existing bug, not introduced by v0.8.0.\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## Production integration feedback (snowfox-app)\n\nOutstanding items from real-world VM integration. See conversation notes; ranked by hours-burned.\n\n- [ ] **`resolveValue` doesn't recurse into plain object literals** — atoms with structured input get `{$expr}` children unresolved. Need canonical `deepResolve(value, ctx)` helper.\n- [ ] **Browser-safe entry point (`tjs-lang/browser`)** — main entry pulls `node:fs/promises` (CLI/playground); breaks webpack 4 and similar bundlers.\n- [ ] **`evaluateExpr` diagnostics** — when a node has missing required fields, wrap with op name + step location instead of raw `Cannot read properties of undefined`.\n- [ ] **`typescript` not resolvable from the main entry** _(confirmed live in 0.8.0–0.8.2 via fresh `npm install` + Node import)_ — `import 'tjs-lang'` throws `Cannot find package 'typescript' imported from dist/index.js`. Cause: `src/lang/index.ts:62` statically re-exports `fromTS`, dragging the TS compiler (~4MB) into `dist/index.js`; `typescript` is only a devDependency, never declared, so Node consumers without it can't import the main entry at all (and it pulls TS at import time → also crashes Cloud Run). The lean `tjs-lang/lang` entry is fine (no fromTS). **Not a 0.8.2 regression** — pre-existing. Fix options: (a) drop the static `fromTS` re-export from the main entry so it's reached only via `tjs-lang/lang/from-ts` (matches the documented usage; makes `import 'tjs-lang'` TS-free) — cleanest, mild breaking change for top-level `fromTS` importers; (b) declare `typescript` as a `peerDependency` (+ optional meta) or `optionalDependency` so it's at least provided/signalled. Recommend (a) + lazy-load. Cut in 0.8.3.\n- [ ] **`const` inside `while` loop body** — `constSet` re-runs each iteration and throws \"Cannot reassign const variable\". Either compile-time error or per-iteration scope.\n- [ ] **AgentVM: warn on unknown atoms referenced in source** — currently fails at execution time with `Unknown Atom: foo` and no hint about `batteryAtoms` / user-defined atoms.\n\n## Language subset invariant (TJS ⊇ AJS) — see PRINCIPLES.md\n\n**Invariant:** every legal AJS source must be legal TJS source (and options-off\nTJS ⊇ JS). TJS may do _more_ with the same source but must never _reject_ it.\nEngraved in `PRINCIPLES.md`. **Now holds** — restored via the signature-test\nchanges below; guarded by `src/lang/subset-invariant.test.ts`.\n\n- [x] **Signature tests: inconclusive (not error) when un-runnable** — a signature test that can't execute (undefined references like AJS atoms `httpFetch`, or a harness that can't run the module) is now reported as `inconclusive: true` (a warning carrying the reason), never a transpile error. Only a test that _runs and mismatches_ stays a hard failure. New `inconclusive` field on `TestResult`; the strict-mode throw in `js.ts` skips inconclusive results. (Playground: surface the `inconclusive` flag distinctly — see playground TODO.)\n- [x] **Multi-function signature-test harness** — the realistic newline-separated multi-function source already executed and validates correctly; only the _same-line_ `} function` edge case failed the harness (\"Unexpected keyword 'function'\"). That failure is now inconclusive (non-fatal) rather than a transpile error, so the invariant holds either way. (Making same-line two-functions actually execute is a nice-to-have, not required.)\n- [x] **Subset guard test** — `src/lang/subset-invariant.test.ts`: representative AJS snippets (helpers with typed sigs, atom-call + return type, helper calling an atom) asserted valid as _both_ AJS and TJS; plain JS asserted valid under options-off TJS; plus controls (un-runnable → inconclusive, genuine mismatch → still throws).\n\n- [x] **Playground: surface inconclusive signature tests** — `renderTestResults` (demo/src/playground-shared.ts) now counts inconclusive separately, renders them with a distinct amber `test-inconclusive`/`test-note` style and a `—` icon (not the ✗ failure), keeps them out of the failure count and editor error markers, and turns the tests-tab indicator amber when only inconclusive. Verified with a happy-dom unit test incl. real transpiler output (`demo/src/playground-test-results.test.ts`).\n- [x] **Source dialect (`dialect: 'js' | 'tjs'`)** — public transpile option that sets the modes-on/off default explicitly. `'js'` preserves plain-JS semantics; `'tjs'` (and the bare-string default) is native TJS. Plus extension→dialect helpers `dialectForFilename`/`sourceKindForFilename` from `tjs-lang/lang`, wired into the CLI (check/types/emit/run) so a `.js` file is never silently given TJS semantics. Makes plain JS first-class for hosts (e.g. the tosijs doc system replacing sucrase). `src/lang/dialect.ts`, `src/lang/dialect.test.ts`.\n- [ ] **`transpileSource` one-call `js | ts | tjs` sugar** — deferred. A single async call wrapping the route in PRINCIPLES.md (\"Routing all three dialects\"). It must NOT live in `tjs-lang/lang`: esbuild emits single-file bundles (no code-splitting), so a `fromTS` import — even a dynamic one — gets inlined and drags the TypeScript compiler into the lean, TS-free lang bundle (this broke the `tjs-lang`/`tjs-eval`/`tjs-vm` builds when first attempted). Correct home is a TS-aware entry (the main `tjs-lang` entry already bundles fromTS + externalizes typescript), or switch the bundler to code-splitting. Until then, consumers use the explicit recipe (tjs for js/tjs, fromTS+tjs for ts).\n\n### Deferred enrichment (parity, not invariant)\n\nAJS and TJS share one parser, so AJS already _accepts_ the full signature syntax — input `(!`/`(?` and return `)-!`/`)-?`/`)->` markers, colon/return examples — they just aren't _enforced_ in AJS. Closing that is a nice-to-have, separate from the subset invariant above.\n\nTJS return-marker semantics (reference for when AJS enforcement lands): `)-!` never checks the return + **bypasses the build-time signature test**; `)-?` always checks at runtime; `)->` checks only under global `safety: 'all'`; plain `): T` captures the type + runs the build-time signature test but isn't runtime-asserted (default `safety: 'inputs'`). In AJS today every signature behaves like `)-!` on the return and gets only coarse JSON-Schema validation on inputs (and `n: 0` integer examples currently emit a no-op `{}` schema — a bug).\n\n- [ ] **Signature-as-test in AJS** — TJS already runs the signature example as a transpile-time test (`scale(x:1.5,factor:0.5):0.75` with an inconsistent body fails with \"Expected 0.75, got 1.5\", `isSignatureTest:true`). AJS runs nothing. The VM can execute the function with the example inputs directly, so AJS is well-positioned to run the same check. Opt-in at first (don't break existing untested agents).\n- [ ] **Enrich AJS entry input schema** — `parametersToJsonSchema` currently coarsens examples (`1.5`→`{type:number}`) and, worse, `n: 0` (integer example) emits `{}` — a no-op that validates nothing. JSON Schema can express `{type:integer}` and `{minimum:0}`; capture int / non-negative / number distinctions so the entry contract isn't silently dropped. (Full predicate parity with TJS `checkType` isn't reachable in JSON Schema — defer.)\n- [ ] **Validate helper params** — helper bodies currently bind args by position with no validation (only arity is checked at transpile). For least-astonishment, helpers should honor their param examples like the entry function once AJS enforcement lands.\n\n### Completed in current session\n\n- [x] **Local helper functions / `TOOL_LIBRARY` pattern** — AJS agent source may now declare multiple top-level functions: the **last** is the entry point, the rest are helpers. Implemented **option 2** (by-reference `callLocal` + per-agent helper table), chosen over inlining because it supports recursion (bounded by fuel/timeout + a `MAX_CALL_DEPTH=256` host-stack guard) and keeps the AST compact (helper bodies stored once, not duplicated per call site — matters since AJS AST travels as data). Helpers run in isolated scopes (top-level siblings, no closure over caller locals). Helper calls must live at statement level (can't be nested in expressions, like template literals); recursion is a runtime loop, not a transpile error. See `src/use-cases/local-helpers.test.ts`, `extractFunctions` (parser), `ensureHelperTransformed`/`callLocal` emit (emitters/ast.ts), `callLocal` atom (vm/runtime.ts).\n- [x] `llmPredictBattery` now has `timeoutMs: 120000` (was using default 1000ms — broken for any real LLM call) + regression test in `batteries.test.ts`.\n- [x] `typesVersions` fallback in `package.json` so legacy `moduleResolution: node` consumers can resolve `tjs-lang/vm`, `tjs-lang/lang`, `tjs-lang/batteries` etc.\n- [x] **Per-atom `timeoutMs` override** — `vm.run({ timeoutOverrides: { llmPredictBattery: 60000 } })` now works, mirroring the existing `costOverrides` pattern. Supports `number` and `(input, ctx) => number`; `0` disables the per-atom timeout. New `TimeoutOverride` type exported from `tjs-lang/vm`. See `src/use-cases/timeout-overrides.test.ts`.\n- [x] **Replaced `vm.run` default `timeoutMs = fuel × 10ms` formula** — now derived from the registered atoms as `max(per-atom timeoutMs) × 2`, floored at 60s (`AgentVM.defaultRunTimeout`). A fixed 60s default (interim) was shorter than the 120s `llmVision`/`llmPredictBattery` budgets, so vision/LLM calls timed out mid-call on slower models; the atom-derived default always covers the slowest atom (and a chained pair) and self-adjusts to custom slow atoms. Updated timeout error message to point at `timeoutMs` / `timeoutOverrides` instead of \"increase fuel\".\n- [x] **`storeVectorize` / `storeVectorAdd` get `timeoutMs: 60000`** — both make embedding network calls but had the 1s atom default, so a cold embedding model timed out. (Same class as the llmVision/llmPredict 120s budgets, missed for the store atoms.) Local ops (`storeSearch`, `storeCreateCollection`) keep the default.\n\n- [x] **Vision-detection probe used a degenerate 1×1 PNG** — real vision preprocessors reject it (gemma-4-e4b: HTTP 400 \"Cannot handle this data type: (1,1,1)\"), so a genuinely multimodal model was false-negatived as `vision: false` and vision examples skipped with \"no vision model available\". Probe now uses a valid 32×32 PNG (gemma returns 200). `src/batteries/audit.ts`.\n\n### Deferred (surfaced this session)\n\n- [ ] **Model-audit vision detection still only checks `res.ok`** (`audit.ts` checkVision) — a text model that _tolerates_ the multimodal format without erroring would false-positive. Stronger: check the _response content_ (does the model actually describe the image?). Lower priority now that the 1×1 false-negative is fixed.\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## Dependencies & Tooling\n\nFollow-ups from the ESLint 8 → 10 + typescript-eslint 5 → 8 flat-config migration:\n\n- [ ] **Decide package-lock.json policy.** Repo is bun-primary (bun.lock is canonical). The committed `package-lock.json` is stale (still references the old eslint v5 tree) and a fresh npm re-resolve balloons it by ~6k lines (full firebase-admin/google-cloud tree) and needs `--legacy-peer-deps` (pre-existing `tosijs-ui` wants `marked@^16` vs pinned `marked@9`). Either regenerate it in its own commit or remove it and let bun.lock be the sole lockfile.\n- [ ] **Clean up 22 pre-existing lint warnings** (unused vars/imports, prefer-const) — surfaced by `bun eslint src`, predate the migration (same `no-unused-vars`/`^_` config), all warnings not errors. Low-risk dead-code sweep across ~10 files.\n- [ ] **Dev-dependency vulns (none shipped to consumers).** `npm audit` shows 28, all dev/peer: Firebase SDK + admin stack, the vitest/vite/esbuild/rollup chain (vitest _critical_, genuinely used by 5 test files → needs v3 major), happy-dom, valibot, ws. Plus one eslint-transitive straggler: `flatted@3.3.3` via `file-entry-cache → flat-cache` (non-major fix).\n- [ ] **Resolve the `marked` peer conflict** — `tosijs-ui` peers on `marked@^16`, repo pins `marked@9.1.6` (bun warns + installs; npm refuses without `--legacy-peer-deps`).\n\n## Self-hosting (TS feature coverage)\n\nFour `it.skip` cases in `src/use-cases/self-hosting.test.ts` — advanced TS that the\nTS→TJS path can't yet handle. Un-skip as support lands:\n\n- [ ] Class with private fields and methods (gated on class support)\n- [ ] Builder pattern with method chaining (gated on class support)\n- [ ] Complex decorator patterns (requires `experimentalDecorators`)\n- [ ] Module augmentation (type-only, no runtime code)\n\n(Also 4 unconditional skips in `src/lang/metadata-cache.test.ts` — the transpile\nmetadata-cache feature is stubbed: store/retrieve, version-invalidation, merge,\nprune.)\n\n## Batteries / LLM tests\n\n- [ ] **Audit misclassifies models under concurrent probing.** Many test files call `LocalModels.audit()` at once, sharing one `.models.cache.json` (cwd, 24h TTL). Clearing the cache before a parallel `bun test` makes several audits probe LM Studio simultaneously and classifications come back scrambled (embedding models tagged `LLM`, an LLM tagged `Embedding`). Tests stay green only by luck of ordering. Fix: serialize the audit, harden the probes, or isolate the cache per run. Workaround documented in [`docs/lm-studio-setup.md`](docs/lm-studio-setup.md). Surfaced 2026-06-10 while getting the LLM suite green.\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\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 (native TJS has all modes ON by default; TS-originated code defaults to OFF)\n\n- [x] Invert mode system - native TJS enables all modes; TS-originated/AJS code defaults to JS semantics\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"
1628
+ "text": "# TJS-Lang TODO\n\n## Predicate types — \"AJS is JSON-Schema's missing piece\"\n\nThe thesis (see the blog draft): JSON-Schema / TS can't express types that need\n**computation**; verified-pure, composable AJS predicates can — serializable\n(the AJS AST), safe (no IO, fuel-bounded), and compiled to native JS so they're\nfast. CSS is the torture-test proof. The engine is built and green on `main`;\nwhat remains is **delivery, measurement, and reach** — not invention.\n\n**Done (engine):**\n\n- [x] Atom `effects: 'pure' | 'io'` keystone — classified, guarded (`src/vm/atom-effects.test.ts`).\n- [x] `verifyPredicate` / `compilePredicate` — `src/lang/predicate.ts`, exported from `tjs-lang/lang`. Transitive closure check, pure-method whitelist, registry-driven effects.\n- [x] Fuel-bounded, global-shadowed native compiler (loops rejected; `__fuel()` at function entry; per-call budget; stack-overflow normalized to `PredicateFuelExhausted`). Zero measurable perf cost.\n- [x] PoC + CSS torture set + perf ballpark in `experiments/predicates/` (theme ~0.13ms, ReDoS-linear).\n\n**Remaining (delivery / north star):**\n\n- [x] **#4 Autocomplete `suggest()` companion** — `src/lang/predicate.ts` (`suggest`, exported from `tjs-lang/lang`). Mines a cluster for completions: keyword sets (array literals + `==` literals) → `value` suggestions, `startsWith(...)` guards → open-ended `stub`s (`var(--`/`calc(`). Mined values are run through the compiled entry predicate so suggestions are _guaranteed valid_, not just enumerated. Beats both TS modes: a `string` fallback offers nothing, a finite union can't offer the open-ended stubs. Prefix-filtered + limited. Tests: `src/lang/suggest.test.ts`, demo `experiments/predicates/suggest.demo.test.ts`.\n- [ ] **#5 Wire into `FunctionPredicate` / `Type`** — predicate bodies authored in this verified-safe substrate; the real consumer.\n- [x] **#6 (tjs-lang side) the `$predicate` keyword + reference evaluator** — `src/lang/predicate-schema.ts` (`compilePredicateSchema` / `validatePredicateSchema`, exported from `tjs-lang/lang`). A JSON-Schema node carries `$predicate` (predicate-cluster _source_; trivially serializable, the verifier makes it safe to run). Structural keywords (type/properties/required/items) validate for everyone; `$predicate` runs only for aware validators → progressive enhancement. Demoed on CSS (`experiments/predicates/css-schema.demo.test.ts`): same JSON, naive sees `string`, aware validates var()/calc()/!important + recursion. Gotcha noted: embed predicate source via `String.raw` (regex backslashes) — moot in real JSON.\n- [ ] **#6 (production) wire `$predicate` into tosijs-schema** — the \"incoming version\" from the blog: tosijs-schema (separate repo) evaluates `$predicate` via this engine. The tjs-lang format + evaluator are ready to consume. **The remaining north-star step.**\n- [ ] **Real CSS predicate library** — productionize beyond the PoC corpus (the tosijs CSS replacement). Recursive structure is plain `$ref` JSON-Schema; only leaf value-grammar needs `$predicate` (progressive enhancement). Schema validates the _serialized/data_ form; `Color` instances + bare-number→`px` are runtime conveniences (duck-typeable via `.toString()` where wanted).\n- [ ] **Regex-linting in the verifier** — the ReDoS path-forward the blog commits to: reject catastrophic-backtracking patterns (the one unbounded primitive fuel can't interrupt). Until then, predicates are \"no worse than `pattern`, with the bounded-tokenizer option.\"\n\n## \"Safe is fast\" — the campaign (measurement + propagation, not invention)\n\nThe architecture already makes the safe path the fast path: boundary-level checks\n(a few comparisons per _call_, not per-op), verify→native for validation, inline\nWASM/SIMD for hot loops, zero-cost happy-path errors. The strip-the-safety\ntranspiler option is the Obj-C-`IMP`-cache / Rust-`unsafe` move — it wins the perf\nargument precisely because, in practice, you leave the safety on. What's left is\nto **prove it and spread it**:\n\n- [ ] **Systematic overhead benchmark** — TJS-checked function call vs raw call across representative code (not just predicates), so \"safe is fast\" is backed by numbers, not just architecture. (Doubles as the CSS-post perf data — re-run on the _real_ tosijs theme with the full predicate set, confirming the ~0.1ms claim on real data.)\n- [ ] **Propagate verify→native** — weave it under the type system / tosijs so the capability is pervasive, not just an engine + PoC.\n- [ ] Frame the announcement around data + a real framework running it, not a promise. The blog draft is the spec: its present-tense claims (#6, the CSS lib, the real-theme number) must be true before publishing.\n\n## Editors - published `.js` is stale (address sooner rather than later)\n\n- [ ] **The `tjs-lang/editors/*` subpaths ship hand-maintained `.js` files that\n are NOT built from the `.ts` sources.** `editors/codemirror/ajs-language.js`\n is from Jan 2026 (~7.5KB, an old standalone CDN-example impl) while\n `ajs-language.ts` is the real ~51KB implementation (used by the playground,\n which bundles from source). So none of the autocomplete work (scope model,\n introspection bridge, member completion) reaches npm consumers of\n `tjs-lang/editors/codemirror` — they get months-old code. Same for\n monaco/ace `.js`. **Fix: add a build step that compiles/bundles\n `editors/**/\\*.ts`→ the published`.js`(wire into`scripts/build.ts`/\n `bun run make`) so it stays current automatically.\\*\\* Not urgent for active\n use cases (user isn't consuming tjs-lang externally yet) but flagged to do\n soon. Context: tosijs-ui's live-example/doc engine now uses tjs (replacing\n sucrase) and is evolving into a portable embeddable playground/IDE — it'll\n switch from ACE to CodeMirror and will want working autocomplete, at which\n point this matters.\n\n## Playground - Introspection-driven autocomplete\n\nThe current completion provider was regex-based and useless on real examples\n(`extractVariables` matched only `const NAME =`, missing ALL destructuring — and\nthe tosijs examples bind everything via destructuring). Direction: introspection,\nChrome-console style — run the user's actual code and read real values; predicates\nfill the value-grammar leaf (a runtime string can't reveal valid CSS colors). See\nthe `introspection-autocomplete` memory.\n\n- [x] **Increment 1a — scope-aware symbol model** — `demo/src/scope-symbols.ts`\n (`collectScopeSymbols`, acorn + acorn-loose fallback, destructuring-aware,\n position-scoped, origin-tracking). Wired into `demo/src/autocomplete.ts`\n (replaces the regex extractors, with regex as never-go-blank fallback).\n `todoApp`, `h1`…`button` now complete; `h1` shows `∈ elements`. Tests:\n `demo/src/scope-symbols.test.ts` (11) + provider regression in\n `demo/autocomplete.test.ts`.\n- [x] **Increment 1b — introspection bridge** — done. (i) path-aware member\n resolution in `ajs-language.ts` (`getPathBeforeDot`/`resolvePath`/\n `getCompletionsFromPath`) so `todoApp.items.` resolves, not just `todoApp.`.\n (ii) `editors/introspect-value.ts` (serializable, self-contained, injectable) + async `AutocompleteConfig.getMembers` + `demo/src/introspection-bridge.ts`\n (hidden disposable iframe, reuses the run pipeline, direct-`eval` handle into\n module scope, caches last good sandbox) wired via `getMembers` in the\n playground. Tested headlessly through the real `tjsCompletionSource`.\n **Verified working well live** (destructured locals + `todoApp.items.push` + proxy members).\n- [ ] **Increment 2 — richer hints from real values** — function arity, `__tjs`\n metadata when present, signature help from the live function.\n- [ ] **Increment 3 — argument-type-driven completion (PINNED) — the convergence\n point.** Infer an argument's type from the callee and complete inside it:\n `h1({ style: { color: ⎸ } })` → arg0 is `ElementPart` → suggest `style` →\n CSS values. A vanilla JS function exposes only `.length`/`.name`, so this\n needs the callee to carry `__tjs` whose param is a type-as-example (itself an\n introspectable value — the bridge reads its keys; `style`'s value is a CSS\n predicate → `suggest()`). **Precondition / the pin: rewrite tosijs `style` +\n the elementCreator in TJS** so creators carry `__tjs` example-typed params —\n then it's pure introspection + `suggest()`, no special-casing, no `.d.ts`\n parsing (the \"smaller declaration files\" payoff). Works TODAY for the user's\n OWN example-typed object-param functions via `getMetadata`/`getSignatureHelp`;\n the one missing primitive is **call-context detection** (enclosing call +\n callee path + arg index + nested-key-vs-value) — unit-testable like\n `collectScopeSymbols`. The `elementParts`/`style` CSS leaf rides this via the\n predicate-schema + `suggest()` work (`src/lang/predicate.ts`).\n- [ ] **Increment 4 — completions-as-functions** — let a value/type carry a\n `suggest` hook (annotation / `__suggest`) the bridge calls; transpiles away\n under build options (dev-only, like the strip-safety pattern). The third leg\n of \"a language, not a type system.\"\n\n## Playground - Leverage tjs documentation system\n\n- [ ] tosijs-ui essentially encapsulates most of what we've done with playgrounds in a more reusable way\n- [ ] where necessary identify shortcomings in tosijs-ui's build / doc system\n- [ ] fold in anything we add / need beyond the new build / doc system\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- [ ] **Wire `ModuleLoader` into the playground's `tjs()` invocation** for transpile-time cross-file `wasm function` composition (Phase 3 of the wasm-library plan). Today the playground resolves imports at runtime via the local-module store — correct but uses the \"boundary form\" with a JS↔wasm crossing per call. With a ModuleLoader, imported `wasm function`s would be composed into the consumer's own `WebAssembly.Module` at transpile time, enabling wasm-to-wasm calls (single-digit nanosecond per-call cost). The `wasm-library-consumer.md` example flags this as a known gap. See `src/lang/module-loader.ts` (already shipped) and `wasm-library-plan.md` § Phase 3.\n\n## Language Features\n\n- [x] Honest boolean coercion (TjsStandard) — `Boolean(new Boolean(false))` and friends now return false. Source rewriter wraps every truthiness context (`if`/`while`/`for`/`do`/`!`/`&&`/`||`/`?:`, `Boolean(x)` calls) with `__tjs.toBool` which unwraps boxed primitives. Always-on under `TjsStandard`. Demo: `examples/js-footguns-fixed.tjs`. Doc: `guides/footguns.md`.\n- [ ] Intra-function type safety — bring TJS to parity with TS / good linters\n - [ ] **Tier 1 (lint):** `TjsTypedLet` mode — warn/error on `let` without type annotation. Follows the `TjsNoVar` precedent (`src/lang/parser.ts:214`). Severity gated by mode (info under `TjsStandard`, error under `TjsStrict`). ~30 lines in `src/lang/linter.ts`.\n - [ ] **Tier 2 (compile-time inference):** infer `TypeDescriptor` from initializer (already have `src/lang/inference.ts`), store per-decl in scope, walk subsequent `AssignmentExpression` nodes, warn on type-incompatible reassignment. ~200–300 lines, linter-only, no codegen changes.\n - [ ] **Tier 3 (runtime checks, long-term):** rewrite `let x = e` / `x = e` in the JS emitter to `__tjs.checkType(...)` so out-of-band assignments return MonadicError. Open design questions: closed-over `let`s, uninitialized `let x`, perf cost of per-assignment call. Defer until we see how Tier 1+2 land.\n- [ ] Audit monadic-error propagation when an error is nested inside a parameter (esp. arrays)\n - Rule: a MonadicError reaching a checked boundary should surface as ONE error, not as data containing an error (e.g. `[5, <error>, 7]`).\n - Caveat: if the function never inspects the param, no error needs to fire — propagation is on-check, not eager.\n - Partial coverage today: input-validation in emitted JS scans top-level array params for an embedded MonadicError and re-propagates it (commit `3db372d`). Other paths likely miss this — return values, deeper nesting (object fields, arrays-of-arrays), function-typed params whose callbacks return arrays containing errors, etc.\n - Investigate: where does a MonadicError survive past a boundary as data? Audit `checkType` in `src/lang/runtime.ts`, the emitted-JS validation prefix in `src/lang/emitters/js.ts`, and `checkFnShape` interaction with array returns.\n- [x] Portable Type predicates — expression-only AJS subset (no loops/async, serializable). **Done** as the predicate engine — see the \"Predicate types\" section above (`src/lang/predicate.ts`).\n- [x] Sync AJS / AJS-to-JS compilation — verified-pure predicates compile to native JS with fuel-injection points. **Done** (`compilePredicate`); see \"Predicate types\" above. (Generalizing this to arbitrary type-checked AJS beyond predicates is the future \"propagate verify→native\" item.)\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## Cross-file WASM Libraries (v0.8.0)\n\nShipped in v0.8.0 — design + history in `wasm-library-plan.md`, user-facing reference in `DOCS-WASM.md`.\n\n- [x] Module consolidation: one `WebAssembly.Module` per file with N exports (was N separate modules sharing memory)\n- [x] Transpile-time `ModuleLoader` (`src/lang/module-loader.ts`) — opt-in `.tjs`/`.ts`/`.js` resolution\n- [x] `(export)? wasm function NAME(params): RetType { body }` declaration syntax\n- [x] Purity enforcement (backend already rejects host imports) + `(!)` unsafe marker reserved\n- [x] Cross-file composition: `import { dot } from 'tjs-lang/linalg'` resolves at transpile time\n- [x] Wasm-to-wasm `call <index>` instructions (Phase 1.5) — no JS↔wasm boundary on intra-module calls\n- [x] Tree-shaking + transitive dep walking in cross-file composition (only reached functions get pulled in)\n- [x] Boundary distribution form: same source → self-contained `.js` for non-tjs consumers\n- [x] `tjs-lang/linalg` MVP — `dot`, `norm_sq`, `dot_at`, `norm_sq_at` (f32x4 SIMD)\n- [x] Canonical 3-way vector-search benchmark proves composed-WASM matches inline perf\n- [x] DOCS-WASM.md + TJS-FOR-JS.md additions + playground examples (`wasm-functions.md`, `wasm-library-author.md`, `wasm-library-consumer.md`)\n- [x] JSDoc `/** */` blocks extracted by playground docs renderer\n\n### Deferred follow-ups\n\n- [ ] Wire `ModuleLoader` into the playground's `tjs()` invocation so cross-file composition works inside the playground (today the playground resolves imports at runtime — works but uses the boundary form). See `Playground - Module Management` section above for the full note. **High priority — the canonical wasm-library demo runs at boundary-form perf in the playground until this lands.**\n- [ ] `i32` / `f32` / `v128` return types in wasm bytecode emitter (currently all returns are f64-or-void). Parsed today via `: RetType` annotation but not driving emission. Needed for top-K (i32 indices) and any wasm function that naturally returns f32 from SIMD.\n- [ ] `tjs-lang/linalg` expansion beyond MVP:\n - Vector: `norm`, `normalize`, `add`, `sub`, `scale`, `lerp` (use `out` parameter for buffer results)\n - Matrix: `matmul`, `transpose`, `identity`, `inverse_3x3`, `inverse_4x4`\n - 3D: `cross`, `quat_mul`, `mat4_from_quat`, `look_at`, `perspective`\n - Batched kernels: `cosine_search(corpus, query, count, dim) → bestIdx`, `top_k_cosine(corpus, query, count, dim, k, outIdx, outScores)` (one boundary crossing for the whole workload regardless of K)\n- [ ] gl-matrix benchmark — measure linalg vs the standard JS vector library at realistic scale\n- [ ] Production `dist/tjs-linalg.js` bundle wired into `scripts/build.ts` (currently `bun` resolves the `.tjs` source directly; production consumers need the pre-transpiled `.js`)\n- [ ] SIMD tail-loop for `n` not a multiple of 4 (today callers must pad)\n- [ ] Inline `wasm{}` blocks still subject to `==` → `Eq()` rewrite (the inline-block extractor runs after `transformEqualityToStructural`; the new `wasm function` extractor runs before it). Fix: move `extractWasmBlocks` earlier in `preprocess()` too. Pre-existing bug, not introduced by v0.8.0.\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## Production integration feedback (snowfox-app)\n\nOutstanding items from real-world VM integration. See conversation notes; ranked by hours-burned.\n\n- [ ] **`resolveValue` doesn't recurse into plain object literals** — atoms with structured input get `{$expr}` children unresolved. Need canonical `deepResolve(value, ctx)` helper.\n- [ ] **Browser-safe entry point (`tjs-lang/browser`)** — main entry pulls `node:fs/promises` (CLI/playground); breaks webpack 4 and similar bundlers.\n- [ ] **`evaluateExpr` diagnostics** — when a node has missing required fields, wrap with op name + step location instead of raw `Cannot read properties of undefined`.\n- [ ] **`typescript` not resolvable from the main entry** _(confirmed live in 0.8.0–0.8.2 via fresh `npm install` + Node import)_ — `import 'tjs-lang'` throws `Cannot find package 'typescript' imported from dist/index.js`. Cause: `src/lang/index.ts:62` statically re-exports `fromTS`, dragging the TS compiler (~4MB) into `dist/index.js`; `typescript` is only a devDependency, never declared, so Node consumers without it can't import the main entry at all (and it pulls TS at import time → also crashes Cloud Run). The lean `tjs-lang/lang` entry is fine (no fromTS). **Not a 0.8.2 regression** — pre-existing. Fix options: (a) drop the static `fromTS` re-export from the main entry so it's reached only via `tjs-lang/lang/from-ts` (matches the documented usage; makes `import 'tjs-lang'` TS-free) — cleanest, mild breaking change for top-level `fromTS` importers; (b) declare `typescript` as a `peerDependency` (+ optional meta) or `optionalDependency` so it's at least provided/signalled. Recommend (a) + lazy-load. Cut in 0.8.3.\n- [ ] **`const` inside `while` loop body** — `constSet` re-runs each iteration and throws \"Cannot reassign const variable\". Either compile-time error or per-iteration scope.\n- [ ] **AgentVM: warn on unknown atoms referenced in source** — currently fails at execution time with `Unknown Atom: foo` and no hint about `batteryAtoms` / user-defined atoms.\n\n## Language subset invariant (TJS ⊇ AJS) — see PRINCIPLES.md\n\n**Invariant:** every legal AJS source must be legal TJS source (and options-off\nTJS ⊇ JS). TJS may do _more_ with the same source but must never _reject_ it.\nEngraved in `PRINCIPLES.md`. **Now holds** — restored via the signature-test\nchanges below; guarded by `src/lang/subset-invariant.test.ts`.\n\n- [x] **Signature tests: inconclusive (not error) when un-runnable** — a signature test that can't execute (undefined references like AJS atoms `httpFetch`, or a harness that can't run the module) is now reported as `inconclusive: true` (a warning carrying the reason), never a transpile error. Only a test that _runs and mismatches_ stays a hard failure. New `inconclusive` field on `TestResult`; the strict-mode throw in `js.ts` skips inconclusive results. (Playground: surface the `inconclusive` flag distinctly — see playground TODO.)\n- [x] **Multi-function signature-test harness** — the realistic newline-separated multi-function source already executed and validates correctly; only the _same-line_ `} function` edge case failed the harness (\"Unexpected keyword 'function'\"). That failure is now inconclusive (non-fatal) rather than a transpile error, so the invariant holds either way. (Making same-line two-functions actually execute is a nice-to-have, not required.)\n- [x] **Subset guard test** — `src/lang/subset-invariant.test.ts`: representative AJS snippets (helpers with typed sigs, atom-call + return type, helper calling an atom) asserted valid as _both_ AJS and TJS; plain JS asserted valid under options-off TJS; plus controls (un-runnable → inconclusive, genuine mismatch → still throws).\n\n- [x] **Playground: surface inconclusive signature tests** — `renderTestResults` (demo/src/playground-shared.ts) now counts inconclusive separately, renders them with a distinct amber `test-inconclusive`/`test-note` style and a `—` icon (not the ✗ failure), keeps them out of the failure count and editor error markers, and turns the tests-tab indicator amber when only inconclusive. Verified with a happy-dom unit test incl. real transpiler output (`demo/src/playground-test-results.test.ts`).\n- [x] **Source dialect (`dialect: 'js' | 'tjs'`)** — public transpile option that sets the modes-on/off default explicitly. `'js'` preserves plain-JS semantics; `'tjs'` (and the bare-string default) is native TJS. Plus extension→dialect helpers `dialectForFilename`/`sourceKindForFilename` from `tjs-lang/lang`, wired into the CLI (check/types/emit/run) so a `.js` file is never silently given TJS semantics. Makes plain JS first-class for hosts (e.g. the tosijs doc system replacing sucrase). `src/lang/dialect.ts`, `src/lang/dialect.test.ts`.\n- [ ] **`transpileSource` one-call `js | ts | tjs` sugar** — deferred. A single async call wrapping the route in PRINCIPLES.md (\"Routing all three dialects\"). It must NOT live in `tjs-lang/lang`: esbuild emits single-file bundles (no code-splitting), so a `fromTS` import — even a dynamic one — gets inlined and drags the TypeScript compiler into the lean, TS-free lang bundle (this broke the `tjs-lang`/`tjs-eval`/`tjs-vm` builds when first attempted). Correct home is a TS-aware entry (the main `tjs-lang` entry already bundles fromTS + externalizes typescript), or switch the bundler to code-splitting. Until then, consumers use the explicit recipe (tjs for js/tjs, fromTS+tjs for ts).\n\n### Deferred enrichment (parity, not invariant)\n\nAJS and TJS share one parser, so AJS already _accepts_ the full signature syntax — input `(!`/`(?` and return `)-!`/`)-?`/`)->` markers, colon/return examples — they just aren't _enforced_ in AJS. Closing that is a nice-to-have, separate from the subset invariant above.\n\nTJS return-marker semantics (reference for when AJS enforcement lands): `)-!` never checks the return + **bypasses the build-time signature test**; `)-?` always checks at runtime; `)->` checks only under global `safety: 'all'`; plain `): T` captures the type + runs the build-time signature test but isn't runtime-asserted (default `safety: 'inputs'`). In AJS today every signature behaves like `)-!` on the return and gets only coarse JSON-Schema validation on inputs (and `n: 0` integer examples currently emit a no-op `{}` schema — a bug).\n\n- [ ] **Signature-as-test in AJS** — TJS already runs the signature example as a transpile-time test (`scale(x:1.5,factor:0.5):0.75` with an inconsistent body fails with \"Expected 0.75, got 1.5\", `isSignatureTest:true`). AJS runs nothing. The VM can execute the function with the example inputs directly, so AJS is well-positioned to run the same check. Opt-in at first (don't break existing untested agents).\n- [ ] **Enrich AJS entry input schema** — `parametersToJsonSchema` currently coarsens examples (`1.5`→`{type:number}`) and, worse, `n: 0` (integer example) emits `{}` — a no-op that validates nothing. JSON Schema can express `{type:integer}` and `{minimum:0}`; capture int / non-negative / number distinctions so the entry contract isn't silently dropped. (Full predicate parity with TJS `checkType` isn't reachable in JSON Schema — defer.)\n- [ ] **Validate helper params** — helper bodies currently bind args by position with no validation (only arity is checked at transpile). For least-astonishment, helpers should honor their param examples like the entry function once AJS enforcement lands.\n\n### Completed in current session\n\n- [x] **Local helper functions / `TOOL_LIBRARY` pattern** — AJS agent source may now declare multiple top-level functions: the **last** is the entry point, the rest are helpers. Implemented **option 2** (by-reference `callLocal` + per-agent helper table), chosen over inlining because it supports recursion (bounded by fuel/timeout + a `MAX_CALL_DEPTH=256` host-stack guard) and keeps the AST compact (helper bodies stored once, not duplicated per call site — matters since AJS AST travels as data). Helpers run in isolated scopes (top-level siblings, no closure over caller locals). Helper calls must live at statement level (can't be nested in expressions, like template literals); recursion is a runtime loop, not a transpile error. See `src/use-cases/local-helpers.test.ts`, `extractFunctions` (parser), `ensureHelperTransformed`/`callLocal` emit (emitters/ast.ts), `callLocal` atom (vm/runtime.ts).\n- [x] `llmPredictBattery` now has `timeoutMs: 120000` (was using default 1000ms — broken for any real LLM call) + regression test in `batteries.test.ts`.\n- [x] `typesVersions` fallback in `package.json` so legacy `moduleResolution: node` consumers can resolve `tjs-lang/vm`, `tjs-lang/lang`, `tjs-lang/batteries` etc.\n- [x] **Per-atom `timeoutMs` override** — `vm.run({ timeoutOverrides: { llmPredictBattery: 60000 } })` now works, mirroring the existing `costOverrides` pattern. Supports `number` and `(input, ctx) => number`; `0` disables the per-atom timeout. New `TimeoutOverride` type exported from `tjs-lang/vm`. See `src/use-cases/timeout-overrides.test.ts`.\n- [x] **Replaced `vm.run` default `timeoutMs = fuel × 10ms` formula** — now derived from the registered atoms as `max(per-atom timeoutMs) × 2`, floored at 60s (`AgentVM.defaultRunTimeout`). A fixed 60s default (interim) was shorter than the 120s `llmVision`/`llmPredictBattery` budgets, so vision/LLM calls timed out mid-call on slower models; the atom-derived default always covers the slowest atom (and a chained pair) and self-adjusts to custom slow atoms. Updated timeout error message to point at `timeoutMs` / `timeoutOverrides` instead of \"increase fuel\".\n- [x] **`storeVectorize` / `storeVectorAdd` get `timeoutMs: 60000`** — both make embedding network calls but had the 1s atom default, so a cold embedding model timed out. (Same class as the llmVision/llmPredict 120s budgets, missed for the store atoms.) Local ops (`storeSearch`, `storeCreateCollection`) keep the default.\n\n- [x] **Vision-detection probe used a degenerate 1×1 PNG** — real vision preprocessors reject it (gemma-4-e4b: HTTP 400 \"Cannot handle this data type: (1,1,1)\"), so a genuinely multimodal model was false-negatived as `vision: false` and vision examples skipped with \"no vision model available\". Probe now uses a valid 32×32 PNG (gemma returns 200). `src/batteries/audit.ts`.\n\n### Deferred (surfaced this session)\n\n- [ ] **Model-audit vision detection still only checks `res.ok`** (`audit.ts` checkVision) — a text model that _tolerates_ the multimodal format without erroring would false-positive. Stronger: check the _response content_ (does the model actually describe the image?). Lower priority now that the 1×1 false-negative is fixed.\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## Dependencies & Tooling\n\nFollow-ups from the ESLint 8 → 10 + typescript-eslint 5 → 8 flat-config migration:\n\n- [ ] **Decide package-lock.json policy.** Repo is bun-primary (bun.lock is canonical). The committed `package-lock.json` is stale (still references the old eslint v5 tree) and a fresh npm re-resolve balloons it by ~6k lines (full firebase-admin/google-cloud tree) and needs `--legacy-peer-deps` (pre-existing `tosijs-ui` wants `marked@^16` vs pinned `marked@9`). Either regenerate it in its own commit or remove it and let bun.lock be the sole lockfile.\n- [ ] **Clean up 22 pre-existing lint warnings** (unused vars/imports, prefer-const) — surfaced by `bun eslint src`, predate the migration (same `no-unused-vars`/`^_` config), all warnings not errors. Low-risk dead-code sweep across ~10 files.\n- [ ] **Dev-dependency vulns (none shipped to consumers).** `npm audit` shows 28, all dev/peer: Firebase SDK + admin stack, the vitest/vite/esbuild/rollup chain (vitest _critical_, genuinely used by 5 test files → needs v3 major), happy-dom, valibot, ws. Plus one eslint-transitive straggler: `flatted@3.3.3` via `file-entry-cache → flat-cache` (non-major fix).\n- [ ] **Resolve the `marked` peer conflict** — `tosijs-ui` peers on `marked@^16`, repo pins `marked@9.1.6` (bun warns + installs; npm refuses without `--legacy-peer-deps`).\n\n## Self-hosting (TS feature coverage)\n\nFour `it.skip` cases in `src/use-cases/self-hosting.test.ts` — advanced TS that the\nTS→TJS path can't yet handle. Un-skip as support lands:\n\n- [ ] Class with private fields and methods (gated on class support)\n- [ ] Builder pattern with method chaining (gated on class support)\n- [ ] Complex decorator patterns (requires `experimentalDecorators`)\n- [ ] Module augmentation (type-only, no runtime code)\n\n(Also 4 unconditional skips in `src/lang/metadata-cache.test.ts` — the transpile\nmetadata-cache feature is stubbed: store/retrieve, version-invalidation, merge,\nprune.)\n\n## Batteries / LLM tests\n\n- [ ] **Audit misclassifies models under concurrent probing.** Many test files call `LocalModels.audit()` at once, sharing one `.models.cache.json` (cwd, 24h TTL). Clearing the cache before a parallel `bun test` makes several audits probe LM Studio simultaneously and classifications come back scrambled (embedding models tagged `LLM`, an LLM tagged `Embedding`). Tests stay green only by luck of ordering. Fix: serialize the audit, harden the probes, or isolate the cache per run. Workaround documented in [`docs/lm-studio-setup.md`](docs/lm-studio-setup.md). Surfaced 2026-06-10 while getting the LLM suite green.\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\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 (native TJS has all modes ON by default; TS-originated code defaults to OFF)\n\n- [x] Invert mode system - native TJS enables all modes; TS-originated/AJS code defaults to JS semantics\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"
1629
1629
  },
1630
1630
  {
1631
1631
  "title": "TJS: Typed JavaScript",
@@ -1701,7 +1701,7 @@
1701
1701
  "group": "docs",
1702
1702
  "order": 0,
1703
1703
  "navTitle": "TJS for JS Devs",
1704
- "text": "<!--{\"section\": \"tjs-for-js\", \"group\": \"docs\", \"order\": 0, \"navTitle\": \"TJS for JS Devs\"}-->\n\n# TJS for JavaScript Programmers\n\n_Everything you already know, plus the things you always wished JavaScript had._\n\n---\n\n## The One-Sentence Pitch\n\nTJS is JavaScript with types that actually exist at runtime, equality that actually works, and errors that don't crash your program.\n\n---\n\n## What Stays the Same\n\nTJS is a superset of JavaScript. All of this works exactly as you expect:\n\n```javascript\nconst x = 42\nlet name = 'Alice'\nconst items = [1, 2, 3].map((n) => n * 2)\nconst user = { name: 'Bob', age: 30 }\nconst { name: userName, age } = user\nconst merged = { ...defaults, ...overrides }\nconst greeting = `Hello, ${name}!`\n\nfor (const item of items) {\n console.log(item)\n}\nwhile (condition) {\n /* ... */\n}\nif (x > 0) {\n /* ... */\n} else {\n /* ... */\n}\n\ntry {\n riskyThing()\n} catch (e) {\n handleError(e)\n}\n\nclass Dog {\n #name\n constructor(name) {\n this.#name = name\n }\n get name() {\n return this.#name\n }\n}\n\nimport { something } from './module.js'\nexport function myFunction() {\n /* ... */\n}\n```\n\nThe _syntax_ above is all valid TJS. But note the next section: when you transpile **native TJS**, TJS also _improves_ some runtime behaviour (structural `==`, honest truthiness, …). Those improvements are exactly the point of writing TJS — but they would change the meaning of existing JavaScript.\n\nSo TJS gates them on a **dialect**:\n\n```javascript\nimport { tjs } from 'tjs-lang/lang'\n\ntjs(jsSource) // → native TJS (improvements ON — the default)\ntjs(jsSource, { dialect: 'js' }) // → plain JS (semantics preserved, untouched)\n```\n\nFor file-based tools, the extension is the dialect — `.js`/`.mjs` ⇒ `dialect: 'js'`, `.tjs` ⇒ native. Use the canonical helper so every tool agrees:\n\n```javascript\nimport { tjs, dialectForFilename } from 'tjs-lang/lang'\ntjs(source, { dialect: dialectForFilename(filename) })\n```\n\nHandling `.ts` too (a doc system, a bundler plugin)? Route the TS path through `fromTS`, imported from its own entry so the TypeScript compiler only loads when you actually transpile TS — `tjs-lang/lang` itself stays TS-free:\n\n```javascript\nimport { tjs, sourceKindForFilename } from 'tjs-lang/lang'\nimport { fromTS } from 'tjs-lang/lang/from-ts' // TS compiler — only on the ts path\n\nconst kind = sourceKindForFilename(filename) // 'js' | 'ts' | 'tjs'\nconst code =\n kind === 'ts'\n ? tjs(fromTS(source, { emitTJS: true }).code).code\n : tjs(source, { dialect: kind }).code\n```\n\nWith `dialect: 'js'` (or a `.js` file), if you don't use any TJS features your code is just JavaScript — same behaviour, no lock-in.\n\nThis extends to advanced patterns too: **Proxies**, **WeakMap/WeakSet**, **Symbols**, **generators**, **async iterators**, **`Object.defineProperty`** — all work identically. TJS adds type checks at function boundaries; it doesn't wrap or intercept any JS runtime behavior.\n\n---\n\n## What's Different\n\n### 1. Types Are Example Values\n\nIn JavaScript, there are no types. In TJS, you annotate parameters with\nan example of a valid value:\n\n```javascript\n// JavaScript\nfunction greet(name) {\n return `Hello, ${name}!`\n}\n\n// TJS - name is required and must be a string (like 'World')\nfunction greet(name: 'World'): '' {\n return `Hello, ${name}!`\n}\n```\n\nThe `: 'World'` means \"required, must be a string, here's an example.\" The `: ''` means \"returns a string.\" These aren't abstract type annotations -- they're concrete values the system can use for testing, documentation, and validation.\n\n| You Write | TJS Infers |\n| ----------------- | ----------------------------- |\n| `name: 'Alice'` | Required string |\n| `count: 42` | Required integer |\n| `rate: 3.14` | Required number (float) |\n| `age: +20` | Required non-negative integer |\n| `flag: true` | Required boolean |\n| `items: [0]` | Required integer[] |\n| `values: [0.0]` | Required number[] |\n| `user: { n: '' }` | Required object |\n| `id: 0 \\|\\| null` | integer or null |\n\nAll of these are valid JavaScript expressions. `42` vs `42.0` vs `+42` are\nall legal JS -- TJS leverages this to distinguish integer, float, and\nnon-negative integer types at the syntax level.\n\nNote: `|| null` means the value accepts the base type _or_ `null`, but not\n`undefined`. TJS treats `null` and `undefined` as distinct types\n(`typeOf(null) === 'null'`, `typeOf(undefined) === 'undefined'`).\n\n**Optional** parameters use `=` (just like JS default values):\n\n```javascript\nfunction greet(name = 'World') {\n /* ... */\n} // optional, defaults to 'World'\nfunction retry(count = 3) {\n /* ... */\n} // optional, defaults to 3 (integer)\n```\n\n**The difference:** `: value` means required. `= value` means optional with a default. In plain JS, both would be written as `= value`.\n\n### 2. Numeric Type Narrowing\n\nTJS distinguishes three numeric types using valid JavaScript syntax:\n\n```javascript\nfunction process(\n rate: 3.14, // number (float) -- has a decimal point\n count: 42, // integer -- whole number, no decimal\n index: +0 // non-negative integer -- prefixed with +\n) { /* ... */ }\n```\n\n| You Write | Type Inferred | Validates |\n| --------- | ---------------------- | ------------------------------- |\n| `3.14` | `number` (float) | Any number |\n| `0.0` | `number` (float) | Any number |\n| `42` | `integer` | `Number.isInteger(x)` |\n| `0` | `integer` | `Number.isInteger(x)` |\n| `+20` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n| `+0` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n| `-5` | `integer` | `Number.isInteger(x)` |\n| `-3.5` | `number` (float) | Any number |\n\nThese are all valid JavaScript expressions -- TJS just reads the syntax more\ncarefully than JS does. At runtime, passing `3.14` to a parameter typed as\ninteger returns a monadic error.\n\n### 3. Return Type Annotations\n\nTJS uses `:` for return types (same as TypeScript):\n\n```javascript\n// Returns an integer\nfunction add(a: 0, b: 0): 0 {\n return a + b\n}\n\n// Returns an object with specific shape\nfunction getUser(id: 0): { name: '', age: 0 } {\n return { name: 'Alice', age: 30 }\n}\n```\n\nThe return type example doubles as an automatic test. When you write `: 0`, TJS will call `add(0, 0)` at transpile time and verify the result is a number.\n\n### 4. Equality That Works\n\nJavaScript's `==` is notoriously broken (type coercion). Its `===` doesn't do structural comparison. TJS fixes this:\n\n```javascript\n// JavaScript\n[1, 2] === [1, 2] // false (different references)\n{ a: 1 } === { a: 1 } // false (different references)\n\n// TJS (structural equality is on by default)\n[1, 2] == [1, 2] // true (same structure)\n{ a: 1 } == { a: 1 } // true (same structure)\n[1, 2] === [1, 2] // false (different references, identity check)\n```\n\nTJS redefines the operators:\n\n| Operator | JavaScript | TJS |\n| -------- | ------------------- | --------------------- |\n| `==` | Coercive equality | Structural equality |\n| `!=` | Coercive inequality | Structural inequality |\n| `===` | Strict equality | Identity (same ref) |\n| `!==` | Strict inequality | Not same reference |\n\nYou can also use the explicit forms `Is` and `IsNot`:\n\n```javascript\nuser Is expectedUser // structural deep equality\nresult IsNot errorValue // structural deep inequality\n```\n\nClasses can define custom equality:\n\n```javascript\nclass Point {\n constructor(x: 0, y: 0) { this.x = x; this.y = y }\n Equals(other) { return this.x === other.x && this.y === other.y }\n}\n\nPoint(1, 2) Is Point(1, 2) // true, uses .Equals\n```\n\nNote: structural equality does not handle circular references. Use `===`\nfor objects that might be circular, or define an `.Equals` method.\n\n### 5. Errors Are Values, Not Exceptions\n\nIn JavaScript, type errors crash your program. In TJS, they're values:\n\n```javascript\nfunction double(x: 0): 0 {\n return x * 2\n}\n\ndouble(5) // 10\ndouble('oops') // { $error: true, message: \"Expected number for 'double.x', got string\" }\n```\n\nNo `try`/`catch`. No crashes. The caller gets a clear error value they can inspect:\n\n```javascript\nconst result = double(input)\nif (result?.$error) {\n console.log(result.message) // handle gracefully\n} else {\n useResult(result)\n}\n```\n\nErrors propagate automatically through function calls. If you pass a monadic error to another TJS function, it passes through without executing:\n\n```javascript\nconst a = step1(badInput) // MonadicError\nconst b = step2(a) // skips execution, returns the same error\nconst c = step3(b) // skips again -- error flows to the surface\n```\n\nThis is sometimes called \"railway-oriented programming.\"\n\n### 6. Classes Without `new`\n\nTJS classes are callable as functions:\n\n```javascript\n// JavaScript\nconst p = new Point(10, 20)\n\n// TJS - both work, but the clean form is preferred\nconst p1 = Point(10, 20) // works\nconst p2 = new Point(10, 20) // also works (linter warns)\n```\n\nThis is cleaner and more consistent with functional style. Under the hood, TJS uses a Proxy to auto-construct when you call a class as a function.\n\n### 7. `const` for Free\n\nUppercase identifiers automatically get `const`:\n\n```javascript\n// TJS\nConfig = { debug: true, version: '1.0' }\nMaxRetries = 3\n\n// Transpiles to\nconst Config = { debug: true, version: '1.0' }\nconst MaxRetries = 3\n```\n\nLowercase identifiers behave normally.\n\n---\n\n## The Type System\n\nJavaScript has no type system. Libraries like Zod and Ajv bolt one on,\nbut they're separate from your function signatures -- a second source of\ntruth that drifts. TJS's `Type()` built-in gives you as much narrowing\nand specificity as you want, in one place, using plain functions you\nalready know how to write.\n\n### From Simple to Sophisticated\n\n```javascript\n// Simple -- infer type from an example value\nType Name 'Alice' // any string\n\n// Descriptive -- add documentation\nType User {\n description: 'a registered user'\n example: { name: '', age: 0, email: '' }\n}\n\n// Constrained -- add a predicate for narrowing\nType PositiveNumber {\n description: 'a number greater than zero'\n example: 1\n predicate(x) { return typeof x === 'number' && x > 0 }\n}\n\n// Domain-specific -- real business rules\nType USZipCode {\n description: '5-digit US zip code'\n example: '90210'\n predicate(v) { return typeof v === 'string' && /^\\d{5}$/.test(v) }\n}\n\nType Email {\n description: 'email address'\n example: 'user@example.com'\n predicate(v) { return typeof v === 'string' && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(v) }\n}\n```\n\nThe key insight: a `predicate` is just a function that returns `true` or\n`false`. If you can express a constraint as a boolean check, you can make\nit a type. No schema DSL to learn, no special syntax -- it's JavaScript\nall the way down.\n\nTypes work in function signatures:\n\n```javascript\nfunction sendWelcome(email: Email, name: Name): '' {\n return `Welcome, ${name}! Confirmation sent to ${email}.`\n}\n\nsendWelcome('alice@example.com', 'Alice') // works\nsendWelcome('not-an-email', 'Alice') // MonadicError\n```\n\nTJS also ships common types out of the box: `TString`, `TNumber`,\n`TBoolean`, `TInteger`, `TPositiveInt`, `TNonEmptyString`, `TEmail`,\n`TUrl`, `TUuid`, `Timestamp`, `LegalDate`. No imports from a validation\nlibrary needed.\n\n### Combinators\n\nCompose types from other types:\n\n```javascript\nType OptionalEmail Nullable(Email) // Email | null\nType UserIds TArray(TPositiveInt) // array of positive integers\n```\n\n### Unions and Enums\n\n```javascript\n// Union of string literals\nUnion Status 'task status' 'pending' | 'active' | 'done'\n\n// Enum with named members\nEnum Color 'CSS color' {\n Red = 'red'\n Green = 'green'\n Blue = 'blue'\n}\n\n// Discriminated union -- like tagged unions or sum types\nconst Shape = Union('kind', {\n circle: { radius: 0 },\n rectangle: { width: 0, height: 0 }\n})\n```\n\n### Generics\n\nRuntime-checkable generic types:\n\n```javascript\nGeneric Box<T> {\n description: 'a boxed value'\n predicate(x, T) {\n return typeof x === 'object' && x !== null && 'value' in x && T(x.value)\n }\n}\n\n// Instantiate with a concrete type\nconst NumberBox = Box(TNumber)\nNumberBox.check({ value: 42 }) // true\nNumberBox.check({ value: 'nope' }) // false\n```\n\n---\n\n## Runtime Metadata\n\nEvery TJS function carries its type information at runtime via `__tjs`:\n\n```javascript\nfunction createUser(input: { name: '', age: 0 }): { id: 0 } {\n return { id: 123 }\n}\n\ncreateUser.__tjs\n// {\n// params: { input: { type: { kind: 'object', shape: {...} }, required: true } },\n// returns: { type: { kind: 'object', shape: { id: { kind: 'number' } } } }\n// }\n```\n\nThis metadata enables autocomplete from live objects, automatic documentation\ngeneration, and runtime reflection -- things that require build tools and\nexternal libraries in vanilla JavaScript.\n\n---\n\n## Safety Levels\n\nYou control how much validation TJS applies:\n\n```javascript\n// Per-module (top of file)\nsafety none // Metadata only -- no runtime checks (fastest)\nsafety inputs // Validate inputs only (default)\nsafety all // Validate everything (debug mode)\n\n// Per-function\nfunction fastPath(! x: 0) { /* ... */ } // Skip validation\nfunction safePath(? x: 0) { /* ... */ } // Force validation\n```\n\nUse `!` (skip validation) only in hot loops where every microsecond counts\nand the data source is already trusted. In all other cases, the ~1.5x\noverhead of `safety inputs` is negligible compared to the bugs it catches.\n\n### Unsafe Blocks\n\nSkip validation for a hot inner loop:\n\n```javascript\nunsafe {\n for (let i = 0; i < million; i++) {\n hotFunction(data[i])\n }\n}\n```\n\n---\n\n## Inline Tests\n\nTests live next to the code they test and run at transpile time:\n\n```javascript\nfunction isPrime(n: 2): true {\n if (n < 2) return false\n for (let i = 2; i * i <= n; i++) {\n if (n % i === 0) return false\n }\n return true\n}\n\ntest 'prime detection' {\n expect(isPrime(2)).toBe(true)\n expect(isPrime(4)).toBe(false)\n expect(isPrime(17)).toBe(true)\n}\n```\n\nTests are stripped from production output. They're also generated\nautomatically from return type annotations -- `: true` means TJS will call\n`isPrime(2)` and verify it returns a boolean.\n\n---\n\n## WASM Blocks\n\nFor compute-heavy code, drop into WebAssembly:\n\n```javascript\nconst add = wasm (a: i32, b: i32): i32 {\n local.get $a\n local.get $b\n i32.add\n}\n\nadd(1, 2) // 3, runs as native WASM\n```\n\nWASM is compiled at transpile time and embedded as base64 in the output.\nNo separate `.wasm` files.\n\n### Reusable WASM Functions\n\nFor a kernel that other files can import, use a top-level `wasm function`\ndeclaration:\n\n```javascript\nexport wasm function dot(a: Float32Array, b: Float32Array, n: i32): f64 {\n // ... SIMD f32x4 implementation ...\n}\n```\n\nOther files can import it directly:\n\n```javascript\nimport { dot } from './my-lib.tjs'\n// `dot` is composed into your file's wasm module at transpile time\n// — no JS↔WASM boundary on intra-library calls\n```\n\nThe first stdlib built on this is `tjs-lang/linalg` — SIMD vector kernels\nready to use:\n\n```javascript\nimport { dot, norm_sq } from 'tjs-lang/linalg'\nconst cos = dot(a, b, n) / Math.sqrt(norm_sq(a, n) * norm_sq(b, n))\n```\n\nSee [`DOCS-WASM.md`](DOCS-WASM.md) for the full story — declaration syntax,\nthe JS-owns-memory model, cross-file composition, and the two distribution\nforms (composed vs. boundary).\n\n---\n\n## Safe Eval\n\nThe killer feature for many use cases. Run untrusted code safely:\n\n```javascript\nimport { Eval, SafeFunction } from 'tjs-lang/eval'\n\n// One-shot evaluation\nconst { result } = await Eval({\n code: 'return items.filter(x => x > threshold)',\n context: { items: [1, 5, 10, 15], threshold: 7 },\n fuel: 1000,\n})\n// result: [10, 15]\n\n// Reusable safe function\nconst transform = await SafeFunction({\n body: 'return x * multiplier',\n params: ['x'],\n fuel: 500,\n})\n\nawait transform(21) // { result: 42, fuelUsed: 8 }\n```\n\n- Gas-limited (fuel runs out = execution stops, no infinite loops)\n- Capability-based (no I/O unless you grant it)\n- No `eval()`, no CSP violations, no containers\n\n---\n\n## What You Give Up\n\nTJS is opinionated. Here's what changes:\n\n| JavaScript | TJS | Why |\n| -------------------------- | ----------------------------- | ------------------------------- |\n| `==` (type coercion) | `==` (structural equality) | Coercion is a bug factory |\n| Exceptions for type bugs | Monadic error values | Exceptions escape, values don't |\n| `new ClassName()` | `ClassName()` preferred | Cleaner, more functional |\n| No runtime types | `__tjs` metadata on functions | Types should exist at runtime |\n| `typeof null === 'object'` | `typeOf(null) === 'null'` | JS got this wrong in 1995 |\n\nNothing is taken away. `new` still works. `===` still works. You can write\nplain JavaScript in a `.tjs` file and it works. The type-related additions\nuse explicit syntax (`:` annotations, `:` return types, `Type` declarations).\nBehavioral modes like structural equality, callable classes, and honest\n`typeof` are enabled by default in native TJS files. Use `TjsCompat` at the\ntop of a file to disable all modes for gradual migration or JS interop.\n\n---\n\n## Getting Started\n\n```bash\nnpm install tjs-lang\n```\n\n### Try It in the Browser\n\nThe playground runs the full TJS compiler in your browser -- no backend needed:\n\n**[tjs-platform.web.app](https://tjs-platform.web.app)**\n\n### CLI\n\n```bash\nbun src/cli/tjs.ts check file.tjs # Parse and type check\nbun src/cli/tjs.ts run file.tjs # Transpile and execute\nbun src/cli/tjs.ts emit file.tjs # Output transpiled JS\nbun src/cli/tjs.ts test file.tjs # Run inline tests\n```\n\n### From Code\n\n```javascript\nimport { tjs } from 'tjs-lang'\n\nconst output = tjs`\n function add(a: 0, b: 0): 0 {\n return a + b\n }\n`\n```\n\n---\n\n## Runtime Traceability\n\nEvery TJS function carries its source identity in `__tjs` metadata.\nWhen validation fails, the error tells you exactly which function and\nparameter failed, in which source file:\n\n```javascript\nfunction add(a: 0, b: 0): 0 {\n return a + b\n}\n\nadd.__tjs.source // \"mymodule.tjs:3\"\nadd('oops', 1) // MonadicError { path: \"mymodule.tjs:3:add.a\", expected: \"integer\", actual: \"string\" }\n```\n\nNo source maps. No build artifacts. The function _knows where it came from_.\n\n---\n\n## Learn More\n\n- [TJS Language Reference](DOCS-TJS.md) -- Full syntax and features\n- [AJS Agent Language](DOCS-AJS.md) -- The sandboxed agent VM\n- [TJS for TypeScript Programmers](TJS-FOR-TS.md) -- Coming from TypeScript?\n- [Playground](https://tjs-platform.web.app) -- Try it live\n"
1704
+ "text": "<!--{\"section\": \"tjs-for-js\", \"group\": \"docs\", \"order\": 0, \"navTitle\": \"TJS for JS Devs\"}-->\n\n# TJS for JavaScript Programmers\n\n_Everything you already know, plus the things you always wished JavaScript had._\n\n---\n\n## The One-Sentence Pitch\n\nTJS is JavaScript with types that actually exist at runtime, equality that actually works, and errors that don't crash your program.\n\n---\n\n## What Stays the Same\n\nTJS is a superset of JavaScript. All of this works exactly as you expect:\n\n```javascript\nconst x = 42\nlet name = 'Alice'\nconst items = [1, 2, 3].map((n) => n * 2)\nconst user = { name: 'Bob', age: 30 }\nconst { name: userName, age } = user\nconst merged = { ...defaults, ...overrides }\nconst greeting = `Hello, ${name}!`\n\nfor (const item of items) {\n console.log(item)\n}\nwhile (condition) {\n /* ... */\n}\nif (x > 0) {\n /* ... */\n} else {\n /* ... */\n}\n\ntry {\n riskyThing()\n} catch (e) {\n handleError(e)\n}\n\nclass Dog {\n #name\n constructor(name) {\n this.#name = name\n }\n get name() {\n return this.#name\n }\n}\n\nimport { something } from './module.js'\nexport function myFunction() {\n /* ... */\n}\n```\n\nThe _syntax_ above is all valid TJS. But note the next section: when you transpile **native TJS**, TJS also _improves_ some runtime behaviour (footgun-free `==`, honest truthiness, …). Those improvements are exactly the point of writing TJS — but they would change the meaning of existing JavaScript.\n\nSo TJS gates them on a **dialect**:\n\n```javascript\nimport { tjs } from 'tjs-lang/lang'\n\ntjs(jsSource) // → native TJS (improvements ON — the default)\ntjs(jsSource, { dialect: 'js' }) // → plain JS (semantics preserved, untouched)\n```\n\nFor file-based tools, the extension is the dialect — `.js`/`.mjs` ⇒ `dialect: 'js'`, `.tjs` ⇒ native. Use the canonical helper so every tool agrees:\n\n```javascript\nimport { tjs, dialectForFilename } from 'tjs-lang/lang'\ntjs(source, { dialect: dialectForFilename(filename) })\n```\n\nHandling `.ts` too (a doc system, a bundler plugin)? Route the TS path through `fromTS`, imported from its own entry so the TypeScript compiler only loads when you actually transpile TS — `tjs-lang/lang` itself stays TS-free:\n\n```javascript\nimport { tjs, sourceKindForFilename } from 'tjs-lang/lang'\nimport { fromTS } from 'tjs-lang/lang/from-ts' // TS compiler — only on the ts path\n\nconst kind = sourceKindForFilename(filename) // 'js' | 'ts' | 'tjs'\nconst code =\n kind === 'ts'\n ? tjs(fromTS(source, { emitTJS: true }).code).code\n : tjs(source, { dialect: kind }).code\n```\n\nWith `dialect: 'js'` (or a `.js` file), if you don't use any TJS features your code is just JavaScript — same behaviour, no lock-in.\n\nThis extends to advanced patterns too: **Proxies**, **WeakMap/WeakSet**, **Symbols**, **generators**, **async iterators**, **`Object.defineProperty`** — all work identically. TJS adds type checks at function boundaries; it doesn't wrap or intercept any JS runtime behavior.\n\n---\n\n## What's Different\n\n### 1. Types Are Example Values\n\nIn JavaScript, there are no types. In TJS, you annotate parameters with\nan example of a valid value:\n\n```javascript\n// JavaScript\nfunction greet(name) {\n return `Hello, ${name}!`\n}\n\n// TJS - name is required and must be a string (like 'World')\nfunction greet(name: 'World'): '' {\n return `Hello, ${name}!`\n}\n```\n\nThe `: 'World'` means \"required, must be a string, here's an example.\" The `: ''` means \"returns a string.\" These aren't abstract type annotations -- they're concrete values the system can use for testing, documentation, and validation.\n\n| You Write | TJS Infers |\n| ----------------- | ----------------------------- |\n| `name: 'Alice'` | Required string |\n| `count: 42` | Required integer |\n| `rate: 3.14` | Required number (float) |\n| `age: +20` | Required non-negative integer |\n| `flag: true` | Required boolean |\n| `items: [0]` | Required integer[] |\n| `values: [0.0]` | Required number[] |\n| `user: { n: '' }` | Required object |\n| `id: 0 \\|\\| null` | integer or null |\n\nAll of these are valid JavaScript expressions. `42` vs `42.0` vs `+42` are\nall legal JS -- TJS leverages this to distinguish integer, float, and\nnon-negative integer types at the syntax level.\n\nNote: `|| null` means the value accepts the base type _or_ `null`, but not\n`undefined`. TJS treats `null` and `undefined` as distinct types\n(`typeOf(null) === 'null'`, `typeOf(undefined) === 'undefined'`).\n\n**Optional** parameters use `=` (just like JS default values):\n\n```javascript\nfunction greet(name = 'World') {\n /* ... */\n} // optional, defaults to 'World'\nfunction retry(count = 3) {\n /* ... */\n} // optional, defaults to 3 (integer)\n```\n\n**The difference:** `: value` means required. `= value` means optional with a default. In plain JS, both would be written as `= value`.\n\n### 2. Numeric Type Narrowing\n\nTJS distinguishes three numeric types using valid JavaScript syntax:\n\n```javascript\nfunction process(\n rate: 3.14, // number (float) -- has a decimal point\n count: 42, // integer -- whole number, no decimal\n index: +0 // non-negative integer -- prefixed with +\n) { /* ... */ }\n```\n\n| You Write | Type Inferred | Validates |\n| --------- | ---------------------- | ------------------------------- |\n| `3.14` | `number` (float) | Any number |\n| `0.0` | `number` (float) | Any number |\n| `42` | `integer` | `Number.isInteger(x)` |\n| `0` | `integer` | `Number.isInteger(x)` |\n| `+20` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n| `+0` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n| `-5` | `integer` | `Number.isInteger(x)` |\n| `-3.5` | `number` (float) | Any number |\n\nThese are all valid JavaScript expressions -- TJS just reads the syntax more\ncarefully than JS does. At runtime, passing `3.14` to a parameter typed as\ninteger returns a monadic error.\n\n### 3. Return Type Annotations\n\nTJS uses `:` for return types (same as TypeScript):\n\n```javascript\n// Returns an integer\nfunction add(a: 0, b: 0): 0 {\n return a + b\n}\n\n// Returns an object with specific shape\nfunction getUser(id: 0): { name: '', age: 0 } {\n return { name: 'Alice', age: 30 }\n}\n```\n\nThe return type example doubles as an automatic test. When you write `: 0`, TJS will call `add(0, 0)` at transpile time and verify the result is a number.\n\n### 4. Equality That Works\n\nJavaScript's `==` is notoriously broken (type coercion). TJS fixes `==` to be a\n**footgun-free `===`** — and adds `Is` for deep structural comparison:\n\n```javascript\n// JavaScript\n'5' == 5 // true (coercion — footgun!)\n0 == false // true (coercion — footgun!)\nnew Boolean(false) == false // false (boxed primitive — footgun!)\n\n// TJS (footgun-free == is on by default)\n'5' == 5 // false (no coercion)\n0 == false // false (no coercion)\nnew Boolean(false) == false // true (unwraps boxed primitives)\nnull == undefined // true (nullish equality)\nNaN == NaN // true (JS gets this wrong)\n\n// TJS == is NOT structural — distinct objects are distinct:\n[1, 2] == [1, 2] // false (different references)\n{ a: 1 } == { a: 1 } // false (different references)\n\n// For deep structural comparison, use Is:\n[1, 2] Is [1, 2] // true (same structure)\n{ a: 1 } Is { a: 1 } // true (same structure)\n```\n\nTJS redefines the operators:\n\n| Operator | JavaScript | TJS |\n| -------- | ------------------- | --------------------------------------------------------------------------------------------- |\n| `==` | Coercive equality | Footgun-free `===` (no coercion, unwraps boxed, `null==undefined`); NOT structural — use `Is` |\n| `!=` | Coercive inequality | Negation of the above |\n| `===` | Strict equality | Identity (same ref) — unchanged from JS |\n| `!==` | Strict inequality | Not same reference — unchanged from JS |\n\nFor deep structural comparison, use the explicit forms `Is` and `IsNot`:\n\n```javascript\nuser Is expectedUser // structural deep equality\nresult IsNot errorValue // structural deep inequality\n```\n\nClasses can define custom equality:\n\n```javascript\nclass Point {\n constructor(x: 0, y: 0) { this.x = x; this.y = y }\n Equals(other) { return this.x === other.x && this.y === other.y }\n}\n\nPoint(1, 2) Is Point(1, 2) // true, uses .Equals\n```\n\nNote: `Is` (structural comparison) does not handle circular references. Use `==`\nor `===` for objects that might be circular, or define an `.Equals` method.\n\n### 5. Errors Are Values, Not Exceptions\n\nIn JavaScript, type errors crash your program. In TJS, they're values:\n\n```javascript\nfunction double(x: 0): 0 {\n return x * 2\n}\n\ndouble(5) // 10\ndouble('oops') // { $error: true, message: \"Expected number for 'double.x', got string\" }\n```\n\nNo `try`/`catch`. No crashes. The caller gets a clear error value they can inspect:\n\n```javascript\nconst result = double(input)\nif (result?.$error) {\n console.log(result.message) // handle gracefully\n} else {\n useResult(result)\n}\n```\n\nErrors propagate automatically through function calls. If you pass a monadic error to another TJS function, it passes through without executing:\n\n```javascript\nconst a = step1(badInput) // MonadicError\nconst b = step2(a) // skips execution, returns the same error\nconst c = step3(b) // skips again -- error flows to the surface\n```\n\nThis is sometimes called \"railway-oriented programming.\"\n\n### 6. Classes Without `new`\n\nTJS classes are callable as functions:\n\n```javascript\n// JavaScript\nconst p = new Point(10, 20)\n\n// TJS - both work, but the clean form is preferred\nconst p1 = Point(10, 20) // works\nconst p2 = new Point(10, 20) // also works (linter warns)\n```\n\nThis is cleaner and more consistent with functional style. Under the hood, TJS uses a Proxy to auto-construct when you call a class as a function.\n\n### 7. `const` for Free\n\nUppercase identifiers automatically get `const`:\n\n```javascript\n// TJS\nConfig = { debug: true, version: '1.0' }\nMaxRetries = 3\n\n// Transpiles to\nconst Config = { debug: true, version: '1.0' }\nconst MaxRetries = 3\n```\n\nLowercase identifiers behave normally.\n\n---\n\n## The Type System\n\nJavaScript has no type system. Libraries like Zod and Ajv bolt one on,\nbut they're separate from your function signatures -- a second source of\ntruth that drifts. TJS's `Type()` built-in gives you as much narrowing\nand specificity as you want, in one place, using plain functions you\nalready know how to write.\n\n### From Simple to Sophisticated\n\n```javascript\n// Simple -- infer type from an example value\nType Name 'Alice' // any string\n\n// Descriptive -- add documentation\nType User {\n description: 'a registered user'\n example: { name: '', age: 0, email: '' }\n}\n\n// Constrained -- add a predicate for narrowing\nType PositiveNumber {\n description: 'a number greater than zero'\n example: 1\n predicate(x) { return typeof x === 'number' && x > 0 }\n}\n\n// Domain-specific -- real business rules\nType USZipCode {\n description: '5-digit US zip code'\n example: '90210'\n predicate(v) { return typeof v === 'string' && /^\\d{5}$/.test(v) }\n}\n\nType Email {\n description: 'email address'\n example: 'user@example.com'\n predicate(v) { return typeof v === 'string' && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(v) }\n}\n```\n\nThe key insight: a `predicate` is just a function that returns `true` or\n`false`. If you can express a constraint as a boolean check, you can make\nit a type. No schema DSL to learn, no special syntax -- it's JavaScript\nall the way down.\n\nTypes work in function signatures:\n\n```javascript\nfunction sendWelcome(email: Email, name: Name): '' {\n return `Welcome, ${name}! Confirmation sent to ${email}.`\n}\n\nsendWelcome('alice@example.com', 'Alice') // works\nsendWelcome('not-an-email', 'Alice') // MonadicError\n```\n\nTJS also ships common types out of the box: `TString`, `TNumber`,\n`TBoolean`, `TInteger`, `TPositiveInt`, `TNonEmptyString`, `TEmail`,\n`TUrl`, `TUuid`, `Timestamp`, `LegalDate`. No imports from a validation\nlibrary needed.\n\n### Combinators\n\nCompose types from other types:\n\n```javascript\nType OptionalEmail Nullable(Email) // Email | null\nType UserIds TArray(TPositiveInt) // array of positive integers\n```\n\n### Unions and Enums\n\n```javascript\n// Union of string literals\nUnion Status 'task status' 'pending' | 'active' | 'done'\n\n// Enum with named members\nEnum Color 'CSS color' {\n Red = 'red'\n Green = 'green'\n Blue = 'blue'\n}\n\n// Discriminated union -- like tagged unions or sum types\nconst Shape = Union('kind', {\n circle: { radius: 0 },\n rectangle: { width: 0, height: 0 }\n})\n```\n\n### Generics\n\nRuntime-checkable generic types:\n\n```javascript\nGeneric Box<T> {\n description: 'a boxed value'\n predicate(x, T) {\n return typeof x === 'object' && x !== null && 'value' in x && T(x.value)\n }\n}\n\n// Instantiate with a concrete type\nconst NumberBox = Box(TNumber)\nNumberBox.check({ value: 42 }) // true\nNumberBox.check({ value: 'nope' }) // false\n```\n\n---\n\n## Runtime Metadata\n\nEvery TJS function carries its type information at runtime via `__tjs`:\n\n```javascript\nfunction createUser(input: { name: '', age: 0 }): { id: 0 } {\n return { id: 123 }\n}\n\ncreateUser.__tjs\n// {\n// params: { input: { type: { kind: 'object', shape: {...} }, required: true } },\n// returns: { type: { kind: 'object', shape: { id: { kind: 'number' } } } }\n// }\n```\n\nThis metadata enables autocomplete from live objects, automatic documentation\ngeneration, and runtime reflection -- things that require build tools and\nexternal libraries in vanilla JavaScript.\n\n---\n\n## Safety Levels\n\nYou control how much validation TJS applies:\n\n```javascript\n// Per-module (top of file)\nsafety none // Metadata only -- no runtime checks (fastest)\nsafety inputs // Validate inputs only (default)\nsafety all // Validate everything (debug mode)\n\n// Per-function\nfunction fastPath(! x: 0) { /* ... */ } // Skip validation\nfunction safePath(? x: 0) { /* ... */ } // Force validation\n```\n\nUse `!` (skip validation) only in hot loops where every microsecond counts\nand the data source is already trusted. In all other cases, the ~1.5x\noverhead of `safety inputs` is negligible compared to the bugs it catches.\n\n### Unsafe Blocks\n\nSkip validation for a hot inner loop:\n\n```javascript\nunsafe {\n for (let i = 0; i < million; i++) {\n hotFunction(data[i])\n }\n}\n```\n\n---\n\n## Inline Tests\n\nTests live next to the code they test and run at transpile time:\n\n```javascript\nfunction isPrime(n: 2): true {\n if (n < 2) return false\n for (let i = 2; i * i <= n; i++) {\n if (n % i === 0) return false\n }\n return true\n}\n\ntest 'prime detection' {\n expect(isPrime(2)).toBe(true)\n expect(isPrime(4)).toBe(false)\n expect(isPrime(17)).toBe(true)\n}\n```\n\nTests are stripped from production output. They're also generated\nautomatically from return type annotations -- `: true` means TJS will call\n`isPrime(2)` and verify it returns a boolean.\n\n---\n\n## WASM Blocks\n\nFor compute-heavy code, drop into WebAssembly:\n\n```javascript\nconst add = wasm (a: i32, b: i32): i32 {\n local.get $a\n local.get $b\n i32.add\n}\n\nadd(1, 2) // 3, runs as native WASM\n```\n\nWASM is compiled at transpile time and embedded as base64 in the output.\nNo separate `.wasm` files.\n\n### Reusable WASM Functions\n\nFor a kernel that other files can import, use a top-level `wasm function`\ndeclaration:\n\n```javascript\nexport wasm function dot(a: Float32Array, b: Float32Array, n: i32): f64 {\n // ... SIMD f32x4 implementation ...\n}\n```\n\nOther files can import it directly:\n\n```javascript\nimport { dot } from './my-lib.tjs'\n// `dot` is composed into your file's wasm module at transpile time\n// — no JS↔WASM boundary on intra-library calls\n```\n\nThe first stdlib built on this is `tjs-lang/linalg` — SIMD vector kernels\nready to use:\n\n```javascript\nimport { dot, norm_sq } from 'tjs-lang/linalg'\nconst cos = dot(a, b, n) / Math.sqrt(norm_sq(a, n) * norm_sq(b, n))\n```\n\nSee [`DOCS-WASM.md`](DOCS-WASM.md) for the full story — declaration syntax,\nthe JS-owns-memory model, cross-file composition, and the two distribution\nforms (composed vs. boundary).\n\n---\n\n## Safe Eval\n\nThe killer feature for many use cases. Run untrusted code safely:\n\n```javascript\nimport { Eval, SafeFunction } from 'tjs-lang/eval'\n\n// One-shot evaluation\nconst { result } = await Eval({\n code: 'return items.filter(x => x > threshold)',\n context: { items: [1, 5, 10, 15], threshold: 7 },\n fuel: 1000,\n})\n// result: [10, 15]\n\n// Reusable safe function\nconst transform = await SafeFunction({\n body: 'return x * multiplier',\n params: ['x'],\n fuel: 500,\n})\n\nawait transform(21) // { result: 42, fuelUsed: 8 }\n```\n\n- Gas-limited (fuel runs out = execution stops, no infinite loops)\n- Capability-based (no I/O unless you grant it)\n- No `eval()`, no CSP violations, no containers\n\n---\n\n## What You Give Up\n\nTJS is opinionated. Here's what changes:\n\n| JavaScript | TJS | Why |\n| -------------------------- | ----------------------------- | ------------------------------- |\n| `==` (type coercion) | `==` (footgun-free `===`) | Coercion is a bug factory |\n| Exceptions for type bugs | Monadic error values | Exceptions escape, values don't |\n| `new ClassName()` | `ClassName()` preferred | Cleaner, more functional |\n| No runtime types | `__tjs` metadata on functions | Types should exist at runtime |\n| `typeof null === 'object'` | `typeOf(null) === 'null'` | JS got this wrong in 1995 |\n\nNothing is taken away. `new` still works. `===` still works. You can write\nplain JavaScript in a `.tjs` file and it works. The type-related additions\nuse explicit syntax (`:` annotations, `:` return types, `Type` declarations).\nBehavioral modes like footgun-free `==`, callable classes, and honest\n`typeof` are enabled by default in native TJS files. Use `TjsCompat` at the\ntop of a file to disable all modes for gradual migration or JS interop.\n\n---\n\n## Getting Started\n\n```bash\nnpm install tjs-lang\n```\n\n### Try It in the Browser\n\nThe playground runs the full TJS compiler in your browser -- no backend needed:\n\n**[tjs-platform.web.app](https://tjs-platform.web.app)**\n\n### CLI\n\n```bash\nbun src/cli/tjs.ts check file.tjs # Parse and type check\nbun src/cli/tjs.ts run file.tjs # Transpile and execute\nbun src/cli/tjs.ts emit file.tjs # Output transpiled JS\nbun src/cli/tjs.ts test file.tjs # Run inline tests\n```\n\n### From Code\n\n```javascript\nimport { tjs } from 'tjs-lang'\n\nconst output = tjs`\n function add(a: 0, b: 0): 0 {\n return a + b\n }\n`\n```\n\n---\n\n## Runtime Traceability\n\nEvery TJS function carries its source identity in `__tjs` metadata.\nWhen validation fails, the error tells you exactly which function and\nparameter failed, in which source file:\n\n```javascript\nfunction add(a: 0, b: 0): 0 {\n return a + b\n}\n\nadd.__tjs.source // \"mymodule.tjs:3\"\nadd('oops', 1) // MonadicError { path: \"mymodule.tjs:3:add.a\", expected: \"integer\", actual: \"string\" }\n```\n\nNo source maps. No build artifacts. The function _knows where it came from_.\n\n---\n\n## Learn More\n\n- [TJS Language Reference](DOCS-TJS.md) -- Full syntax and features\n- [AJS Agent Language](DOCS-AJS.md) -- The sandboxed agent VM\n- [TJS for TypeScript Programmers](TJS-FOR-TS.md) -- Coming from TypeScript?\n- [Playground](https://tjs-platform.web.app) -- Try it live\n"
1705
1705
  },
1706
1706
  {
1707
1707
  "title": "TJS for TypeScript Programmers",
@@ -1711,6 +1711,6 @@
1711
1711
  "group": "docs",
1712
1712
  "order": 0,
1713
1713
  "navTitle": "TJS for TS Devs",
1714
- "text": "<!--{\"section\": \"tjs-for-ts\", \"group\": \"docs\", \"order\": 0, \"navTitle\": \"TJS for TS Devs\"}-->\n\n# TJS for TypeScript Programmers\n\n_What if your types didn't disappear at runtime?_\n\n---\n\nTypeScript is great. It catches bugs at compile time, makes refactoring safer,\nand gives you autocomplete. But it has a fundamental limitation: types are\nfiction. They exist only in your editor, and they vanish completely at runtime.\n\nTJS starts from a different premise: **types are example values that survive\nto runtime**. This gives you everything TypeScript gives you, plus runtime\nvalidation, reflection, documentation, inline tests of private methods, and traceability of errors back to source code -- from a single source of truth.\n\nThis guide is split into two paths:\n\n1. **[Using TJS from TypeScript](#part-1-using-tjs-from-typescript)** -- Keep your TS codebase, use TJS for safe eval and agent execution\n2. **[Migrating to TJS](#part-2-migrating-to-tjs)** -- Convert your codebase from TypeScript to TJS\n\n---\n\n# Part 1: Using TJS from TypeScript\n\nYou don't have to rewrite anything. TJS provides tools you can use directly\nfrom your TypeScript codebase.\n\n## Safe Eval\n\nThe most common reason to reach for TJS from TypeScript: running untrusted\ncode safely.\n\n```typescript\nimport { Eval, SafeFunction } from 'tjs-lang/eval'\n\n// Run user-provided code with a gas limit\nconst { result, fuelUsed } = await Eval({\n code: userCode,\n context: { items: data, threshold: 10 },\n fuel: 1000,\n capabilities: {\n fetch: sandboxedFetch, // your whitelist-wrapped fetch\n },\n})\n\n// Or create a reusable safe function\nconst transform = await SafeFunction({\n body: 'return items.filter(x => x.price < budget)',\n params: ['items', 'budget'],\n fuel: 500,\n})\n\nconst { result } = await transform(products, 100)\n```\n\nNo `eval()`. No CSP violations. No Docker containers. The code runs in a\nfuel-metered sandbox with only the capabilities you inject.\n\n## Agent VM\n\nBuild and execute JSON-serializable agents:\n\n```typescript\nimport { ajs, AgentVM } from 'tjs-lang'\n\n// Parse agent source to JSON AST\nconst agent = ajs`\n function analyze({ data, query }) {\n let filtered = data.filter(x => x.score > 0.5)\n let summary = llmPredict({\n prompt: 'Summarize findings for: ' + query,\n data: filtered\n })\n return { query, summary, count: filtered.length }\n }\n`\n\n// Execute with resource limits\nconst vm = new AgentVM()\nconst { result } = await vm.run(\n agent,\n { data, query },\n {\n fuel: 1000,\n timeoutMs: 5000,\n capabilities: { fetch: myFetch, llm: myLlm },\n }\n)\n```\n\nThe agent AST is JSON. You can store it in a database, send it over the\nnetwork, version it, diff it, audit it.\n\n## Type-Safe Builder\n\nConstruct agents programmatically with full TypeScript support:\n\n```typescript\nimport { Agent, AgentVM, s } from 'tjs-lang'\n\nconst pipeline = Agent.take(s.object({ url: s.string, maxResults: s.number }))\n .httpFetch({ url: { $kind: 'arg', path: 'url' } })\n .as('response')\n .varSet({\n key: 'results',\n value: {\n $expr: 'member',\n object: { $expr: 'ident', name: 'response' },\n property: 'items',\n },\n })\n .return(s.object({ results: s.array(s.any) }))\n\nconst vm = new AgentVM()\nconst { result } = await vm.run(\n pipeline.toJSON(),\n { url, maxResults: 10 },\n {\n fuel: 500,\n capabilities: { fetch },\n }\n)\n```\n\n## TypeScript Entry Points\n\nTJS is tree-shakeable. Import only what you need:\n\n```typescript\nimport { Agent, AgentVM, ajs, tjs } from 'tjs-lang' // Everything\nimport { Eval, SafeFunction } from 'tjs-lang/eval' // Safe eval only\nimport { tjs, transpile } from 'tjs-lang/lang' // Language tools\nimport { fromTS } from 'tjs-lang/lang/from-ts' // TS -> TJS converter\n```\n\n## When to Stay in TypeScript\n\nIf your codebase is TypeScript and you're happy with it, you probably only\nneed TJS for:\n\n- Running user-provided or LLM-generated code safely\n- Building agents that travel over the network\n- Adding runtime type validation at system boundaries\n- Eval without `eval()`\n\nYou don't need to migrate anything. The libraries work from TypeScript.\n\n---\n\n# Part 2: Migrating to TJS\n\nIf you want the full TJS experience -- runtime types, structural equality,\nmonadic errors, inline tests -- here's how to convert.\n\n## The Core Idea: Types as Examples\n\nTypeScript describes types abstractly. TJS describes them concretely:\n\n```typescript\n// TypeScript: what TYPE is this?\nfunction greet(name: string): string { ... }\n\n// TJS: what's an EXAMPLE of this?\nfunction greet(name: 'World'): '' { ... }\n```\n\n`'World'` tells TJS: this is a string, it's required, and here's a valid\nexample. The example doubles as documentation and test data.\n\n## Conversion Reference\n\n### Primitives\n\n```typescript\n// TypeScript // TJS\nname: string name: ''\ncount: number count: 0.0 // float (any number)\nindex: number index: 0 // integer\nage: number age: +0 // non-negative integer\nflag: boolean flag: true\nitems: string[] items: ['']\nnested: number[][] nested: [[0]]\n```\n\n**Important:** The example value determines the _type_, not a literal\nconstraint. `name: 'World'` means \"required string\" -- not \"must be the\nstring `'World'`.\" Any string passes validation. The example is there for\ndocumentation, testing, and type inference. Think of it as `string` with\na built-in `@example` tag.\n\n**Numeric precision:** TJS distinguishes three numeric types using valid\nJavaScript syntax that JS itself ignores:\n\n| You Write | TJS Type | Runtime Check |\n| --------- | ---------------------- | ------------------------------- |\n| `3.14` | `number` (float) | Any number |\n| `0.0` | `number` (float) | Any number |\n| `42` | `integer` | `Number.isInteger(x)` |\n| `0` | `integer` | `Number.isInteger(x)` |\n| `+20` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n| `+0` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n\nTypeScript's `number` is a single type. TJS gives you three levels of\nprecision -- all using expressions that are already legal JavaScript.\nThe automatic converter maps TypeScript `number` to `0.0` (float) to\npreserve the widest behavior; you can then narrow manually to `0` (integer)\nor `+0` (non-negative integer) where appropriate.\n\n### Optional Parameters\n\n```typescript\n// TypeScript // TJS\nfunction f(x?: string) {}\nfunction f(x = '') {}\nfunction f(x: string = 'hi') {}\nfunction f(x = 'hi') {}\n```\n\nIn TypeScript, `?` means optional with type `string | undefined`.\nIn TJS, `= value` means optional with that default. Same semantics, less syntax.\n\n### Object Shapes\n\n```typescript\n// TypeScript\nfunction createUser(opts: { name: string; age: number; email?: string }) {}\n\n// TJS\nfunction createUser(opts: { name: '', age: 0, email = '' }) {}\n```\n\nRequired properties use `:`, optional ones use `=`.\n\n### Return Types\n\n```typescript\n// TypeScript // TJS\nfunction add(a: number, b: number): number function add(a: 0, b: 0): 0\nfunction getUser(): { name: string } function getUser(): { name: '' }\nfunction fetchData(): Promise<string[]> function fetchData(): ['']\n```\n\nThe `fromTS` converter unwraps `Promise<T>` in return type annotations --\nyou annotate the resolved type, not the wrapper. This only applies when\nconverting from TypeScript; in native TJS you just write normal\n`async`/`await` and annotate what the function resolves to.\n\nThe return annotation also generates an automatic test: `add(0, 0)` must\nreturn a number. If it doesn't, you get an error at transpile time.\n\n### Interfaces and Type Aliases\n\n```typescript\n// TypeScript\ninterface User {\n name: string\n age: number\n email?: string\n}\n\ntype Status = 'active' | 'inactive' | 'banned'\n\n// TJS\nType User {\n description: 'a registered user'\n example: { name: '', age: 0, email = '' }\n}\n\nUnion Status 'account status' 'active' | 'inactive' | 'banned'\n```\n\n### Enums\n\n```typescript\n// TypeScript\nenum Color {\n Red = 'red',\n Green = 'green',\n Blue = 'blue',\n}\n\n// TJS\nEnum Color 'CSS color' {\n Red = 'red'\n Green = 'green'\n Blue = 'blue'\n}\n```\n\n### Classes\n\n```typescript\n// TypeScript\nclass Point {\n private x: number\n private y: number\n\n constructor(x: number, y: number) {\n this.x = x\n this.y = y\n }\n\n distanceTo(other: Point): number {\n return Math.sqrt((this.x - other.x) ** 2 + (this.y - other.y) ** 2)\n }\n}\n\nconst p = new Point(10, 20)\n\n// TJS\nclass Point {\n #x\n #y\n\n constructor(x: 0, y: 0) {\n this.#x = x\n this.#y = y\n }\n\n distanceTo(other: Point): 0 {\n return Math.sqrt((this.#x - other.#x) ** 2 + (this.#y - other.#y) ** 2)\n }\n}\n\nconst p = Point(10, 20) // no 'new' needed\n```\n\nKey differences:\n\n- `private` is stripped by default (TS `private` is compile-time only).\n With `TjsClass` (on by default in native TJS, add via `/* @tjs TjsClass */` for TS-originated code), `private` converts to `#` (true JS runtime privacy).\n- Type annotations become example values\n- With `TjsClass`, `new` is optional (linter warns against it)\n\n### Generics\n\nTJS takes a different approach to generics. TypeScript has function-level\ntype parameters (`<T>`) that vanish at runtime. TJS has `Generic` declarations\nthat produce runtime-checkable type constructors:\n\n```typescript\n// TypeScript -- compile-time only, gone at runtime\nfunction identity<T>(x: T): T { return x }\ninterface Box<T> { value: T }\n\n// TJS -- no function-level generics; use Generic for container types\nGeneric Box<T> {\n description: 'a boxed value'\n predicate(x, T) {\n return typeof x === 'object' && x !== null && 'value' in x && T(x.value)\n }\n}\n\n// Usage: Box(Number) is a runtime type checker\nconst isNumberBox = Box(Number)\nisNumberBox({ value: 42 }) // true\nisNumberBox({ value: 'nope' }) // false\n```\n\nFor simple generic functions like `identity` or `first`, you don't need\ngenerics at all -- just skip the type parameter. TJS validates the\nconcrete types at call sites, not the abstract relationship between them.\n\n```javascript\n// Simple -- no generics needed, the function just works\nfunction first(arr: [0]) { return arr[0] }\n\n// If you need runtime-checked containers, use Generic\nGeneric Pair<T, U> {\n description: 'a typed pair'\n predicate(x, T, U) { return T(x[0]) && U(x[1]) }\n}\n```\n\nWhen converting from TypeScript, the `fromTS` converter preserves generic\nmetadata but types become `any`. This is a place where manual review helps.\n\n### Nullability\n\n```typescript\n// TypeScript\nfunction find(id: number): User | null { ... }\n\n// TJS\nfunction find(id: 0): { name: '', age: 0 } || null { ... }\n```\n\nTJS distinguishes `null` from `undefined` -- they're different types, just\nas `typeOf(null)` returns `'null'` and `typeOf(undefined)` returns\n`'undefined'`. Writing `|| null` means the value can be the base type or\n`null`, but not `undefined`. Optional parameters (using `=`) accept\n`undefined` because that's what you get when the caller omits the argument.\n\n## What TypeScript Has That TJS Doesn't\n\nTJS intentionally skips TypeScript features that don't survive to runtime\nor add complexity without proportional value:\n\n| TypeScript Feature | TJS Equivalent |\n| --------------------------- | ------------------------------------------- |\n| `interface` | `Type` with example |\n| `type` aliases | `Type`, `Union`, or `Enum` |\n| Conditional types | Preserved in `.d.ts` via declaration blocks |\n| Mapped types | Preserved in `.d.ts` via declaration blocks |\n| `keyof`, `typeof` | Use runtime `Object.keys()`, `typeOf()` |\n| `Partial<T>`, `Pick<T>` | Define the shape you need directly |\n| Declaration files (`.d.ts`) | Generated from `__tjs` metadata (`--dts`) |\n| `as` type assertions | Not needed (values are checked) |\n| `any` escape hatch | `safety none` per-module or `!` per-fn |\n| Decorators | Not supported |\n| `namespace` | Use modules |\n\nThe philosophy: if a type feature doesn't do something at runtime, it's\ncomplexity without payoff.\n\n### But What About Narrowing?\n\nTypeScript's type system is Turing-complete. You can express astonishing\nconstraints -- `Pick<Omit<T, K>, Extract<keyof T, string>>` -- but the\nresulting types are often harder to understand than the code they describe.\nAnd they vanish at runtime, so they can't protect you from bad API data.\n\nTJS takes the opposite approach: `Type()` gives you a predicate function.\nIf you can write a boolean expression, you can define a type. No type-level\nprogramming language to learn.\n\n```javascript\n// TypeScript: branded types + manual validation\ntype Email = string & { __brand: 'email' }\nfunction isEmail(s: string): s is Email {\n return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(s)\n}\nfunction validateEmail(input: string): Email {\n if (!isEmail(input)) throw new Error('Invalid email')\n return input\n}\n\n// TJS: one line, works at runtime\nType Email {\n description: 'email address'\n example: 'user@example.com'\n predicate(v) { return typeof v === 'string' && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(v) }\n}\n```\n\nThe TJS `Type()` built-in handles everything from simple shapes to\nsophisticated domain constraints:\n\n```javascript\n// Simple -- infer from example\nType Name 'Alice' // string\n\n// Constrained -- predicate narrows beyond the base type\nType PositiveInt {\n description: 'a positive integer'\n example: 1\n predicate(v) { return typeof v === 'number' && Number.isInteger(v) && v > 0 }\n}\n\n// Domain-specific -- readable business rules\nType USZipCode {\n description: '5-digit US zip code'\n example: '90210'\n predicate(v) { return typeof v === 'string' && /^\\d{5}$/.test(v) }\n}\n\n// Combinators -- compose types\nType OptionalEmail Nullable(Email) // Email | null\n\n// Schema-based -- use tosijs-schema for structured validation\nType AgeRange {\n description: 'valid age'\n example: 25\n predicate(v) { return typeof v === 'number' && v >= 0 && v <= 150 }\n}\n```\n\nCompare the TypeScript equivalents:\n\n| What you want | TypeScript | TJS |\n| ------------------- | ----------------------------------------------------------- | -------------------------------------- |\n| String with format | Branded type + type guard + validation function | `Type Email { predicate(v) {...} }` |\n| Number in range | Branded type + manual check | `Type Age { predicate(v) {...} }` |\n| Non-empty string | Template literal type (compile-only) | `Type NonEmpty { predicate(v) {...} }` |\n| Nullable variant | `T \\| null` (compile-only) | `Nullable(MyType)` (runtime-checked) |\n| Union of literals | `'a' \\| 'b' \\| 'c'` (compile-only) | `Union Status 'a' \\| 'b' \\| 'c'` |\n| Discriminated union | `type Shape = { kind: 'circle' } \\| ...` + manual narrowing | `Union('kind', { circle: {...} })` |\n| Generic container | `interface Box<T>` (compile-only) | `Generic Box<T> { predicate(...) }` |\n\nEvery row in the TypeScript column is compile-time fiction that disappears\nwhen your code runs. Every row in the TJS column is a runtime check that\nactually catches bugs in production. And the TJS versions are shorter,\nbecause a predicate is just a function -- not a type-level program.\n\nTJS also ships common types out of the box: `TString`, `TNumber`,\n`TBoolean`, `TInteger`, `TPositiveInt`, `TNonEmptyString`, `TEmail`,\n`TUrl`, `TUuid`, `Timestamp`, `LegalDate`. No imports from a validation\nlibrary needed.\n\n### Tooling Comparison\n\n| Concern | TypeScript | TJS |\n| ------------------- | ---------------------------------- | ---------------------------------------------------------------------- |\n| **Type checking** | `tsc` (compile-time only) | Runtime validation (survives build) |\n| **Runtime schemas** | Zod / io-ts / Ajv (separate) | Built-in (types _are_ schemas) |\n| **Linting** | ESLint + plugins | Built-in linter (unused vars, unreachable code, no-explicit-new) |\n| **Testing** | Vitest / Jest (separate files) | Inline `test` blocks (transpile-time) |\n| **Equality** | Reference-based only | Honest `==` (no coercion), `Is`/`IsNot` (structural), `===` (identity) |\n| **Build toolchain** | tsc + bundler (webpack/Vite/etc) | Transpiles in-browser, no build step |\n| **Debugging** | Source maps (brittle, build bloat) | Functions carry source identity via `__tjs` metadata |\n| **Documentation** | JSDoc / TypeDoc (manual) | Generated from `__tjs` metadata |\n| **Editor support** | Mature (VSCode, etc) | Monaco/CodeMirror/Ace + VSCode/Cursor extensions |\n\n## What TJS Has That TypeScript Doesn't\n\n### Runtime Validation\n\nTypeScript:\n\n```typescript\n// Types are a promise. A lie, if the data comes from outside.\nfunction processOrder(order: Order) {\n // If order came from an API, nothing guarantees it matches Order.\n // You need Zod/io-ts/ajv AND the TypeScript type AND keep them in sync.\n}\n```\n\nTJS:\n\n```javascript\n// Types are checked at runtime. One source of truth.\nfunction processOrder(order: { items: [{ id: 0, qty: 0 }], total: 0 }): {\n status: '',\n} {\n // If order doesn't match, caller gets a MonadicError -- no crash.\n}\n```\n\n### Honest Equality\n\nTypeScript inherits JavaScript's broken equality. Native TJS fixes this by default. For TS-originated code, add the `TjsEquals` directive (or use `/* @tjs TjsEquals */` in the source `.ts` file):\n\n```javascript\nTjsEquals // needed for TS-originated code; native TJS has this on by default\n\n// == is honest: no coercion, unwraps boxed primitives\n0 == '' // false (JS: true!)\n[] == ![] // false (JS: true!)\nnew String('foo') == 'foo' // true (unwraps boxed)\nnull == undefined // true (useful pattern preserved)\ntypeof null // 'null' (JS: 'object')\n\n// == is fast: O(1) reference equality for objects/arrays\n{a: 1} == {a: 1} // false (different refs)\n[1, 2] == [1, 2] // false (different refs)\n\n// Is/IsNot for explicit deep structural comparison (O(n))\n{a: 1} Is {a: 1} // true\n[1, 2, 3] Is [1, 2, 3] // true\nnew Set([1,2]) Is new Set([2,1]) // true (Sets are order-independent)\n\n// === unchanged: identity check\nobj === obj // true (same reference)\n```\n\n`==` fixes coercion without the performance cost of deep comparison.\nUse `Is`/`IsNot` when you explicitly need structural comparison.\n\n### Monadic Errors\n\nTypeScript uses exceptions. TJS uses values:\n\n```javascript\n// TypeScript -- you have to remember to try/catch\nfunction divide(a: number, b: number): number {\n if (b === 0) throw new Error('Division by zero')\n return a / b\n}\n// Caller forgets try/catch? Crash.\n\n// TJS -- errors flow through the pipeline\nfunction divide(a: 0, b: 0): 0 {\n if (b === 0) return MonadicError('Division by zero')\n return a / b\n}\n// Caller gets an error value. No crash. Ever.\n```\n\n**How the caller handles it:**\n\n```javascript\n// Option 1: Check the result\nconst result = divide(10, 0)\nif (result instanceof Error) {\n console.log(result.message) // 'Division by zero'\n} else {\n useResult(result)\n}\n\n// Option 2: Just keep going -- errors propagate automatically\nconst a = divide(10, 0) // MonadicError\nconst b = double(a) // Receives error, returns it immediately (skips execution)\nconst c = format(b) // Same -- error flows through the whole chain\n// c is still the original MonadicError from divide()\n```\n\nIf you've used Rust's `Result<T, E>` or Haskell's `Either`, the pattern\nis familiar. The key difference from TypeScript: you never have to guess\nwhether a function might throw. Type errors and validation failures are\nalways values, never exceptions.\n\n### Inline Tests\n\n```javascript\nfunction fibonacci(n: 0): 0 {\n if (n <= 1) return n\n return fibonacci(n - 1) + fibonacci(n - 2)\n}\n\ntest 'fibonacci sequence' {\n expect(fibonacci(0)).toBe(0)\n expect(fibonacci(1)).toBe(1)\n expect(fibonacci(10)).toBe(55)\n}\n```\n\nTests run at transpile time. They're stripped from production output.\nNo separate test files, no test runner configuration.\n\n### Safety Controls\n\n```javascript\nsafety none // This module: skip all validation (performance)\nsafety inputs // This module: validate inputs only (default)\nsafety all // This module: validate everything (debug)\n\n// Per-function overrides\nfunction hot(! x: 0) {} // Skip validation even if module says 'inputs'\nfunction safe(? x: 0) {} // Force validation even if module says 'none'\n```\n\nUse `!` (skip validation) only in hot loops where every microsecond counts\nand the data source is already trusted. In all other cases, the ~1.5x\noverhead of `safety inputs` is negligible compared to the bugs it catches.\n\n#### Additional Safety Features\n\n```javascript\nTjsNoVar // var declarations are syntax errors\nconst! config = {} // Compile-time immutability (zero runtime cost)\n\n// Debug mode: make type errors visible\nimport { configure } from 'tjs-lang/lang'\nconfigure({ logTypeErrors: true }) // console.error on every type error\nconfigure({ throwTypeErrors: true }) // throw instead of returning MonadicError\n```\n\nTypeScript has no equivalent to most of these. You're either all-in on\ntypes or you use `as any` to escape.\n\n---\n\n## Automatic Conversion\n\nTJS includes a TypeScript-to-TJS converter that has been validated against\nthe tosijs production codebase (35 files, 523 tests passing):\n\n```bash\n# Convert a single file\nbun src/cli/tjs.ts convert input.ts --emit-tjs > output.tjs\n\n# Convert a directory (produces .js + .d.ts + .md per file)\nbun src/cli/tjs.ts convert src/ -o tjs-out/\n\n# Emit JavaScript directly\nbun src/cli/tjs.ts convert input.ts > output.js\n```\n\nFrom code:\n\n```typescript\nimport { fromTS } from 'tjs-lang/lang/from-ts'\n\nconst result = fromTS(tsSource, { emitTJS: true })\nconsole.log(result.code) // TJS source\n```\n\n### What `fromTS` Handles\n\n- Primitive annotations (`string`, `number`, `boolean`) → example values\n- Interfaces → `Type` declarations with declaration blocks for `.d.ts` round-tripping\n- Generic interfaces → `Generic` with predicates + declaration blocks\n- Conditional/mapped types → preserved verbatim in declaration blocks\n- Function type aliases → `FunctionPredicate` declarations (including generics)\n- String literal unions → `Union`\n- Enums → `Enum`\n- Rest parameters (`...args: T[]`) → preserved with `...` prefix\n- Nullable types (`T | null`) → proper null guards in runtime checks\n- Optional params, default values\n- `private` → stripped (or `#` with `TjsClass`)\n- Static getters/setters → `static` keyword preserved\n- `Promise<T>` → unwrapped return types\n- DOM types (130+) → `{}` (opaque object, keeps params annotated)\n- JSDoc comments → TDoc comments\n- Exported constants → type-inferred `.d.ts` entries\n\n### `@tjs` Annotations in TypeScript\n\nAnnotate your `.ts` files with `/* @tjs ... */` comments to enrich\nthe TJS output. The TS compiler ignores them.\n\n```typescript\n/* @tjs TjsClass TjsEquals */ // Enable TJS modes (off by default in TS-originated code)\n\n/* @tjs-skip */ // Skip this type declaration\nexport type Unboxed<T> = T extends { value: infer U } ? U : T\n\n/* @tjs predicate(x, T) { return typeof x === 'object' && T(x.value) } */\nexport interface Box<T> {\n value: T\n}\n\n/* @tjs example: { name: 'Alice', age: 30 } */\nexport interface User {\n name: string\n age: number\n}\n\n/* @tjs declaration { value: T; path: string } */\nexport interface BoxedProxy<T> {\n /* complex conditional type */\n}\n```\n\n### `.d.ts` Generation\n\nThe DTS emitter produces TypeScript declarations from TJS transpilation:\n\n```bash\nbun src/cli/tjs.ts emit input.tjs # emits .js, .d.ts, .md\n```\n\n- **Interfaces with declaration blocks** → `export interface Name<T> { ... }`\n- **Conditional/mapped types** → `export type Name<T> = ...` (verbatim TS body)\n- **Function types** → `export type Name = (...) => T`\n- **Simple type aliases** → `export type Name = original TS body`\n- **Constants** → `export declare const Name: type`\n- **Functions** → `export declare function Name(params): returnType`\n- **Classes** → callable function + class declaration\n\n### Constrained Generics\n\nWhen the converter encounters a constrained generic like\n`<T extends { id: number }>`, it uses the constraint shape as the\nexample value instead of falling back to `any`. This means:\n\n```typescript\n// TypeScript\nfunction first<T extends { id: number }>(items: T[]): T {\n return items[0]\n}\n\n// Converted TJS — uses constraint shape, not 'any'\nfunction first(items: [{ id: 0.0 }]):! { id: 0.0 } { ... }\n```\n\nGeneric defaults also work: `<T = string>` uses `string` as the example.\nUnconstrained generics (`<T>` with no `extends` or default) still degrade\nto `any` — there's genuinely no information about what T is.\n\n### What `fromTS` Can't Fully Express\n\nTJS types are example values, not abstract type algebra. Some TypeScript\npatterns have no direct TJS equivalent — but most now preserve their\noriginal TS body for `.d.ts` round-tripping:\n\n| TypeScript Pattern | What Happens |\n| ------------------------------------------- | ----------------------------------------------------- |\n| Conditional types (`T extends U ? X : Y`) | TS body preserved verbatim in `.d.ts` |\n| Mapped types (`{ [K in keyof T]: ... }`) | TS body preserved verbatim in `.d.ts` |\n| Intersection types (`A & B`) | TS body preserved verbatim in `.d.ts` |\n| `Partial<T>`, `Required<T>`, `Pick`, `Omit` | Emits warning, uses base shape |\n| `ReturnType<T>`, `Parameters<T>` | Drops to `any` |\n| Template literal types (`` `${A}-${B}` ``) | Becomes `string` |\n| Deeply nested generics (`Foo<Bar<U>>`) | Inner params become `any` |\n| `readonly`, `as const` | Stripped (use `const!` for compile-time immutability) |\n\nThe key improvement: complex types that can't be expressed as runtime\npredicates are still preserved in the `.d.ts` output via declaration blocks.\nThe runtime code works with `any`, but TypeScript consumers of your library\nget the full type information.\n\n## Migration Strategy\n\n### Incremental Adoption\n\nYou don't have to convert everything at once:\n\n1. **Start at boundaries.** Convert API handlers and validation layers\n first -- these benefit most from runtime types.\n2. **Convert hot modules.** Modules with frequent type-related bugs are\n good candidates.\n3. **Leave internals for last.** Pure computational code that's already\n well-tested benefits least from migration.\n\n### The Bun Plugin\n\nIf you use Bun, `.tjs` files work alongside `.ts` files with zero config:\n\n```javascript\n// bunfig.toml already preloads the TJS plugin\nimport { processOrder } from './orders.tjs' // just works\nimport { validateUser } from './users.ts' // also works\n```\n\n### What to Watch For\n\n**Example values matter.** `count: 0` means \"number, example is 0.\" If\nyour function breaks on 0 (division, array index), the automatic signature\ntest will catch it immediately. Choose examples that exercise the\nhappy path.\n\n**Return types generate tests.** `: 0` means TJS will call your function\nwith the parameter examples and check the result. If your function has\nside effects or requires setup, use `:! 0` to skip the signature test.\n\n**Structural equality changes behavior.** If your code relies on `==`\nfor type coercion (comparing numbers to strings, etc.), you'll need to\nupdate those comparisons. This is almost always a bug fix.\n\n---\n\n## Side-by-Side: A Complete Example\n\n### TypeScript\n\n```typescript\ninterface Product {\n id: string\n name: string\n price: number\n tags: string[]\n}\n\ninterface CartItem {\n product: Product\n quantity: number\n}\n\nfunction calculateTotal(items: CartItem[], taxRate: number = 0.1): number {\n const subtotal = items.reduce(\n (sum, item) => sum + item.product.price * item.quantity,\n 0\n )\n return Math.round(subtotal * (1 + taxRate) * 100) / 100\n}\n\nfunction applyDiscount(\n total: number,\n code: string | null\n): { final: number; discount: number } {\n const discounts: Record<string, number> = {\n SAVE10: 0.1,\n SAVE20: 0.2,\n }\n const rate = code ? discounts[code] ?? 0 : 0\n return {\n final: Math.round(total * (1 - rate) * 100) / 100,\n discount: rate,\n }\n}\n```\n\n### TJS\n\n```javascript\nType Product {\n description: 'a product in the catalog'\n example: { id: '', name: '', price: 0.0, tags: [''] }\n}\n\nType CartItem {\n description: 'a product with quantity'\n example: { product: { id: '', name: '', price: 0.0, tags: [''] }, quantity: +0 }\n}\n\nfunction calculateTotal(items: [CartItem], taxRate = 0.1): 0.0 {\n const subtotal = items.reduce(\n (sum, item) => sum + item.product.price * item.quantity,\n 0\n )\n return Math.round(subtotal * (1 + taxRate) * 100) / 100\n}\n\nfunction applyDiscount(total: 0.0, code: '' || null): { final: 0.0, discount: 0.0 } {\n const discounts = {\n SAVE10: 0.1,\n SAVE20: 0.2,\n }\n const rate = code ? discounts[code] ?? 0 : 0\n return {\n final: Math.round(total * (1 - rate) * 100) / 100,\n discount: rate,\n }\n}\n\ntest 'cart calculation' {\n const items = [\n { product: { id: '1', name: 'Widget', price: 10, tags: [] }, quantity: 3 }\n ]\n expect(calculateTotal(items, 0)).toBe(30)\n expect(calculateTotal(items, 0.1)).toBe(33)\n}\n\ntest 'discount codes' {\n expect(applyDiscount(100, 'SAVE10')).toEqual({ final: 90, discount: 0.1 })\n expect(applyDiscount(100, null)).toEqual({ final: 100, discount: 0 })\n expect(applyDiscount(100, 'INVALID')).toEqual({ final: 100, discount: 0 })\n}\n```\n\nThe TJS version is about the same length, but the types exist at runtime,\nthe tests live with the code, and invalid inputs return errors instead\nof crashing.\n\n---\n\n## Traceability: The Death of Source Maps\n\nTypeScript debugging relies on source maps -- external files that try to\nmap minified, transpiled JavaScript back to your original code. They're\nbrittle, often out of sync, and fail entirely in complex build pipelines.\n\nTJS eliminates source maps. Every function carries its source identity\nin `__tjs` metadata:\n\n```javascript\nfunction add(a: 0, b: 0): 0 {\n return a + b\n}\n\nadd.__tjs.source // \"mymodule.tjs:3\"\n```\n\n- **Zero-config debugging:** If a function fails validation, the error\n points to the exact line in your `.tjs` source, not a generated `.js` file.\n- **Transparent eval:** Even code run via `Eval()` or the `AgentVM`\n provides clear traces because AST and source metadata are preserved.\n- **No build bloat:** You don't ship `.map` files to production just to\n know why your app crashed.\n\n---\n\n## FAQ\n\n### How do I use TJS with existing NPM packages?\n\nTJS is a superset of JavaScript. Import any NPM package as usual:\n\n```javascript\nimport lodash from 'https://esm.sh/lodash@4.17.21'\nimport { z } from 'zod' // works, though you won't need it\n```\n\nWhen you import a vanilla JS library, its exports have no TJS metadata.\nYou can wrap them in a TJS boundary to get runtime safety:\n\n```javascript\nimport { rawGeocode } from 'legacy-geo-pkg'\n\n// Wrap to validate at your system boundary\nfunction geocode(addr: ''): { lat: 0.0, lon: 0.0 } {\n return rawGeocode(addr)\n}\n```\n\nThe untyped library code runs freely. Your TJS wrapper validates the\nresult before it enters your typed world.\n\n### Do Proxies, WeakMaps, and other advanced patterns work?\n\nYes. TJS is purely additive — it adds inline type checks and metadata\nproperties but does not wrap, intercept, or modify JavaScript runtime\nbehavior. Specifically:\n\n- **Proxies** work identically to plain JS. TJS attaches `.__tjs` as a\n plain property on function objects, which doesn't trigger Proxy traps.\n If your Proxy needs custom equality, use the `[tjsEquals]` symbol protocol.\n- **WeakMap/WeakSet** are unaffected. TJS doesn't inspect collection contents.\n- **Symbols** work normally. TJS reserves `Symbol.for('tjs.equals')` for\n custom equality but doesn't interfere with other symbols.\n- **`Object.defineProperty`**, getters/setters, non-enumerable properties\n — all work as expected. TJS validation checks value types, not property\n descriptors.\n- **Prototype chains** are preserved. `instanceof` works correctly with\n TJS-wrapped classes.\n\nIf you're building a Proxy-heavy library (reactive state, ORMs, etc.),\nTJS will not interfere. The transpiled output is plain JavaScript with\nsome `typeof` checks at function entry points.\n\n### Does structural equality (`==`) handle circular references?\n\nNo. Circular structures will cause infinite recursion. Use identity\ncomparison (`===`) for objects that might be circular, or define a\ncustom `.Equals` method on the class.\n\n### What happens to TypeScript's `strict` mode checks?\n\nTJS doesn't have `strictNullChecks` or `noImplicitAny` because the\nproblems they solve don't exist:\n\n- **Null safety:** `|| null` explicitly marks nullable parameters.\n Functions without it reject null at runtime.\n- **Implicit any:** Every TJS parameter has an example value that\n determines its type. There's nothing to be implicit about.\n- **Strict property access:** Runtime validation catches missing\n properties with a clear error message instead of `undefined`.\n\n---\n\n## Learn More\n\n- [TJS Language Reference](DOCS-TJS.md) -- Full syntax and features\n- [TJS for JavaScript Programmers](TJS-FOR-JS.md) -- Coming from vanilla JS?\n- [AJS Agent Language](DOCS-AJS.md) -- The sandboxed agent VM\n- [Playground](https://tjs-platform.web.app) -- Try it live\n"
1714
+ "text": "<!--{\"section\": \"tjs-for-ts\", \"group\": \"docs\", \"order\": 0, \"navTitle\": \"TJS for TS Devs\"}-->\n\n# TJS for TypeScript Programmers\n\n_What if your types didn't disappear at runtime?_\n\n---\n\nTypeScript is great. It catches bugs at compile time, makes refactoring safer,\nand gives you autocomplete. But it has a fundamental limitation: types are\nfiction. They exist only in your editor, and they vanish completely at runtime.\n\nTJS starts from a different premise: **types are example values that survive\nto runtime**. This gives you everything TypeScript gives you, plus runtime\nvalidation, reflection, documentation, inline tests of private methods, and traceability of errors back to source code -- from a single source of truth.\n\nThis guide is split into two paths:\n\n1. **[Using TJS from TypeScript](#part-1-using-tjs-from-typescript)** -- Keep your TS codebase, use TJS for safe eval and agent execution\n2. **[Migrating to TJS](#part-2-migrating-to-tjs)** -- Convert your codebase from TypeScript to TJS\n\n---\n\n# Part 1: Using TJS from TypeScript\n\nYou don't have to rewrite anything. TJS provides tools you can use directly\nfrom your TypeScript codebase.\n\n## Safe Eval\n\nThe most common reason to reach for TJS from TypeScript: running untrusted\ncode safely.\n\n```typescript\nimport { Eval, SafeFunction } from 'tjs-lang/eval'\n\n// Run user-provided code with a gas limit\nconst { result, fuelUsed } = await Eval({\n code: userCode,\n context: { items: data, threshold: 10 },\n fuel: 1000,\n capabilities: {\n fetch: sandboxedFetch, // your whitelist-wrapped fetch\n },\n})\n\n// Or create a reusable safe function\nconst transform = await SafeFunction({\n body: 'return items.filter(x => x.price < budget)',\n params: ['items', 'budget'],\n fuel: 500,\n})\n\nconst { result } = await transform(products, 100)\n```\n\nNo `eval()`. No CSP violations. No Docker containers. The code runs in a\nfuel-metered sandbox with only the capabilities you inject.\n\n## Agent VM\n\nBuild and execute JSON-serializable agents:\n\n```typescript\nimport { ajs, AgentVM } from 'tjs-lang'\n\n// Parse agent source to JSON AST\nconst agent = ajs`\n function analyze({ data, query }) {\n let filtered = data.filter(x => x.score > 0.5)\n let summary = llmPredict({\n prompt: 'Summarize findings for: ' + query,\n data: filtered\n })\n return { query, summary, count: filtered.length }\n }\n`\n\n// Execute with resource limits\nconst vm = new AgentVM()\nconst { result } = await vm.run(\n agent,\n { data, query },\n {\n fuel: 1000,\n timeoutMs: 5000,\n capabilities: { fetch: myFetch, llm: myLlm },\n }\n)\n```\n\nThe agent AST is JSON. You can store it in a database, send it over the\nnetwork, version it, diff it, audit it.\n\n## Type-Safe Builder\n\nConstruct agents programmatically with full TypeScript support:\n\n```typescript\nimport { Agent, AgentVM, s } from 'tjs-lang'\n\nconst pipeline = Agent.take(s.object({ url: s.string, maxResults: s.number }))\n .httpFetch({ url: { $kind: 'arg', path: 'url' } })\n .as('response')\n .varSet({\n key: 'results',\n value: {\n $expr: 'member',\n object: { $expr: 'ident', name: 'response' },\n property: 'items',\n },\n })\n .return(s.object({ results: s.array(s.any) }))\n\nconst vm = new AgentVM()\nconst { result } = await vm.run(\n pipeline.toJSON(),\n { url, maxResults: 10 },\n {\n fuel: 500,\n capabilities: { fetch },\n }\n)\n```\n\n## TypeScript Entry Points\n\nTJS is tree-shakeable. Import only what you need:\n\n```typescript\nimport { Agent, AgentVM, ajs, tjs } from 'tjs-lang' // Everything\nimport { Eval, SafeFunction } from 'tjs-lang/eval' // Safe eval only\nimport { tjs, transpile } from 'tjs-lang/lang' // Language tools\nimport { fromTS } from 'tjs-lang/lang/from-ts' // TS -> TJS converter\n```\n\n## When to Stay in TypeScript\n\nIf your codebase is TypeScript and you're happy with it, you probably only\nneed TJS for:\n\n- Running user-provided or LLM-generated code safely\n- Building agents that travel over the network\n- Adding runtime type validation at system boundaries\n- Eval without `eval()`\n\nYou don't need to migrate anything. The libraries work from TypeScript.\n\n---\n\n# Part 2: Migrating to TJS\n\nIf you want the full TJS experience -- runtime types, honest equality,\nmonadic errors, inline tests -- here's how to convert.\n\n## The Core Idea: Types as Examples\n\nTypeScript describes types abstractly. TJS describes them concretely:\n\n```typescript\n// TypeScript: what TYPE is this?\nfunction greet(name: string): string { ... }\n\n// TJS: what's an EXAMPLE of this?\nfunction greet(name: 'World'): '' { ... }\n```\n\n`'World'` tells TJS: this is a string, it's required, and here's a valid\nexample. The example doubles as documentation and test data.\n\n## Conversion Reference\n\n### Primitives\n\n```typescript\n// TypeScript // TJS\nname: string name: ''\ncount: number count: 0.0 // float (any number)\nindex: number index: 0 // integer\nage: number age: +0 // non-negative integer\nflag: boolean flag: true\nitems: string[] items: ['']\nnested: number[][] nested: [[0]]\n```\n\n**Important:** The example value determines the _type_, not a literal\nconstraint. `name: 'World'` means \"required string\" -- not \"must be the\nstring `'World'`.\" Any string passes validation. The example is there for\ndocumentation, testing, and type inference. Think of it as `string` with\na built-in `@example` tag.\n\n**Numeric precision:** TJS distinguishes three numeric types using valid\nJavaScript syntax that JS itself ignores:\n\n| You Write | TJS Type | Runtime Check |\n| --------- | ---------------------- | ------------------------------- |\n| `3.14` | `number` (float) | Any number |\n| `0.0` | `number` (float) | Any number |\n| `42` | `integer` | `Number.isInteger(x)` |\n| `0` | `integer` | `Number.isInteger(x)` |\n| `+20` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n| `+0` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n\nTypeScript's `number` is a single type. TJS gives you three levels of\nprecision -- all using expressions that are already legal JavaScript.\nThe automatic converter maps TypeScript `number` to `0.0` (float) to\npreserve the widest behavior; you can then narrow manually to `0` (integer)\nor `+0` (non-negative integer) where appropriate.\n\n### Optional Parameters\n\n```typescript\n// TypeScript // TJS\nfunction f(x?: string) {}\nfunction f(x = '') {}\nfunction f(x: string = 'hi') {}\nfunction f(x = 'hi') {}\n```\n\nIn TypeScript, `?` means optional with type `string | undefined`.\nIn TJS, `= value` means optional with that default. Same semantics, less syntax.\n\n### Object Shapes\n\n```typescript\n// TypeScript\nfunction createUser(opts: { name: string; age: number; email?: string }) {}\n\n// TJS\nfunction createUser(opts: { name: '', age: 0, email = '' }) {}\n```\n\nRequired properties use `:`, optional ones use `=`.\n\n### Return Types\n\n```typescript\n// TypeScript // TJS\nfunction add(a: number, b: number): number function add(a: 0, b: 0): 0\nfunction getUser(): { name: string } function getUser(): { name: '' }\nfunction fetchData(): Promise<string[]> function fetchData(): ['']\n```\n\nThe `fromTS` converter unwraps `Promise<T>` in return type annotations --\nyou annotate the resolved type, not the wrapper. This only applies when\nconverting from TypeScript; in native TJS you just write normal\n`async`/`await` and annotate what the function resolves to.\n\nThe return annotation also generates an automatic test: `add(0, 0)` must\nreturn a number. If it doesn't, you get an error at transpile time.\n\n### Interfaces and Type Aliases\n\n```typescript\n// TypeScript\ninterface User {\n name: string\n age: number\n email?: string\n}\n\ntype Status = 'active' | 'inactive' | 'banned'\n\n// TJS\nType User {\n description: 'a registered user'\n example: { name: '', age: 0, email = '' }\n}\n\nUnion Status 'account status' 'active' | 'inactive' | 'banned'\n```\n\n### Enums\n\n```typescript\n// TypeScript\nenum Color {\n Red = 'red',\n Green = 'green',\n Blue = 'blue',\n}\n\n// TJS\nEnum Color 'CSS color' {\n Red = 'red'\n Green = 'green'\n Blue = 'blue'\n}\n```\n\n### Classes\n\n```typescript\n// TypeScript\nclass Point {\n private x: number\n private y: number\n\n constructor(x: number, y: number) {\n this.x = x\n this.y = y\n }\n\n distanceTo(other: Point): number {\n return Math.sqrt((this.x - other.x) ** 2 + (this.y - other.y) ** 2)\n }\n}\n\nconst p = new Point(10, 20)\n\n// TJS\nclass Point {\n #x\n #y\n\n constructor(x: 0, y: 0) {\n this.#x = x\n this.#y = y\n }\n\n distanceTo(other: Point): 0 {\n return Math.sqrt((this.#x - other.#x) ** 2 + (this.#y - other.#y) ** 2)\n }\n}\n\nconst p = Point(10, 20) // no 'new' needed\n```\n\nKey differences:\n\n- `private` is stripped by default (TS `private` is compile-time only).\n With `TjsClass` (on by default in native TJS, add via `/* @tjs TjsClass */` for TS-originated code), `private` converts to `#` (true JS runtime privacy).\n- Type annotations become example values\n- With `TjsClass`, `new` is optional (linter warns against it)\n\n### Generics\n\nTJS takes a different approach to generics. TypeScript has function-level\ntype parameters (`<T>`) that vanish at runtime. TJS has `Generic` declarations\nthat produce runtime-checkable type constructors:\n\n```typescript\n// TypeScript -- compile-time only, gone at runtime\nfunction identity<T>(x: T): T { return x }\ninterface Box<T> { value: T }\n\n// TJS -- no function-level generics; use Generic for container types\nGeneric Box<T> {\n description: 'a boxed value'\n predicate(x, T) {\n return typeof x === 'object' && x !== null && 'value' in x && T(x.value)\n }\n}\n\n// Usage: Box(Number) is a runtime type checker\nconst isNumberBox = Box(Number)\nisNumberBox({ value: 42 }) // true\nisNumberBox({ value: 'nope' }) // false\n```\n\nFor simple generic functions like `identity` or `first`, you don't need\ngenerics at all -- just skip the type parameter. TJS validates the\nconcrete types at call sites, not the abstract relationship between them.\n\n```javascript\n// Simple -- no generics needed, the function just works\nfunction first(arr: [0]) { return arr[0] }\n\n// If you need runtime-checked containers, use Generic\nGeneric Pair<T, U> {\n description: 'a typed pair'\n predicate(x, T, U) { return T(x[0]) && U(x[1]) }\n}\n```\n\nWhen converting from TypeScript, the `fromTS` converter preserves generic\nmetadata but types become `any`. This is a place where manual review helps.\n\n### Nullability\n\n```typescript\n// TypeScript\nfunction find(id: number): User | null { ... }\n\n// TJS\nfunction find(id: 0): { name: '', age: 0 } || null { ... }\n```\n\nTJS distinguishes `null` from `undefined` -- they're different types, just\nas `typeOf(null)` returns `'null'` and `typeOf(undefined)` returns\n`'undefined'`. Writing `|| null` means the value can be the base type or\n`null`, but not `undefined`. Optional parameters (using `=`) accept\n`undefined` because that's what you get when the caller omits the argument.\n\n## What TypeScript Has That TJS Doesn't\n\nTJS intentionally skips TypeScript features that don't survive to runtime\nor add complexity without proportional value:\n\n| TypeScript Feature | TJS Equivalent |\n| --------------------------- | ------------------------------------------- |\n| `interface` | `Type` with example |\n| `type` aliases | `Type`, `Union`, or `Enum` |\n| Conditional types | Preserved in `.d.ts` via declaration blocks |\n| Mapped types | Preserved in `.d.ts` via declaration blocks |\n| `keyof`, `typeof` | Use runtime `Object.keys()`, `typeOf()` |\n| `Partial<T>`, `Pick<T>` | Define the shape you need directly |\n| Declaration files (`.d.ts`) | Generated from `__tjs` metadata (`--dts`) |\n| `as` type assertions | Not needed (values are checked) |\n| `any` escape hatch | `safety none` per-module or `!` per-fn |\n| Decorators | Not supported |\n| `namespace` | Use modules |\n\nThe philosophy: if a type feature doesn't do something at runtime, it's\ncomplexity without payoff.\n\n### But What About Narrowing?\n\nTypeScript's type system is Turing-complete. You can express astonishing\nconstraints -- `Pick<Omit<T, K>, Extract<keyof T, string>>` -- but the\nresulting types are often harder to understand than the code they describe.\nAnd they vanish at runtime, so they can't protect you from bad API data.\n\nTJS takes the opposite approach: `Type()` gives you a predicate function.\nIf you can write a boolean expression, you can define a type. No type-level\nprogramming language to learn.\n\n```javascript\n// TypeScript: branded types + manual validation\ntype Email = string & { __brand: 'email' }\nfunction isEmail(s: string): s is Email {\n return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(s)\n}\nfunction validateEmail(input: string): Email {\n if (!isEmail(input)) throw new Error('Invalid email')\n return input\n}\n\n// TJS: one line, works at runtime\nType Email {\n description: 'email address'\n example: 'user@example.com'\n predicate(v) { return typeof v === 'string' && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(v) }\n}\n```\n\nThe TJS `Type()` built-in handles everything from simple shapes to\nsophisticated domain constraints:\n\n```javascript\n// Simple -- infer from example\nType Name 'Alice' // string\n\n// Constrained -- predicate narrows beyond the base type\nType PositiveInt {\n description: 'a positive integer'\n example: 1\n predicate(v) { return typeof v === 'number' && Number.isInteger(v) && v > 0 }\n}\n\n// Domain-specific -- readable business rules\nType USZipCode {\n description: '5-digit US zip code'\n example: '90210'\n predicate(v) { return typeof v === 'string' && /^\\d{5}$/.test(v) }\n}\n\n// Combinators -- compose types\nType OptionalEmail Nullable(Email) // Email | null\n\n// Schema-based -- use tosijs-schema for structured validation\nType AgeRange {\n description: 'valid age'\n example: 25\n predicate(v) { return typeof v === 'number' && v >= 0 && v <= 150 }\n}\n```\n\nCompare the TypeScript equivalents:\n\n| What you want | TypeScript | TJS |\n| ------------------- | ----------------------------------------------------------- | -------------------------------------- |\n| String with format | Branded type + type guard + validation function | `Type Email { predicate(v) {...} }` |\n| Number in range | Branded type + manual check | `Type Age { predicate(v) {...} }` |\n| Non-empty string | Template literal type (compile-only) | `Type NonEmpty { predicate(v) {...} }` |\n| Nullable variant | `T \\| null` (compile-only) | `Nullable(MyType)` (runtime-checked) |\n| Union of literals | `'a' \\| 'b' \\| 'c'` (compile-only) | `Union Status 'a' \\| 'b' \\| 'c'` |\n| Discriminated union | `type Shape = { kind: 'circle' } \\| ...` + manual narrowing | `Union('kind', { circle: {...} })` |\n| Generic container | `interface Box<T>` (compile-only) | `Generic Box<T> { predicate(...) }` |\n\nEvery row in the TypeScript column is compile-time fiction that disappears\nwhen your code runs. Every row in the TJS column is a runtime check that\nactually catches bugs in production. And the TJS versions are shorter,\nbecause a predicate is just a function -- not a type-level program.\n\nTJS also ships common types out of the box: `TString`, `TNumber`,\n`TBoolean`, `TInteger`, `TPositiveInt`, `TNonEmptyString`, `TEmail`,\n`TUrl`, `TUuid`, `Timestamp`, `LegalDate`. No imports from a validation\nlibrary needed.\n\n### Tooling Comparison\n\n| Concern | TypeScript | TJS |\n| ------------------- | ---------------------------------- | ---------------------------------------------------------------------- |\n| **Type checking** | `tsc` (compile-time only) | Runtime validation (survives build) |\n| **Runtime schemas** | Zod / io-ts / Ajv (separate) | Built-in (types _are_ schemas) |\n| **Linting** | ESLint + plugins | Built-in linter (unused vars, unreachable code, no-explicit-new) |\n| **Testing** | Vitest / Jest (separate files) | Inline `test` blocks (transpile-time) |\n| **Equality** | Reference-based only | Honest `==` (no coercion), `Is`/`IsNot` (structural), `===` (identity) |\n| **Build toolchain** | tsc + bundler (webpack/Vite/etc) | Transpiles in-browser, no build step |\n| **Debugging** | Source maps (brittle, build bloat) | Functions carry source identity via `__tjs` metadata |\n| **Documentation** | JSDoc / TypeDoc (manual) | Generated from `__tjs` metadata |\n| **Editor support** | Mature (VSCode, etc) | Monaco/CodeMirror/Ace + VSCode/Cursor extensions |\n\n## What TJS Has That TypeScript Doesn't\n\n### Runtime Validation\n\nTypeScript:\n\n```typescript\n// Types are a promise. A lie, if the data comes from outside.\nfunction processOrder(order: Order) {\n // If order came from an API, nothing guarantees it matches Order.\n // You need Zod/io-ts/ajv AND the TypeScript type AND keep them in sync.\n}\n```\n\nTJS:\n\n```javascript\n// Types are checked at runtime. One source of truth.\nfunction processOrder(order: { items: [{ id: 0, qty: 0 }], total: 0 }): {\n status: '',\n} {\n // If order doesn't match, caller gets a MonadicError -- no crash.\n}\n```\n\n### Honest Equality\n\nTypeScript inherits JavaScript's broken equality. Native TJS fixes this by default. For TS-originated code, add the `TjsEquals` directive (or use `/* @tjs TjsEquals */` in the source `.ts` file):\n\n```javascript\nTjsEquals // needed for TS-originated code; native TJS has this on by default\n\n// == is honest: no coercion, unwraps boxed primitives\n0 == '' // false (JS: true!)\n[] == ![] // false (JS: true!)\nnew String('foo') == 'foo' // true (unwraps boxed)\nnull == undefined // true (useful pattern preserved)\ntypeof null // 'null' (JS: 'object')\n\n// == is fast: O(1) reference equality for objects/arrays\n{a: 1} == {a: 1} // false (different refs)\n[1, 2] == [1, 2] // false (different refs)\n\n// Is/IsNot for explicit deep structural comparison (O(n))\n{a: 1} Is {a: 1} // true\n[1, 2, 3] Is [1, 2, 3] // true\nnew Set([1,2]) Is new Set([2,1]) // true (Sets are order-independent)\n\n// === unchanged: identity check\nobj === obj // true (same reference)\n```\n\n`==` fixes coercion without the performance cost of deep comparison.\nUse `Is`/`IsNot` when you explicitly need structural comparison.\n\n### Monadic Errors\n\nTypeScript uses exceptions. TJS uses values:\n\n```javascript\n// TypeScript -- you have to remember to try/catch\nfunction divide(a: number, b: number): number {\n if (b === 0) throw new Error('Division by zero')\n return a / b\n}\n// Caller forgets try/catch? Crash.\n\n// TJS -- errors flow through the pipeline\nfunction divide(a: 0, b: 0): 0 {\n if (b === 0) return MonadicError('Division by zero')\n return a / b\n}\n// Caller gets an error value. No crash. Ever.\n```\n\n**How the caller handles it:**\n\n```javascript\n// Option 1: Check the result\nconst result = divide(10, 0)\nif (result instanceof Error) {\n console.log(result.message) // 'Division by zero'\n} else {\n useResult(result)\n}\n\n// Option 2: Just keep going -- errors propagate automatically\nconst a = divide(10, 0) // MonadicError\nconst b = double(a) // Receives error, returns it immediately (skips execution)\nconst c = format(b) // Same -- error flows through the whole chain\n// c is still the original MonadicError from divide()\n```\n\nIf you've used Rust's `Result<T, E>` or Haskell's `Either`, the pattern\nis familiar. The key difference from TypeScript: you never have to guess\nwhether a function might throw. Type errors and validation failures are\nalways values, never exceptions.\n\n### Inline Tests\n\n```javascript\nfunction fibonacci(n: 0): 0 {\n if (n <= 1) return n\n return fibonacci(n - 1) + fibonacci(n - 2)\n}\n\ntest 'fibonacci sequence' {\n expect(fibonacci(0)).toBe(0)\n expect(fibonacci(1)).toBe(1)\n expect(fibonacci(10)).toBe(55)\n}\n```\n\nTests run at transpile time. They're stripped from production output.\nNo separate test files, no test runner configuration.\n\n### Safety Controls\n\n```javascript\nsafety none // This module: skip all validation (performance)\nsafety inputs // This module: validate inputs only (default)\nsafety all // This module: validate everything (debug)\n\n// Per-function overrides\nfunction hot(! x: 0) {} // Skip validation even if module says 'inputs'\nfunction safe(? x: 0) {} // Force validation even if module says 'none'\n```\n\nUse `!` (skip validation) only in hot loops where every microsecond counts\nand the data source is already trusted. In all other cases, the ~1.5x\noverhead of `safety inputs` is negligible compared to the bugs it catches.\n\n#### Additional Safety Features\n\n```javascript\nTjsNoVar // var declarations are syntax errors\nconst! config = {} // Compile-time immutability (zero runtime cost)\n\n// Debug mode: make type errors visible\nimport { configure } from 'tjs-lang/lang'\nconfigure({ logTypeErrors: true }) // console.error on every type error\nconfigure({ throwTypeErrors: true }) // throw instead of returning MonadicError\n```\n\nTypeScript has no equivalent to most of these. You're either all-in on\ntypes or you use `as any` to escape.\n\n---\n\n## Automatic Conversion\n\nTJS includes a TypeScript-to-TJS converter that has been validated against\nthe tosijs production codebase (35 files, 523 tests passing):\n\n```bash\n# Convert a single file\nbun src/cli/tjs.ts convert input.ts --emit-tjs > output.tjs\n\n# Convert a directory (produces .js + .d.ts + .md per file)\nbun src/cli/tjs.ts convert src/ -o tjs-out/\n\n# Emit JavaScript directly\nbun src/cli/tjs.ts convert input.ts > output.js\n```\n\nFrom code:\n\n```typescript\nimport { fromTS } from 'tjs-lang/lang/from-ts'\n\nconst result = fromTS(tsSource, { emitTJS: true })\nconsole.log(result.code) // TJS source\n```\n\n### What `fromTS` Handles\n\n- Primitive annotations (`string`, `number`, `boolean`) → example values\n- Interfaces → `Type` declarations with declaration blocks for `.d.ts` round-tripping\n- Generic interfaces → `Generic` with predicates + declaration blocks\n- Conditional/mapped types → preserved verbatim in declaration blocks\n- Function type aliases → `FunctionPredicate` declarations (including generics)\n- String literal unions → `Union`\n- Enums → `Enum`\n- Rest parameters (`...args: T[]`) → preserved with `...` prefix\n- Nullable types (`T | null`) → proper null guards in runtime checks\n- Optional params, default values\n- `private` → stripped (or `#` with `TjsClass`)\n- Static getters/setters → `static` keyword preserved\n- `Promise<T>` → unwrapped return types\n- DOM types (130+) → `{}` (opaque object, keeps params annotated)\n- JSDoc comments → TDoc comments\n- Exported constants → type-inferred `.d.ts` entries\n\n### `@tjs` Annotations in TypeScript\n\nAnnotate your `.ts` files with `/* @tjs ... */` comments to enrich\nthe TJS output. The TS compiler ignores them.\n\n```typescript\n/* @tjs TjsClass TjsEquals */ // Enable TJS modes (off by default in TS-originated code)\n\n/* @tjs-skip */ // Skip this type declaration\nexport type Unboxed<T> = T extends { value: infer U } ? U : T\n\n/* @tjs predicate(x, T) { return typeof x === 'object' && T(x.value) } */\nexport interface Box<T> {\n value: T\n}\n\n/* @tjs example: { name: 'Alice', age: 30 } */\nexport interface User {\n name: string\n age: number\n}\n\n/* @tjs declaration { value: T; path: string } */\nexport interface BoxedProxy<T> {\n /* complex conditional type */\n}\n```\n\n### `.d.ts` Generation\n\nThe DTS emitter produces TypeScript declarations from TJS transpilation:\n\n```bash\nbun src/cli/tjs.ts emit input.tjs # emits .js, .d.ts, .md\n```\n\n- **Interfaces with declaration blocks** → `export interface Name<T> { ... }`\n- **Conditional/mapped types** → `export type Name<T> = ...` (verbatim TS body)\n- **Function types** → `export type Name = (...) => T`\n- **Simple type aliases** → `export type Name = original TS body`\n- **Constants** → `export declare const Name: type`\n- **Functions** → `export declare function Name(params): returnType`\n- **Classes** → callable function + class declaration\n\n### Constrained Generics\n\nWhen the converter encounters a constrained generic like\n`<T extends { id: number }>`, it uses the constraint shape as the\nexample value instead of falling back to `any`. This means:\n\n```typescript\n// TypeScript\nfunction first<T extends { id: number }>(items: T[]): T {\n return items[0]\n}\n\n// Converted TJS — uses constraint shape, not 'any'\nfunction first(items: [{ id: 0.0 }]):! { id: 0.0 } { ... }\n```\n\nGeneric defaults also work: `<T = string>` uses `string` as the example.\nUnconstrained generics (`<T>` with no `extends` or default) still degrade\nto `any` — there's genuinely no information about what T is.\n\n### What `fromTS` Can't Fully Express\n\nTJS types are example values, not abstract type algebra. Some TypeScript\npatterns have no direct TJS equivalent — but most now preserve their\noriginal TS body for `.d.ts` round-tripping:\n\n| TypeScript Pattern | What Happens |\n| ------------------------------------------- | ----------------------------------------------------- |\n| Conditional types (`T extends U ? X : Y`) | TS body preserved verbatim in `.d.ts` |\n| Mapped types (`{ [K in keyof T]: ... }`) | TS body preserved verbatim in `.d.ts` |\n| Intersection types (`A & B`) | TS body preserved verbatim in `.d.ts` |\n| `Partial<T>`, `Required<T>`, `Pick`, `Omit` | Emits warning, uses base shape |\n| `ReturnType<T>`, `Parameters<T>` | Drops to `any` |\n| Template literal types (`` `${A}-${B}` ``) | Becomes `string` |\n| Deeply nested generics (`Foo<Bar<U>>`) | Inner params become `any` |\n| `readonly`, `as const` | Stripped (use `const!` for compile-time immutability) |\n\nThe key improvement: complex types that can't be expressed as runtime\npredicates are still preserved in the `.d.ts` output via declaration blocks.\nThe runtime code works with `any`, but TypeScript consumers of your library\nget the full type information.\n\n## Migration Strategy\n\n### Incremental Adoption\n\nYou don't have to convert everything at once:\n\n1. **Start at boundaries.** Convert API handlers and validation layers\n first -- these benefit most from runtime types.\n2. **Convert hot modules.** Modules with frequent type-related bugs are\n good candidates.\n3. **Leave internals for last.** Pure computational code that's already\n well-tested benefits least from migration.\n\n### The Bun Plugin\n\nIf you use Bun, `.tjs` files work alongside `.ts` files with zero config:\n\n```javascript\n// bunfig.toml already preloads the TJS plugin\nimport { processOrder } from './orders.tjs' // just works\nimport { validateUser } from './users.ts' // also works\n```\n\n### What to Watch For\n\n**Example values matter.** `count: 0` means \"number, example is 0.\" If\nyour function breaks on 0 (division, array index), the automatic signature\ntest will catch it immediately. Choose examples that exercise the\nhappy path.\n\n**Return types generate tests.** `: 0` means TJS will call your function\nwith the parameter examples and check the result. If your function has\nside effects or requires setup, use `:! 0` to skip the signature test.\n\n**Honest equality changes behavior.** Native TJS `==` is a footgun-free\n`===` — no coercion (it unwraps boxed primitives and treats `null`/`undefined`\nas equal, but does not coerce across types), and it is **not** structural. If\nyour code relies on `==` for type coercion (comparing numbers to strings, etc.),\nyou'll need to update those comparisons. This is almost always a bug fix. For\ndeep structural comparison, use `Is`/`IsNot`.\n\n---\n\n## Side-by-Side: A Complete Example\n\n### TypeScript\n\n```typescript\ninterface Product {\n id: string\n name: string\n price: number\n tags: string[]\n}\n\ninterface CartItem {\n product: Product\n quantity: number\n}\n\nfunction calculateTotal(items: CartItem[], taxRate: number = 0.1): number {\n const subtotal = items.reduce(\n (sum, item) => sum + item.product.price * item.quantity,\n 0\n )\n return Math.round(subtotal * (1 + taxRate) * 100) / 100\n}\n\nfunction applyDiscount(\n total: number,\n code: string | null\n): { final: number; discount: number } {\n const discounts: Record<string, number> = {\n SAVE10: 0.1,\n SAVE20: 0.2,\n }\n const rate = code ? discounts[code] ?? 0 : 0\n return {\n final: Math.round(total * (1 - rate) * 100) / 100,\n discount: rate,\n }\n}\n```\n\n### TJS\n\n```javascript\nType Product {\n description: 'a product in the catalog'\n example: { id: '', name: '', price: 0.0, tags: [''] }\n}\n\nType CartItem {\n description: 'a product with quantity'\n example: { product: { id: '', name: '', price: 0.0, tags: [''] }, quantity: +0 }\n}\n\nfunction calculateTotal(items: [CartItem], taxRate = 0.1): 0.0 {\n const subtotal = items.reduce(\n (sum, item) => sum + item.product.price * item.quantity,\n 0\n )\n return Math.round(subtotal * (1 + taxRate) * 100) / 100\n}\n\nfunction applyDiscount(total: 0.0, code: '' || null): { final: 0.0, discount: 0.0 } {\n const discounts = {\n SAVE10: 0.1,\n SAVE20: 0.2,\n }\n const rate = code ? discounts[code] ?? 0 : 0\n return {\n final: Math.round(total * (1 - rate) * 100) / 100,\n discount: rate,\n }\n}\n\ntest 'cart calculation' {\n const items = [\n { product: { id: '1', name: 'Widget', price: 10, tags: [] }, quantity: 3 }\n ]\n expect(calculateTotal(items, 0)).toBe(30)\n expect(calculateTotal(items, 0.1)).toBe(33)\n}\n\ntest 'discount codes' {\n expect(applyDiscount(100, 'SAVE10')).toEqual({ final: 90, discount: 0.1 })\n expect(applyDiscount(100, null)).toEqual({ final: 100, discount: 0 })\n expect(applyDiscount(100, 'INVALID')).toEqual({ final: 100, discount: 0 })\n}\n```\n\nThe TJS version is about the same length, but the types exist at runtime,\nthe tests live with the code, and invalid inputs return errors instead\nof crashing.\n\n---\n\n## Traceability: The Death of Source Maps\n\nTypeScript debugging relies on source maps -- external files that try to\nmap minified, transpiled JavaScript back to your original code. They're\nbrittle, often out of sync, and fail entirely in complex build pipelines.\n\nTJS eliminates source maps. Every function carries its source identity\nin `__tjs` metadata:\n\n```javascript\nfunction add(a: 0, b: 0): 0 {\n return a + b\n}\n\nadd.__tjs.source // \"mymodule.tjs:3\"\n```\n\n- **Zero-config debugging:** If a function fails validation, the error\n points to the exact line in your `.tjs` source, not a generated `.js` file.\n- **Transparent eval:** Even code run via `Eval()` or the `AgentVM`\n provides clear traces because AST and source metadata are preserved.\n- **No build bloat:** You don't ship `.map` files to production just to\n know why your app crashed.\n\n---\n\n## FAQ\n\n### How do I use TJS with existing NPM packages?\n\nTJS is a superset of JavaScript. Import any NPM package as usual:\n\n```javascript\nimport lodash from 'https://esm.sh/lodash@4.17.21'\nimport { z } from 'zod' // works, though you won't need it\n```\n\nWhen you import a vanilla JS library, its exports have no TJS metadata.\nYou can wrap them in a TJS boundary to get runtime safety:\n\n```javascript\nimport { rawGeocode } from 'legacy-geo-pkg'\n\n// Wrap to validate at your system boundary\nfunction geocode(addr: ''): { lat: 0.0, lon: 0.0 } {\n return rawGeocode(addr)\n}\n```\n\nThe untyped library code runs freely. Your TJS wrapper validates the\nresult before it enters your typed world.\n\n### Do Proxies, WeakMaps, and other advanced patterns work?\n\nYes. TJS is purely additive — it adds inline type checks and metadata\nproperties but does not wrap, intercept, or modify JavaScript runtime\nbehavior. Specifically:\n\n- **Proxies** work identically to plain JS. TJS attaches `.__tjs` as a\n plain property on function objects, which doesn't trigger Proxy traps.\n If your Proxy needs custom equality, use the `[tjsEquals]` symbol protocol.\n- **WeakMap/WeakSet** are unaffected. TJS doesn't inspect collection contents.\n- **Symbols** work normally. TJS reserves `Symbol.for('tjs.equals')` for\n custom equality but doesn't interfere with other symbols.\n- **`Object.defineProperty`**, getters/setters, non-enumerable properties\n — all work as expected. TJS validation checks value types, not property\n descriptors.\n- **Prototype chains** are preserved. `instanceof` works correctly with\n TJS-wrapped classes.\n\nIf you're building a Proxy-heavy library (reactive state, ORMs, etc.),\nTJS will not interfere. The transpiled output is plain JavaScript with\nsome `typeof` checks at function entry points.\n\n### Does `Is`/`IsNot` (structural comparison) handle circular references?\n\nNo. (Note that `==` is not structural — it is footgun-free `===`, so it never\nrecurses. Only `Is`/`IsNot` do deep comparison.) Circular structures will cause\ninfinite recursion in `Is`/`IsNot`. Use `==` or identity comparison (`===`) for\nobjects that might be circular, or define a custom `.Equals` method on the class.\n\n### What happens to TypeScript's `strict` mode checks?\n\nTJS doesn't have `strictNullChecks` or `noImplicitAny` because the\nproblems they solve don't exist:\n\n- **Null safety:** `|| null` explicitly marks nullable parameters.\n Functions without it reject null at runtime.\n- **Implicit any:** Every TJS parameter has an example value that\n determines its type. There's nothing to be implicit about.\n- **Strict property access:** Runtime validation catches missing\n properties with a clear error message instead of `undefined`.\n\n---\n\n## Learn More\n\n- [TJS Language Reference](DOCS-TJS.md) -- Full syntax and features\n- [TJS for JavaScript Programmers](TJS-FOR-JS.md) -- Coming from vanilla JS?\n- [AJS Agent Language](DOCS-AJS.md) -- The sandboxed agent VM\n- [Playground](https://tjs-platform.web.app) -- Try it live\n"
1715
1715
  }
1716
1716
  ]