tjs-lang 0.2.0
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/CONTEXT.md +594 -0
- package/LICENSE +190 -0
- package/README.md +220 -0
- package/bin/benchmarks.ts +351 -0
- package/bin/dev.ts +205 -0
- package/bin/docs.js +170 -0
- package/bin/install-cursor.sh +71 -0
- package/bin/install-vscode.sh +71 -0
- package/bin/select-local-models.d.ts +1 -0
- package/bin/select-local-models.js +28 -0
- package/bin/select-local-models.ts +31 -0
- package/demo/autocomplete.test.ts +232 -0
- package/demo/docs.json +186 -0
- package/demo/examples.test.ts +598 -0
- package/demo/index.html +91 -0
- package/demo/src/autocomplete.ts +482 -0
- package/demo/src/capabilities.ts +859 -0
- package/demo/src/demo-nav.ts +2097 -0
- package/demo/src/examples.test.ts +161 -0
- package/demo/src/examples.ts +476 -0
- package/demo/src/imports.test.ts +196 -0
- package/demo/src/imports.ts +421 -0
- package/demo/src/index.ts +639 -0
- package/demo/src/module-store.ts +635 -0
- package/demo/src/module-sw.ts +132 -0
- package/demo/src/playground.ts +949 -0
- package/demo/src/service-host.ts +389 -0
- package/demo/src/settings.ts +440 -0
- package/demo/src/style.ts +280 -0
- package/demo/src/tjs-playground.ts +1605 -0
- package/demo/src/ts-examples.ts +478 -0
- package/demo/src/ts-playground.ts +1092 -0
- package/demo/static/favicon.svg +30 -0
- package/demo/static/photo-1.jpg +0 -0
- package/demo/static/photo-2.jpg +0 -0
- package/demo/static/texts/ai-history.txt +9 -0
- package/demo/static/texts/coffee-origins.txt +9 -0
- package/demo/static/texts/renewable-energy.txt +9 -0
- package/dist/index.js +256 -0
- package/dist/index.js.map +37 -0
- package/dist/tjs-batteries.js +4 -0
- package/dist/tjs-batteries.js.map +15 -0
- package/dist/tjs-full.js +256 -0
- package/dist/tjs-full.js.map +37 -0
- package/dist/tjs-transpiler.js +220 -0
- package/dist/tjs-transpiler.js.map +21 -0
- package/dist/tjs-vm.js +4 -0
- package/dist/tjs-vm.js.map +14 -0
- package/docs/CNAME +1 -0
- package/docs/favicon.svg +30 -0
- package/docs/index.html +91 -0
- package/docs/index.js +10468 -0
- package/docs/index.js.map +92 -0
- package/docs/photo-1.jpg +0 -0
- package/docs/photo-1.webp +0 -0
- package/docs/photo-2.jpg +0 -0
- package/docs/photo-2.webp +0 -0
- package/docs/texts/ai-history.txt +9 -0
- package/docs/texts/coffee-origins.txt +9 -0
- package/docs/texts/renewable-energy.txt +9 -0
- package/docs/tjs-lang.svg +31 -0
- package/docs/tosijs-agent.svg +31 -0
- package/editors/README.md +325 -0
- package/editors/ace/ajs-mode.js +328 -0
- package/editors/ace/ajs-mode.ts +269 -0
- package/editors/ajs-syntax.ts +212 -0
- package/editors/build-grammars.ts +510 -0
- package/editors/codemirror/ajs-language.js +287 -0
- package/editors/codemirror/ajs-language.ts +1447 -0
- package/editors/codemirror/autocomplete.test.ts +531 -0
- package/editors/codemirror/component.ts +404 -0
- package/editors/monaco/ajs-monarch.js +243 -0
- package/editors/monaco/ajs-monarch.ts +225 -0
- package/editors/tjs-syntax.ts +115 -0
- package/editors/vscode/language-configuration.json +37 -0
- package/editors/vscode/package.json +65 -0
- package/editors/vscode/syntaxes/ajs-injection.tmLanguage.json +107 -0
- package/editors/vscode/syntaxes/ajs.tmLanguage.json +252 -0
- package/editors/vscode/syntaxes/tjs.tmLanguage.json +333 -0
- package/package.json +83 -0
- package/src/cli/commands/check.ts +41 -0
- package/src/cli/commands/convert.ts +133 -0
- package/src/cli/commands/emit.ts +260 -0
- package/src/cli/commands/run.ts +68 -0
- package/src/cli/commands/test.ts +194 -0
- package/src/cli/commands/types.ts +20 -0
- package/src/cli/create-app.ts +236 -0
- package/src/cli/playground.ts +250 -0
- package/src/cli/tjs.ts +166 -0
- package/src/cli/tjsx.ts +160 -0
- package/tjs-lang.svg +31 -0
package/demo/docs.json
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"text": "<!--{\"section\": \"home\", \"order\": 0, \"navTitle\": \"Home\"}-->\n\n# TJS Platform\n\n\n\n[playground](https://tjs-platform.web.app) | [github](https://github.com/tonioloewald/tjs-lang#readme) | [npm](https://www.npmjs.com/package/tjs-lang) | [discord](https://discord.gg/ramJ9rgky5)\n\n## The Problem\n\n**TypeScript is fragile.** It pretends to be a superset of JavaScript, but it isn't. It pretends to be typesafe, but it isn't. Its Turing-complete type system is harder to reason about than the code it supposedly documents—and then it all disappears at runtime.\n\n> TypeScript is also difficult to transpile. Your browser can run full virtual machines in JavaScript, but most TypeScript playgrounds either fake it by stripping type declarations or use a server backend to do the real work.\n\n**JavaScript is dangerous.** `eval()` and `Function()` are so powerful they're forbidden almost everywhere—blocked by CSP in most production environments. The industry's answer? The **Container Fallacy**: wrap every function in a 200MB Linux container just to run it safely. We ship buildings to deliver letters.\n\n**Security is a mess.** Every layer validates. Gateway validates. Auth validates. Business logic validates. Database validates. We spend 90% of our time building pipelines to move data to code, re-checking it at every hop.\n\n## What If?\n\nWhat if your language were:\n\n- **Honest** — types that actually exist at runtime, not fiction that evaporates\n- **Safe** — a gas-metered VM where infinite loops are impossible, no container required\n- **Mobile** — logic that travels to data, not oceans of data dragged to logic\n- **Unified** — one source of truth, not TypeScript interfaces *plus* Zod schemas *plus* JSDoc\n\nThat's what TJS Platform provides: **TJS** for writing your infrastructure, and **AJS** for shipping logic that runs anywhere.\n\n## TJS — Types That Survive\n\nWrite typed JavaScript where the type *is* an example. No split-brain validation.\n\n```typescript\n// TJS: The type is an example AND a test\nfunction greet(name: 'World') -> 'Hello, World!' {\n return `Hello, ${name}!`\n}\n// At transpile time: greet('World') is called and checked against 'Hello, World!'\n\n// Runtime: The type becomes a contract\nconsole.log(greet.__tjs.params) // { name: { type: 'string', example: 'World', required: true } }\n\n// Safety: Errors are values, not crashes\nconst result = greet(123) // { $error: true, message: 'type mismatch' }\n```\n\n**Why it matters:**\n- **One source of truth** — no more TS interfaces + Zod schemas + JSDoc comments\n- **Types as examples** — `name: 'Alice'` means \"required string, like 'Alice'\"\n- **Runtime metadata** — `__tjs` enables reflection, autocomplete, documentation from live objects\n- **Monadic errors** — type failures return values, never throw\n- **Zero build step** — transpiles in the browser, no webpack/Vite/Babel\n- **The compiler *is* the client** — TJS transpiles itself *and* TypeScript entirely client-side\n\n## AJS — Code That Travels\n\nWrite logic that compiles to JSON and runs in a gas-limited sandbox. Send agents to data instead of shipping data to code.\n\n```typescript\nconst agent = ajs`\n function research(topic: 'AI') {\n let data = httpFetch({ url: '/search?q=' + topic })\n let summary = llmPredict({ prompt: 'Summarize: ' + data })\n return { topic, summary }\n }\n`\n\n// Run it safely—no Docker required\nconst result = await vm.run(agent, { topic: 'Agents' }, {\n fuel: 500, // Strict CPU budget\n capabilities: { fetch: http } // Allow ONLY http, block everything else\n})\n```\n\n**Why it matters:**\n- **Safe eval** — run untrusted code without containers\n- **Code is JSON** — store in databases, diff, version, transmit\n- **Fuel metering** — every operation costs gas, infinite loops impossible\n- **Capability-based** — zero I/O by default, grant only what's needed\n- **LLM-native** — simple enough for small models to generate correctly\n\n## The Architecture Shift\n\n**Old way (data-to-code):**\nClient requests data → Server fetches 100 rows → Server filters to 5 → Client receives 5.\n*High latency. High bandwidth. Validate at every layer.*\n\n**TJS way (code-to-data):**\nClient sends agent → Edge runs agent at data → Edge returns 5 rows.\n*Low latency. Zero waste. Validate once.*\n\nThe agent carries its own validation. The server grants capabilities. Caching happens automatically because the query *is* the code.\n\n## Safe Eval\n\nThe holy grail: `eval()` that's actually safe.\n\n```typescript\nimport { Eval } from 'tjs-lang/eval'\n\n// Whitelist-wrapped fetch - untrusted code only reaches your domains\nconst safeFetch = (url: string) => {\n const allowed = ['api.example.com', 'cdn.example.com']\n const host = new URL(url).host\n if (!allowed.includes(host)) {\n return { error: 'Domain not allowed' }\n }\n return fetch(url)\n}\n\nconst { result, fuelUsed } = await Eval({\n code: `\n let data = fetch('https://api.example.com/products')\n return data.filter(x => x.price < budget)\n `,\n context: { budget: 100 },\n fuel: 1000,\n capabilities: { fetch: safeFetch } // Only whitelisted domains\n})\n```\n\nThe untrusted code thinks it has `fetch`, but it only has *your* `fetch`. No CSP violations. No infinite loops. No access to anything you didn't explicitly grant.\n\n## Quick Start\n\n```bash\nnpm install tjs-lang\n```\n\n### Run an Agent\n\n```typescript\nimport { ajs, AgentVM } from 'tjs-lang'\n\nconst agent = ajs`\n function double(value: 21) {\n return { result: value * 2 }\n }\n`\n\nconst vm = new AgentVM()\nconst { result } = await vm.run(agent, { value: 21 })\nconsole.log(result) // { result: 42 }\n```\n\n### Write Typed Code\n\n```typescript\nimport { tjs } from 'tjs-lang'\n\nconst { code, metadata } = tjs`\n function add(a: 0, b: 0) -> 0 {\n return a + b\n }\n`\n// code: JavaScript with __tjs metadata attached\n// metadata: { add: { params: { a: { type: 'number', example: 0 }, b: { type: 'number', example: 0 } }, returns: { type: 'number' } } }\n```\n\n### Try the Playground\n\nSince TJS compiles itself, the playground is the full engine running entirely in your browser.\n\n**[tjs-platform.web.app](https://tjs-platform.web.app)**\n\n## At a Glance\n\n| | TJS | AJS |\n|--|-----|-----|\n| **Purpose** | Write your platform | Write your agents |\n| **Trust level** | Your code | Anyone's code |\n| **Compiles to** | JavaScript + metadata | JSON AST |\n| **Runs in** | Browser, Node, Bun | Sandboxed VM |\n| **Types** | Examples → runtime validation | Schemas for I/O |\n| **Errors** | Monadic (values, not exceptions) | Monadic |\n| **Build step** | None | None |\n\n## Bundle Size\n\nThe cost of \"safe eval\"—compare to a 200MB Docker image:\n\n| Bundle | Size | Gzipped |\n|--------|------|---------|\n| VM only | 42 KB | **14 KB** |\n| + Batteries (LLM, vector) | 56 KB | 19 KB |\n| + Transpiler | 89 KB | 27 KB |\n| Full (with TS support) | 180 KB | 56 KB |\n\n**Dependencies:** `acorn` (JS parser), `tosijs-schema` (validation). Both have zero transitive dependencies.\n\n## Documentation\n\n- **[TJS Language Guide](DOCS-TJS.md)** — Types, syntax, runtime\n- **[AJS Runtime Guide](DOCS-AJS.md)** — VM, atoms, capabilities\n- **[Architecture Deep Dive](CONTEXT.md)** — How it all fits together\n- **[Playground](https://tjs-platform.web.app)** — Try it now\n\n## Installation\n\n```bash\n# npm\nnpm install tjs-lang\n\n# bun \nbun add tjs-lang\n\n# pnpm\npnpm add tjs-lang\n```\n\n## License\n\nApache 2.0\n",
|
|
4
|
+
"title": "TJS Platform",
|
|
5
|
+
"filename": "README.md",
|
|
6
|
+
"path": "README.md",
|
|
7
|
+
"section": "home",
|
|
8
|
+
"order": 0,
|
|
9
|
+
"navTitle": "Home"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"text": "<!--{\"section\": \"meta\", \"order\": 1, \"navTitle\": \"For Builders\"}-->\n\n# Applied Laziness: The \"Zero-Infrastructure\" Stack\n\n**Stop building pipelines. Start shipping logic.**\n\n---\n\n## The Problem\n\nYou wanted to build a feature. Instead you're debugging webpack configs, waiting for Docker builds, and wondering why your TypeScript types don't match reality.\n\nThe modern stack has become a tax on productivity. Every \"improvement\" adds another layer of indirection, another config file, another thing that can break.\n\n---\n\n## The TJS Win\n\n**\"I write the type as an example, and I get validation, documentation, and autocomplete for free.\"**\n\nTJS flips the script on types. Instead of describing what a value *could* be, you show what it *looks like*:\n\n```typescript\n// TypeScript: describe the abstract shape\nfunction greet(name: string): string\n\n// TJS: show a concrete example\nfunction greet(name: 'World') -> '' { return `Hello, ${name}!` }\n```\n\nThe type *is* the example. The example *is* the documentation. One thing, not three.\n\n### What You Stop Doing\n\n- **No webpack config to debug.** The browser is the compiler.\n- **No TypeScript errors about missing `.d.ts` files.** Types survive to runtime.\n- **No \"works on my machine.\"** Browser is the single source of truth.\n- **No rebuild cycles.** Edit, save, see.\n\n### What You Start Doing\n\n- Write code in the browser or your editor\n- Save it\n- It runs\n\nThat's it. No build server. No bundler. No transpilation step. The loop is: write → run.\n\n### The \"Oops, It's Robust\" Moment\n\nHere's the happy accident: you left Safe Mode on.\n\nYou didn't write validation logic. You didn't write `if (x == null) throw`. You just wrote `function add(x: 0)`.\n\nBecause you left Safe Mode on, your function now automatically rejects bad inputs with a clean, traceable report:\n\n```javascript\n{\n $error: true,\n message: \"Expected 'number', got 'banana'\",\n path: \"add.input.x\"\n}\n```\n\nYou accidentally built an enterprise-grade API while prototyping.\n\n**This is the physics change:** In every other language, a type error at runtime is a catastrophe—uncaught exception, stack trace, 500 server error. In TJS Safe Mode, a type error is just *data*. You trace the error to the *source* (the caller), not the *symptom* (the crash).\n\n| Mode | Behavior | The \"Oops\" Factor |\n|------|----------|-------------------|\n| **Unsafe (`!`)** | \"Trust me, bro.\" Fast, loose. | You write tests to catch edge cases. |\n| **Safe (`?`)** | The Contract. Inputs validated, outputs guaranteed. | \"I forgot to turn it off, and now my API is bulletproof.\" |\n\nLaziness = Quality. You did less work (no validation code), got a better result (robust contracts).\n\n---\n\n## The AJS Win\n\n**\"I don't deploy microservices. I save AJS records to Postgres. My 'deployment' takes 10ms.\"**\n\nAJS compiles to JSON. Your agent *is* a database record:\n\n```sql\nINSERT INTO agents (name, logic) VALUES (\n 'summarize-article',\n '{\"$seq\": [{\"$op\": \"httpFetch\", ...}, {\"$op\": \"llmPredict\", ...}]}'\n);\n```\n\nWant to update the agent? Update the row. Want to A/B test? Query different rows. Want to audit what changed? `git diff` the JSON.\n\n### What You Stop Doing\n\n- **No deploying microservices.** One endpoint runs any agent.\n- **No managing containers.** The VM is a function call.\n- **No CI/CD pipelines for logic changes.** Logic is data.\n- **No cold starts.** The endpoint is always hot.\n\n### What You Start Doing\n\n- Store agents as JSON in your database\n- Load and run them on demand\n- Ship new logic by inserting rows\n\nYour \"deployment\" is a database write. Your \"rollback\" is loading the previous row.\n\n---\n\n## The Stack You Delete\n\n| Before | After |\n|--------|-------|\n| TypeScript | TJS |\n| Webpack / Vite / Babel | Browser |\n| `node_modules` (300MB) | Import URLs |\n| Source maps | Direct execution |\n| Build server | Nothing |\n| Microservices | One endpoint |\n| Docker / K8s | A function call |\n| CI/CD for logic | Database write |\n\n---\n\n## The Vibe\n\nThis isn't about doing less work. It's about doing *the right* work.\n\nEvery hour you spend debugging build tools is an hour you're not shipping features. Every deployment pipeline you maintain is cognitive overhead you carry forever.\n\nTJS and AJS are opinionated about one thing: **the fastest path from idea to running code wins.**\n\n- Types should validate themselves\n- Code should run where you write it\n- Logic should travel as data\n- Deployment should be instant\n\nIf your stack disagrees, your stack is wrong.\n\n---\n\n## Who This Is For\n\n- **The solo dev** who doesn't have time for infrastructure theater\n- **The startup** that needs to ship features, not manage K8s\n- **The AI builder** whose agents need to evolve faster than deploy cycles allow\n- **The pragmatist** who knows that the best infrastructure is no infrastructure\n- **The TypeScript dev** who doesn't care about TJS but wants the tooling (see below)\n\n---\n\n## For TypeScript Developers Who Don't Care About TJS\n\nYou're happy with TypeScript. You don't want a new language. Fine.\n\nYou can still use TJS tooling for one thing: **inline tests that live with your code.**\n\n```typescript\n// mymath.ts - normal TypeScript\nfunction add(a: number, b: number): number {\n return a + b\n}\n\n/*test 'adds positive numbers' {\n expect(add(2, 3)).toBe(5)\n}*/\n\n/*test 'handles negatives' {\n expect(add(-1, 1)).toBe(0)\n}*/\n```\n\nThe `/*test ... */` comments survive TypeScript compilation. TJS extracts and runs them.\n\n### What You Get\n\n- **Literate programming.** Tests live next to the code they verify.\n- **Faster debug loops.** No switching between files.\n- **Private tests.** They're comments—they don't ship to production.\n- **Zero buy-in.** Keep your tsconfig, your build, your everything.\n\n### How It Works\n\n```typescript\nimport { extractTests, testUtils } from 'tjs-lang'\n\n// Read your compiled JS (or TS source)\nconst source = fs.readFileSync('mymath.js', 'utf-8')\n\n// Extract tests from comments\nconst { tests, testRunner } = extractTests(source)\n\n// Run them\nconst result = eval(source + '\\n' + testUtils + '\\n' + testRunner)\n// { passed: 2, failed: 0 }\n```\n\nThat's it. You don't adopt TJS. You don't change your types. You just get inline tests for free.\n\nSet `safety none` and keep living in your world. We're not here to convert you.\n\n---\n\n## Quick Start\n\n```bash\nnpm install tjs-lang\n```\n\n```typescript\nimport { tjs, AgentVM } from 'tjs-lang'\n\n// TJS: types as examples, runs in browser\nconst greet = tjs`\n function greet(name: 'World') -> '' {\n return 'Hello, ' + name + '!'\n }\n`\n\n// AJS: logic as data, runs in sandbox\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(agent, { url: 'https://api.example.com' })\n```\n\nThat's the whole stack. No config files. No build step. No deployment.\n\n---\n\n## Learn More\n\n- [TJS Documentation](DOCS-TJS.md) — The language reference\n- [AJS Documentation](DOCS-AJS.md) — The agent runtime\n- [Enterprise Guide](MANIFESTO-ENTERPRISE.md) — For when you need governance\n- [Playground](demo/) — Try it now, no install\n",
|
|
13
|
+
"title": "Applied Laziness: The \"Zero-Infrastructure\" Stack",
|
|
14
|
+
"filename": "MANIFESTO-BUILDER.md",
|
|
15
|
+
"path": "MANIFESTO-BUILDER.md",
|
|
16
|
+
"section": "meta",
|
|
17
|
+
"order": 1,
|
|
18
|
+
"navTitle": "For Builders"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"text": "<!--{\"section\": \"meta\", \"order\": 2, \"navTitle\": \"For Enterprise\"}-->\n\n# Governance: Safe Execution of Untrusted Logic\n\n**Adopt AI Agents without exposing your database to the Wild West.**\n\n---\n\n## The Problem\n\nYou want to run user-submitted code. Or LLM-generated agents. Or third-party integrations. But JavaScript has no sandbox, no resource limits, and no capability controls.\n\nEvery eval is a security incident waiting to happen. Every webhook is a potential DoS vector. Every \"plugin system\" is an attack surface.\n\nThe choice has been: accept the risk, or don't run untrusted code at all.\n\n---\n\n## The TJS Win\n\n**\"The Platform defines the capabilities. The Guest cannot escape them.\"**\n\nTJS is the language you use to build the trusted infrastructure—your servers, your APIs, your capability boundaries. It's designed to never crash, even when guests misbehave.\n\n### Monadic Errors: Exceptions are for Amateurs\n\nNo unhandled exceptions. Ever.\n\n```typescript\nconst result = createUser({ name: 123 })\n// Returns: { $error: true, message: 'Invalid input', path: 'createUser.input' }\n\nif (result.$error) {\n // Handle gracefully - log, retry, return to caller\n return { status: 'invalid', details: result }\n}\n```\n\nType failures return error objects, not exceptions. The host survives anything the guest throws at it.\n\n**TJS changes the physics of failure.** In every other language, a runtime type error is a catastrophe—uncaught exception, stack trace, 500 server error. In TJS, a type error is just *data*.\n\nMost developers see: `TypeError: Cannot read property 'x' of undefined at anonymous:5:12`\n\nTranslation: \"Something broke somewhere. Good luck.\"\n\nIn TJS Safe Mode, the error looks like:\n\n```javascript\n{\n $error: true,\n message: \"Expected 'positive number', got -5\",\n path: \"calculateTax.input.price\"\n}\n```\n\nTranslation: \"The function `calculateTax` received a bad `price`.\"\n\n**You trace the error to the source (the caller), not the symptom (the crash).**\n\nThis means your \"Universal Endpoint\" cannot crash due to bad data. It simply refuses the contract and tells the caller exactly why. Contracts are for pros.\n\n### Full Introspection\n\nEvery function carries its type metadata at runtime:\n\n```typescript\nconsole.log(createUser.__tjs)\n// {\n// params: { input: { type: { kind: 'object', shape: { name: 'string' } } } },\n// returns: { kind: 'object', shape: { id: 'number' } }\n// }\n```\n\nAudit trail of what code does what. No runtime surprises.\n\n### Minimal Supply Chain\n\n- **2 dependencies** (acorn parser, tosijs-schema)\n- **Zero transitive dependencies**\n- **~33KB gzipped** total\n\nLess code to audit. Smaller attack surface.\n\n---\n\n## The AJS Win\n\n**\"Every agent execution is gas-limited, auditable, and sandboxed. No infinite loops. No data exfiltration.\"**\n\nAJS is the language for untrusted code—user scripts, LLM-generated agents, third-party logic. It compiles to JSON and runs in an isolated VM with strict resource controls.\n\n### Capability-Based Security\n\nThe VM starts with **zero capabilities**. No network. No storage. No filesystem. Nothing.\n\nYou grant exactly what each agent needs:\n\n```typescript\nconst capabilities = {\n fetch: createFetchCapability({ \n allowedHosts: ['api.example.com'] // Only these domains\n }),\n store: createReadOnlyStore(), // Read but not write\n // No LLM capability - this agent can't call AI\n}\n\nawait vm.run(agent, args, { capabilities })\n```\n\nIf you don't grant it, the agent can't do it.\n\n### Fuel Metering\n\nEvery operation costs fuel. Loops can't run forever:\n\n```typescript\nconst result = await vm.run(agent, args, { \n fuel: 1000, // CPU budget\n timeoutMs: 5000 // Wall-clock limit\n})\n\nif (result.fuelExhausted) {\n // Agent tried to run forever - stopped safely\n}\n```\n\nLarge allocations cost more fuel. Memory bombs exhaust their budget before they explode.\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 // 5 second hard limit\n})\n```\n\nSlow network calls can't hang your servers.\n\n---\n\n## Threat Model\n\n| Threat | Defense |\n|--------|---------|\n| **Infinite loops** | Fuel exhaustion - every op costs gas |\n| **Memory bombs** | Proportional charging - large allocs cost more |\n| **SSRF** | URL allowlists in fetch capability |\n| **Prototype pollution** | Blocked property access (`__proto__`, `constructor`) |\n| **Code injection** | AST nodes, not string eval |\n| **ReDoS** | Suspicious regex rejection |\n| **Data exfiltration** | Zero capabilities by default |\n| **Resource exhaustion** | Per-request fuel + timeout limits |\n\n### What the Platform Controls\n\n| Resource | Mechanism |\n|----------|-----------|\n| CPU | Fuel budget |\n| Memory | Proportional charging |\n| Time | Timeout enforcement |\n| Network | Capability allowlists |\n| Storage | Capability scoping |\n| Recursion | Depth protocol |\n\n### What the Platform Trusts\n\n- **Custom atoms** are host code - you write them, you trust them\n- **Capabilities** determine exposure - misconfigured fetch is still dangerous\n- **The VM** prevents malicious *agents*, not malicious *atom implementations*\n\n---\n\n## Compliance\n\n### Auditable Execution\n\nEvery agent run can produce a trace:\n\n```typescript\nconst { result, trace } = await vm.run(agent, args, { trace: true })\n\n// trace contains:\n// - Every operation executed\n// - Inputs and outputs\n// - Fuel consumption\n// - Timestamps\n```\n\nFull visibility into what untrusted code actually did.\n\n### Per-Request Limits\n\n```typescript\nawait vm.run(agent, args, {\n fuel: 1000, // CPU budget per request\n timeoutMs: 5000, // Wall-clock limit\n capabilities: {...} // Scoped access\n})\n```\n\nNo single request can monopolize resources.\n\n### Per-Key Scoping\n\n```typescript\napp.post('/execute', async (req, res) => {\n const capabilities = getCapabilitiesForApiKey(req.apiKey)\n // Different API keys get different permissions\n await vm.run(agent, args, { capabilities })\n})\n```\n\nTenant isolation at the capability level.\n\n### Test Coverage\n\n- **966 tests passing**\n- **98% coverage** on security-critical runtime code\n- **Dedicated security tests** for malicious actor scenarios\n\n---\n\n## Who This Is For\n\n- **The CTO** who needs to adopt AI agents without security theater\n- **The security team** that wants defense in depth, not defense in hope\n- **The compliance officer** who needs audit trails and resource limits\n- **The platform team** building multi-tenant systems with untrusted code\n\n---\n\n## The Architecture\n\n```\n┌─────────────────────────────────────────────────┐\n│ TJS Platform │\n│ (Your trusted code - servers, APIs, capabilities)│\n├─────────────────────────────────────────────────┤\n│ │\n│ ┌─────────────────────────────────────────┐ │\n│ │ AJS Sandbox │ │\n│ │ (Untrusted code - agents, user scripts) │ │\n│ │ │ │\n│ │ • Zero capabilities by default │ │\n│ │ • Fuel-limited execution │ │\n│ │ • Timeout enforcement │ │\n│ │ • No direct I/O │ │\n│ └─────────────────────────────────────────┘ │\n│ │ │\n│ Capability Boundary │\n│ │ │\n│ ┌─────────────────────────────────────────┐ │\n│ │ Granted Capabilities │ │\n│ │ fetch: allowlist only │ │\n│ │ store: read-only or scoped │ │\n│ │ llm: if needed │ │\n│ └─────────────────────────────────────────┘ │\n│ │\n└─────────────────────────────────────────────────┘\n```\n\nThe guest can only reach resources you explicitly provide. Everything else is blocked.\n\n---\n\n## Learn More\n\n- [TJS Documentation](DOCS-TJS.md) — The language reference\n- [AJS Documentation](DOCS-AJS.md) — The agent runtime\n- [Builder's Manifesto](MANIFESTO-BUILDER.md) — For when you want speed\n- [Technical Context](CONTEXT.md) — Deep dive into architecture\n",
|
|
22
|
+
"title": "Governance: Safe Execution of Untrusted Logic",
|
|
23
|
+
"filename": "MANIFESTO-ENTERPRISE.md",
|
|
24
|
+
"path": "MANIFESTO-ENTERPRISE.md",
|
|
25
|
+
"section": "meta",
|
|
26
|
+
"order": 2,
|
|
27
|
+
"navTitle": "For Enterprise"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"text": "<!--{\"section\": \"tjs\", \"group\": \"docs\", \"order\": 0, \"navTitle\": \"Documentation\"}-->\n\n# TJS: Typed JavaScript\n\n*Types as Examples. Zero Build. Runtime Metadata.*\n\n---\n\n## What is TJS?\n\nTJS is a typed superset of JavaScript where **types are concrete values**, not abstract annotations.\n\n```typescript\n// TypeScript: abstract type annotation\nfunction greet(name: string): string\n\n// TJS: concrete example value\nfunction greet(name: 'World') -> '' { return `Hello, ${name}!` }\n```\n\nThe example `'World'` tells TJS that `name` is a string. The example `''` tells TJS the return type is a string. Types are inferred from the examples you provide.\n\nTJS transpiles to JavaScript with embedded `__tjs` metadata, enabling runtime type checking, autocomplete from live objects, and documentation generation.\n\n---\n\n## The Compiler\n\nTJS compiles in the browser. No webpack, no node_modules, no build server.\n\n```typescript\nimport { tjs } from 'tjs-lang'\n\nconst code = tjs`\n function add(a: 0, b: 0) -> 0 {\n return a + b\n }\n`\n\n// Returns transpiled JavaScript with __tjs metadata\n```\n\nYou can also use the CLI:\n\n```bash\nbun src/cli/tjs.ts check file.tjs # Parse and type check\nbun src/cli/tjs.ts run file.tjs # Transpile and execute\nbun src/cli/tjs.ts emit file.tjs # Output transpiled JS\nbun src/cli/tjs.ts types file.tjs # Output type metadata\n```\n\n---\n\n## Syntax\n\n### Parameter Types (Colon Syntax)\n\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: number\nfunction toggle(flag: true) { } // flag is required, type: boolean\n```\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\n```\n\n### TypeScript-Style Optional\n\nYou can also use `?:` syntax:\n\n```typescript\nfunction greet(name?: '') { } // same as name = ''\n```\n\n### Object Parameters\n\nObject shapes are defined by example:\n\n```typescript\nfunction createUser(user: { name: '', age: 0 }) { }\n// user must be an object with string name and number age\n```\n\n### Nullable Types\n\nUse `||` for union with null:\n\n```typescript\nfunction find(id: 0 || null) { } // number or null\n```\n\n### Return Types (Arrow Syntax)\n\nReturn types use `->`:\n\n```typescript\nfunction add(a: 0, b: 0) -> 0 {\n return a + b\n}\n\nfunction getUser(id: 0) -> { name: '', age: 0 } {\n return { name: 'Alice', age: 30 }\n}\n```\n\n### Array Types\n\nArrays use bracket syntax with an example element:\n\n```typescript\nfunction sum(numbers: [0]) -> 0 { // array of numbers\n return numbers.reduce((a, b) => a + b, 0)\n}\n\nfunction names(users: [{ name: '' }]) { // array of objects\n return users.map(u => u.name)\n}\n```\n\n---\n\n## Safety Markers\n\n### Unsafe Functions\n\nSkip validation for hot paths:\n\n```typescript\nfunction fastAdd(! a: 0, b: 0) { return a + b }\n```\n\nThe `!` marker after the function name skips input validation.\n\n### Safe Functions\n\nExplicit validation (for emphasis):\n\n```typescript\nfunction safeAdd(? a: 0, b: 0) { return a + b }\n```\n\n### Unsafe Blocks\n\nSkip validation for a block of code:\n\n```typescript\nunsafe {\n fastPath(data)\n anotherHotFunction(moreData)\n}\n```\n\n### Module Safety Directive\n\nSet the default validation level for an entire file:\n\n```typescript\nsafety none // No validation (metadata only)\nsafety inputs // Validate function inputs (default)\nsafety all // Validate everything (debug mode)\n```\n\n---\n\n## Type System\n\n### Type()\n\nDefine named types with predicates:\n\n```typescript\n// Simple type from example\nType Name 'Alice'\n\n// Type with description\nType User {\n description: 'a user object'\n example: { name: '', age: 0 }\n}\n\n// Type with predicate\nType PositiveNumber {\n description: 'a positive number'\n example: 1\n predicate(x) { return x > 0 }\n}\n```\n\nTypes can be used in function signatures:\n\n```typescript\nfunction greet(name: Name) -> '' {\n return `Hello, ${name}!`\n}\n```\n\n### Generic()\n\nRuntime-checkable generics:\n\n```typescript\nGeneric Box<T> {\n description: 'a boxed value'\n predicate(x, T) { \n return typeof x === 'object' && x !== null && 'value' in x && T(x.value) \n }\n}\n\n// With default type parameter\nGeneric Container<T, U = ''> {\n description: 'container with label'\n predicate(obj, T, U) { \n return T(obj.item) && U(obj.label) \n }\n}\n```\n\n### Union()\n\nDiscriminated unions:\n\n```typescript\nconst Shape = Union('kind', {\n circle: { radius: 0 },\n rectangle: { width: 0, height: 0 }\n})\n\nfunction area(shape: Shape) -> 0 {\n if (shape.kind === 'circle') {\n return Math.PI * shape.radius ** 2\n }\n return shape.width * shape.height\n}\n```\n\n### Enum()\n\nString or numeric enums:\n\n```typescript\nconst Status = Enum(['pending', 'active', 'completed'])\nconst Priority = Enum({ low: 1, medium: 2, high: 3 })\n\nfunction setStatus(status: Status) { }\n```\n\n---\n\n## Structural Equality: Is / IsNot\n\nJavaScript's `==` is broken (type coercion). TJS provides structural equality:\n\n```typescript\n// Structural comparison - no coercion\n[1, 2] Is [1, 2] // true\n5 Is \"5\" // false (different types)\n{ a: 1 } Is { a: 1 } // true\n\n// Arrays compared element-by-element\n[1, [2, 3]] Is [1, [2, 3]] // true\n\n// Negation\n5 IsNot \"5\" // true\n```\n\n### Custom Equality\n\nClasses can define an `.Equals` method:\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---\n\n## Classes\n\n### Callable Without `new`\n\nTJS classes are callable without the `new` keyword:\n\n```typescript\nclass User {\n constructor(name: '') { this.name = name }\n}\n\n// Both work identically:\nconst u1 = User('Alice') // TJS way - clean\nconst u2 = new User('Alice') // Also works (linter warns)\n```\n\n### Private Fields\n\nUse `#` for private fields:\n\n```typescript\nclass Counter {\n #count = 0\n \n increment() { this.#count++ }\n get value() { return this.#count }\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## 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- Autocomplete from live objects\n- Runtime type validation\n- Automatic documentation generation\n\n### Monadic Errors\n\nType failures return error objects, not exceptions:\n\n```typescript\nconst result = createUser({ name: 123 }) // wrong type\n// { $error: true, message: 'Invalid input', path: 'createUser.input' }\n\nif (result.$error) {\n // Handle gracefully\n}\n```\n\nNo try/catch gambling. The host survives invalid inputs.\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- 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---\n\n## Module System\n\n### Imports\n\nTJS supports URL imports:\n\n```typescript\nimport { Button } from 'https://cdn.example.com/ui-kit.tjs'\nimport { validate } from './utils/validation.tjs'\n```\n\nModules are:\n- Fetched on demand\n- Transpiled in the browser\n- Cached independently (IndexedDB + service worker)\n\n### CDN Integration\n\nExternal packages from esm.sh with pinned versions:\n\n```typescript\nimport lodash from 'https://esm.sh/lodash@4.17.21'\n```\n\n---\n\n## TypeScript Compatibility\n\n### TS → TJS Converter\n\nConvert existing TypeScript:\n\n```bash\nbun src/cli/tjs.ts convert file.ts\n```\n\n```typescript\n// TypeScript\nfunction greet(name: string, age?: number): string { ... }\n\n// Converts to TJS\nfunction greet(name: '', age = 0) -> '' { ... }\n```\n\n### What Gets Converted\n\n| TypeScript | TJS |\n|------------|-----|\n| `name: string` | `name: ''` |\n| `age: number` | `age: 0` |\n| `flag: boolean` | `flag: true` |\n| `items: string[]` | `items: ['']` |\n| `age?: number` | `age = 0` |\n| `private foo` | `#foo` |\n| `interface User` | `Type User` |\n| `type Status = 'a' \\| 'b'` | `Union(['a', 'b'])` |\n| `enum Color` | `Enum(...)` |\n\n---\n\n## Performance\n\n| Mode | Overhead | Use Case |\n|------|----------|----------|\n| `safety none` | **1.0x** | Metadata only, no validation |\n| `safety inputs` | **~1.5x** | Production (single-arg objects) |\n| `(!) unsafe` | **1.0x** | Hot paths |\n| `wasm {}` | **<1.0x** | Compute-heavy code |\n\n### Why 1.5x, Not 25x\n\nMost validators interpret schemas at runtime (~25x overhead). TJS generates inline checks at transpile time:\n\n```typescript\n// Generated (JIT-friendly)\nif (typeof input !== 'object' || input === null ||\n typeof input.name !== 'string' ||\n typeof input.age !== 'number') {\n return { $error: true, message: 'Invalid input', path: 'fn.input' }\n}\n```\n\nNo schema interpretation. No object iteration. The JIT inlines these completely.\n\n---\n\n## Bare Assignments\n\nUppercase identifiers automatically get `const`:\n\n```typescript\nFoo = Type('test', 'example') // becomes: const Foo = Type(...)\nMyConfig = { debug: true } // becomes: const MyConfig = { ... }\n```\n\n---\n\n## Limitations\n\n### What TJS Doesn't Do\n\n- **No gradual typing** - types are all-or-nothing per function\n- **No complex type inference** - you provide examples, not constraints\n- **No declaration files** - types live in the code, not `.d.ts`\n- **No type-level computation** - no conditional types, mapped types, etc.\n\n### What TJS Intentionally Avoids\n\n- Build steps\n- External type checkers\n- Complex tooling configuration\n- Separation of types from runtime\n\n---\n\n## Learn More\n\n- [AJS Documentation](DOCS-AJS.md) — The agent runtime\n- [Builder's Manifesto](MANIFESTO-BUILDER.md) — Why TJS is fun\n- [Enterprise Guide](MANIFESTO-ENTERPRISE.md) — Why TJS is safe\n- [Technical Context](CONTEXT.md) — Architecture deep dive\n",
|
|
31
|
+
"title": "TJS: Typed JavaScript",
|
|
32
|
+
"filename": "DOCS-TJS.md",
|
|
33
|
+
"path": "DOCS-TJS.md",
|
|
34
|
+
"section": "tjs",
|
|
35
|
+
"group": "docs",
|
|
36
|
+
"order": 0,
|
|
37
|
+
"navTitle": "Documentation"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"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- 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(agent, { url: 'https://api.example.com' }, {\n fuel: 1000, // CPU budget\n timeoutMs: 5000 // Wall-clock limit\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## 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 'big'\n} else {\n return 'small'\n}\n\n// Loops\nfor (let i = 0; i < 10; i++) {\n total = total + i\n}\n\nfor (let item of items) {\n results.push(item.name)\n}\n\nwhile (count > 0) {\n count = count - 1\n}\n\n// Try/catch\ntry {\n riskyOperation()\n} catch (e) {\n return { error: e.message }\n}\n\n// Template literals\nlet message = `Hello, ${name}!`\n\n// Object/array literals\nlet obj = { a: 1, b: 2 }\nlet arr = [1, 2, 3]\n\n// Destructuring\nlet { name, age } = user\nlet [first, second] = items\n\n// Spread\nlet merged = { ...defaults, ...overrides }\nlet combined = [...arr1, ...arr2]\n\n// Ternary\nlet result = x > 0 ? 'positive' : 'non-positive'\n\n// Logical operators\nlet value = a && b\nlet fallback = a || defaultValue\nlet nullish = a ?? defaultValue\n```\n\n### What's Forbidden\n\n| Feature | Why Forbidden |\n|---------|---------------|\n| `class` | Too complex for LLMs, enables prototype pollution |\n| `new` | Arbitrary object construction |\n| `this` | Implicit context, hard to sandbox |\n| Closures | State escapes the sandbox |\n| `async`/`await` | VM handles async internally |\n| `eval`, `Function` | Code injection |\n| `__proto__`, `constructor` | Prototype pollution |\n| `import`/`export` | Module system handled by host |\n\nAJS is intentionally simple—simple enough for 4B parameter LLMs to generate correctly.\n\n---\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\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### AI\n\n| Atom | Description |\n|------|-------------|\n| `llmPredict` | LLM inference |\n| `agentRun` | Run a sub-agent |\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## 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('user', 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## 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 { \"$op\": \"return\", \"value\": { \"$expr\": \"binary\", \"op\": \"+\", \"left\": \"x\", \"right\": \"y\" } }\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- 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- 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\n .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\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- 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",
|
|
41
|
+
"title": "AJS: The Agent Language",
|
|
42
|
+
"filename": "DOCS-AJS.md",
|
|
43
|
+
"path": "DOCS-AJS.md",
|
|
44
|
+
"section": "ajs",
|
|
45
|
+
"group": "docs",
|
|
46
|
+
"order": 0,
|
|
47
|
+
"navTitle": "Documentation"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"text": "<!--{\"section\": \"ajs\", \"group\": \"docs\", \"order\": 1, \"navTitle\": \"Technical Context\"}-->\n\n# tjs-lang Technical Context\n\n**Note:** This document provides a technical deep-dive into tjs-lang's architecture and security model. For a general overview, installation instructions, and usage examples, please refer to the main [README.md](./README.md).\n\n**tjs-lang** is a secure, environment-agnostic runtime for executing AI agents and logic chains defined as JSON ASTs.\n\n**Bundle Size:** ~17KB gzipped. Expressions are evaluated via lightweight AST nodes at runtime, eliminating the need for a parser library (the previous JSEP-based approach was ~50KB gzipped).\n\n## 1. Architecture\n\n### The Builder (`TypedBuilder`)\n\nA fluent TypeScript API that generates a portable JSON AST. It uses a `Proxy` to dynamically infer methods from the registered Atoms, providing a strongly-typed developer experience.\n\nIt is important to understand that the builder is only for constructing the AST; it does not contain any of the actual implementation logic for the atoms. All execution is handled by the Runtime.\n\n**Usage Pattern:**\n\n- All logic chains **must** start with `Agent.take()` to define the input schema for the agent.\n- Subsequent atom calls are chained together fluently (e.g., `.varSet(...)`, `.httpFetch(...)`). This creates an implicit `seq` (sequence) of operations.\n- The chain is terminated by calling `.toJSON()` to produce the serializable AST.\n\nYou can access the builder via `Agent` (for core atoms) or `vm.Agent` (the recommended way to access both core and any custom atoms registered with the VM instance).\n\n```typescript\nimport { Agent, s, AgentVM } from 'tjs-lang'\n\n// Global Builder (Core Atoms)\nconst logic = Agent.take(s.object({ input: s.string }))\n .varSet({ key: 'sum', value: { $expr: 'binary', op: '+', left: { $expr: 'literal', value: 1 }, right: { $expr: 'literal', value: 1 } } })\n\n// VM Builder (Custom Atoms)\nconst vm = new AgentVM({ myAtom })\nconst customLogic = vm.Agent\n .myAtom({ ... })\n .varSet({ ... })\n```\n\n### The Runtime (`AgentVM`)\n\nA stateless Virtual Machine that executes the AST. The runtime contains all the actual implementation logic for the atoms.\n\n- **Sandboxed:** No `eval()`. Math/Logic is evaluated safely via AST expression nodes.\n- **Resource Limited:** Enforces `fuel` (gas) limits and execution timeouts per atom.\n- **Capability-Based:** All IO (Network, DB, AI) must be injected via `capabilities` object.\n\n```typescript\nimport { AgentVM } from 'tjs-lang'\nconst vm = new AgentVM()\nconst { result, fuelUsed } = await vm.run(ast, args, {\n capabilities,\n fuel: 1000,\n})\n```\n\n## 2. Expression Syntax (ExprNode)\n\nExpressions use AST expression nodes (`$expr`) for safe, sandboxed evaluation. Conditions in `if` and `while` atoms use expression strings that are parsed at transpile time.\n\nFor security, expressions are sandboxed and cannot directly access the agent's state. Use the `vars` parameter to explicitly pass variables from state into the expression scope.\n\n### ExprNode Types\n\n- **literal:** `{ $expr: 'literal', value: 5 }` - A constant value\n- **ident:** `{ $expr: 'ident', name: 'x' }` - A variable reference\n- **member:** `{ $expr: 'member', object: {...}, property: 'foo' }` - Property access\n- **binary:** `{ $expr: 'binary', op: '+', left: {...}, right: {...} }` - Binary operations\n- **unary:** `{ $expr: 'unary', op: '-', argument: {...} }` - Unary operations\n- **conditional:** `{ $expr: 'conditional', test: {...}, consequent: {...}, alternate: {...} }` - Ternary\n\n### Supported Operators\n\n- **Binary ops:** `+`, `-`, `*`, `/`, `%`, `**`\n- **Logic:** `&&`, `||`\n- **Comparison:** `==`, `!=`, `>`, `<`, `>=`, `<=`\n- **Member Access:** `obj.prop`, `arr[0]`\n\n### Security\n\n- **Forbidden:** Function calls, `new`, `this`, global access\n- **Blocked:** Prototype access (`__proto__`, `constructor`)\n\n### Fuel Consumption\n\nEach expression node evaluation consumes **0.01 fuel**. This prevents deeply nested or recursive expressions from running unchecked. A simple `a + b` costs ~0.03 fuel (two identifiers + one binary op), while complex nested expressions accumulate cost proportionally.\n\n## 3. Security Model\n\n- **Capabilities:** The VM has no default IO. You must provide `fetch`, `store`, etc., allowing you to mock, proxy, or limit access.\n- **Fuel:** Every atom consumes \"fuel\". Execution aborts if fuel reaches 0.\n- **Execution Timeout:** The VM enforces a global timeout based on fuel budget (see below).\n- **Atom Timeouts:** Individual atoms have a default timeout (1s) to prevent hangs.\n- **State Isolation:** Each run creates a fresh context. Scopes (loops/maps) use prototype inheritance to isolate local variables.\n\n### Execution Timeout\n\nThe VM enforces a hard timeout on execution to prevent hung agents—safeguarding against code that effectively halts by waiting on slow or non-responsive IO.\n\n**How it works:**\n\n1. **Automatic Safety Net:** By default, timeout = `fuel × 10ms`. So 1000 fuel = 10 seconds. _For IO-heavy agents with low fuel costs, explicitly set `timeoutMs` to prevent premature timeouts._\n2. **Explicit SLA:** Pass `timeoutMs` to enforce a strict time limit regardless of fuel.\n3. **External Cancellation:** Pass an `AbortSignal` to integrate with external controllers (user cancellation, HTTP timeouts, etc.).\n\n```typescript\n// Default: 1000 fuel = 10 second timeout\nawait vm.run(ast, args, { fuel: 1000 })\n\n// Explicit timeout: 5 seconds regardless of fuel\nawait vm.run(ast, args, { fuel: 10000, timeoutMs: 5000 })\n\n// External abort signal\nconst controller = new AbortController()\nsetTimeout(() => controller.abort(), 3000) // Cancel after 3s\nawait vm.run(ast, args, { signal: controller.signal })\n```\n\n**Resource Cleanup:** When a timeout occurs, the VM passes the abort signal to the currently executing atom via `ctx.signal`. Loop atoms (`while`, `map`, `filter`, `reduce`, `find`) check the signal between iterations. `httpFetch` passes the signal to `fetch` for immediate request cancellation.\n\n**Timeout vs Fuel:**\n\n- **Fuel** protects against CPU-bound abuse (tight loops burning compute)\n- **Timeout** protects against IO-bound abuse (slow network calls, hung promises)\n\nBoth work together to ensure the VM cannot be held hostage by untrusted code.\n\n**Trust Boundary:** The sandbox protects against malicious _agents_ (untrusted AST), not malicious _atom implementations_. Atoms are registered by the host and are trusted to:\n\n1. Be non-blocking (no synchronous CPU-heavy work)\n2. Respect `ctx.signal` for cancellation\n3. Clean up resources when aborted\n\nIf you write custom atoms, ensure they check `ctx.signal?.aborted` in loops and pass `ctx.signal` to any async operations like `fetch`.\n\n### Cost Overrides\n\nDefault atom costs are guesses. Override them per-run to match your deployment reality:\n\n```typescript\nawait vm.run(ast, args, {\n costOverrides: {\n // Static: fixed cost per invocation\n httpFetch: 50,\n llmPredict: 500,\n\n // Dynamic: cost based on input\n storeSet: (input) => JSON.stringify(input.value).length * 0.001,\n llmPredict: (input) => (input.model?.includes('gpt-4') ? 1000 : 100),\n },\n})\n```\n\nUse cases:\n\n- **API rate limits:** Make external API calls expensive to stay under quota\n- **Metered billing:** Reflect actual dollar costs in fuel consumption\n- **Resource protection:** Make database writes cost more than reads\n- **Testing:** Set all costs to 0 to focus on logic, not budgeting\n\n### Request Context\n\nThe `context` option passes request-scoped metadata to atoms. Unlike `args` (agent input) or `capabilities` (IO implementations), context carries ambient data like auth, permissions, and request tracing.\n\n```typescript\nawait vm.run(ast, args, {\n context: {\n userId: 'user-123',\n role: 'admin',\n permissions: ['read:data', 'write:data', 'fetch:external'],\n requestId: 'req-abc-123',\n },\n})\n```\n\nAtoms access it via `ctx.context`:\n\n```typescript\nconst secureFetch = defineAtom(\n 'secureFetch',\n s.object({ url: s.string }),\n s.any,\n async (input, ctx) => {\n // Check permissions\n if (!ctx.context?.permissions?.includes('fetch:external')) {\n throw new Error('Not authorized for external fetch')\n }\n return ctx.capabilities.fetch(input.url)\n }\n)\n```\n\n**Design rationale:**\n\n- **Immutable:** Context is read-only; agents cannot modify their own permissions\n- **Separate from args:** Auth data doesn't pollute the agent's input schema\n- **Separate from capabilities:** Same capability implementation, different authorization\n- **Composable:** Works with cost overrides for user-tier-based fuel costs\n\n**Production patterns:**\n\n```typescript\n// Firebase/Express integration\napp.post('/run-agent', async (req, res) => {\n const ast = req.body.ast\n const args = req.body.args\n\n // Extract auth from request\n const user = await verifyToken(req.headers.authorization)\n\n const result = await vm.run(ast, args, {\n context: {\n userId: user.id,\n role: user.role,\n permissions: user.permissions,\n requestId: req.id,\n },\n // User-tier-based costs\n costOverrides: {\n llmPredict: user.tier === 'premium' ? 10 : 100,\n },\n })\n\n res.json(result)\n})\n```\n\n## 4. Stored Procedures\n\nThe procedure store provides a built-in mechanism for storing ASTs as callable tokens. This enables function-pointer-like patterns where behavior can be passed as data.\n\n### Storage Model\n\n```typescript\n// Module-level storage in runtime.ts\nconst procedureStore = new Map<\n string,\n {\n ast: any\n createdAt: number\n expiresAt: number\n }\n>()\n```\n\n**Constants:**\n\n- `PROCEDURE_TOKEN_PREFIX`: `'proc_'` - All tokens start with this prefix\n- `DEFAULT_PROCEDURE_TTL`: 3,600,000ms (1 hour)\n- `DEFAULT_MAX_AST_SIZE`: 102,400 bytes (100KB)\n\n### Token Resolution\n\nTokens can be used anywhere an AST is accepted:\n\n1. **`vm.run(token, args)`** - Direct execution via VM\n2. **`agentRun({ agentId: token, input })`** - Execution from within an agent\n3. **`agentRun({ agentId: ast, input })`** - Raw AST also accepted (no storage needed)\n\nResolution happens at runtime. If a string starts with `proc_`, the VM looks it up in the store. Expired or missing tokens throw clear errors.\n\n### Fuel Costs\n\n| Atom | Cost | Notes |\n| ------------------------ | ---- | ------------------------------- |\n| `storeProcedure` | 1.0 | Plus 0.001 per byte of AST |\n| `releaseProcedure` | 0.5 | Constant |\n| `clearExpiredProcedures` | 0.5 | Plus 0.01 per procedure scanned |\n\n### Security Considerations\n\n**Memory bounds:** The `maxSize` parameter prevents storing arbitrarily large ASTs. Default 100KB is generous for most agents.\n\n**Expiry:** TTL prevents memory leaks from abandoned procedures. The store is in-memory, so procedures don't survive process restarts.\n\n**No capability escalation:** Stored procedures inherit the capabilities of the calling context, not the storing context. A malicious agent cannot store a procedure that later executes with elevated privileges.\n\n**Token predictability:** Tokens are UUIDs, not sequential. They cannot be enumerated or guessed.\n\n### Use Cases\n\n**Dynamic dispatch (strategy pattern):**\n\n```typescript\nconst strategies = ajs`\n function dispatch({ strategyToken, data }) {\n let result = agentRun({ agentId: strategyToken, input: { data } })\n return result\n }\n`\n```\n\n**Worker pool:**\n\n```typescript\nconst orchestrator = ajs`\n function orchestrate({ workers, tasks }) {\n let results = []\n for (let i = 0; i < tasks.length; i = i + 1) {\n let workerToken = workers[i % workers.length]\n let r = agentRun({ agentId: workerToken, input: tasks[i] })\n results.push(r)\n }\n return { results }\n }\n`\n```\n\n**Callback registration:**\n\n```typescript\nconst registerCallback = ajs`\n function register({ handler }) {\n let token = storeProcedure({ ast: handler, ttl: 300000 })\n return { callbackId: token }\n }\n`\n```\n\n## 5. Production Considerations\n\n### Recursive Agent Fuel\n\nWhen an agent calls sub-agents via `agentRun`, each sub-agent gets its own fuel budget (passed via the capability). Fuel is **not shared** across the call tree by default.\n\n**Why:** The `agentRun` atom delegates to `ctx.capabilities.agent.run`, which the host implements. This gives operators full control over sub-agent resource allocation.\n\n**Patterns for shared fuel:**\n\n```typescript\n// Option 1: Pass remaining fuel to children\nconst sharedFuel = { current: 1000 }\n\nconst caps = {\n agent: {\n run: async (agentId, input) => {\n if (sharedFuel.current <= 0) throw new Error('Out of shared fuel')\n const result = await vm.run(agents[agentId], input, {\n fuel: sharedFuel.current,\n capabilities: caps,\n })\n sharedFuel.current -= result.fuelUsed\n return result.result\n },\n },\n}\n\n// Option 2: Fixed budget per recursion depth\nconst caps = {\n agent: {\n run: async (agentId, input) => {\n // Each child gets 10% of parent's budget\n return vm.run(agents[agentId], input, {\n fuel: 100, // Fixed small budget\n capabilities: caps,\n })\n },\n },\n}\n```\n\n### Streaming and Long-Running Agents\n\nThe VM returns results only after complete execution. For long-running agents:\n\n- Use `timeoutMs` to enforce SLAs\n- Use `AbortSignal` for user-initiated cancellation\n- Use `trace: true` for post-hoc debugging\n\n**For real-time streaming**, implement a custom atom that emits intermediate results:\n\n```typescript\nconst streamingAtom = defineAtom(\n 'streamChunk',\n s.object({ data: s.any }),\n s.null,\n async ({ data }, ctx) => {\n // ctx.context contains your streaming callback\n await ctx.context?.onChunk?.(data)\n return null\n }\n)\n\n// Usage\nawait vm.run(ast, args, {\n context: {\n onChunk: (data) => res.write(JSON.stringify(data) + '\\n'),\n },\n})\n```\n\n### Condition String Syntax\n\nThe condition parser in `if`/`while` atoms supports a subset of expression syntax:\n\n| Supported | Example |\n| ------------- | --------------------------------- |\n| Comparisons | `a > b`, `x == 'hello'`, `n != 0` |\n| Logical | `a && b`, `a \\|\\| b`, `!a` |\n| Arithmetic | `a + b * c`, `(a + b) / c` |\n| Member access | `obj.foo.bar` |\n| Literals | `42`, `\"string\"`, `true`, `null` |\n\n| **Unsupported** | Alternative |\n| ---------------------- | ---------------------------------- |\n| Ternary `a ? b : c` | Use nested `if` atoms |\n| Array index `a[0]` | Use ExprNode with `computed: true` |\n| Function calls `fn(x)` | Use atoms |\n| Chained `a > b > c` | Use `a > b && b > c` |\n\nUnsupported syntax now throws a clear error at build time with suggestions.\n\n### State Semantics\n\nAgents are **not transactional**. If an atom fails mid-execution:\n\n- Previous state changes persist\n- No automatic rollback\n- Error is captured in monadic flow (`ctx.error`)\n\nThis is by design—agents are stateful pipelines, not database transactions. If you need atomicity, implement checkpoint/restore in your capabilities:\n\n```typescript\nconst caps = {\n store: {\n set: async (key, value) => {\n await db.runTransaction(async (tx) => {\n await tx.set(key, value)\n })\n },\n },\n}\n```\n\n### Error Handling Granularity\n\nThe `try/catch` atom catches all errors in the try block. There's no selective catch by error type.\n\n**Pattern for error type handling:**\n\n```typescript\nAgent.take(s.object({})).try({\n try: (b) => b.httpFetch({ url: '...' }).as('response'),\n catch: (b) =>\n b\n .varSet({ key: 'errorType', value: 'unknown' })\n // Check error message patterns\n .if(\n 'msg.includes(\"timeout\")',\n { msg: 'error.message' },\n (then) => then.varSet({ key: 'errorType', value: 'timeout' }),\n (el) =>\n el.if('msg.includes(\"404\")', { msg: 'error.message' }, (then) =>\n then.varSet({ key: 'errorType', value: 'not_found' })\n )\n ),\n})\n```\n\n## 9. Test Coverage\n\n**Summary (as of January 2025):**\n\n| Metric | Value |\n| --------- | ------ |\n| Tests | 508 |\n| Functions | 84.77% |\n| Lines | 80.36% |\n\n### Coverage by Component\n\n**Core Runtime (security-critical):**\n\n| File | Functions | Lines | Notes |\n| ------------------------- | --------- | ------- | ---------------------------- |\n| `src/runtime.ts` | 100% | 100% | Re-exports |\n| `src/vm.ts` | 100% | 100% | Re-exports |\n| `src/vm/runtime.ts` | 84% | **98%** | Atoms, expression eval, fuel |\n| `src/vm/vm.ts` | 90% | 94% | VM entry point |\n| `src/transpiler/index.ts` | 100% | 100% | AJS transpiler |\n| `src/builder.ts` | 92% | 90% | Fluent builder |\n\n**Language/Transpiler:**\n\n| File | Functions | Lines | Notes |\n| -------------------------- | --------- | ----- | ------------------------------- |\n| `src/lang/emitters/ast.ts` | 94% | 83% | TJS → AST |\n| `src/lang/parser.ts` | 92% | 82% | TJS parser |\n| `src/lang/inference.ts` | 57% | 60% | Type inference (lower priority) |\n\n**Test Categories:**\n\n| Category | Tests | Coverage |\n| -------------------------- | ----- | ---------------------------------------------------- |\n| Security (malicious actor) | 10 | Prototype access, SSRF, ReDoS, path traversal |\n| Runtime core | 25+ | Fuel, timeout, tracing, expressions |\n| Stress/Memory | 6 | Large arrays, deep nesting, memory pressure |\n| Capability failures | 10 | Network errors, store failures, partial capabilities |\n| Allocation fuel | 4 | Proportional charging for strings/arrays |\n| Transpiler | 50+ | Language features, edge cases |\n| Use cases | 100+ | RAG, orchestration, client-server patterns |\n\n### Running Tests\n\n```bash\n# Full suite\nbun test\n\n# Fast (skip LLM and benchmarks)\nSKIP_LLM_TESTS=1 AGENT99_TESTS_SKIP_BENCHMARKS=1 bun test\n\n# With coverage\nbun test --coverage\n```\n\n## 10. Dependencies\n\n### Runtime Dependencies\n\nThese ship with the library and affect bundle size and security posture.\n\n| Package | Version | Size | Purpose | Risk |\n| --------------- | ------- | ----- | ------------------------------------------- | ---------------------------------------------------- |\n| `acorn` | ^8.15.0 | ~30KB | JavaScript parser for AJS transpilation | **Low** - Mature, widely audited, Mozilla-maintained |\n| `tosijs-schema` | ^1.2.0 | ~5KB | JSON Schema validation | **Low** - Our library, 96.6% coverage, zero deps |\n| `@codemirror/*` | various | ~50KB | Editor syntax highlighting (optional) | **Low** - Only loaded for editor integration |\n\n**Total runtime footprint:** ~33KB gzipped (core), ~83KB with editor support.\n\n### tosijs-schema 1.2.0 Coverage\n\nOur validation dependency maintains comprehensive test coverage:\n\n- **98.25% function coverage, 96.62% line coverage** (146 tests, 349 assertions)\n- **Edge cases tested:** NaN, Infinity, -0, sparse arrays, unicode, deeply nested structures\n- **Complex unions:** nested unions, discriminated unions, union of arrays, union with null/undefined\n- **Format validators:** email, ipv4 boundaries, url protocols, datetime variants\n- **Strict mode:** `validate(data, schema, { strict: true })` validates every item\n\nBecause tosijs-schema schemas are JSON data (not code), library coverage extends to user-defined schemas—unlike Zod where user schema compositions are untested code.\n\n### Development Dependencies\n\nNot shipped to users. Used for building, testing, and development.\n\n| Package | Purpose | Notes |\n| ---------------------- | ----------------------------- | ----------------------- |\n| `typescript` | Type checking and compilation | Standard |\n| `bun` | Runtime, bundler, test runner | Fast, modern |\n| `eslint` / `prettier` | Code quality | Standard |\n| `acorn-walk` | AST traversal for transpiler | Only used at build time |\n| `codemirror` | Editor components for demo | Demo only |\n| `tosijs` / `tosijs-ui` | Demo UI framework | Demo only |\n| `happy-dom` | DOM mocking for tests | Test only |\n| `vitest` | Alternative test runner | Optional |\n\n### Dependency Risk Assessment\n\n**Supply Chain:**\n\n| Risk | Mitigation |\n| ------------------------ | -------------------------------------------------------- |\n| Acorn compromise | Mature project (10+ years), Mozilla backing, widely used |\n| tosijs-schema compromise | We control this library |\n| Transitive dependencies | Minimal—acorn has 0 deps, tosijs-schema has 0 deps |\n\n**Version Pinning:**\n\n- Production dependencies use caret (`^`) for patch updates\n- Consider using exact versions or lockfile for production deployments\n\n**Audit:**\n\n```bash\n# Check for known vulnerabilities\nbun audit\n# or\nnpm audit\n```\n\n### What We Don't Depend On\n\nNotably absent from our dependency tree:\n\n| Common Dependency | Why We Don't Use It |\n| ----------------- | --------------------------------------- |\n| lodash | Native JS methods suffice |\n| axios | Native fetch + capability injection |\n| moment/dayjs | Built-in Date wrapper in expressions |\n| zod/yup | tosijs-schema is lighter and sufficient |\n| jsep | Replaced with acorn + custom AST nodes |\n\nThis minimal dependency approach reduces supply chain risk and bundle size.\n",
|
|
51
|
+
"title": "tjs-lang Technical Context",
|
|
52
|
+
"filename": "CONTEXT.md",
|
|
53
|
+
"path": "CONTEXT.md",
|
|
54
|
+
"section": "ajs",
|
|
55
|
+
"group": "docs",
|
|
56
|
+
"order": 1,
|
|
57
|
+
"navTitle": "Technical Context"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"text": "# Agent Instructions\n\nThis project uses **bd** (beads) for issue tracking. Run `bd onboard` to get started.\n\n## Quick Reference\n\n```bash\nbd ready # Find available work\nbd show <id> # View issue details\nbd update <id> --status in_progress # Claim work\nbd close <id> # Complete work\nbd sync # Sync with git\n```\n\n## Landing the Plane (Session Completion)\n\n**When ending a work session**, you MUST complete ALL steps below. Work is NOT complete until `git push` succeeds.\n\n**MANDATORY WORKFLOW:**\n\n1. **File issues for remaining work** - Create issues for anything that needs follow-up\n2. **Run quality gates** (if code changed) - Tests, linters, builds\n3. **Update issue status** - Close finished work, update in-progress items\n4. **PUSH TO REMOTE** - This is MANDATORY:\n ```bash\n git pull --rebase\n bd sync\n git push\n git status # MUST show \"up to date with origin\"\n ```\n5. **Clean up** - Clear stashes, prune remote branches\n6. **Verify** - All changes committed AND pushed\n7. **Hand off** - Provide context for next session\n\n**CRITICAL RULES:**\n- Work is NOT complete until `git push` succeeds\n- NEVER stop before pushing - that leaves work stranded locally\n- NEVER say \"ready to push when you are\" - YOU must push\n- If push fails, resolve and retry until it succeeds\n\n",
|
|
61
|
+
"title": "Agent Instructions",
|
|
62
|
+
"filename": "AGENTS.md",
|
|
63
|
+
"path": "AGENTS.md"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"text": "# AJS (.ajs) - A Better JavaScript for AI Agents\n\nAJS is a JavaScript subset designed for writing AI agent logic. It compiles to Agent99's secure JSON AST format, providing familiar syntax with cleaner semantics.\n\n> **For LLM Integration:** See [ajs-llm-prompt.md](./ajs-llm-prompt.md) for a system prompt optimized for code generation.\n\n## File Extension\n\nAJS files use the `.ajs` extension to distinguish them from standard JavaScript:\n\n```\nmy-agent.ajs\nsearch-tool.ajs\n```\n\n## Why AJS?\n\n| Problem with JavaScript | AJS Solution |\n| ----------------------------- | ----------------------------------------------- |\n| `async/await` boilerplate | All calls are implicitly async |\n| Complex error handling | Monadic error flow - errors propagate as values |\n| No built-in type safety | Types through example values |\n| Security concerns with `eval` | Compiles to sandboxed VM |\n\n## Quick Example\n\n```javascript\n// search-agent.ajs\n\n/**\n * Search and summarize information about a topic\n * @param topic - The topic to research\n * @param maxResults - Maximum number of results\n */\nfunction searchAgent(topic: 'climate change', maxResults = 5) {\n let results = search({ query: topic, limit: maxResults })\n\n if (results.length == 0) {\n return { summary: 'No results found', sources: [] }\n }\n\n let summary = llmPredict({\n system: 'Summarize these search results concisely',\n user: results,\n })\n\n return { summary, sources: results }\n}\n```\n\n## Core Differences from JavaScript\n\n### 1. Implicit Async\n\nAll function calls that invoke atoms are automatically awaited. No `async/await` keywords needed.\n\n```javascript\n// AJS - clean and simple\nfunction agent(topic: 'machine learning') {\n let results = search({ query: topic })\n let summary = summarize({ text: results })\n return { summary }\n}\n\n// Equivalent JavaScript would require:\n// async function agent(topic) {\n// let results = await search({ query: topic })\n// let summary = await summarize({ text: results })\n// return { summary }\n// }\n```\n\n### 2. Types Through Example Values\n\nTypes are inferred from example values. The example shows both the type AND a realistic value:\n\n```javascript\nfunction greet(\n name: 'Anne Example', // required string\n age: 21, // required number\n greeting = 'Hello' // optional string, defaults to 'Hello'\n) {\n // ...\n}\n```\n\n- **Colon (`:`)** = required parameter, example shows the type\n- **Equals (`=`)** = optional parameter with default value\n\nThe example value IS the type. `age: 21` means \"required number\". `name: 'Anne'` means \"required string\".\n\n### 3. Monadic Error Flow\n\nErrors propagate automatically as values. When an atom fails, subsequent steps are skipped and the error flows through to the result.\n\n```javascript\nfunction pipeline(topic: 'quantum computing') {\n let results = search({ query: topic }) // might fail\n let summary = summarize({ text: results }) // skipped if search fails\n let formatted = format({ content: summary }) // skipped if any above fails\n return { formatted }\n}\n// If search() fails, the error flows through without executing subsequent steps\n// The result will have an `error` property containing the AgentError\n```\n\nThe VM returns a `RunResult` with both `result` and `error` fields:\n\n```typescript\nconst { result, error, fuelUsed } = await vm.run(ast, args)\n\nif (error) {\n console.log('Failed:', error.message)\n console.log('Failed at atom:', error.op)\n} else {\n console.log('Success:', result)\n}\n```\n\nUse `try/catch` to recover from errors:\n\n```javascript\nfunction resilientPipeline(topic: 'neural networks') {\n let data = null\n try {\n data = fetchData({ topic })\n } catch (e) {\n data = fallbackData({ topic })\n }\n return { data }\n}\n```\n\n### 4. Function Introspection\n\nEvery function has a `.signature` property for self-documentation:\n\n```javascript\n/**\n * Search the knowledge base\n * @param query - The search query\n * @param limit - Max results to return\n */\nfunction search(\n query: 'example query',\n limit = 10\n) -> [{ title: 'Example Title', url: 'https://example.com' }] {\n // implementation\n}\n\n// Automatically gets:\nsearch.signature = {\n name: 'search',\n description: 'Search the knowledge base',\n parameters: {\n query: { type: 'string', required: true, description: 'The search query' },\n limit: { type: 'number', required: false, default: 10, description: 'Max results to return' }\n },\n returns: { type: 'array', items: { type: 'object', shape: { title: 'string', url: 'string' } } }\n}\n```\n\n## Type System Reference\n\n### Parameter Types\n\n| Syntax | Meaning | Example |\n| ----------------------- | ---------------- | -------------------------------- |\n| `name: 'Anne'` | Required string | The example value shows the type |\n| `age: 21` | Required number | |\n| `active: true` | Required boolean | |\n| `tags: ['a', 'b']` | Required array | |\n| `user: { name: 'Bob' }` | Required object | |\n| `limit = 10` | Optional number | Defaults to 10 |\n| `query = 'default'` | Optional string | Defaults to 'default' |\n\n### Destructured Parameter Defaults\n\nAJS supports default values in destructured object parameters. Unlike JavaScript/TypeScript where destructuring defaults can be tricky, AJS makes them work reliably:\n\n```javascript\nfunction calculate({ a = 10, b = 5 }) {\n return { sum: a + b, product: a * b }\n}\n\n// Called with no arguments - uses defaults\ncalculate({}) // { sum: 15, product: 50 }\n\n// Called with partial arguments - missing ones use defaults\ncalculate({ a: 20 }) // { sum: 25, product: 100 }\n\n// Called with all arguments - no defaults used\ncalculate({ a: 3, b: 7 }) // { sum: 10, product: 21 }\n```\n\nThis works seamlessly with type annotations too:\n\n```javascript\nfunction greet({ name: 'World', greeting = 'Hello' }) {\n return { message: `${greeting}, ${name}!` }\n}\n\ngreet({}) // { message: \"Hello, World!\" }\ngreet({ name: 'Alice' }) // { message: \"Hello, Alice!\" }\ngreet({ greeting: 'Hi' }) // { message: \"Hi, World!\" }\n```\n\n### Return Types\n\nReturn types can be specified with arrow syntax:\n\n```javascript\nfunction search(query: 'search term') -> { results: [], count: 0 } {\n // Must return object with results array and count number\n}\n```\n\nOr inferred from the return statement:\n\n```javascript\nfunction search(query: 'search term') {\n return { results: [], count: 0 } // Return type inferred\n}\n```\n\n## Supported Constructs\n\n### Variables\n\n```javascript\nlet x = 5 // Variable declaration\nx = 10 // Assignment\nlet { a, b } = obj // Destructuring (limited)\n```\n\n### Control Flow\n\n```javascript\n// Conditionals\nif (condition) {\n // ...\n} else {\n // ...\n}\n\n// Loops\nwhile (condition) {\n // ...\n}\n\nfor (const item of items) {\n // Becomes a map operation\n}\n\n// Error handling\ntry {\n // ...\n} catch (e) {\n // ...\n}\n```\n\n### Expressions\n\n```javascript\n// Arithmetic\na + b, a - b, a * b, a / b, a % b\n\n// Comparison\na == b, a != b, a < b, a > b, a <= b, a >= b\n\n// Logical\na && b, a || b, !a\n\n// Member access\nobj.property\nobj.nested.property\narr[0]\n\n// Optional chaining (safe access)\nobj?.property\nobj?.nested?.value\narr?.[0]\n\n// Template literals\n`Hello ${name}!`\n\n// Function calls\natomName({ param1: value1, param2: value2 })\n```\n\n### Built-in Objects\n\nAJS provides safe implementations of common JavaScript built-in objects:\n\n#### Math\n\nAll standard Math methods and constants are available:\n\n```javascript\nlet floor = Math.floor(3.7) // 3\nlet ceil = Math.ceil(3.2) // 4\nlet abs = Math.abs(-5) // 5\nlet max = Math.max(1, 5, 3) // 5\nlet sqrt = Math.sqrt(16) // 4\nlet pi = Math.PI // 3.14159...\nlet random = Math.random() // Cryptographically secure when available\n```\n\n**Note:** `Math.random()` uses `crypto.getRandomValues()` when available for cryptographically secure random numbers.\n\n#### JSON\n\n```javascript\nlet obj = { name: 'test', value: 42 }\nlet str = JSON.stringify(obj) // '{\"name\":\"test\",\"value\":42}'\nlet parsed = JSON.parse(str) // { name: 'test', value: 42 }\n```\n\n#### Array Static Methods\n\n```javascript\nlet isArr = Array.isArray([1, 2, 3]) // true\nlet arr = Array.from([1, 2, 3]) // Creates new array\nlet created = Array.of(1, 2, 3) // [1, 2, 3]\n```\n\n#### Object Static Methods\n\n```javascript\nlet obj = { a: 1, b: 2, c: 3 }\nlet keys = Object.keys(obj) // ['a', 'b', 'c']\nlet values = Object.values(obj) // [1, 2, 3]\nlet entries = Object.entries(obj) // [['a',1], ['b',2], ['c',3]]\n```\n\n#### Number Static Methods\n\n```javascript\nlet isInt = Number.isInteger(5) // true\nlet isNan = Number.isNaN(NaN) // true\nlet max = Number.MAX_SAFE_INTEGER // 9007199254740991\n```\n\n#### Global Functions\n\n```javascript\nlet n = parseInt('42') // 42\nlet f = parseFloat('3.14') // 3.14\nlet encoded = encodeURIComponent('hello world') // 'hello%20world'\n```\n\n#### String Instance Methods\n\n```javascript\nlet str = 'hello world'\nlet upper = str.toUpperCase() // 'HELLO WORLD'\nlet parts = str.split(' ') // ['hello', 'world']\nlet trimmed = ' padded '.trim() // 'padded'\nlet replaced = str.replace('world', 'there') // 'hello there'\n```\n\n#### Array Instance Methods\n\n```javascript\nlet arr = [3, 1, 4, 1, 5]\nlet joined = arr.join('-') // '3-1-4-1-5'\nlet has = arr.includes(4) // true\nlet idx = arr.indexOf(1) // 1\nlet sliced = arr.slice(1, 3) // [1, 4]\n```\n\n### Set and Date Builtins\n\nAJS provides `Set()` and `Date()` as factory functions - no `new` keyword needed.\n\n#### Set\n\nCreate sets with `Set([items])`. Sets have both mutable operations (modify in place) and immutable set algebra (return new sets):\n\n```javascript\n// Create a Set\nlet tags = Set(['javascript', 'typescript', 'rust'])\nlet empty = Set()\n\n// Mutable operations (modify the set, return this for chaining)\ntags.add('go') // Add item\ntags.remove('rust') // Remove item\ntags.clear() // Remove all items\n\n// Query operations\nlet has = tags.has('typescript') // true/false\nlet count = tags.size // Number of items\nlet arr = tags.toArray() // Convert to array\n\n// Immutable set algebra (return NEW sets)\nlet a = Set([1, 2, 3])\nlet b = Set([2, 3, 4])\n\nlet union = a.union(b) // Set([1, 2, 3, 4])\nlet inter = a.intersection(b) // Set([2, 3])\nlet diff = a.diff(b) // Set([1]) - items in a but not b\n```\n\n#### Date\n\nCreate dates with `Date()` or `Date(initializer)`. Date objects are **immutable** - methods like `add()` return new Date objects:\n\n```javascript\n// Create a Date\nlet now = Date() // Current date/time\nlet specific = Date('2024-06-15') // From ISO string\nlet fromTs = Date(1718409600000) // From timestamp\n\n// Static methods\nlet timestamp = Date.now() // Current timestamp (number)\nlet parsed = Date.parse('2024-06-15T10:30:00Z') // Parse to Date object\n\n// Component accessors (read-only)\nlet d = Date('2024-06-15T10:30:45Z')\nd.year // 2024\nd.month // 6 (1-12, not 0-11 like JS!)\nd.day // 15\nd.hours // 10\nd.minutes // 30\nd.seconds // 45\nd.timestamp // Unix timestamp in ms\nd.value // ISO string\n\n// Immutable arithmetic (returns NEW Date)\nlet later = d.add({ days: 5, hours: 3 })\nlet earlier = d.add({ months: -1 })\n// Supported: years, months, days, hours, minutes, seconds\n\n// Comparison\nlet before = d.isBefore(later) // true\nlet after = later.isAfter(d) // true\nlet diffDays = d.diff(later, 'days') // -5\n\n// Formatting\nlet formatted = d.format('date') // '2024-06-15'\nlet iso = d.format('iso') // '2024-06-15T10:30:45.000Z'\nlet time = d.format('time') // '10:30:45'\n```\n\n**Note:** Unlike JavaScript's `Date`, months are 1-12 (not 0-11), and all methods are immutable.\n\n#### Serialization\n\nSets and Dates serialize cleanly to JSON:\n\n```javascript\nlet result = {\n tags: Set(['a', 'b', 'c']),\n created: Date('2024-06-15'),\n}\n// JSON.stringify(result) produces:\n// { \"tags\": [\"a\", \"b\", \"c\"], \"created\": \"2024-06-15T00:00:00.000Z\" }\n```\n\n- **Sets** serialize to arrays\n- **Dates** serialize to ISO 8601 strings\n\n### Schema Filtering\n\nThe `filter()` builtin validates and strips extra properties from objects based on a schema:\n\n```javascript\n// Strip extra properties from an object\nlet raw = { name: 'Alice', age: 30, secret: 'password', extra: 123 }\nlet clean = filter(raw, { name: 'string', age: 0 })\n// clean = { name: 'Alice', age: 30 }\n\n// Works with nested objects\nlet data = {\n user: { name: 'Bob', age: 25, ssn: '123-45-6789' },\n tags: ['a', 'b'],\n internal: 'hidden',\n}\nlet filtered = filter(data, {\n user: { name: 'string', age: 0 },\n tags: ['string'],\n})\n// filtered = { user: { name: 'Bob', age: 25 }, tags: ['a', 'b'] }\n\n// Throws on validation failure (missing required fields)\nlet bad = filter({ name: 'Alice' }, { name: 'string', age: 0 })\n// Error: Missing age\n```\n\n**Use cases:**\n\n- Sanitize LLM outputs - strip unexpected properties from JSON responses\n- API input validation - accept only the fields you expect\n- Data projection - reduce objects to a known shape\n\n**Note:** Return values are automatically filtered when a return type is declared. This makes return types act as projections:\n\n```javascript\nfunction getUser(id: 'user-123') -> { name: 'string', email: 'string' } {\n let user = fetchUser({ id }) // might return { name, email, password, ... }\n return { user } // password automatically stripped\n}\n```\n\n### Array Methods with Lambdas\n\n```javascript\n// Map - transform each element\nitems.map((x) => x * 2)\nitems.map((x) => {\n let doubled = x * 2\n return doubled\n})\n\n// Filter - keep elements matching condition\nitems.filter((x) => x > 5)\nitems.filter((x) => x % 2 == 0)\n\n// Find - get first matching element\nitems.find((x) => x.id == targetId)\nusers.find((u) => u.age >= 18)\n\n// Reduce - accumulate to single value\nitems.reduce((acc, x) => acc + x, 0)\nitems.reduce((sum, item) => sum + item.price, 0)\n\n// Other array operations\nitems.push(newItem) // Add to array\nstr.split(',') // Split string to array\nparts.join('-') // Join array to string\n```\n\nLambdas support closures - they can access variables from the outer scope:\n\n```javascript\nfunction processItems({ items, threshold }) {\n let above = items.filter((x) => x >= threshold) // threshold from outer scope\n let scaled = above.map((x) => x * threshold) // still accessible\n return { scaled }\n}\n```\n\n## Unsupported Constructs\n\nThese JavaScript features are intentionally not supported:\n\n| Feature | Reason | Alternative |\n| ------------------ | ---------------------------- | ------------------- |\n| `class` | Use functional composition | Plain functions |\n| `this` | Implicit state is confusing | Explicit parameters |\n| `new` | Classes not supported | Factory functions |\n| `import/require` | Atoms must be registered | Register with VM |\n| `async/await` | Implicit async | Just call functions |\n| `yield/generators` | Complex control flow | Use `map`/`while` |\n| `eval` | Security (though VM is safe) | Use transpiler |\n| `with` | Deprecated | Explicit references |\n| `var` | Scoping issues | Use `let` |\n\n## API Usage\n\n### transpile()\n\nFull transpilation with signature and metadata:\n\n```typescript\nimport { transpile } from 'tjs-lang'\n\nconst { ast, signature, warnings } = transpile(`\n function greet(name: 'World') {\n let msg = template({ tmpl: 'Hello {{name}}!', vars: { name } })\n return { msg }\n }\n`)\n\nconsole.log(signature.parameters.name.type) // 'string'\nconsole.log(signature.parameters.name.required) // true\n```\n\n### ajs()\n\nConvenience function returning just the AST (works as both a function and tagged template literal):\n\n```typescript\nimport { ajs } from 'tjs-lang'\n\nconst ast = ajs(`\n function add(a: 5, b: 3) {\n let sum = a + b\n return { sum }\n }\n`)\n\n// Execute with VM\nconst vm = new AgentVM()\nconst result = await vm.run(ast, { a: 5, b: 3 })\nconsole.log(result.result.sum) // 8\n```\n\n### agent\\`\\`\n\nTagged template for inline definitions:\n\n```typescript\nimport { agent } from 'tjs-lang'\n\nconst searchAST = agent`\n function search(query: 'example search', limit = 10) {\n let results = storeSearch({ query, limit })\n return { results }\n }\n`\n```\n\n### getToolDefinitions()\n\nGenerate OpenAI-compatible tool schemas for LLM integration:\n\n```typescript\nimport { getToolDefinitions, transpile } from 'tjs-lang'\n\nconst { signature } = transpile(source)\nconst tools = getToolDefinitions([signature])\n\n// Returns format compatible with OpenAI/Anthropic tool calling:\n// [{\n// type: 'function',\n// function: {\n// name: 'search',\n// description: 'Search the knowledge base',\n// parameters: { type: 'object', properties: {...}, required: [...] }\n// }\n// }]\n```\n\n## Error Handling\n\n### Monadic Error Flow\n\nAgent99 uses monadic error flow - when an atom fails, the error becomes a value that propagates through the pipeline:\n\n```typescript\nconst { result, error, fuelUsed } = await vm.run(ast, args)\n\nif (error) {\n // error is an AgentError with:\n // - message: string - the error message\n // - op: string - the atom that failed\n // - cause?: Error - the original exception\n console.log(`Error in ${error.op}: ${error.message}`)\n} else {\n // Success - use result\n console.log(result)\n}\n```\n\n### Checking for Errors\n\n```typescript\nimport { isAgentError } from 'tjs-lang'\n\nconst { result, error } = await vm.run(ast, args)\n\nif (isAgentError(result)) {\n // result itself is the error (when error occurs before return)\n}\n```\n\n### Recovery with try/catch\n\nUse `try/catch` in your AJS code to handle errors gracefully:\n\n```javascript\nfunction resilientAgent({ query }) {\n let result = null\n\n try {\n result = riskyOperation({ query })\n } catch (e) {\n // e contains the error message\n result = safeDefault({ error: e })\n }\n\n return { result }\n}\n```\n\n### Triggering Errors with Error()\n\nUse the `Error()` built-in to trigger monadic error flow from your AJS code:\n\n```javascript\nfunction validateInput({ value }) {\n if (value < 0) {\n Error('Value must be non-negative')\n // Execution stops here - subsequent code is skipped\n }\n\n return { validated: value }\n}\n```\n\nWhen `Error()` is called:\n\n- The error message is stored in the context\n- Subsequent operations are skipped (monadic error flow)\n- The error can be caught with `try/catch` or returned to the caller\n\n```javascript\nfunction safeDivide({ a, b }) {\n if (b === 0) {\n Error('Division by zero')\n }\n return { result: a / b }\n}\n\nfunction calculate({ x, y }) {\n let result = null\n\n try {\n result = safeDivide({ a: x, b: y })\n } catch (e) {\n result = { result: 0, error: e }\n }\n\n return result\n}\n```\n\n### Why No `throw` Statement?\n\nAJS intentionally does not support the `throw` statement. Instead, use `Error()`:\n\n```javascript\n// DON'T DO THIS - throw is not supported:\nif (invalid) {\n throw new Error('Something went wrong') // Transpiler error!\n}\n\n// DO THIS INSTEAD:\nif (invalid) {\n Error('Something went wrong') // Triggers monadic error flow\n}\n```\n\nThe `throw` keyword will show as an error in your editor (red underline) and the transpiler will provide a helpful error message pointing you to use `Error()` instead.\n\n## Gotchas and Common Pitfalls\n\n### Unavailable JavaScript Features\n\nThese common JavaScript APIs are **not available** in AJS. The transpiler will catch these and provide helpful error messages:\n\n| Feature | Error Message | Alternative |\n| ---------------- | -------------------------- | -------------------------------------- |\n| `setTimeout` | Use the `delay` atom | `delay({ ms: 1000 })` |\n| `setInterval` | Use while loops with delay | `while (cond) { delay({ ms: 1000 }) }` |\n| `fetch` | Use the `httpFetch` atom | `httpFetch({ url })` |\n| `RegExp` | Use string methods | `str.match()`, `str.replace()` |\n| `Promise` | Implicit async | All calls are automatically awaited |\n| `Map` | Use plain objects | `{ key: value }` |\n| `require/import` | Register atoms with VM | `new AgentVM({ customAtom })` |\n\n### The `new` Keyword\n\nThe `new` keyword is not supported. AJS provides factory functions instead:\n\n```javascript\n// DON'T DO THIS - the transpiler catches these with helpful errors:\nlet date = new Date() // Error: Use Date() or Date('2024-01-15') instead\nlet set = new Set([1, 2]) // Error: Use Set([items]) instead\nlet arr = new Array(5) // Error: Use array literals like [1, 2, 3] instead\n\n// DO THIS INSTEAD - no 'new' needed:\nlet date = Date() // Current date/time\nlet date2 = Date('2024-06-15') // Specific date\nlet set = Set([1, 2, 3]) // Create a Set\nlet arr = [1, 2, 3, 4, 5] // Array literal\n```\n\nSee [Set and Date Builtins](#set-and-date-builtins) for full documentation.\n\n### No `this` or Classes\n\nAJS is purely functional. There's no `this`, no classes, no prototypes:\n\n```javascript\n// DON'T DO THIS\nclass Agent {\n constructor(name) {\n this.name = name\n }\n}\n\n// DO THIS INSTEAD\nfunction createAgent(name: 'Agent Smith') {\n return { name }\n}\n```\n\n### Equality Semantics\n\nAJS uses JavaScript's standard equality (`==` and `===`). There is no special deep equality:\n\n```javascript\nlet a = { x: 1 }\nlet b = { x: 1 }\nlet same = a == b // false (reference comparison)\n\n// For deep comparison, use JSON.stringify or write a comparison function\nlet equal = JSON.stringify(a) == JSON.stringify(b) // true\n```\n\n### Optional Chaining (`?.`)\n\nOptional chaining is fully supported for safe property access:\n\n```javascript\nlet x = obj?.nested?.value // Returns null if any step is null/undefined\nlet result = user?.profile?.name\n\n// Works with method calls too\nlet len = items?.length\nlet upper = str?.toUpperCase()\n```\n\n**Note:** Nullish coalescing (`??`) is not yet supported. Use explicit checks:\n\n```javascript\nlet x = obj?.nested?.value\nif (x == null) {\n x = 'default'\n}\n```\n\n### Atom Calls vs Built-in Methods\n\nAtoms use object parameter syntax, while built-ins use normal function syntax:\n\n```javascript\n// Atom call - object parameter\nlet result = search({ query: 'hello', limit: 10 })\n\n// Built-in method - normal parameters\nlet floor = Math.floor(3.7)\nlet upper = str.toUpperCase()\n```\n\n### Async Is Implicit\n\nAll atom calls are automatically awaited. Don't use `async/await`:\n\n```javascript\n// DON'T DO THIS\nasync function search(query) {\n let result = await fetch(query) // Error: async/await not supported\n}\n\n// DO THIS INSTEAD\nfunction search(query: 'https://api.example.com') {\n let result = httpFetch({ url: query }) // Automatically awaited\n return { result }\n}\n```\n\n### Error Propagation\n\nErrors propagate monadically - if one step fails, subsequent steps are skipped:\n\n```javascript\nfunction pipeline(input: 'raw data') {\n let a = stepOne({ input }) // If this fails...\n let b = stepTwo({ data: a }) // ...this is skipped\n let c = stepThree({ data: b }) // ...and this too\n return { c } // Result contains the error\n}\n```\n\nUse `try/catch` to recover from expected errors:\n\n```javascript\nfunction resilient(input: 'user input') {\n let result = null\n try {\n result = riskyStep({ input })\n } catch (e) {\n result = fallback({ error: e })\n }\n return { result }\n}\n```\n\n### Fuel Limits\n\nAll operations consume fuel. Complex operations may hit limits:\n\n```javascript\n// This might run out of fuel for large arrays\nfunction processLarge({ items }) {\n let mapped = items.map((x) => complexOperation({ x }))\n return { mapped }\n}\n\n// Run with higher fuel limit\nconst result = await vm.run(ast, args, { fuel: 10000 })\n```\n\n## Security Model\n\nAJS compiles to Agent99's JSON AST, which executes in a completely sandboxed VM:\n\n- **No file system access** - unless explicitly provided via atoms\n- **No network access** - unless explicitly provided via atoms\n- **No global state** - each execution is isolated\n- **Fuel-limited execution** - prevents infinite loops and runaway expressions\n- **Type-checked at runtime** - invalid operations fail safely\n- **Prototype access blocked** - `__proto__`, `constructor`, `prototype` are forbidden\n\nThe transpiler is permissive because security is enforced at the VM level, not the language level. Even if malicious code somehow made it through, the VM cannot execute dangerous operations unless atoms for those operations are registered.\n\n### Fuel System\n\nEvery operation consumes fuel. When fuel runs out, execution stops with an `Out of Fuel` error:\n\n```typescript\nconst result = await vm.run(ast, args, { fuel: 100 })\n// Limits total computation to prevent infinite loops\n```\n\nExpression evaluation also consumes fuel (0.01 per node), preventing deeply nested or recursive expressions from running unchecked.\n\n## Migration from TypedBuilder\n\nIf you have existing TypedBuilder code, here's how to convert:\n\n```typescript\n// Before: TypedBuilder\nconst ast = Agent.take()\n .varsImport(['topic'])\n .step({ op: 'search', query: 'topic', result: 'results' })\n .if('results.length > 0', { results: 'results' }, (b) =>\n b.step({ op: 'summarize', text: 'results', result: 'summary' })\n )\n .return({ properties: { results: {}, summary: {} } })\n .toJSON()\n\n// After: AJS\nconst ast = ajs(`\n function searchAgent(topic: 'climate change') {\n let results = search({ query: topic })\n if (results.length > 0) {\n let summary = summarize({ text: results })\n }\n return { results, summary }\n }\n`)\n```\n\n## Best Practices\n\n1. **Use descriptive JSDoc comments** - They become part of the function signature for LLM agents\n2. **Prefer explicit types** - Even though inference works, explicit types document intent\n3. **Keep functions small** - Each function should do one thing\n4. **Use meaningful variable names** - The VM state is inspectable during debugging\n5. **Return structured objects** - Makes output types clear and composable\n6. **Handle errors appropriately** - Use try/catch for expected failures, let others propagate\n7. **Set appropriate fuel limits** - Balance between allowing complex operations and preventing abuse\n",
|
|
67
|
+
"title": "AJS (.ajs) - A Better JavaScript for AI Agents",
|
|
68
|
+
"filename": "ajs.md",
|
|
69
|
+
"path": "guides/ajs.md"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"text": "# AJS LLM System Prompt\n\n> **Maintenance Note:** This prompt must be updated when [ajs.md](./ajs.md) changes.\n> Key areas to sync: type syntax, built-ins (Set/Date), control flow, and forbidden constructs.\n\nUse this system prompt when asking an LLM to generate AJS code.\n\n---\n\n## System Prompt\n\n````\nYou are an expert code generator for **AJS**, a specialized subset of JavaScript for AI Agents.\nAJS looks like JavaScript but has strict differences. You must adhere to these rules:\n\n### 1. SYNTAX & TYPES\n- **Types by Example:** Do NOT use TypeScript types. Use \"Example Types\" where the value implies the type.\n - WRONG: `function search(query: string, limit?: number)`\n - RIGHT: `function search(query: 'search term', limit = 10)`\n - `name: 'value'` means REQUIRED string. `count: 5` means REQUIRED number. `name = 'value'` means OPTIONAL.\n- **Number Parameters:** Use ACTUAL NUMBER LITERALS, never strings or type names.\n - WRONG: `function add(a: 'number', b: 'number')` - 'number' is a STRING!\n - WRONG: `function add(a: '5', b: '10')` - these are STRINGS in quotes!\n - RIGHT: `function add(a: 0, b: 0)` - bare numbers, no quotes\n - RIGHT: `function factorial(n: 5)` - bare number literal\n- **No Return Type Annotations:** Do NOT add return types after the parameter list.\n - WRONG: `function foo(x: 0): number { ... }`\n - WRONG: `function foo(x: 0) -> number { ... }`\n - RIGHT: `function foo(x: 0) { ... }`\n- **No Classes:** Do NOT use `class`, `new`, `this`, or `prototype`.\n- **No Async/Await:** Do NOT use `async` or `await`. All functions are implicitly asynchronous.\n - WRONG: `let x = await fetch(...)`\n - RIGHT: `let x = httpFetch({ url: '...' })`\n\n### 2. BUILT-INS & FACTORIES\n- **No `new` Keyword:** Never use `new`. Use factory functions.\n - WRONG: `new Date()`, `new Set()`, `new Array()`\n - RIGHT: `Date()`, `Set([1,2])`, `['a','b']`\n- **Date Objects:** `Date()` returns an **immutable** object.\n - Months are 1-indexed (1=Jan, not 0=Jan).\n - Methods like `.add({ days: 5 })` return a NEW Date object.\n - Access components: `.year`, `.month`, `.day`, `.hours`, `.minutes`, `.seconds`\n - Format: `.format('date')`, `.format('iso')`, `.format('YYYY-MM-DD')`\n- **Set Objects:** `Set([items])` returns an object with:\n - Mutable: `.add(x)`, `.remove(x)`, `.clear()`\n - Immutable algebra: `.union(other)`, `.intersection(other)`, `.diff(other)` - return NEW Sets\n - Query: `.has(x)`, `.size`, `.toArray()`\n- **Optional Chaining:** Use `?.` for safe property access: `obj?.nested?.value`\n- **Schema Filtering:** `filter(data, schema)` strips extra properties:\n - `filter({ a: 1, b: 2, extra: 3 }, { a: 0, b: 0 })` returns `{ a: 1, b: 2 }`\n - Useful for sanitizing LLM outputs or API responses\n\n### 3. ATOMS VS. BUILT-INS\n- **Atoms (External Tools):** ALWAYS accept a single object argument.\n - Pattern: `atomName({ param: value })`\n - Examples: `search({ query: topic })`, `llmPredict({ system: '...', user: '...' })`\n - **template atom:** `template({ tmpl: 'Hello, {{name}}!', vars: { name } })` - for string interpolation\n - IMPORTANT: Use SINGLE QUOTES for tmpl, NOT backticks! Backticks cause parse errors.\n - WRONG: `template({ tmpl: \\`{{name}}\\`, vars: { name } })`\n - RIGHT: `template({ tmpl: '{{name}}', vars: { name } })`\n- **Built-ins (Math, JSON, String, Array):** Use standard JS syntax.\n - `Math.max(1, 2)`, `JSON.parse(str)`, `str.split(',')`, `arr.map(x => x * 2)`\n\n### 4. ERROR HANDLING\n- Errors propagate automatically (Monadic flow). If one step fails, subsequent steps are skipped.\n- Only use `try/catch` if you need to recover from a failure and continue.\n\n### 5. FORBIDDEN CONSTRUCTS\nThese will cause transpile errors:\n- `async`, `await` - not needed, all calls are implicitly async\n- `new` - use factory functions instead\n- `class`, `this` - use plain functions and objects\n- `var` - use `let` instead\n- `import`, `require` - atoms must be registered with the VM\n- `console.log` - use trace capabilities if needed\n\n### EXAMPLES\n\n**Example 1: Search Agent**\n```javascript\nfunction researchAgent(topic: 'quantum computing') {\n let searchResults = search({ query: topic, limit: 5 })\n if (searchResults?.length == 0) {\n return { error: 'No results found' }\n }\n let summary = summarize({ text: JSON.stringify(searchResults), length: 'short' })\n return { summary }\n}\n```\n\n**Example 2: Factorial with while loop (number parameter)**\n```javascript\nfunction factorial(n: 5) {\n let result = 1\n let i = n\n while (i > 1) {\n result = result * i\n i = i - 1\n }\n return { result }\n}\n```\n\n**Example 3: Math with multiple number parameters**\n```javascript\nfunction calculateVolume(width: 2, height: 3, depth: 4) {\n let volume = width * height * depth\n return { volume }\n}\n```\nNote: width, height, depth are BARE NUMBERS (2, 3, 4), NOT strings like '2' or 'number'!\n\n**Example 4: Greeting with template atom**\n```javascript\nfunction greet(name: 'World', greeting = 'Hello') {\n let message = template({ tmpl: '{{greeting}}, {{name}}!', vars: { greeting, name } })\n return { message }\n}\n```\n````\n\n```\n\n---\n\n## Self-Correction Loop\n\nWhen testing with local LLMs, implement error feedback:\n\n1. Run the LLM with this prompt\n2. If output contains `async`, `await`, `new`, `class`, or `this`, feed back:\n > \"Error: You used '[keyword]'. AJS forbids '[keyword]'. [Alternative].\"\n3. The model typically fixes it on the second attempt\n\nExample corrections:\n- `new Date()` → \"Use `Date()` factory function instead\"\n- `await fetch()` → \"Remove `await`, use `httpFetch({ url })` - all calls are implicitly async\"\n- `class Agent` → \"Use plain functions, AJS is purely functional\"\n\n---\n\n## Compact Version (for context-limited models)\n\n```\n\nYou generate AJS code. Rules:\n\n1. Types by example: `fn(name: 'string', count: 10)` - string in quotes, numbers BARE (no quotes!)\n - WRONG: `fn(x: 'number')` or `fn(x: '5')` - these are STRINGS\n - RIGHT: `fn(x: 0)` or `fn(x: 5)` - bare number literals\n2. NO: async/await, new, class, this, var, import, return type annotations\n3. Atoms use object args: `search({ query: x })`. Built-ins normal: `Math.max(1,2)`\n4. Factories: `Date()`, `Set([1,2])` - no `new` keyword\n5. Date is immutable, months 1-12. Set has .add/.remove (mutable) and .union/.diff (immutable)\n6. Use `?.` for optional chaining: `obj?.prop?.value`\n7. Use `filter(data, schema)` to strip extra properties from objects\n\n```\n\n```\n",
|
|
73
|
+
"title": "AJS LLM System Prompt",
|
|
74
|
+
"filename": "ajs-llm-prompt.md",
|
|
75
|
+
"path": "guides/ajs-llm-prompt.md"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"text": "# AJS Patterns\n\nThis document covers common patterns and workarounds for features not directly supported in AJS.\n\n## Table of Contents\n\n- [Parallel Execution](#parallel-execution)\n- [Retry with Backoff](#retry-with-backoff)\n- [Rate Limiting](#rate-limiting)\n- [Break/Continue](#breakcontinue)\n- [Switch Statements](#switch-statements)\n- [Error Handling Patterns](#error-handling-patterns)\n- [Expression Limitations](#expression-limitations)\n\n---\n\n## Parallel Execution\n\n**Status:** Not supported\n\nAJS executes sequentially by design. This is intentional for:\n\n- Predictable fuel consumption\n- Deterministic execution order\n- Simpler debugging and tracing\n\n**Workaround:** If you need parallel execution, orchestrate at the capability level:\n\n```javascript\n// Capability that handles parallelism\nconst parallelFetch = {\n fetchAll: async (urls) => {\n return Promise.all(urls.map((url) => fetch(url).then((r) => r.json())))\n },\n}\n\n// AJS code calls the capability\nconst results = parallelFetch.fetchAll(urls)\n```\n\n**Future:** Parallel execution may be added as an explicit atom (e.g., `parallel([...steps])`) where fuel is consumed for the most expensive branch.\n\n---\n\n## Retry with Backoff\n\n**Status:** Manual pattern required\n\nAJS doesn't have built-in retry. Implement with a while loop:\n\n```javascript\nlet attempts = 0\nlet result = null\nlet success = false\n\nwhile (attempts < 3 && !success) {\n attempts = attempts + 1\n\n try {\n result = fetch(url)\n success = true\n } catch (err) {\n // Exponential backoff: 100ms, 200ms, 400ms\n // Note: sleep is a capability, not built-in\n if (attempts < 3) {\n sleep(100 * Math.pow(2, attempts - 1))\n }\n }\n}\n\nif (!success) {\n console.error('Failed after 3 attempts')\n}\nreturn result\n```\n\n**Note:** The `sleep` capability must be injected. AJS doesn't include timing primitives to keep the VM deterministic.\n\n---\n\n## Fetch Security\n\n**Status:** Capability responsibility\n\n### The Problem: Recursive Agent Attacks\n\nA malicious or buggy agent could use `fetch` to call other agent endpoints, creating:\n\n- **Amplification attacks** - One request triggers many downstream requests\n- **Ping-pong loops** - Two endpoints repeatedly calling each other\n- **Resource exhaustion** - Consuming compute/tokens across multiple services\n\nFuel budgets only protect the _current_ VM, not downstream services. SSRF protection blocks private IPs but not public agent endpoints.\n\n### The Solution: Capability-Level Enforcement\n\nSince agents are untrusted code, security must be enforced at the **capability layer**:\n\n1. **Depth tracking** - The fetch capability (not the agent) adds/increments an `X-Agent-Depth` header\n2. **Domain allowlist** - Fetch only works for explicitly allowed domains\n3. **Receiving endpoints** - Check depth headers and reject requests that are too deep\n\n```typescript\n// Host provides a secure fetch capability\ncapabilities: {\n fetch: createSecureFetch({\n allowedDomains: ['api.weather.com', 'api.github.com'],\n maxDepth: 5,\n currentDepth: requestDepth, // From incoming request header\n })\n}\n```\n\nThe agent cannot bypass this because:\n\n- It only has access to the capability, not raw `fetch`\n- The capability is trusted code provided by the host\n- Headers are added automatically - the agent can't see or modify them\n\n### Why This Can't Be Solved in Agent Code\n\n- **Agent honors depth?** - A malicious agent would just not increment it\n- **Agent checks allowlist?** - A malicious agent would skip the check\n- **Request budget in agent?** - Agent could ignore or reset it\n\nThe **capability is the trust boundary**. Agent code is untrusted; capabilities are trusted code injected by the host.\n\n### Built-in Fetch Behavior\n\nThe default fetch atom:\n\n- Requires a domain allowlist OR restricts to localhost only\n- Automatically adds `X-Agent-Depth` header based on `ctx.context.requestDepth`\n- Rejects requests exceeding `MAX_AGENT_DEPTH` (default: 10)\n\nFor production, always provide a custom fetch capability with appropriate restrictions.\n\n---\n\n## Rate Limiting\n\n**Status:** Capability responsibility\n\nRate limiting should be implemented in the capability layer, not in AJS:\n\n```typescript\n// Inject a rate-limited fetch capability\nconst rateLimitedFetch = createRateLimitedFetch({\n requestsPerSecond: 10,\n burstSize: 5,\n})\n\nconst result = await runAgent(ast, {\n capabilities: {\n fetch: rateLimitedFetch,\n },\n})\n```\n\n**Rationale:** Rate limits are deployment-specific. A sandboxed agent shouldn't control its own rate limits since that would allow circumvention.\n\n---\n\n## Break/Continue\n\n**Status:** Not supported\n\nUse conditional logic instead:\n\n```javascript\n// Instead of break:\nlet found = null\nlet i = 0\nwhile (i < items.length && found === null) {\n if (items[i].matches) {\n found = items[i]\n }\n i = i + 1\n}\n\n// Instead of continue (skip items):\nfor (const item of items) {\n if (!item.shouldProcess) {\n // Just don't do anything - effectively a continue\n } else {\n processItem(item)\n }\n}\n\n// Or use filter to pre-process:\nconst toProcess = items.filter((item) => item.shouldProcess)\nfor (const item of toProcess) {\n processItem(item)\n}\n```\n\n---\n\n## Switch Statements\n\n**Status:** Not supported\n\nUse chained if/else:\n\n```javascript\n// Instead of switch(action):\nlet result\nif (action === 'create') {\n result = handleCreate(data)\n} else if (action === 'update') {\n result = handleUpdate(data)\n} else if (action === 'delete') {\n result = handleDelete(data)\n} else {\n result = { error: 'Unknown action' }\n}\n\n// For many cases, consider a lookup object:\nconst handlers = {\n create: () => handleCreate(data),\n update: () => handleUpdate(data),\n delete: () => handleDelete(data),\n}\nconst handler = handlers[action]\nif (handler) {\n result = handler()\n} else {\n result = { error: 'Unknown action' }\n}\n```\n\n---\n\n## Error Handling Patterns\n\n### Monadic Error Flow\n\nAJS uses monadic error handling. When an error occurs, subsequent atoms are skipped until a `try/catch` block handles it:\n\n```javascript\ntry {\n const data = fetch(url) // If this fails...\n const parsed = JSON.parse(data) // ...this is skipped\n storeSet('data', parsed) // ...this is skipped too\n} catch (err) {\n console.warn('Fetch failed, using cached data')\n const cached = storeGet('data')\n return cached ?? { fallback: true }\n}\n```\n\n### Graceful Degradation\n\nUse `try/catch` with fallbacks:\n\n```javascript\nlet result\n\ntry {\n result = llmPredict(prompt, { model: 'gpt-4' })\n} catch (err) {\n // Fall back to simpler model\n try {\n result = llmPredict(prompt, { model: 'gpt-3.5-turbo' })\n } catch (err2) {\n // Fall back to static response\n result = \"I'm unable to process your request right now.\"\n }\n}\n\nreturn result\n```\n\n### Error Aggregation\n\nCollect errors without stopping execution:\n\n```javascript\nconst errors = []\nconst results = []\n\nfor (const item of items) {\n try {\n const result = processItem(item)\n results.push(result)\n } catch (err) {\n errors.push({ item: item.id, error: err.message })\n // Continue processing - no re-throw\n }\n}\n\nreturn {\n results: results,\n errors: errors,\n success: errors.length === 0,\n}\n```\n\n---\n\n## Unsupported JavaScript Features\n\nThese JavaScript features are intentionally not supported:\n\n| Feature | Reason | Alternative |\n| --------------- | ------------------------------ | --------------------- |\n| `async/await` | All atoms are already async | Direct calls work |\n| `class` | OOP not needed for agent logic | Use plain objects |\n| `this` | No object context | Pass data explicitly |\n| `new` | No constructors | Use factory functions |\n| `import/export` | Single-file execution | Use capabilities |\n| `eval` | Security | N/A |\n| `throw` | Use monadic errors | `console.error()` |\n| `typeof` | Limited runtime type info | Use Schema validation |\n| `instanceof` | No classes | Use duck typing |\n\n---\n\n## Performance Patterns\n\n### Memoization\n\nUse the built-in `memoize` atom for expensive operations:\n\n```javascript\n// Builder API\nAgent.take().memoize(\n (b) => b.llmPredict({ prompt: expensivePrompt }).as('result'),\n 'expensive-key'\n)\n\n// Results are cached by key within the execution\n```\n\n### Caching\n\nUse `cache` atom with TTL for persistence across executions:\n\n```javascript\n// Cache for 1 hour\nconst result = cache('weather-' + city, 3600000, () => {\n return fetch('https://api.weather.com/' + city)\n})\n```\n\n### Fuel Budgeting\n\nMonitor and limit computation:\n\n```javascript\n// Check remaining fuel before expensive operation\nif (fuel.current < 100) {\n console.warn('Low fuel, using cached result')\n return storeGet('cached-result')\n}\n\n// Proceed with expensive operation\nconst result = complexComputation()\n```\n\n---\n\n## Testing Patterns\n\n### Mock Capabilities\n\n```typescript\nimport {\n createMockStore,\n createMockLLM,\n createCapabilities,\n} from 'tjs-lang/test-utils'\n\nconst caps = createCapabilities({\n store: createMockStore({ key: 'value' }),\n llm: createMockLLM('mocked response'),\n})\n\nconst result = await runAgent(ast, { capabilities: caps })\n```\n\n### Snapshot Testing\n\n```typescript\nconst ast = ajs`\n const x = 1 + 2\n return x\n`\n\n// Snapshot the AST for regression testing\nexpect(ast).toMatchSnapshot()\n```\n\n### Trace Inspection\n\n```typescript\nconst result = await runAgent(ast, {\n trace: true,\n capabilities: caps,\n})\n\n// Inspect execution trace\nexpect(result.trace).toContainEqual(expect.objectContaining({ op: 'storeGet' }))\n```\n\n---\n\n## Expression Limitations\n\nSome JavaScript expressions have limitations in AJS due to the compilation model.\n\n### Template Literals in Nested Expressions\n\nTemplate literals work at statement level but not inside other expressions:\n\n```javascript\n// Works - statement level\nconst greeting = `Hello, ${name}!`\n\n// Does NOT work - nested in object\nconst obj = { msg: `Hello, ${name}!` } // Error\n\n// Workaround - assign first\nconst msg = `Hello, ${name}!`\nconst obj = { msg: msg } // Works\n```\n\n### Computed Member Access\n\nDynamic property access with variables is not supported:\n\n```javascript\n// Works - literal index\nconst first = items[0]\nconst name = user.name\n\n// Does NOT work - variable index\nconst key = 'name'\nconst value = obj[key] // Error\n\n// Workaround - use Object.entries or restructure\nconst entries = Object.entries(obj)\nconst found = entries.find((e) => e[0] === key)\nconst value = found ? found[1] : null\n```\n\n### Atom Calls in Expressions\n\nAtom/function calls that produce side effects cannot be embedded in expressions:\n\n```javascript\n// Does NOT work - call inside expression\nconst result = items.map((x) => fetch(url + x)) // Error\n\n// Workaround - use explicit loop\nconst results = []\nfor (const x of items) {\n const res = fetch(url + x)\n results.push(res)\n}\n```\n\nThese limitations exist because AJS compiles to a JSON AST that executes step-by-step. Complex nested expressions would require runtime evaluation that could bypass fuel tracking and capability checks.\n",
|
|
79
|
+
"title": "AJS Patterns",
|
|
80
|
+
"filename": "patterns.md",
|
|
81
|
+
"path": "guides/patterns.md"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"text": "...",
|
|
85
|
+
"title": "ast (inline docs)",
|
|
86
|
+
"filename": "ast.ts",
|
|
87
|
+
"path": "src/lang/emitters/ast.ts"
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"text": "# Math Functions\n Basic arithmetic operations.",
|
|
91
|
+
"title": "bootstrap.test (inline docs)",
|
|
92
|
+
"filename": "bootstrap.test.ts",
|
|
93
|
+
"path": "src/use-cases/bootstrap.test.ts"
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"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** is a type-safe virtual machine (~33KB) for safe execution of untrusted code in any JavaScript environment. It compiles logic chains and AI agents to JSON-serializable ASTs that run sandboxed with fuel (gas) limits.\n\nKey concept: Code travels to data (rather than shipping data to code). Agents are defined as data, not deployed code.\n\n## Common Commands\n\n```bash\n# Development\nnpm run format # ESLint fix + Prettier\nnpm run test:fast # Core tests (skips LLM & benchmarks)\nnpm run make # Full build (clean, format, grammars, tsc, esbuild)\nnpm run dev # Development server with file watcher\n\n# Testing\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\n\n# Other\nnpm run test:llm # LM Studio integration tests\nnpm run bench # Vector search benchmarks\nnpm run show-size # Show gzipped bundle size\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 (~2700 lines, security-critical).\n\n### Key Source Files\n\n- `src/index.ts` - Main entry, re-exports everything\n- `src/vm/runtime.ts` - All atom implementations, expression evaluation, fuel charging (~2900 lines, security-critical)\n- `src/vm/vm.ts` - AgentVM class (~226 lines)\n- `src/vm/atoms/batteries.ts` - Battery atoms (vector search, LLM, store operations)\n- `src/builder.ts` - TypedBuilder fluent API (~19KB)\n- `src/lang/parser.ts` - TJS parser with colon shorthand, unsafe markers, return type extraction\n- `src/lang/emitters/ast.ts` - Emits Agent99 AST from parsed source\n- `src/lang/emitters/js.ts` - Emits JavaScript with `__tjs` metadata\n- `src/lang/emitters/from-ts.ts` - TypeScript to TJS/JS transpiler with class metadata extraction\n- `src/lang/inference.ts` - Type inference from example values\n- `src/lang/linter.ts` - Static analysis (unused vars, unreachable code, no-explicit-new)\n- `src/lang/runtime.ts` - TJS runtime (monadic errors, type checking, wrapClass)\n- `src/batteries/` - LM Studio integration (lazy init, model audit, vector search)\n- `src/use-cases/` - Integration tests and real-world examples (27 test files)\n- `src/cli/tjs.ts` - CLI tool for check/run/types/emit commands\n\n### Core APIs\n\n```typescript\n// Language\najs`...` // Parse AJS to AST\ntjs`...` // Parse TypeScript variant with type metadata\ntranspile(source, options) // Full transpilation with signature extraction\ncreateAgent(source, vm) // Creates callable agent\n\n// VM\nconst vm = new AgentVM(customAtoms)\nawait vm.run(ast, args, { fuel, capabilities, timeoutMs, trace })\n\n// Builder\nAgent.take(schema).varSet(...).httpFetch(...).return(schema)\nvm.Agent // Builder with custom atoms included\n```\n\n### 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```typescript\nimport { fromTS } from 'tosijs/lang/from-ts'\n\nconst tsSource = `\nfunction greet(name: string): string {\n return \\`Hello, \\${name}!\\`\n}\n`\n\nconst result = fromTS(tsSource, { emitTJS: true })\n// result.code contains TJS:\n// function greet(name: '') -> '' {\n// return \\`Hello, \\${name}!\\`\n// }\n```\n\n**Step 2: TJS → JavaScript** (`tjs`)\n```typescript\nimport { tjs } from 'tosijs/lang'\n\nconst tjsSource = `\nfunction greet(name: '') -> '' {\n return \\`Hello, \\${name}!\\`\n}\n`\n\nconst jsCode = tjs(tjsSource)\n// Generates JavaScript with __tjs metadata for runtime validation\n```\n\n**Full Chain Example:**\n```typescript\nimport { fromTS } from 'tosijs/lang/from-ts'\nimport { tjs } from 'tosijs/lang'\n\n// TypeScript source with type annotations\nconst tsSource = `\nfunction add(a: number, b: number): number {\n return a + b\n}\n`\n\n// Step 1: TS → TJS\nconst tjsResult = fromTS(tsSource, { emitTJS: true })\n\n// Step 2: TJS → JS (with runtime validation)\nconst jsCode = tjs(tjsResult.code)\n\n// Execute the result\nconst fn = new Function('__tjs', jsCode + '; return add')(__tjs_runtime)\nfn(1, 2) // Returns 3\nfn('a', 'b') // Returns { error: 'type mismatch', ... }\n```\n\n**CLI Commands:**\n```bash\n# Convert TypeScript to TJS\nbun src/cli/tjs.ts convert input.ts --emit-tjs > output.tjs\n\n# Emit TJS to JavaScript\nbun src/cli/tjs.ts emit input.tjs > output.js\n\n# Run TJS file directly (transpiles and executes)\nbun src/cli/tjs.ts run input.tjs\n```\n\n**Design Notes:**\n- The two steps are intentionally separate for tree-shaking (TS support is optional)\n- `fromTS` lives in a separate entry point (`tosijs/lang/from-ts`)\n- Import only what you need to keep bundle size minimal\n- Each step is independently testable (see `src/lang/codegen.test.ts`)\n\n### Security Model\n\n- **Capability-based**: VM has zero IO by default; inject `fetch`, `store`, `llm` via capabilities\n- **Fuel metering**: Every atom has a cost; execution stops when fuel exhausted\n- **Timeout enforcement**: Default `fuel × 10ms`; explicit `timeoutMs` overrides\n- **Monadic errors**: Errors wrapped in `AgentError`, not thrown (prevents exception exploits)\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## Testing Strategy\n\n- Unit tests alongside source files (`*.test.ts`)\n- Integration tests in `src/use-cases/` (RAG, orchestration, malicious actors)\n- Security tests in `src/use-cases/malicious-actor.test.ts`\n- Language tests in `src/lang/lang.test.ts` (~46KB comprehensive)\n\nCoverage targets: 98% lines on `src/vm/runtime.ts` (security-critical), 80%+ overall.\n\n## Key Patterns\n\n### Adding a New Atom\n\n1. Define with `defineAtom(opCode, inputSchema, outputSchema, implementation, { cost, timeoutMs, docs })`\n2. Add to `src/vm/atoms/` and export from `src/vm/atoms/index.ts`\n3. Add tests\n4. Run `npm run test:fast`\n\n**Atom implementation notes:**\n\n- `cost` can be static number or dynamic: `(input, ctx) => number`\n- `timeoutMs` defaults to 1000ms; use `0` for no timeout (e.g., `seq`)\n- Atoms are always async; fuel deduction is automatic in the `exec` wrapper\n\n### Debugging Agents\n\nEnable tracing: `vm.run(ast, args, { trace: true })` returns `TraceEvent[]` with execution path, fuel consumption, and state changes.\n\n### Custom Atoms Must\n\n- Be non-blocking (no synchronous CPU-heavy work)\n- Respect `ctx.signal` for cancellation\n- Access IO only via `ctx.capabilities`\n\n### Value Resolution\n\nThe `resolveValue()` function handles multiple input patterns:\n\n- `{ $kind: 'arg', path: 'varName' }` → lookup in `ctx.args`\n- `{ $expr: ... }` → evaluate ExprNode via `evaluateExpr()`\n- String with dots `'obj.foo.bar'` → traverse state with forbidden property checks\n- Bare strings → lookup in state, else return literal\n\n### Monadic Error Flow\n\nWhen `ctx.error` is set, subsequent atoms in a `seq` skip execution. Errors are wrapped in `AgentError`, not thrown. This prevents exception-based exploits.\n\n### TJS Parser Syntax Extensions\n\nTJS extends JavaScript with type annotations that survive to runtime.\n\n#### Classes (Callable Without `new`)\n\nTJS classes are wrapped to be callable without the `new` keyword:\n\n```typescript\nclass Point {\n constructor(public x: number, public y: number) {}\n}\n\n// Both work identically:\nconst p1 = Point(10, 20) // TJS way - clean\nconst p2 = new Point(10, 20) // Still works, but linter warns\n\n// The linter flags explicit `new` usage:\n// Warning: Unnecessary 'new' keyword. In TJS, classes are callable without 'new'\n```\n\nThe `wrapClass()` function in the runtime uses a Proxy to intercept calls and auto-construct.\n\n#### Function Parameters\n\n```typescript\n// Required param with example value (colon shorthand)\nfunction greet(name: 'Alice') { } // name is required, type inferred as string\n\n// 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) { } // number or null\n\n// Optional TS-style\nfunction greet(name?: '') { } // same as name = ''\n```\n\n#### Return Types\n\n```typescript\n// Return type annotation (arrow syntax)\nfunction add(a: 0, b: 0) -> 0 { return a + b }\n\n// Object return type\nfunction getUser(id: 0) -> { name: '', age: 0 } { ... }\n```\n\n#### Safety Markers\n\n```typescript\n// Unsafe function (skips runtime validation)\nfunction fastAdd(! a: 0, b: 0) { return a + b }\n\n// Safe function (explicit validation)\nfunction safeAdd(? a: 0, b: 0) { return a + b }\n\n// Unsafe block\nunsafe {\n // All calls in here skip validation\n fastPath(data)\n}\n```\n\n#### Type Declarations\n\n```typescript\n// Simple type from example\nType Name 'Alice'\n\n// Type with description and example\nType User {\n description: 'a user object'\n example: { name: '', age: 0 }\n}\n\n// Type with predicate (auto-generates type guard from example)\nType EvenNumber {\n description: 'an even number'\n example: 2\n predicate(x) { return x % 2 === 0 }\n}\n```\n\n#### Generic Declarations\n\n```typescript\n// Simple generic\nGeneric Box<T> {\n description: 'a boxed value'\n predicate(x, T) { \n return typeof x === 'object' && x !== null && 'value' in x && T(x.value) \n }\n}\n\n// Generic with default type parameter\nGeneric Container<T, U = ''> {\n description: 'container with label'\n predicate(obj, T, U) { \n return T(obj.item) && U(obj.label) \n }\n}\n```\n\n#### Bare Assignments\n\n```typescript\n// Uppercase identifiers auto-get const\nFoo = Type('test', 'example') // becomes: const Foo = Type(...)\nMyConfig = { debug: true } // becomes: const MyConfig = { ... }\n```\n\n#### Module Safety Directive\n\n```typescript\n// At top of file - sets default validation level\nsafety none // No validation (metadata only)\nsafety inputs // Validate function inputs (default)\nsafety all // Validate everything (debug mode)\n```\n\n#### Equality Operators\n\nTJS redefines equality to be structural by default, fixing JavaScript's confusing `==` vs `===` semantics.\n\n**Normal TJS Mode (default):**\n\n| Operator | Meaning | Example |\n|----------|---------|---------|\n| `==` | Structural equality | `{a:1} == {a:1}` is `true` |\n| `!=` | Structural inequality | `{a:1} != {a:2}` is `true` |\n| `===` | Identity (same reference) | `obj === obj` is `true` |\n| `!==` | Not same reference | `{a:1} !== {a:1}` is `true` |\n| `a Is b` | Structural equality (explicit) | Same as `==` |\n| `a IsNot b` | Structural inequality (explicit) | Same as `!=` |\n\n```typescript\n// Structural equality - compares values deeply\nconst a = { x: 1, y: [2, 3] }\nconst b = { x: 1, y: [2, 3] }\na == b // true (same structure)\na === b // false (different objects)\n\n// Works with arrays too\n[1, 2, 3] == [1, 2, 3] // true\n\n// Infix operators for readability\nuser Is expectedUser\nresult IsNot errorValue\n```\n\n**LegacyEquals Mode (for TS-emitted code):**\n\nAdd `LegacyEquals` at the top of a file to preserve JavaScript's original equality semantics:\n\n```typescript\nLegacyEquals\n\n// Now == and === work like standard JavaScript\n'1' == 1 // true (JS coercion)\n'1' === 1 // false (strict equality)\n\n// Use explicit Is/IsNot for structural equality in legacy mode\na Is b // structural equality\na IsNot b // structural inequality\n```\n\n**Implementation Notes:**\n\n- **AJS (VM)**: The VM's expression evaluator handles `==`/`!=` with structural semantics at runtime\n- **TJS (browser/Node)**: Source transformation converts `==` to `Is()` and `!=` to `IsNot()` calls\n- **`===` and `!==`**: Always preserved as identity checks, never transformed\n- The `Is()` and `IsNot()` functions are available in `src/lang/runtime.ts` and exposed globally\n\n## 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",
|
|
97
|
+
"title": "CLAUDE.md",
|
|
98
|
+
"filename": "CLAUDE.md",
|
|
99
|
+
"path": "CLAUDE.md"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"text": "# Context: Working with `tosijs-schema`\n\nYou are an expert in `tosijs-schema`, a lightweight, schema-first, LLM-native replacement for Zod. Use this guide to generate correct code, migrate from Zod, and understand the library's design philosophy.\n\n## 1. Core Philosophy & Design\n\n- **Schema-First:** The primary artifact is a standard JSON Schema object. `tosijs-schema` is a fluent API to generate these schemas.\n- **LLM-Native:** The generated schemas are optimized for LLM consumption (cleaner, flatter, fewer tokens than Zod-to-JSON-Schema adapters).\n- **Strict by Default:** Objects automatically set `additionalProperties: false` and mark all keys as `required` to satisfy OpenAI Structured Output requirements out-of-the-box.\n- **Performance:** Uses \"Ghost Constraints\" for expensive checks (like `maxProperties` on large objects) and a \"prime-jump\" strategy for validating large arrays in O(1) time.\n- **Validation Separation:** Separates \"Is this valid?\" (fast, boolean return) from \"Why is it invalid?\" (detailed debugging).\n\n## 2. Basic Setup & Syntax\n\n### Imports\n\n```typescript\nimport { s, type Infer } from 'tosijs-schema'\n```\n\n### Defining Schemas\n\nUse the `s` proxy to define schemas. The syntax is property-based and chainable.\n\n```typescript\nconst UserSchema = s.object({\n id: s.string.uuid, // Format shorthand\n username: s.string.min(3).max(20), // Chainable constraints\n email: s.email, // First-class email type (no .string wrapper needed)\n age: s.integer.min(0).optional, // Optional integer\n tags: s.array(s.string).min(1), // Array with constraints\n role: s.enum(['admin', 'user']), // Enums\n meta: s.record(s.string), // Record/Dictionary\n})\n```\n\n### Type Inference\n\nInference works similarly to Zod but exports `Infer` directly.\n\n```typescript\ntype User = Infer<typeof UserSchema>\n```\n\n### Accessing the JSON Schema\n\nYou can access the raw JSON schema object via the `.schema` property.\n\n```typescript\nconsole.log(UserSchema.schema)\n// Outputs standard JSON Schema object: { type: \"object\", properties: { ... } }\n```\n\n## 3. Validation API\n\n**Crucial Difference from Zod:**\n\n- `tosijs-schema` validation is optimized for speed and returns a **boolean** by default.\n- It does **not** throw errors or return a parsed object like Zod's `.parse()`.\n\n```typescript\nconst data = { ... };\n\n// Fast validation (returns true/false)\nif (UserSchema.validate(data)) {\n // logic here\n} else {\n // Handle invalid data\n}\n```\n\n## 4. Migration Guide (Zod vs. tosijs-schema)\n\n| Feature | Zod (`z`) | tosijs-schema (`s`) |\n| --------------- | -------------------------- | ---------------------------------------- |\n| **String** | `z.string()` | `s.string` |\n| **Email** | `z.string().email()` | `s.email` (First-class citizen) |\n| **UUID** | `z.string().uuid()` | `s.string.uuid` or `s.uuid` |\n| **Optional** | `schema.optional()` | `schema.optional` (Property, not method) |\n| **Objects** | `z.object({...})` | `s.object({...})` |\n| **Strict Mode** | `z.object({...}).strict()` | **Default** (No method needed) |\n| **Arrays** | `z.array(schema)` | `s.array(schema)` |\n| **Enums** | `z.enum(['a', 'b'])` | `s.enum(['a', 'b'])` |\n| **Unions** | `z.union([a, b])` | `s.union([a, b])` |\n| **Inference** | `z.infer<typeof T>` | `Infer<typeof T>` |\n| **Metadata** | `.describe(\"...\")` | `.describe(\"...\")` / `.title(\"...\")` |\n\n## 5. Monadic Pipelines (`M`)\n\n`tosijs-schema` includes a \"Railway Oriented Programming\" module for building type-safe tool chains. This is especially useful for **AI Agents**, ensuring that hallucinations or bad data are caught immediately at the source (Input vs Output) rather than cascading.\n\n### 1. Guarded Functions (`M.func`)\n\nCreate functions that enforce schemas on both input and output.\n\n```typescript\nimport { M, s } from 'tosijs-schema'\n\n// M.func(InputSchema, OutputSchema, Implementation)\nconst getSize = M.func(s.string, s.number, (str) => {\n return str.length\n})\n\n// Usage:\nconst len = getSize('hello') // Returns 5\n// getSize(123) // Throws SchemaError (Input mismatch)\n```\n\n### 2. Execution Contexts (`new M`)\n\nChain multiple functions together. The execution context handles error propagation automatically.\n\n```typescript\nconst pipeline = new M({\n getSize,\n isEven: M.func(s.number, s.boolean, (n) => n % 2 === 0),\n})\n\nconst result = pipeline\n .getSize('hello') // Output: 5\n .isEven() // Input: 5 -> Output: false\n .result() // Returns false | Error\n\n// If any step fails schema validation, .result() returns the specific error.\n```\n\n## 6. Advanced Features\n\n### Ghost Constraints\n\nConstraints that are computationally expensive (O(N)) are documented in the schema but skipped by the runtime validator for performance (O(1)).\n\n- **Example:** `.max(n)` on Objects/Records.\n- `minProperties` is strictly validated.\n- `maxProperties` is a \"Ghost\" constraint (documentation only).\n\n### Metadata & LLM Optimization\n\nUse metadata methods to enrich schemas for LLMs or OpenAPI docs without affecting runtime validation.\n\n```typescript\nconst ApiKey = s.string\n .min(32)\n .describe(\"The user's secret API key\") // standard JSON schema \"description\"\n .title('API Key')\n .default('sk-...')\n```\n\n### Date Handling\n\n`tosijs-schema` treats dates as strings with format validation, aligning with JSON transport.\n\n```typescript\nconst Timestamp = s.string.datetime // Validates ISO string format\n```\n\n## 6. Common Patterns & Gotchas\n\n1. **Chaining Order:** Primitives (like `s.string`) start the chain. Constraints (`.min()`) and metadata (`.describe()`) follow. The `.optional` flag can be placed anywhere in the chain but usually goes last for readability.\n2. **No Transformers:** Unlike Zod, `tosijs` is a pure validation/schema library. It does not \"transform\" data (e.g., string to number coercion) during validation.\n3. **Strict Objects:** Remember that `s.object()` disallows unknown keys by default. If you need a flexible object, use `s.record()` or explicitly allow additional properties if the API supports it (though strict is preferred for LLM outputs).\n4. **Tuples:** Use `s.tuple([s.string, s.number])` for fixed-length arrays.\n\n## 7. Example: LLM Structured Output\n\nWhen defining a response format for an LLM:\n\n```typescript\nconst ResponseSchema = s.object({\n reasoning: s.string.describe('Step-by-step thinking process'),\n final_answer: s.string.describe('The concise final answer'),\n confidence: s.number\n .min(0)\n .max(1)\n .describe('Confidence score between 0 and 1'),\n})\n\n// Pass to LLM\nconst jsonSchema = ResponseSchema.schema\n```\n",
|
|
103
|
+
"title": "Context: Working with tosijs-schema",
|
|
104
|
+
"filename": "tosijs-schema.md",
|
|
105
|
+
"path": "guides/tosijs-schema.md"
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"text": "# Module Header\n\nThis is the intro.\n\n---\n\nMore docs here.\n\n---\n\n# First Section\n\n---\n\n# Second Section\n\n---\n\n# Heading\n\n- List item 1\n- List item 2\n\n\\`code example\\`\n\n---\n\n## Parameter Syntax\n| Syntax | Meaning |\n|--------|---------|\n| \\`x: 0\\` | Required number |\n| \\`x = 0\\` | Optional, defaults to 0 |\n\n---\n\nIndented content.\n More indented.\n\n---\n\n# Hello World\n\nThis is documentation.\n\n---\n\nIntro\n\n---\n\nMiddle\n\n---\n\n# Math Utilities\n\nA collection of math functions.\n\n---\n\n## Notes\n\nThese functions are pure.",
|
|
109
|
+
"title": "docs.test (inline docs)",
|
|
110
|
+
"filename": "docs.test.ts",
|
|
111
|
+
"path": "src/lang/docs.test.ts"
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"text": "...",
|
|
115
|
+
"title": "js (inline docs)",
|
|
116
|
+
"filename": "js.ts",
|
|
117
|
+
"path": "src/lang/emitters/js.ts"
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"text": ")', () => {\n it('should extract markdown doc comment', () => {\n const { signature } = transpile(`\n /*#\n The classic greeting function.\n\n This demonstrates TJS doc comments.\n\n---\n\n# Heading\n\n - List item 1\n - List item 2\n\n \\`code example\\`\n\n---\n\nIndented content here.\n More indented content.\n\n---\n\nover /** when both present', () => {\n const { signature } = transpile(`\n /**\n * JSDoc description\n\n---\n\nTJS doc description\n\n---\n\nblock, not earlier ones', () => {\n const { signature } = transpile(`\n /*#\n # File-level documentation\n\n This is a header comment that should NOT be attached to the function.\n\n---\n\nFunction-specific documentation.\n\n---\n\nblock when nothing immediately precedes function', () => {\n const { signature } = transpile(`\n /*#\n # File-level documentation\n\n This should NOT be attached to the function below.\n\n---\n\n# Example\n\n\\`\\`\\`javascript\nexport function add(a: 0, b: 0) -> 0 {\n return a + b\n}\n\\`\\`\\`",
|
|
121
|
+
"title": "lang.test (inline docs)",
|
|
122
|
+
"filename": "lang.test.ts",
|
|
123
|
+
"path": "src/lang/lang.test.ts"
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"text": "... * / syntax and preserve full markdown content.\n * Legacy JSDoc (/** ... * /) is supported as a fallback.\n\n---\n\n...\n\n---\n\n...",
|
|
127
|
+
"title": "parser (inline docs)",
|
|
128
|
+
"filename": "parser.ts",
|
|
129
|
+
"path": "src/lang/parser.ts"
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"text": "## seq (Sequence)\n\nThe root atom for all agent programs. Executes steps in order.\n\n- Stops on `return` (when `ctx.output` is set)\n- Stops on error (monadic error flow)\n- Cost: 0.1\n\n```javascript\n// AsyncJS compiles to seq at the top level\nconst x = 1\nconst y = 2\nreturn { sum: x + y }\n```\n\n---\n\n## if (Conditional)\n\nConditional branching based on expression evaluation.\n\n```javascript\nif (count > 0) {\n console.log(\"Has items\")\n} else {\n console.log(\"Empty\")\n}\n```\n\n---\n\n## while (Loop)\n\nRepeats body while condition is truthy. Consumes fuel each iteration.\n\n```javascript\nlet i = 0\nwhile (i < 10) {\n console.log(i)\n i = i + 1\n}\n```\n\n**Note:** No `break`/`continue`. Use condition variables instead.\n\n---\n\n## return\n\nEnds execution and returns values from state. The schema defines which\nstate variables to include in the output.\n\n```javascript\nconst result = compute()\nreturn { result } // Returns { result: <computed value> }\n```\n\n---\n\n## try/catch\n\nError handling with monadic error flow. When an error occurs, subsequent\nsteps are skipped until caught.\n\n```javascript\ntry {\n const data = fetch(url)\n processData(data)\n} catch (err) {\n console.warn(\"Failed: \" + err)\n return { error: err }\n}\n```\n\nThe catch block receives:\n- `err` (or custom name): error message\n- `errorOp`: the atom that failed\n\n---\n\n## for...of / map\n\nTransforms each item in an array. The `result` variable in each iteration\nbecomes the new item value.\n\n```javascript\nconst doubled = items.map(x => x * 2)\n\n// Or with for...of:\nconst results = []\nfor (const item of items) {\n results.push(process(item))\n}\n```\n\n---\n\n## filter\n\nKeeps items that match a condition.\n\n```javascript\nconst adults = users.filter(u => u.age >= 18)\n```\n\n---\n\n## reduce\n\nAccumulates a single value from an array.\n\n```javascript\nconst sum = numbers.reduce((acc, n) => acc + n, 0)\n```\n\n---\n\n## find\n\nReturns first item matching condition, or null.\n\n```javascript\nconst admin = users.find(u => u.role === \"admin\")\n```\n\n---\n\n## fetch\n\nHTTP requests. Requires `fetch` capability or uses global fetch with SSRF protection.\n\n```javascript\nconst data = fetch(\"https://api.example.com/data\")\nconst posted = fetch(\"https://api.example.com/items\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: { name: \"New Item\" }\n})\n```\n\nResponse types: `\"json\"` (default for JSON content-type), `\"text\"`, `\"dataUrl\"` (for images)\n\nSecurity:\n- Requires `ctx.context.allowedFetchDomains` allowlist OR restricts to localhost\n- Automatically adds `X-Agent-Depth` header to prevent recursive agent loops\n- Custom fetch capability can override all restrictions\n\n---\n\n## storeGet / storeSet\n\nPersistent key-value storage. Requires `store` capability.\n\n```javascript\n// Save data\nstoreSet(\"user:123\", { name: \"Alice\", prefs: {} })\n\n// Retrieve later\nconst user = storeGet(\"user:123\")\n```\n\n**Warning:** Default in-memory store is not suitable for production.\n\n---\n\n## llmPredict\n\nCall language model. Requires `llm` capability with `predict` method.\n\n```javascript\nconst response = llmPredict(\"Summarize this: \" + text)\n\n// With options\nconst structured = llmPredict(prompt, {\n model: \"gpt-4\",\n temperature: 0.7,\n responseFormat: { type: \"json_object\" }\n})\n```\n\n---\n\n## transpileCode (Code to AST)\n\nTranspiles AsyncJS code to an AST without executing it.\nUseful for generating agents to send to other services via fetch.\n\n```javascript\n// Generate an agent and send it to a worker\nlet code = llmPredict({ prompt: 'Write an AsyncJS data processor' })\nlet ast = transpileCode({ code })\nlet result = httpFetch({\n url: 'https://worker.example.com/run',\n method: 'POST',\n body: JSON.stringify({ ast, args: { data: myData } })\n})\n```\n\nSecurity: Only available when the `code.transpile` capability is provided.\n\n---\n\n## runCode (Dynamic Code Execution)\n\nTranspiles and executes AsyncJS code at runtime. The generated code\nruns in the same context, sharing fuel budget, capabilities, and trace.\n\nThis enables agents to write and execute code to solve problems.\n\n```javascript\n// Agent writes code to solve a problem\nlet code = llmPredict({ prompt: 'Write AsyncJS to calculate fibonacci(10)' })\nlet result = runCode({ code, args: {} })\nreturn { answer: result }\n```\n\nThe code must be a valid AsyncJS function. The function's return value\nbecomes the result of runCode.\n\nSecurity: Only available when the `code.transpile` capability is provided.\nThe transpiled code runs with the same permissions as the parent.\nRecursion depth is limited to prevent stack overflow.\n\n---\n\n## memoize\n\nIn-memory caching within a single execution. Same key returns cached result.\n\n```javascript\n// Expensive computation cached by key\nconst result = memoize(\"expensive-\" + id, () => {\n return heavyComputation(data)\n})\n```\n\n---\n\n## cache\n\nPersistent caching across executions using store capability.\n\n```javascript\n// Cache API result for 1 hour (3600000 ms)\nconst weather = cache(\"weather-\" + city, 3600000, () => {\n return fetch(\"https://api.weather.com/\" + city)\n})\n```\n\n---\n\n## console.log / console.warn / console.error\n\nLogging utilities that integrate with trace and error flow.\n\n```javascript\nconsole.log(\"Debug info: \" + value) // Adds to trace\nconsole.warn(\"Potential issue\") // Adds to trace + warnings summary\nconsole.error(\"Fatal: \" + msg) // Triggers monadic error flow\n```\n\n- `log`: trace only (no side effects)\n- `warn`: trace + appears in `result.warnings`\n- `error`: stops execution, sets `result.error`",
|
|
133
|
+
"title": "runtime (inline docs)",
|
|
134
|
+
"filename": "runtime.ts",
|
|
135
|
+
"path": "src/vm/runtime.ts"
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"text": "# TJS Benchmarks\n\nGenerated: 2026-01-19\nRuntime: Bun 1.3.6\nPlatform: darwin arm64\nIterations: 100,000 per test\n\n## Summary\n\n| Benchmark | Baseline | Safe (default) | Unsafe (!) |\n|-----------|----------|----------------|------------|\n| CLI: Bun + TypeScript | 14.1ms | - | - |\n| CLI: tjsx (execute TJS) | 143.0ms | - | - |\n| CLI: tjs emit | 143.2ms | - | - |\n| CLI: tjs check | 143.1ms | - | - |\n| Simple arithmetic (100K iterations) | 0.5ms | 0.8ms (1.5x) | 0.5ms (1.1x) |\n| Object manipulation (100K iterations) | 1.2ms | 1.3ms (1.1x) | 1.1ms (~1.0x) |\n| 3-function chain (100K iterations) | 0.6ms | 1.4ms (2.2x) | 0.6ms (~1.0x) |\n\n## Key Findings\n\n### CLI Cold Start\n\n- **Bun + TypeScript**: ~14ms (native, baseline)\n- **tjsx**: ~143ms (includes TJS transpiler load)\n- **Overhead**: 129ms for transpiler initialization\n\nThe ~129ms overhead is from loading the acorn parser and TJS transpiler.\nA compiled binary (via `bun build --compile`) reduces this to ~20ms.\n\n### Safe vs Unsafe Functions\n\nTJS functions are **safe by default** with runtime type validation.\nUse `(!)` to mark functions as unsafe for performance-critical code:\n\n```javascript\n// Safe (default) - validates types at runtime\nfunction add(a: 0, b: 0) -> 0 { return a + b }\n\n// Unsafe - no validation, maximum performance\nfunction fastAdd(! a: 0, b: 0) -> 0 { return a + b }\n```\n\nPerformance comparison:\n- Simple arithmetic: Safe 1.5x vs Unsafe 1.1x\n- Object manipulation: Safe 1.1x vs Unsafe ~1.0x\n- 3-function chain: Safe 2.2x vs Unsafe ~1.0x\n\n## Recommendations\n\n1. **Use safe functions at API boundaries** - The default is correct for most code\n2. **Use `(!)` for internal hot paths** - When inputs are already validated\n3. **Consider compiled binary for CLI** - `bun build --compile` for ~20ms startup\n\n## Running Benchmarks\n\n```bash\nbun run bench\n```\n\nOr run the test suite with timing output:\n\n```bash\nbun test src/lang/perf.test.ts\n```\n",
|
|
139
|
+
"title": "TJS Benchmarks",
|
|
140
|
+
"filename": "benchmarks.md",
|
|
141
|
+
"path": "benchmarks.md"
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"text": "# TJS Benchmarks\n\nGenerated: 2026-01-19\nRuntime: Bun 1.3.6\nPlatform: darwin arm64\nIterations: 100,000 per test\n\n## Summary\n\n| Benchmark | Baseline | Safe (wrapped) | Unsafe (!) |\n| ----------------------------------- | -------- | -------------- | ----------- |\n| Simple arithmetic (100K calls) | 0.5ms | 13ms (26x) | 0.7ms (1.3x) |\n| 3-function chain (100K calls) | 0.7ms | 19ms (28x) | 0.8ms (1.2x) |\n| Loop with helper (100 elem × 10K) | 1.7ms | 20ms (12x) | 1.5ms (0.9x) |\n\n## Key Findings\n\n### Runtime Validation Overhead\n\nSafe TJS functions use `wrap()` for monadic type validation:\n- **~17-28x overhead** for simple operations\n- **~0.02µs per validation** (absolute time is small)\n- Overhead becomes negligible when actual work dominates\n\n### Safe vs Unsafe Functions\n\n```javascript\n// Safe (default) - validates types at runtime\nfunction add(a: 0, b: 0) -> 0 { return a + b }\n\n// Unsafe - no validation, plain JS performance\nfunction fastAdd(! a: 0, b: 0) -> 0 { return a + b }\n```\n\n| Mode | Overhead | Use Case |\n| ----------- | -------- | ------------------------------- |\n| Safe | ~17-28x | API boundaries, untrusted input |\n| Unsafe (!) | ~1.2x | Hot paths, internal functions |\n\n### ⚠️ Safe Helpers in Loops\n\n**Critical insight**: Wrapping the outer function in `unsafe {}` does NOT help if the inner helper is safe:\n\n```javascript\nfunction process(arr: [0]) -> 0 {\n unsafe {\n let sum = 0\n for (const x of arr) {\n sum += double(x) // If double() is safe, still pays 12x per call!\n }\n return sum\n }\n}\n```\n\n**Fix**: Mark the helper as unsafe:\n\n```javascript\nfunction double(! x: 0) -> 0 { return x * 2 } // No validation overhead\n```\n\n### `unsafe {}` Block\n\nThe `unsafe {}` block wraps code in try-catch for error handling:\n- Converts exceptions to monadic errors\n- Does NOT affect validation of called functions\n- Minimal overhead (~1.3x)\n\n## Recommendations\n\n1. **Safe by default** - Use for API boundaries and untrusted input\n2. **Unsafe (!) for helpers** - Mark hot inner functions that are called in loops\n3. **Validate once at the edge** - Check types at entry, use unsafe internally\n4. **Don't micro-optimize** - 0.02µs matters only in tight loops\n\n## Future: Type Flow Optimization\n\nPlanned compile-time optimization will automatically skip redundant checks:\n- Output type matches next input → skip validation\n- Array element type known → skip per-iteration checks\n- Target: ~1.2x overhead automatically (no manual `!` needed)\n\n## Running Benchmarks\n\n```bash\nbun test src/lang/perf.test.ts\n```\n",
|
|
145
|
+
"title": "TJS Benchmarks",
|
|
146
|
+
"filename": "benchmarks.md",
|
|
147
|
+
"path": "guides/benchmarks.md"
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"text": "<!--{\"pin\": \"top\"}-->\n\n# TJS Interactive Examples\n\nTry these examples in the playground! Each demonstrates a key TJS feature.\n\n## Types by Example\n\nTJS types ARE example values - self-documenting and runtime-checkable:\n\n```javascript\n// The example IS the type AND the documentation\nfunction greet(name: 'World', times: 3) -> '' {\n let result = ''\n for (let i = 0; i < times; i++) {\n result = result + `Hello, ${name}! `\n }\n return result.trim()\n}\n\n// greet.__tjs shows: name: string, times: number -> string\nconsole.log(greet('TJS', 2)) // \"Hello, TJS! Hello, TJS!\"\n```\n\n## Type Declarations with Defaults\n\nDefine reusable types with the `Type` keyword:\n\n```javascript\n// Type with default value\nType Name = 'Anonymous'\nType Count = 0\nType PositiveAge = +18 // positive number\n\n// Use types as parameter annotations\nfunction introduce(name: Name, age: PositiveAge) -> '' {\n return `Hi, I'm ${name} and I'm ${age} years old`\n}\n\nconsole.log(introduce('Alice', 30))\nconsole.log(Name.default) // 'Anonymous'\n```\n\n## Generic Types\n\nRuntime-checkable generics:\n\n```javascript\n// Define a generic Box<T> type\nGeneric Box<T> {\n description: 'a boxed value'\n predicate(obj, T) { \n return typeof obj === 'object' && \n obj !== null && \n 'value' in obj && \n T(obj.value) \n }\n}\n\n// Instantiate with different types\nconst StringBox = Box('')\nconst NumberBox = Box(0)\n\nconsole.log(StringBox.check({ value: 'hello' })) // true\nconsole.log(StringBox.check({ value: 123 })) // false\nconsole.log(NumberBox.check({ value: 42 })) // true\n```\n\n## Compile-Time Tests\n\nTests run during transpilation and evaporate from output:\n\n```javascript\nType Email {\n example: 'test@example.com'\n predicate(x) { \n return typeof x === 'string' && x.includes('@') \n }\n}\n\n// This test runs at transpile time, not runtime\ntest 'email validation' {\n if (!Email.check('user@example.com')) {\n throw new Error('valid email should pass')\n }\n if (Email.check('not-an-email')) {\n throw new Error('invalid email should fail')\n }\n}\n\n// The test code is stripped from the transpiled output\nfunction sendEmail(to: Email) -> { sent: true } {\n return { sent: true }\n}\n```\n\n## Safety Markers\n\nControl validation with `(!)` and `(?)`:\n\n```javascript\n// Default: safe function with validation\nfunction safeAdd(a: 0, b: 0) -> 0 {\n return a + b\n}\n\n// (!) Unsafe: skip input validation for performance\nfunction fastAdd(! a: 0, b: 0) -> 0 {\n return a + b\n}\n\n// (?) Force safe: always validate even if module is unsafe\nfunction alwaysSafe(? data: { x: 0 }) -> 0 {\n return data.x * 2\n}\n\nconsole.log(safeAdd(1, 2)) // 3\nconsole.log(fastAdd(1, 2)) // 3 (faster, no validation)\n```\n\n## SafeFunction and Eval\n\nSafe replacements for `new Function()` and `eval()`:\n\n```javascript\n// Create a typed async function from code\nconst multiply = await SafeFunction({\n inputs: { a: 0, b: 0 },\n output: 0,\n body: 'return a * b'\n})\n\nconsole.log(await multiply(3, 4)) // 12\n\n// Evaluate code with typed result\nconst result = await Eval({\n code: 'x + y',\n context: { x: 10, y: 5 },\n output: 0\n})\n\nconsole.log(result) // 15\n```\n\n## Monadic Error Handling\n\nErrors are values, not exceptions:\n\n```javascript\nfunction divide(a: 0, b: 0) -> 0 {\n if (b === 0) {\n return error('Division by zero')\n }\n return a / b\n}\n\nconst result = divide(10, 0)\n\nif (isError(result)) {\n console.log('Error:', result.message)\n} else {\n console.log('Result:', result)\n}\n\n// Errors propagate automatically through function chains\n```\n\n## Native Type Checking\n\nCheck platform types pragmatically:\n\n```javascript\n// typeOf returns constructor names for objects\nconsole.log(typeOf(new Map())) // 'Map'\nconsole.log(typeOf(new Date())) // 'Date'\nconsole.log(typeOf([1, 2, 3])) // 'array'\nconsole.log(typeOf(null)) // 'null'\n\n// isNativeType checks prototype chain\nconsole.log(isNativeType(new TypeError('oops'), 'Error')) // true\nconsole.log(isNativeType(new Map(), 'Map')) // true\n```\n\n## Union Types\n\nUse `||` for union types:\n\n```javascript\nUnion Status 'task status' {\n 'pending' | 'active' | 'done'\n}\n\nfunction updateTask(id: '', status: Status) -> { updated: true } {\n console.log(`Task ${id} -> ${status}`)\n return { updated: true }\n}\n\nupdateTask('task-1', 'active')\n```\n\n## Running TJS\n\n```bash\n# CLI commands\ntjs check file.tjs # Type check\ntjs run file.tjs # Execute\ntjs emit file.tjs # Output JavaScript\ntjs emit --debug file.tjs # Include source locations\n\n# Or use the Bun plugin for native support\nbun --preload ./src/bun-plugin/tjs-plugin.ts file.tjs\n```\n",
|
|
151
|
+
"title": "TJS Interactive Examples",
|
|
152
|
+
"filename": "tjs-examples.md",
|
|
153
|
+
"path": "guides/tjs-examples.md",
|
|
154
|
+
"pin": "top"
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"text": "# TJS Roadmap\n\n## Philosophy\n\nTJS is a practical language that targets multiple runtimes. The type system is _descriptive_ rather than _prescriptive_ - types explain what they are, validate at runtime, and degrade gracefully. No TypeScript gymnastics.\n\nThe runtime is JavaScript today, but it's _our_ JavaScript - the sandboxed expression evaluator, the fuel-metered VM. When we target LLVM or SwiftUI, we compile our AST, not arbitrary JS.\n\n---\n\n## Executive Summary\n\nTJS delivers **runtime type safety with near-zero overhead**. The key insight: single structured arguments enable inline validation that's 20x faster than schema interpretation.\n\n### The Performance Story\n\n| Mode | Overhead | Use Case |\n|------|----------|----------|\n| `safety none` | **1.0x** | Production - metadata only, no wrappers |\n| `safety inputs` | **~1.5x** | Production with validation (single-arg objects) |\n| `safety inputs` | ~11x | Multi-arg functions (schema-based) |\n| `safety all` | ~14x | Debug - validates inputs and outputs |\n| `(!) unsafe` | **1.0x** | Hot paths - explicit opt-out |\n| WASM blocks | **<1.0x** | Heavy computation - faster than JS |\n\n**The happy path**: Single structured argument + inline validation = **1.5x overhead** with full runtime type checking.\n\n### Why Single-Arg Objects Win\n\n```typescript\n// TJS: pleasant syntax, fast validation (1.5x)\nfunction createUser(input: { name: 'Alice', email: 'a@b.com', age: 30 }) {\n return save(input)\n}\n\n// TypeScript: painful syntax, no runtime safety (1.0x but unsafe)\nfunction createUser({ name, email, age }: { name: string, email: string, age: number }) {\n return save({ name, email, age })\n}\n```\n\nTJS generates inline type checks at transpile time:\n```javascript\nif (typeof input !== 'object' || input === null ||\n typeof input.name !== 'string' ||\n typeof input.email !== 'string' ||\n typeof input.age !== 'number') {\n return { $error: true, message: 'Invalid input', path: 'createUser.input' }\n}\n```\n\nNo schema interpretation. JIT-friendly. **20x faster** than Zod/io-ts style validation.\n\n### What You Get\n\n- **Runtime safety in production** - 1.5x overhead is acceptable\n- **Autocomplete always works** - `__tjs` metadata attached regardless of safety\n- **Monadic errors** - type failures return error objects, not exceptions\n- **Escape hatches** - `(!)` for hot functions, `unsafe {}` for hot blocks\n- **WASM acceleration** - `wasm {}` blocks for compute-heavy code\n\n### The Design Alignment\n\nThe idiomatic way to write TJS (single structured argument) is also the fastest way. Language design and performance goals are aligned - you don't have to choose between clean code and fast code.\n\n### Future: Compile to LLVM\n\nThe AST is the source of truth. Today we emit JavaScript. Tomorrow:\n- LLVM IR for native binaries\n- Compete with Go and Rust on performance\n- Same type safety, same developer experience\n\n---\n\n## Technical Aspects\n\n### Performance\n\n**Runtime Validation Overhead:**\n```\nPlain function call: 0.5ms / 100K calls (baseline)\nsafety: 'none': 0.5ms / 100K calls (~1.0x) - no wrapper\nsafety: 'inputs': 0.8ms / 100K calls (~1.5x) - inline validation*\nsafety: 'all': 7.0ms / 100K calls (~14x) - validates args + return\n\n* For single-arg object types (the happy path)\n Multi-arg functions use schema-based validation (~11x)\n```\n\n**Why single-arg objects are fast:**\n```typescript\n// The happy path - single structured argument\nfunction process(input: { x: 0, y: 0, name: 'default' }) {\n return input.x + input.y\n}\n\n// Generates inline type checks (20x faster than schema interpretation):\nif (typeof input !== 'object' || input === null ||\n typeof input.x !== 'number' ||\n typeof input.y !== 'number' ||\n typeof input.name !== 'string') {\n return { $error: true, message: 'Invalid input', path: 'process.input' }\n}\n```\n\nThis makes `safety: 'inputs'` viable for **production** with single-arg patterns.\n\n**Why `safety: 'none'` is free:**\n- `wrap()` attaches `__tjs` metadata but returns original function\n- No wrapper function, no `fn.apply()`, no argument spreading\n- Introspection/autocomplete still works - metadata is always there\n\n**The `(!) unsafe` marker:**\n```typescript\nfunction hot(! x: number) -> number { return x * 2 }\n```\n- Returns original function even with `safety: inputs`\n- Use for hot paths where validation cost matters\n- Autocomplete still works (metadata attached)\n\n**WASM blocks:**\n```typescript\nfunction compute(x: 0, y: 0) {\n const scale = 2\n return wasm {\n return x * y * scale // Compiles to WebAssembly\n }\n}\n// Variables (x, y, scale) captured automatically from scope\n// Same code runs as JS fallback if WASM unavailable\n```\n\nWith explicit fallback (when WASM and JS need different code):\n```typescript\nfunction transform(arr: []) {\n wasm {\n for (let i = 0; i < arr.length; i++) { arr[i] *= 2 }\n } fallback {\n return arr.map(x => x * 2) // Different JS implementation\n }\n}\n```\n\nWASM compilation is implemented as a proof-of-concept:\n- Parser extracts `wasm { }` blocks with automatic variable capture\n- Compiler generates valid WebAssembly binary from the body\n- Runtime dispatches to WASM when available, body runs as JS fallback\n- Benchmark: ~1.3x faster than equivalent JS (varies by workload)\n\nThe POC supports: arithmetic (+, -, *, /), captured variables, parentheses.\nFull implementation would add: loops, conditionals, typed arrays, memory access.\n\n### Debugging\n\n**Source locations in errors:**\n```typescript\n{\n $error: true,\n message: 'Expected string but got number',\n path: 'greet.name', // which parameter\n loc: { start: 15, end: 29 }, // source position\n stack: ['main', 'processUser', 'greet.name'] // call chain (debug mode)\n}\n```\n\n**Debug mode:**\n```typescript\nconfigure({ debug: true })\n// Errors now include full call stacks\n```\n\n**The `--debug` flag (planned):**\n- Functions know where they're defined\n- Errors include source file and line\n- No source maps needed - metadata is inline\n\n### For Human Coding\n\n**Intuitive syntax:**\n```typescript\n// Types ARE examples - self-documenting\nfunction greet(name: 'World', times: 3) -> string {\n return (name + '!').repeat(times)\n}\n\n// Autocomplete shows: greet(name: string, times: number) -> string\n// With examples: greet('World', 3)\n```\n\n**Module-level safety:**\n```typescript\nsafety none // This module skips validation\n\nfunction hot(x: number) -> number {\n return x * 2 // No wrapper, but autocomplete still works\n}\n```\n\n**Escape hatches:**\n```typescript\n// Per-function: skip validation for this function\nfunction critical(! data: object) { ... }\n\n// Per-block: skip validation for calls inside\nunsafe {\n for (let i = 0; i < 1000000; i++) {\n hot(i) // No validation overhead\n }\n}\n```\n\n### For Agent Coding\n\n**Introspectable functions:**\n```typescript\ngreet.__tjs = {\n params: { name: { type: 'string', required: true, example: 'World' } },\n returns: { type: 'string' }\n}\n\n// Agents can read this to understand function signatures\n// LLMs can generate function call schemas automatically\n```\n\n**Monadic errors:**\n```typescript\nconst result = riskyOperation()\nif (result.$error) {\n // Error is a value, not an exception\n // Agent can inspect and handle gracefully\n}\n```\n\n**Fuel metering:**\n```typescript\n// Agents run with fuel limits - can't run forever\nvm.run(agentCode, { fuel: 10000 })\n```\n\n---\n\n## 1. Type() Builtin\n\nA new builtin for defining types with descriptions and runtime validation.\n\n### Forms\n\n```typescript\n// Full form: description + predicate\nconst ZipCode = Type('5-digit US zip code', (s) => /^\\d{5}$/.test(s))\nconst PositiveInt = Type(\n 'positive integer',\n (n) => Number.isInteger(n) && n > 0\n)\nconst MatchingPasswords = Type(\n 'passwords must match',\n (o) => o.password === o.confirmPassword\n)\n\n// Schema shorthand (common case)\nconst Email = Type('valid email', s.string.email)\nconst Age = Type(s.number.min(0).max(150))\n\n// Description optional when schema is self-explanatory\nconst UserId = Type(s.string.uuid)\n```\n\n### Why\n\n- **Self-documenting**: description IS the type for humans and LLMs\n- **Runtime-checkable**: predicate runs when needed\n- **Composable**: union/intersection combine predicates\n- **Replaces regex-as-type**: more expressive, leaves regexes for actual patterns\n- **Escapes TypeScript corner cases**: no `Pick<Omit<Partial<Required<...>>>>`\n\n### Predicates\n\nPredicates are sync JS functions that run in our runtime:\n\n- Pure expression evaluation (same `$expr` nodes we have)\n- No async, no IO - type checks are in the hot path\n- Sandboxed: no prototype access, no globals\n- Portable: can be translated to any target\n\n### Simple Syntax Sugar Remains\n\n```typescript\n// These still work - Type() is the escape hatch, not the default\nfunction greet(name: string, times: number = 1) { ... }\nfunction delay(ms = 1000) { ... }\nfunction fetch(url: string, timeout = +5000) { ... }\n```\n\n## 2. Conditional Compilation with target()\n\nExplicit target blocks for platform-specific code.\n\n```typescript\ntarget(browser) {\n document.body.appendChild(el)\n}\n\ntarget(node) {\n process.stdout.write(str)\n}\n\ntarget(browser & debug) {\n console.log('Debug mode in browser')\n}\n\ntarget(browser | node) {\n // Runs in either\n}\n```\n\n### Targets\n\n**Current:**\n\n- `browser`\n- `node`\n- `bun`\n- `debug`\n- `production`\n\n**Future:**\n\n- `swiftui`\n- `android`\n- `ios`\n- `win64`\n- `llvm`\n\n### Composition\n\n- `&` - both must match\n- `|` - either matches\n- `target(production)` strips `test {}` blocks and debug code\n\n## 3. Monadic Errors and Debug Mode\n\n### try {} Without catch\n\nBare `try` blocks convert to monadic error returns:\n\n```typescript\ntry {\n let data = riskyOperation()\n process(data)\n}\n// No catch - transforms to:\n// if error, return { $error: true, message, op, cause }\n```\n\nErrors become values, not exceptions. Subsequent code is skipped (monadic flow).\n\n### AgentError Introspection\n\nErrors carry full context:\n\n```typescript\n{\n $error: true,\n message: 'Connection refused',\n op: 'httpFetch', // which atom failed\n cause: <original exception>, // for debugging\n // With --debug:\n source: 'orders.tjs:47:3', // exact location\n callStack: [ // how we got here\n 'ship() at orders.tjs:47:3',\n 'processOrder() at checkout.tjs:123:5',\n 'handleSubmit() at form.tjs:89:12'\n ]\n}\n```\n\n### --debug Flag\n\nWhen transpiled with `--debug`:\n\n- Functions know what they were called and where they came from\n- Errors include source locations and call stacks\n- Runtime can reconstruct the full path to failure\n\n```typescript\n// With --debug, errors show:\n// Error: Invalid ZipCode at ship() (orders.tjs:47:3)\n// called from processOrder() (checkout.tjs:123:5)\n// called from handleSubmit() (form.tjs:89:12)\n```\n\n### Current State\n\n- ✅ Monadic errors (AgentError class)\n- ✅ try {} without catch transforms\n- ⏳ Error introspection (op and message, not full stack)\n- ❌ --debug source mapping\n- ❌ Call stack in errors\n\n## 4. test('description') {} Blocks\n\nInline tests that hoist to bottom of file for execution.\n\n```typescript\nconst ZipCode = Type('5-digit US zip code', (s) => /^\\d{5}$/.test(s))\n\ntest('ZipCode validates correctly') {\n assert(ZipCode.check('12345'))\n assert(!ZipCode.check('1234'))\n assert(!ZipCode.check('123456'))\n assert(!ZipCode.check('abcde'), 'letters should fail')\n}\n\nfunction ship(to: ZipCode, quantity: PositiveInt) {\n // ...\n}\n\ntest('ship requires valid zip and quantity') {\n ship('12345', 1) // ok\n assertThrows(() => ship('bad', 1))\n}\n```\n\n### Failure Output\n\n```\nFAIL: ZipCode validates correctly\n assert(!ZipCode.check('1234')) ← auto-generated from source\n\nFAIL: ship requires valid zip and quantity\n letters should fail ← custom message when provided\n assert(!ZipCode.check('abcde'), 'letters should fail')\n```\n\n### Rules\n\n- `test('description')` - description required, explains what's being tested\n- `assert(expr)` - auto-describes from source code on failure\n- `assert(expr, 'reason')` - custom message overrides auto-description\n- Tests live next to the code they test\n- Hoisted to bottom for execution order\n- Stripped in `target(production)`\n- Like Rust's `#[test]` but inline\n\n## 5. Pragmatic Native Types\n\nTrust constructor names for platform types:\n\n```typescript\n// Instead of shipping 50KB of DOM type definitions:\n// - el instanceof HTMLElement checks constructor.name\n// - If someone lies about their constructor, that's on them\n```\n\nThis applies to:\n\n- DOM types (HTMLElement, Event, etc.)\n- Node types (Buffer, Stream, etc.)\n- Platform types (SwiftUI views, Android widgets)\n\n## 6. Future: Multi-Target Emission\n\nThe same TJS source compiles to:\n\n- JavaScript (current)\n- LLVM IR (native binaries)\n- Swift (iOS/macOS)\n- Kotlin (Android)\n\nPlatform builtins vary by target:\n\n- `browser`: `document`, `window`, `fetch`\n- `swiftui`: `VStack`, `HStack`, `Text`, `Button`\n- `android`: `View`, `TextView`, `LinearLayout`\n\nThe AST is the source of truth. Targets are just emission strategies.\n\n---\n\n## Implementation Status\n\n| # | Feature | Status | Notes |\n| --- | ---------------------- | ------ | ---------------------------------------------- |\n| 1 | Type() | ✅ | Full form with description + predicate, Union, Generic, Enum |\n| 2 | target() | ❌ | Conditional compilation |\n| 3 | Monadic Errors | ✅ | AgentError with path, loc, debug call stacks |\n| 4 | test() blocks | ✅ | extractTests, assert/expect, mock blocks, CLI |\n| 5 | Pragmatic natives | ⏳ | Some constructor checks exist |\n| 6 | Multi-target | ❌ | Future - JS only for now |\n| 7 | Safety levels | ✅ | none/inputs/all + (!)/(?) + unsafe {} |\n| 8 | Module-level safety | ✅ | `safety none` directive parsed and passed |\n| 9 | Single-pass | ✅ | Bun plugin: direct `bun file.tjs` execution |\n| 10 | Module system | ✅ | IndexedDB store, esm.sh CDN, pinned versions |\n| 11 | Autocomplete | ✅ | CodeMirror integration, globals, introspection |\n| 12 | Eval() / SafeFunction | ✅ | Both exported and tested in runtime |\n| 13 | Function introspection | ✅ | __tjs metadata with params, returns, examples |\n| 14 | Generic() | ✅ | Runtime-checkable generics with TPair, TRecord |\n| 15 | Asymmetric get/set | ✅ | JS native get/set captures asymmetric types |\n| 16 | `==` that works | ✅ | Is/IsNot with infix syntax + .Equals hook |\n| 17 | WASM blocks | ✅ | POC: parser + compiler for simple expressions |\n| 18 | Death to `new` | ✅ | wrapClass + no-explicit-new lint rule |\n| 19 | Linter | ✅ | unused vars, unreachable code, no-explicit-new |\n| 20 | TS→TJS converter | ✅ | `tjs convert` command |\n| 21 | Docs generation | ✅ | Auto-generated with emit, --no-docs, --docs-dir|\n| 22 | Class support | ✅ | TS→TJS class conversion, private→#, Proxy wrap |\n\n## Implementation Priority (Updated)\n\n| Priority | Feature | Status | Notes |\n| -------- | ------------------------- | ------ | -------------------------- |\n| 1 | **Type()** | ✅ | Full implementation |\n| 2 | **Autocomplete** | ✅ | Working in playground |\n| 3 | **test() blocks** | ✅ | Full implementation |\n| 4 | **--debug / call stacks** | ✅ | Full stacks with maxStackSize limit |\n| 5 | **Eval() / SafeFunction** | ✅ | Done |\n| 6 | **target()** | ❌ | Conditional compilation |\n| 7 | **Safety flags** | ✅ | Done |\n| 8 | **Single-pass** | ✅ | Bun plugin: `bun file.tjs` |\n| 9 | **Modules** | ✅ | Local store + CDN |\n\n## Next Up (Post-MVP)\n\n| Priority | Feature | Why |\n| -------- | ------------------------- | -------------------------- |\n| 1 | **target()** | Conditional compilation for build flags |\n| 2 | **Multi-target emission** | LLVM, SwiftUI, Android |\n\nNote: `wasm { } fallback { }` is already implemented. `target()` is for build-time code stripping (e.g., `target(browser) { }` vs `target(node) { }`), which is useful for multi-platform deployment but not MVP.\n\n## 7. Safety Levels and Flags\n\n### Defaults: Safe and Correct\n\nBy default, TJS is strict:\n\n- All type contracts enforced\n- Lint errors block compilation\n- Unknown types are errors\n\n### Escape Hatches\n\n```bash\ntjs build app.tjs # strict, safe defaults\ntjs build app.tjs --allow-unsafe # let nasty TS libs pass through\ntjs build app.tjs --yolo # bypass all safeguards (--just-fucking-doit)\n```\n\n- `--allow-unsafe`: Complex/unknown types become best-effort runtime checks, warnings not errors\n- `--yolo`: Skip all validation, emit anyway (for when you know what you're doing)\n\n### Lint Integration\n\nLint errors block safe builds. Not warnings - errors. If you want to ship broken code, use `--yolo`.\n\n## 8. Single-Pass Pipeline\n\nOne command does everything:\n\n```bash\ntjs build app.tjs\n```\n\nIn a single pass:\n\n1. **Lint** - catch errors early\n2. **Transpile** - emit target code\n3. **Test** - run inline tests (unless `--no-test`)\n4. **Docs** - extract documentation from types and descriptions\n\nNo separate `tjs lint && tjs build && tjs test && tjs docs`. One pass, all the information is right there.\n\n## 9. Module System ✅\n\n### Local Module Store (IndexedDB)\n\nThe playground provides persistent module storage:\n\n```typescript\n// Save a module\nawait store.save({ name: 'my-utils', type: 'tjs', code: source })\n\n// Import it in another module\nimport { helper } from 'my-utils'\n```\n\n- Modules stored in IndexedDB (persistent across sessions)\n- Validation on save (transpilation + inline tests)\n- Version tracking and timestamps\n- Local modules resolved first, then CDN\n\n### CDN Integration (esm.sh)\n\nnpm packages resolve via esm.sh with pinned versions:\n\n```typescript\nimport { debounce } from 'lodash' // -> https://esm.sh/lodash@4.17.21\nimport { z } from 'zod' // -> https://esm.sh/zod@3.22.0\n```\n\n- Common packages have pinned versions for stability\n- Service Worker caches fetched modules\n- Import maps generated at runtime for browser\n\n### Bundler Compatibility\n\nTJS also works inside conventional bundlers:\n\n- Emits standard ES modules\n- Bun plugin for direct `.tjs` execution\n- Or use playground's zero-build approach\n\nYour choice. We don't force either approach.\n\n## 10. Autocomplete by Introspection\n\nIDE support via runtime introspection, not static `.d.ts` files.\n\n### Heuristic Levels (Progressive Fallback)\n\n**Level 0: Local symbols** (instant, always)\n\n- Scan current file for identifiers\n- Function names, variable names, parameter names\n- Known atoms\n- Like BBEdit - fast, useful, no dependencies\n\n**Level 1: Type-aware** (fast, from syntax)\n\n- Parameter `name: 'Sarah'` → string, offer string methods\n- Variable `x: 17` → number\n- No runtime needed, just syntax analysis\n\n**Level 2: Runtime introspection** (when idle)\n\n- Actually run code with mocks up to cursor\n- Get real shapes from execution\n- Nice to have, not blocking\n\n### Strategy\n\n- Start fast (Level 0+1), upgrade async in background\n- Typing rapidly? Stay at Level 0\n- Paused 200ms? Try Level 1\n- Paused 500ms? Try Level 2\n- Cache aggressively - same signature = same completions\n\n### CSS: Use the Browser\n\nCSS autocomplete is pathological to implement manually - hundreds of properties, thousands of values, vendor prefixes. The browser already knows all of this.\n\n```typescript\n// Let the browser do the work\nconst style = document.createElement('div').style\nObject.keys(style) // Every CSS property\nCSS.supports('display', 'grid') // Validate values\n```\n\nDon't ship CSS type definitions. Query the browser at runtime for:\n\n- Property names\n- Valid values for each property\n- Vendor prefix variants\n\n### Versioned Imports Make This Insane\n\n```typescript\nimport { ship } from 'https://pkg.example.com/shipping@2.0.0/mod.tjs'\n```\n\n- Module already transpiled (cached by URL+version)\n- Already introspected (we know its exports)\n- Immutable (version pinned, never changes)\n- Autocomplete for `ship.` is instant forever\n\nNo node_modules crawling. No LSP server eating 4GB RAM. One file, one unit, instant knowledge.\n\n### Non-Goals\n\n- External LSP dependencies\n- TypeScript language server\n- Crawling dependency graphs\n\n## 11. Eval() - Safe Expression Evaluation\n\nA builtin for evaluating expressions with fuel limits:\n\n```typescript\n// Low default fuel - won't run away\nEval('2 + 2') // ~100 fuel default\n\n// Explicitly allow more for complex work\nEval('fibonacci(20)', { fuel: 1000 })\n\n// Restrict for untrusted input\nEval(userInput, { fuel: 10 })\n```\n\n### Why\n\n- Same sandboxed evaluator we already have, exposed as builtin\n- Safe by default - low fuel limit prevents runaway computation\n- No `eval()` - this is AST evaluation, not string execution\n- Fuel exhaustion returns error, doesn't throw\n\n### Options\n\n```typescript\nEval(expression, {\n fuel: 100, // max fuel (default: 100)\n context: {}, // variables available to expression\n timeout: 1000, // ms timeout (default: fuel * 10)\n})\n```\n\n## Ideas Parking Lot\n\n### Type Flow Optimization (Compile-Time)\n\nSkip redundant type checks when types are already proven. The transpiler tracks type information through the call graph:\n\n**Scenario 1: Chained Functions**\n\n```typescript\nfunction validate(x: number) -> number { return x * 2 }\nfunction process(x: number) -> number { return x + 1 }\n\n// Source\nconst result = process(validate(input))\n\n// Naive: validate checks input, process checks validate's output\n// Optimized: validate's return type matches process's input - skip second check\n\n// Transpiled (optimized)\nconst _v = validate(input) // validates input once\nconst result = process.__unchecked(_v) // skips redundant check\n```\n\n**Scenario 2: Loop Bodies**\n\n```typescript\nfunction double(x: number) -> number { return x * 2 }\nconst nums: number[] = [1, 2, 3]\n\n// Source\nnums.map(double)\n\n// Naive: double validates x on every iteration (3 checks)\n// Optimized: nums is number[], so each element is number - skip all checks\n\n// Transpiled (optimized) \nnums.map(double.__unchecked) // zero validation overhead in loop\n```\n\n**Scenario 3: Subtype Relationships**\n\n```typescript\nconst PositiveInt = Type('positive integer', n => Number.isInteger(n) && n > 0)\nfunction increment(x: number) -> number { return x + 1 }\n\nconst val: PositiveInt = 5\nincrement(val) // PositiveInt is subtype of number - skip check\n```\n\n**Implementation:**\n\n1. Track return types through call graph\n2. Generate `fn.__unchecked` variants that skip input validation\n3. Emit unchecked calls when input type is proven\n4. Array/iterable element types flow into loop bodies\n5. Subtype relationships allow broader → narrower without checks\n\n**Performance Target:**\n\n- Current `wrap()`: ~17x overhead\n- With type flow: ~1.2x overhead (matching safe TJS functions)\n- Hot loops: 0x overhead (unchecked path)\n\n### JIT-Compiled Type Predicates\n\nWe own the language, so we can optimize hot type checks:\n\n1. **Interpreted mode** (default): Predicate runs as-is\n2. **Compiled mode** (hot path): If a Type validates thousands of times, JIT-compile it\n\n```typescript\nconst ZipCode = Type('5-digit zip', (s) => /^\\d{5}$/.test(s))\n\n// First N calls: interpreted, collecting stats\n// Call N+1: \"this is hot, compile it\"\n// Now it's TypeBox-fast without ahead-of-time compilation\n```\n\nFor `target(production)`, we could inline validators entirely:\n\n```typescript\n// Source\nfunction ship(to: ZipCode) { ... }\n\n// Transpiled (production)\nfunction ship(to) {\n if (typeof to !== 'string' || !/^\\d{5}$/.test(to))\n throw new TypeError('expected 5-digit zip')\n ...\n}\n```\n\nNo runtime Type object, no .check() call - just inlined validation.\n\nUnlike TypeBox (which precompiles via eval and can't handle dynamic types), we can do both interpreted and compiled because we control the compiler.\n\n---\n\n## 12. Function Introspection\n\nFunctions are self-describing. A single signature provides types, examples, and tests:\n\n```typescript\nfunction checkAge(name: 'Anne', age = 17) -> { canDrink: false } {\n return { canDrink: age >= 21 }\n}\n```\n\nFrom this you get:\n\n| Extracted | Value |\n| ----------------- | -------------------------------------------------------------- |\n| **Types** | `name: string`, `age: number`, returns `{ canDrink: boolean }` |\n| **Examples** | `name = 'Anne'`, `age = 17`, output `{ canDrink: false }` |\n| **Implicit test** | `checkAge('Anne', 17)` should return `{ canDrink: false }` |\n| **Docs** | The signature IS the documentation |\n\n### Runtime Metadata\n\nEvery function carries introspectable metadata:\n\n```typescript\ncheckAge.meta\n// {\n// name: 'checkAge',\n// params: [\n// { name: 'name', type: 'string', example: 'Anne' },\n// { name: 'age', type: 'number', example: 17, default: 17 }\n// ],\n// returns: { type: { canDrink: 'boolean' }, example: { canDrink: false } },\n// source: 'users.tjs:42:1' // in debug builds\n// }\n```\n\n### Debug Builds\n\nWith `--debug`, functions know where they are and where they were called from:\n\n```typescript\n// Error output includes full trace:\n// Error: Invalid ZipCode at ship() (orders.tjs:47:3)\n// called from processOrder() (checkout.tjs:123:5)\n// called from handleSubmit() (form.tjs:89:12)\n```\n\n### No Source Maps\n\nSource maps are a hack - external files that get out of sync, break in large builds, and require tooling support. TJS replaces them entirely:\n\n- The function _knows_ where it's from (`fn.meta.source`)\n- Can't get out of sync (it's part of the function)\n- No external files, no tooling required\n- Works in production without `.map` files\n\n### Why This Matters\n\n- **Auto-generated tests**: Run with examples, expect example output\n- **API documentation**: Always accurate, extracted from source\n- **LLM tool schemas**: Generate OpenAI function calling format automatically\n- **Debug traces**: Full path to failure with source locations\n- **Zero extra effort**: You write the function, you get all of this\n\n## 13. Generic() Builtin\n\nTuring completeness by design, not by accident. TypeScript's generics grew into an unreadable type-level programming language. TJS assumes Turing completeness from the start - the predicate is just code:\n\nFollowing the `Type()` pattern, generics are runtime-inspectable and predicate-validated:\n\n```typescript\nconst List = Generic(\n 'homogeneous list of items',\n [T],\n (x, [T]) => Array.isArray(x) && x.every(item => T.check(item))\n)\n\nconst Map = Generic(\n 'key-value mapping',\n [K, V = any],\n (x, [K, V]) => x instanceof Map && [...x.keys()].every(k => K.check(k))\n)\n\n// Usage\nconst strings: List(string) = ['a', 'b', 'c']\nconst lookup: Map(string, number) = new Map([['age', 42]])\n```\n\n### Why\n\n- **Runtime-checkable**: Not erased like TypeScript generics\n- **Self-documenting**: Description for humans and LLMs\n- **Composable**: Predicates can do real validation\n- **Practical**: Makes complex generics achievable without gymnastics\n\nConverting convoluted TypeScript generics (`Pick<Omit<Partial<...>>>`) is nice-to-have, not a priority.\n\n## 14. Asymmetric Get/Set ✅\n\nProperties that accept a broader type on write but return a narrower type on read. TJS uses JavaScript's native getter/setter syntax which naturally captures asymmetric types:\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 // Setter accepts string, number, or null\n set value(v: '' | 0 | null) {\n this.#value = v === null ? new Date() : new Date(v)\n }\n \n // Getter always returns Date\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 (always normalized)\n```\n\nThe type metadata captures the asymmetry:\n- Setter param type: `'' | 0 | null` (union of string, number, null)\n- Getter return type: `Date` (inferred from implementation)\n\nThis matches real-world APIs (DOM, dates, etc.) without TypeScript's painful workarounds.\n\n## 15. `==` That Works (via Is/IsNot)\n\nJavaScript's `==` is broken (type coercion chaos). TJS provides `Is` and `IsNot` operators as stepping stones toward eventually fixing `==` and `!=`:\n\n| Operator | Behavior |\n| -------- | ----------------------------------------------------------------------------------------- |\n| `Is` | **Value equality** - structural comparison for arrays/objects, calls `.Equals` if defined |\n| `IsNot` | **Value inequality** - negation of Is |\n| `===` | **Identity** - same object reference (rarely needed) |\n\n```typescript\n// Infix syntax - clean and readable\n[1, 2] Is [1, 2] // true (structural)\n[1, 2] IsNot [1, 2, 3] // true (different length)\n5 Is \"5\" // false (no coercion - different types)\n\n// Custom equality via .Equals hook\nconst p1 = {\n x: 1,\n Equals(o) {\n return this.x === o.x\n },\n}\nconst p2 = { x: 1 }\np1 Is p2 // true (via .Equals hook)\np1 === p2 // false (different objects)\n```\n\n### Rules\n\n1. If left has `.Equals`, call `left.Equals(right)`\n2. If right has `.Equals`, call `right.Equals(left)`\n3. Arrays/objects: recursive structural comparison\n4. Primitives: strict equality (no coercion)\n\n### Implementation Status\n\n- ✅ `Is()` and `IsNot()` functions in runtime\n- ✅ Infix syntax transformation (`a Is b` → `Is(a, b)`)\n- ✅ Legacy JS/TS code works unchanged (uses native `==`/`!=`)\n\n## 16. Death to Semicolons\n\nNewlines are meaningful. This:\n\n```typescript\nfoo()\n```\n\nIs **two statements** (`foo` and `()`), not a function call `foo()`.\n\nThis eliminates:\n\n- Defensive semicolons\n- ASI gotchas\n- The entire \"semicolon debate\"\n\nThe only code this breaks is pathological formatting that nobody writes intentionally.\n\n## 17. Polyglot Blocks (WASM, Shaders, etc.)\n\nTarget-specific code blocks with automatic variable capture and fallback:\n\n```typescript\n// WASM for performance-critical path - variables captured automatically\nfunction matmul(vertices: Float32Array, matrix: Float32Array) {\n wasm {\n for (let i = 0; i < vertices.length; i += 3) {\n // matrix multiply using vertices and matrix from scope\n }\n }\n // Body runs as JS if WASM unavailable\n}\n\n// With explicit fallback when implementations differ:\nfunction transform(data: Float32Array) {\n wasm {\n // WASM-optimized in-place mutation\n for (let i = 0; i < data.length; i++) { data[i] *= 2 }\n } fallback {\n // JS uses different approach\n return data.map(x => x * 2)\n }\n}\n\n// GPU shader (future)\nglShader {\n gl_Position = projection * view * vec4(position, 1.0)\n fragColor = color\n} fallback {\n // CPU fallback\n}\n\n// Debug-only code (stripped in production)\ndebug {\n console.log('state:', state)\n validateInvariants()\n}\n// No fallback needed - just doesn't run in production\n```\n\n### Pattern\n\n```\ntarget(args?) {\n // target-specific code (compiled/translated)\n} fallback? {\n // universal TJS fallback (optional for some targets)\n}\n```\n\n### Targets\n\n| Target | Compiles to | Fallback | Use case |\n| ---------- | ---------------------- | -------- | ------------------------- |\n| `wasm` | WebAssembly | Required | CPU-intensive computation |\n| `glShader` | GLSL | Required | GPU graphics |\n| `metal` | Metal Shading Language | Required | Apple GPU |\n| `debug` | TJS (stripped in prod) | None | Debugging, invariants |\n\n### Why\n\n- **Performance**: WASM/GPU where it matters, TJS everywhere else\n- **Graceful degradation**: Fallback ensures code always runs\n- **Single source**: Don't maintain separate WASM/shader files\n- **Type-safe boundary**: Args translated automatically at the boundary\n\n## 18. Classes and Components\n\nTJS embraces classes, but eliminates JS footguns and enables cross-platform UI components.\n\n### Death to `new` ✅\n\nThe `new` keyword is redundant ceremony. TJS handles it automatically:\n\n```typescript\nclass User {\n constructor(public name: string) {}\n}\n\n// Both work identically in TJS:\nconst u1 = User('Alice') // TJS way - clean\nconst u2 = new User('Alice') // Lint warning: \"use User() instead of new User()\"\n```\n\nIf you call `Foo()` and `Foo` is a class, TJS calls it with `new` internally. No more \"Cannot call a class as a function\" errors.\n\n**Implementation:**\n- `wrapClass()` in `src/lang/runtime.ts` - wraps classes with Proxy for callable behavior\n- `emitClassWrapper()` generates wrapper code for transpiled classes\n- `no-explicit-new` lint rule warns about unnecessary `new` keyword usage\n\n### Component Base Class\n\n`Component` is the platform-agnostic UI primitive:\n\n```typescript\nclass MyDropdown extends Component {\n // Shared logic - runs everywhere\n items: string[] = []\n selectedIndex: number = 0\n \n select(index: number) {\n this.selectedIndex = index\n this.emit('change', this.items[index])\n }\n \n // Platform-specific blocks\n web() {\n // CSS, DOM events, ARIA attributes\n this.style = `\n .dropdown { position: relative; }\n .dropdown-menu { position: absolute; }\n `\n }\n \n swift() {\n // SwiftUI modifiers, gestures\n Menu {\n ForEach(items) { item in\n Button(item) { select(items.indexOf(item)) }\n }\n }\n }\n \n android() {\n // Jetpack Compose\n DropdownMenu(expanded = expanded) {\n items.forEach { item ->\n DropdownMenuItem(onClick = { select(items.indexOf(item)) }) {\n Text(item)\n }\n }\n }\n }\n}\n```\n\n### Web Components (HTMLElement)\n\nFor web, `extends HTMLElement` auto-registers custom elements:\n\n```typescript\nclass MyDropdown extends HTMLElement {\n // Automatically registers <my-dropdown>\n}\n\nclass UserCard extends HTMLElement {\n // Automatically registers <user-card>\n}\n\n// Error: can't infer tag name\nclass Thang extends HTMLElement { } // \"can't infer tag-name from 'Thang'\"\n\n// OK: modest names work\nclass MyThang extends HTMLElement { } // <my-thang>\n```\n\n**Key features:**\n\n1. **Auto-registration**: Class name → tag name (`MyDropdown` → `my-dropdown`)\n2. **Inferrable names required**: Must be PascalCase with multiple words\n3. **Hot-reloadable**: Components are hollow shells - redefining rebuilds all instances\n4. **Smart inheritance**: ARIA roles and behaviors wired automatically\n\n### Why Hollow Components?\n\nThe web component registry is a source of pain - you can't redefine elements. TJS sidesteps this:\n\n```typescript\n// First definition\nclass MyButton extends HTMLElement {\n render() { return '<button>v1</button>' }\n}\n\n// Later redefinition (hot reload, live coding)\nclass MyButton extends HTMLElement {\n render() { return '<button>v2</button>' }\n}\n// All existing <my-button> elements rebuild with new implementation\n```\n\nThe registered element is a hollow proxy that delegates to the current class definition.\n\n### Platform Adapters\n\nThe `Component` class compiles to platform-native code:\n\n| TJS Source | Web Output | SwiftUI Output | Compose Output |\n|------------|------------|----------------|----------------|\n| `class Foo extends Component` | Custom Element | `struct Foo: View` | `@Composable fun Foo()` |\n| `this.state = x` | Reactive update | `@State var state` | `mutableStateOf()` |\n| `this.emit('click')` | `dispatchEvent()` | Callback closure | Lambda |\n| `web { }` | Compiled | Stripped | Stripped |\n| `swift { }` | Stripped | Compiled | Stripped |\n\nThe class definition is the source of truth. Platform blocks contain native code for each target - no CSS-in-JS gymnastics trying to map everywhere.\n\n## Non-Goals\n\n- Full JS semantics (we're a subset that's portable)\n- Convoluted TS type gymnastics (maximum effort - best-effort conversion, ignore what we can't handle)\n",
|
|
158
|
+
"title": "TJS Roadmap",
|
|
159
|
+
"filename": "PLAN.md",
|
|
160
|
+
"path": "PLAN.md"
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"text": "# tjs-lang Guides\n\nDetailed documentation for tjs-lang.\n\n## Language References\n\n- **[ajs.md](./ajs.md)** - AJS language reference (the main language for writing agents)\n- **[ajs-llm-prompt.md](./ajs-llm-prompt.md)** - System prompt for LLM code generation\n- **[tjs.md](./tjs.md)** - TJS (Typed JavaScript) specification\n\n## Patterns & Best Practices\n\n- **[patterns.md](./patterns.md)** - Common patterns and workarounds for AJS\n\n## Reference\n\n- **[tosijs-schema.md](./tosijs-schema.md)** - Guide to the tosijs-schema validation library\n- **[benchmarks.md](./benchmarks.md)** - Performance benchmarks\n\n## See Also\n\n- [README.md](../README.md) - Main project overview and quick start\n- [CONTEXT.md](../CONTEXT.md) - Technical architecture deep-dive\n",
|
|
164
|
+
"title": "tjs-lang Guides",
|
|
165
|
+
"filename": "README.md",
|
|
166
|
+
"path": "guides/README.md"
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"text": "# TJS-Lang TODO\n\n## Playground - Error Navigation\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- [ ] Import one example from another in playground\n- [ ] Save/Load TS examples (consistency with TJS examples)\n- [ ] File name should be linked to example name\n- [ ] New example button in playground\n- [ ] UI for managing stored modules (browse/delete IndexedDB)\n- [ ] Auto-discover and build local dependencies in module resolution\n\n## Language Features\n- [ ] Portable Type predicates - expression-only AJS subset (no loops, no async, serializable)\n- [ ] Expand WASM support beyond POC (currently: single return + numeric ops only)\n - [ ] For loops with numeric bounds\n - [ ] Conditionals (if/else)\n - [ ] Local variables within block\n - [ ] Typed array access (Float32Array, etc.)\n - [ ] Memory operations\n- [ ] Compelling WASM demo, e.g. iTunes-visualizer like animation\n\n## Editor\n- [ ] Embedded AJS syntax highlighting\n\n## Documentation / Examples\n- [ ] Create an endpoint example\n\n## Infrastructure\n- [ ] Make playground components reusable for others\n- [ ] Web worker for transpiles (freezer - not needed yet)\n- [ ] Retarget Firebase as host platform (vs GitHub Pages)\n- [ ] Universal LLM endpoint with real LLMs (OpenAI, Anthropic, etc.)\n- [ ] ESM-as-a-service: versioned library endpoints\n- [ ] User accounts (Google sign-in) for API key storage\n- [ ] AJS-based Firestore and Storage security rules\n- [ ] npx tjs-playground - run playground locally with LM Studio\n- [ ] Virtual subdomains for user apps (yourapp.tjs.land)\n - [ ] Wildcard DNS to Firebase\n - [ ] Subdomain routing in Cloud Function\n - [ ] Deploy button in playground\n - [ ] Public/private visibility toggle\n- [ ] Rate limiting / abuse prevention for LLM endpoint\n- [ ] Usage tracking / billing foundation (for future paid tiers)\n\n---\n\n## Completed (this session)\n\n### Project Rename\n- [x] Rename from tosijs-agent to tjs-lang\n- [x] Update all references in package.json, docs, scripts\n- [x] Remove bd (beads) issue tracker, replace with TODO.md\n\n### Timestamp & LegalDate Utilities\n- [x] Timestamp - pure functions, 1-based months, no Date warts (53 tests)\n - now, from, parse, tryParse\n - addDays/Hours/Minutes/Seconds/Weeks/Months/Years\n - diff, diffSeconds/Minutes/Hours/Days\n - year/month/day/hour/minute/second/millisecond/dayOfWeek\n - toLocal, format, formatDate, formatTime, toDate\n - isBefore/isAfter/isEqual/min/max\n - startOf/endOf Day/Month/Year\n- [x] LegalDate - pure functions, YYYY-MM-DD strings (55 tests)\n - today, todayIn, from, parse, tryParse\n - addDays/Weeks/Months/Years\n - diff, diffMonths, diffYears\n - year/month/day/dayOfWeek/weekOfYear/dayOfYear/quarter\n - isLeapYear, daysInMonth, daysInYear\n - toTimestamp, toUnix, fromUnix\n - format, formatLong, formatShort\n - isBefore/isAfter/isEqual/min/max/isBetween\n - startOf/endOf Month/Quarter/Year/Week\n- [x] Portable predicate helpers: isValidUrl, isValidTimestamp, isValidLegalDate\n\n### TJS Mode System (JS is now the default)\n- [x] Invert mode system - JS semantics are default, improvements opt-in\n- [x] TjsEquals directive - structural == and != (null == undefined)\n- [x] TjsClass directive - classes callable without new\n- [x] TjsDate directive - bans Date constructor/methods\n- [x] TjsNoeval directive - bans eval() and new Function()\n- [x] TjsStrict directive - enables all of the above\n- [x] Deprecate LegacyEquals (now a no-op with warning)\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- [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",
|
|
170
|
+
"title": "TJS-Lang TODO",
|
|
171
|
+
"filename": "TODO.md",
|
|
172
|
+
"path": "TODO.md"
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"text": "# TJS: Typed JavaScript\n\nTJS is a typed superset of JavaScript where **types are examples**.\n\n```javascript\nfunction greet(name: 'World', times: 3) -> '' {\n let result = ''\n let i = 0\n while (i < times) {\n result = result + `Hello, ${name}! `\n i = i + 1\n }\n return result.trim()\n}\n```\n\n## Philosophy\n\nTJS takes a different approach to typing than TypeScript:\n\n| Aspect | TypeScript | TJS |\n| -------------- | --------------------------------------- | ----------------------------- |\n| Types | Abstract declarations | Concrete examples |\n| Runtime | Erased completely | Preserved as metadata |\n| Validation | Compile-time only | Runtime optional |\n| Learning curve | Learn type syntax | Use values you know |\n| Error messages | \"Type 'string' is not assignable to...\" | \"Expected string, got number\" |\n\n### Why Examples?\n\nConsider how you'd explain a function to another developer:\n\n> \"This function takes a name like 'World' and a count like 3, and returns a greeting string\"\n\nThat's exactly how TJS works. The example _is_ the type:\n\n```javascript\n// TypeScript\nfunction greet(name: string, times: number): string\n\n// TJS - the example IS the documentation\nfunction greet(name: 'World', times: 3) -> ''\n```\n\n## Core Concepts\n\n### 1. Types by Example\n\nInstead of abstract type names, use example values:\n\n```javascript\n// Strings\nname: '' // any string\nname: 'default' // string with default value\n\n// Numbers\ncount: 0 // any number\nport: 8080 // number with default\n\n// Booleans\nenabled: true // boolean (default true)\ndisabled: false // boolean (default false)\n\n// Arrays\nitems: [''] // array of strings\nnumbers: [0] // array of numbers\nmixed: [0, ''] // tuple: number, string\n\n// Objects\nuser: { name: '', age: 0 } // object with shape\n\n// Null/Undefined\nnullable: null\noptional: undefined\n```\n\n### 2. Required vs Optional (`:` vs `=`)\n\nThe colon `:` means required, equals `=` means optional:\n\n```javascript\nfunction createUser(\n name: 'Anonymous', // required string\n email: 'user@example.com', // required string\n age = 0, // optional number (defaults to 0)\n role = 'user' // optional string (defaults to 'user')\n) -> { id: '', name: '', email: '', age: 0, role: '' } {\n return {\n id: crypto.randomUUID(),\n name,\n email,\n age,\n role\n }\n}\n\n// Valid calls:\ncreateUser('Alice', 'alice@example.com')\ncreateUser('Bob', 'bob@example.com', 30)\ncreateUser('Carol', 'carol@example.com', 25, 'admin')\n\n// Invalid - missing required params:\ncreateUser('Dave') // Error: missing required parameter 'email'\n```\n\n### 3. Return Type Annotation\n\nUse `->` to declare the return type:\n\n```javascript\nfunction add(a: 0, b: 0) -> 0 {\n return a + b\n}\n\nfunction getUser(id: '') -> { name: '', email: '' } | null {\n // Returns user object or null\n}\n```\n\n### 4. Union Types\n\nUse `||` for unions (not `|` like TypeScript):\n\n```javascript\nfunction parseInput(value: '' || 0 || null) -> '' {\n if (value === null) return 'null'\n if (typeof value === 'number') return `number: ${value}`\n return `string: ${value}`\n}\n```\n\n### 5. The `any` Type\n\nWhen you genuinely don't know the type:\n\n```javascript\nfunction identity(x: any) -> any {\n return x\n}\n```\n\nGenerics from TypeScript become `any` but preserve metadata:\n\n```javascript\n// TypeScript: function identity<T>(x: T): T\n// TJS: any, but __tjs.typeParams captures the generic info\nfunction identity(x: any) -> any {\n return x\n}\n// identity.__tjs.typeParams = { T: {} }\n```\n\n### 6. Type Declarations\n\nDefine reusable types with the `Type` keyword:\n\n```javascript\n// Type with default value (= syntax)\nType Name = 'Alice'\nType Count = 0\nType Age = +18 // positive number\n\n// Type with description and default\nType Name 'a person name' = 'Alice'\n\n// Type with example (for testing/documentation)\nType User 'registered user' {\n example: { name: '', age: 0 }\n}\n\n// Type with both default and example\nType PositiveAge = +1 {\n example: 30\n}\n\n// Type with predicate (auto-generates type guard from example)\nType EvenNumber {\n example: 2\n predicate(x) { return x % 2 === 0 }\n}\n\n// Complex validation with predicate\nType Email {\n example: 'test@example.com'\n predicate(x) { return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(x) }\n}\n```\n\n**Default vs Example:**\n- `= value` sets a **default** for instantiation\n- `example:` in block sets an **example** for testing/documentation\n- When both are present, they serve different purposes\n\nWhen `example` and `predicate` are provided, the type guard auto-checks the example's shape, then your predicate refines it.\n\n### 7. Generic Declarations\n\nDefine parameterized types with the `Generic` keyword:\n\n```javascript\n// Simple generic\nGeneric Box<T> {\n description: 'a boxed value'\n predicate(x, T) { \n return typeof x === 'object' && x !== null && 'value' in x && T(x.value) \n }\n}\n\n// Generic with default type parameter\nGeneric Container<T, U = ''> {\n description: 'container with label'\n predicate(obj, T, U) { \n return T(obj.item) && U(obj.label) \n }\n}\n```\n\nIn the predicate, `T` and `U` are type-checking functions that validate values against the provided type parameters.\n\n### 8. Bare Assignments\n\nUppercase identifiers automatically get `const`:\n\n```javascript\n// These are equivalent:\nFoo = Type('test', 'example')\nconst Foo = Type('test', 'example')\n\n// Works for any uppercase identifier\nMyConfig = { debug: true }\nconst MyConfig = { debug: true }\n```\n\n## Runtime Features\n\n### Monadic Error Handling\n\nTJS functions propagate errors automatically:\n\n```javascript\n// If any input is an error, it passes through\nconst result = processData(maybeError)\n// If maybeError is an error, result is that error (processData not called)\n\n// Check for errors\nif (isError(result)) {\n console.log(result.message)\n}\n```\n\n### Safe by Default\n\nTJS functions are wrapped with runtime type validation by default:\n\n```javascript\nfunction add(a: 0, b: 0) -> 0 {\n return a + b\n}\n\nadd(1, 2) // 3\nadd('1', 2) // Error: expected number, got string\nadd(null, 2) // Error: expected number, got null\n```\n\nThis provides excellent error messages and catches type mismatches at runtime.\n\n### Safety Markers: `(?)` and `(!)`\n\nControl input validation with markers after the opening paren:\n\n```javascript\n// (?) - Safe function: force input validation\nfunction safeAdd(? a: 0, b: 0) -> 0 {\n return a + b\n}\n\n// (!) - Unsafe function: skip input validation\nfunction fastAdd(! a: 0, b: 0) -> 0 {\n return a + b\n}\n\nfastAdd(1, 2) // 3 (fast path, no validation)\nfastAdd('1', 2) // NaN (no validation, garbage in = garbage out)\n```\n\nThe `!` is borrowed from TypeScript's non-null assertion operator - it means \"I know what I'm doing, trust me.\"\n\n### Return Type Safety: `->`, `-?`, `-!`\n\nControl output validation with different arrow styles:\n\n```javascript\n// -> normal return type (validation depends on module settings)\nfunction add(a: 0, b: 0) -> 0 { return a + b }\n\n// -? force output validation (safe return)\nfunction critical(a: 0, b: 0) -? 0 { return a + b }\n\n// -! skip output validation (unsafe return)\nfunction fast(a: 0, b: 0) -! 0 { return a + b }\n```\n\nCombine input and output markers for full control:\n\n```javascript\n// Fully safe: validate inputs AND outputs\nfunction critical(? x: 0) -? 0 { return x * 2 }\n\n// Fully unsafe: skip all validation\nfunction blazingFast(! x: 0) -! 0 { return x * 2 }\n```\n\n### The `unsafe` Block\n\nFor unsafe sections within a safe function, use `unsafe {}`:\n\n```javascript\nfunction sum(numbers: [0]) -> 0 {\n // Parameters are validated, but the inner loop is unsafe\n unsafe {\n let total = 0\n for (let i = 0; i < numbers.length; i++) {\n total += numbers[i]\n }\n return total\n }\n}\n```\n\n### Performance Characteristics\n\n| Mode | Overhead | Use Case |\n| ----------------- | -------- | ---------------------------------------- |\n| Default (safe) | ~50x | API boundaries, user input |\n| `unsafe {}` block | ~1.2x | Hot loops within validated functions |\n| `(!)` function | 0x | Internal utilities, performance critical |\n\nUse `(!)` for internal functions that are called frequently with known-good data. Keep public APIs safe.\n\n### SafeFunction and Eval\n\nSafe replacements for `new Function()` and `eval()` with typed inputs/outputs:\n\n```javascript\n// SafeFunction - create a typed async function from code\nconst add = await SafeFunction({\n inputs: { a: 0, b: 0 }, // typed parameters\n output: 0, // typed return\n body: 'return a + b'\n})\nawait add(1, 2) // 3\nawait add('x', 2) // Error: invalid input 'a'\n\n// Eval - evaluate code once with typed result\nconst result = await Eval({\n code: 'a + b',\n context: { a: 1, b: 2 },\n output: 0\n}) // 3\n```\n\n**Key safety features:**\n- **Typed inputs/outputs** - validated at runtime\n- **Async execution** - can timeout, won't block\n- **Explicit context** - no implicit scope access\n- **Injectable capabilities** - fetch, console, etc. must be provided\n\n```javascript\n// With capabilities and timeout\nconst fetcher = await SafeFunction({\n inputs: { url: '' },\n output: { data: [] },\n body: 'return await fetch(url).then(r => r.json())',\n capabilities: { fetch: globalThis.fetch },\n timeoutMs: 10000\n})\n\nconst data = await Eval({\n code: 'await fetch(url).then(r => r.json())',\n context: { url: 'https://api.example.com' },\n output: { items: [] },\n capabilities: { fetch: globalThis.fetch }\n})\n```\n\nBoth functions return errors as values (monadic) rather than throwing.\n\n## Testing\n\n### Compile-Time Tests\n\nTests run at **transpile time** and are stripped from output:\n\n```javascript\nType Email {\n example: 'test@example.com'\n predicate(x) { return x.includes('@') }\n}\n\n// This test runs during transpilation\ntest 'email validation' {\n if (!Email.check('user@example.com')) {\n throw new Error('valid email should pass')\n }\n if (Email.check('invalid')) {\n throw new Error('invalid email should fail')\n }\n}\n\nfunction sendEmail(to: Email) {\n // ...\n}\n```\n\nThe transpiled output contains only:\n```javascript\nconst Email = Type('Email', ...)\nfunction sendEmail(to) { ... }\n```\n\nThe test code **evaporates** - it verified correctness at build time.\n\n### Implicit Type Tests\n\nTypes with `example` have implicit tests - the example must pass the type check:\n\n```javascript\nType PositiveInt {\n example: 42\n predicate(x) { return Number.isInteger(x) && x > 0 }\n}\n// Implicit test: PositiveInt.check(42) must be true\n```\n\nIf the example fails the predicate, transpilation fails.\n\n### Skip Tests Flag\n\nFor debugging or speed, skip test execution:\n\n```bash\ntjs emit file.tjs --dangerously-skip-tests\n```\n\nTests are still stripped from output, but not executed.\n\n### Legacy Inline Tests\n\nFor runtime tests (e.g., integration tests), use standard test frameworks:\n\n```javascript\ntest('async operations work') {\n const data = await fetchData()\n expect(data).toBeDefined()\n}\n```\n\n## Differences from JavaScript\n\n### Removed/Discouraged\n\n| Feature | Reason |\n| -------------- | --------------------------- |\n| `var` | Use `let` or `const` |\n| `class` | Use functions and objects |\n| `this` | Explicit context passing |\n| `new` (mostly) | Factory functions preferred |\n| `throw` | Return errors as values |\n| `for...in` | Use `Object.keys()` |\n\n### Added\n\n| Feature | Purpose |\n| ----------------- | -------------------------------------------- |\n| `: example` | Required parameter with type |\n| `= example` | Optional parameter with default |\n| `-> Type` | Return type annotation |\n| `-? Type` | Return type with forced output validation |\n| `-! Type` | Return type with skipped output validation |\n| `(?)` | Mark function as safe (force validation) |\n| `(!)` | Mark function as unsafe (skip validation) |\n| `test 'name' {}` | Compile-time test block (evaporates) |\n| `mock {}` | Test setup block |\n| `unsafe {}` | Skip validation for a block |\n| `\\|\\|` in types | Union types |\n| `Type Name = val` | Define runtime type with default |\n| `Generic<T>` | Define a parameterized runtime type |\n| `Foo = ...` | Bare assignment (auto-adds `const`) |\n| `SafeFunction` | Safe typed async replacement for `Function` |\n| `Eval` | Safe typed async replacement for `eval()` |\n\n## Differences from TypeScript\n\n### Types are Values\n\n```typescript\n// TypeScript - abstract type\ninterface User {\n name: string\n age: number\n email?: string\n}\n\n// TJS - concrete example\nconst User = { name: '', age: 0, email = '' }\n```\n\n### Runtime Preservation\n\nTypeScript erases types at compile time. TJS preserves them:\n\n```javascript\nfunction greet(name: 'World') -> '' {\n return `Hello, ${name}!`\n}\n\n// At runtime:\ngreet.__tjs = {\n params: { name: { type: 'string', required: true } },\n returns: { type: 'string' }\n}\n```\n\nThis enables:\n\n- Runtime validation\n- Auto-generated documentation\n- API schema generation\n- Better error messages\n\n### Generics\n\nTypeScript generics become `any` in TJS, but constraints are preserved:\n\n```typescript\n// TypeScript\nfunction process<T extends { id: number }>(item: T): T\n\n// TJS - constraint becomes validatable schema\nfunction process(item: any) -> any\n// process.__tjs.typeParams = { T: { constraint: '{ id: 0 }' } }\n```\n\nThe constraint `{ id: number }` becomes the example `{ id: 0 }` - and can be validated at runtime!\n\n### No Type Gymnastics\n\nTJS doesn't support:\n\n- Conditional types\n- Mapped types\n- Template literal types\n- `infer` keyword\n\nIf you need these, you probably need to rethink your approach. TJS favors simple, explicit types over clever type-level programming.\n\n## The `__tjs` Metadata\n\nEvery TJS function has attached metadata:\n\n```javascript\nfunction createUser(name: 'Anonymous', age = 0) -> { id: '', name: '', age: 0 } {\n return { id: crypto.randomUUID(), name, age }\n}\n\ncreateUser.__tjs = {\n params: {\n name: { type: 'string', required: true, default: 'Anonymous' },\n age: { type: 'number', required: false, default: 0 }\n },\n returns: { type: 'object', shape: { id: 'string', name: 'string', age: 'number' } },\n // For generic functions:\n typeParams: {\n T: { constraint: '{ id: 0 }', default: null }\n }\n}\n```\n\nThis metadata enables:\n\n1. **Runtime validation** via `wrap()`\n2. **Documentation generation** via `generateDocs()`\n3. **API schema export** (OpenAPI, JSON Schema)\n4. **IDE autocompletion**\n5. **Version-safe serialization**\n\n## CLI Tools\n\n### `tjs` - The TJS Compiler\n\n```bash\ntjs check file.tjs # Parse and type check\ntjs emit file.tjs # Output transpiled JavaScript\ntjs run file.tjs # Transpile and execute\ntjs types file.tjs # Output type metadata as JSON\n```\n\n### `tjsx` - Quick Execution\n\n```bash\ntjsx script.tjs # Run a TJS file\ntjsx script.tjs --name=value # Pass arguments\ntjsx -e \"function f() { return 42 }\" # Evaluate inline\necho '{\"x\": 1}' | tjsx script.tjs --json # JSON from stdin\n```\n\n### Bun Plugin - Native `.tjs` Support\n\nRun `.tjs` files directly with Bun using the preload plugin:\n\n```bash\n# Run a single file\nbun --preload ./src/bun-plugin/tjs-plugin.ts script.tjs\n\n# Enable globally in bunfig.toml\n[run]\npreload = [\"./src/bun-plugin/tjs-plugin.ts\"]\n```\n\nThe plugin transpiles `.tjs` files on-the-fly with full runtime support (Type, Generic, Union, etc.).\n\n## Best Practices\n\n### 1. Use Examples That Document\n\n```javascript\n// Bad - meaningless example\nfunction send(to: '', subject: '', body: '') {}\n\n// Good - self-documenting\nfunction send(\n to: 'user@example.com',\n subject: 'Hello!',\n body: 'Message content here...'\n) {}\n```\n\n### 2. Validate at Boundaries\n\n```javascript\n// Public API - safe by default\nexport function createUser(name: '', email: '') -> { id: '', name: '', email: '' } {\n return createUserImpl(name, email)\n}\n\n// Internal - mark as unsafe for speed\nfunction createUserImpl(! name: '', email: '') -> { id: '', name: '', email: '' } {\n return { id: crypto.randomUUID(), name, email }\n}\n```\n\n### 3. Return Errors, Don't Throw\n\n```javascript\n// Bad\nfunction divide(a: 0, b: 0) -> 0 {\n if (b === 0) throw new Error('Division by zero')\n return a / b\n}\n\n// Good\nfunction divide(a: 0, b: 0) -> 0 {\n if (b === 0) return error('Division by zero')\n return a / b\n}\n```\n\n### 4. Keep Types Simple\n\n```javascript\n// Bad - over-engineered\nfunction process(data: {\n items: [{ id: '', meta: { created: 0, tags: [''] } }],\n}) {}\n\n// Good - extract complex types\nconst Item = { id: '', meta: { created: 0, tags: [''] } }\nfunction process(data: { items: [Item] }) {}\n```\n\n## Transpilation\n\nTJS transpiles to standard JavaScript:\n\n```javascript\n// Input (TJS)\nfunction greet(name: 'World') -> '' {\n return `Hello, ${name}!`\n}\n\n// Output (JavaScript)\nfunction greet(name = 'World') {\n return `Hello, ${name}!`\n}\ngreet.__tjs = {\n params: { name: { type: 'string', required: true, default: 'World' } },\n returns: { type: 'string' }\n}\n```\n\nThe output is valid ES modules that work with any bundler (Vite, esbuild, webpack, Bun).\n\n## Further Reading\n\n- [Benchmarks](./benchmarks.md) - Performance characteristics\n- [ajs.md](./ajs.md) - The sandboxed agent language\n- [API Documentation](./docs/) - Generated from source\n",
|
|
176
|
+
"title": "TJS: Typed JavaScript",
|
|
177
|
+
"filename": "tjs.md",
|
|
178
|
+
"path": "guides/tjs.md"
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
"text": "# Using Haltija with tjs-lang\n\nThis project uses [Haltija](https://github.com/tonioloewald/haltija) for AI-assisted browser debugging and testing.\n\n## Quick Start\n\n```bash\n# Terminal 1: Start tjs-lang playground\nbun run dev\n\n# Terminal 2: Start haltija\ncd ~/Documents/GitHub/haltija\nbunx haltija\n```\n\nThen add this one-liner to your browser console or the app's entry point:\n\n```javascript\n;/^localhost$|^127\\./.test(location.hostname) &&\n import('http://localhost:8700/dev.js')\n```\n\n## Why We Use It\n\nBefore haltija, debugging frontend issues was painful:\n\n1. Describe what's wrong → AI guesses → doesn't work → repeat\n2. Screenshots, copy-pasted HTML, \"it's still not working\"\n3. Context lost, velocity destroyed\n\nWith haltija:\n\n1. AI sees the actual DOM via `/tree`\n2. Understands immediately: \"the selector expects `.tab-bar > .active` but structure is `.tab-bar > .tab-container > .tab.active`\"\n3. Fixes it in one shot\n\n## Common Commands\n\n```bash\n# What page am I on?\ncurl http://localhost:8700/location\n\n# See DOM structure\ncurl -X POST http://localhost:8700/tree -d '{\"selector\":\"body\",\"depth\":3}'\n\n# Find all buttons\ncurl -X POST http://localhost:8700/inspectAll -d '{\"selector\":\"button\"}'\n\n# Click something\ncurl -X POST http://localhost:8700/click -d '{\"selector\":\"#submit\"}'\n\n# Watch for DOM changes\ncurl -X POST http://localhost:8700/mutations/watch -d '{\"preset\":\"smart\"}'\ncurl http://localhost:8700/messages\n\n# Full docs\ncurl http://localhost:8700/docs\n```\n\n## Semantic Events (for debugging)\n\nInstead of raw DOM events, haltija captures meaningful actions:\n\n- `input:typed` - \"user typed 'hello@example.com'\"\n- `interaction:click` - \"user clicked Submit button\"\n- `navigation:navigate` - \"user went from /login to /dashboard\"\n\n```bash\ncurl -X POST http://localhost:8700/events/watch -d '{\"preset\":\"interactive\"}'\ncurl http://localhost:8700/events\n```\n\n## Reference Docs\n\n```bash\n# List available docs\ncurl http://localhost:8700/docs/list\n\n# UX anti-patterns database\ncurl http://localhost:8700/docs/ux-crimes\n```\n\n## Repo Location\n\nHaltija source: `~/Documents/GitHub/haltija`\n",
|
|
182
|
+
"title": "Using Haltija with tjs-lang",
|
|
183
|
+
"filename": ".haltija.md",
|
|
184
|
+
"path": ".haltija.md"
|
|
185
|
+
}
|
|
186
|
+
]
|