tjs-lang 0.7.3 → 0.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/demo/docs.json +30 -30
- package/dist/index.js +175 -176
- package/dist/index.js.map +5 -44
- package/dist/tjs-batteries.js +3 -4
- package/dist/tjs-batteries.js.map +5 -13
- package/dist/tjs-eval.js +47 -0
- package/dist/tjs-eval.js.map +7 -0
- package/dist/tjs-from-ts.js +58 -0
- package/dist/tjs-from-ts.js.map +7 -0
- package/dist/tjs-lang.js +349 -0
- package/dist/tjs-lang.js.map +7 -0
- package/dist/tjs-vm.js +51 -52
- package/dist/tjs-vm.js.map +4 -19
- package/package.json +17 -11
- package/dist/tjs-full.js +0 -437
- package/dist/tjs-full.js.map +0 -46
- package/dist/tjs-transpiler.js +0 -3
- package/dist/tjs-transpiler.js.map +0 -11
package/demo/docs.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"section": "home",
|
|
7
7
|
"order": 0,
|
|
8
8
|
"navTitle": "Home",
|
|
9
|
-
"text": "<!--{\"section\": \"home\", \"order\": 0, \"navTitle\": \"Home\"}-->\n\n# TJS Platform\n\n<center>\n <tosi-lottie style=\"width: 40vmin; height: 40vmin;\" src=\"./tosi-platform.json\"><img alt=\"tjs-lang logo\" style=\"width: 40vmin; height: 40vmin\" src=\"tjs-lang.svg\"></tosi-lottie>\n</center>\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## What is TJS?\n\n**TJS is a language.** It's what JavaScript always promised, but never quite delivered. Indeed it's what Apple's Dylan promised and never delivered. Instead of \"a lot of the power of Lisp\", _all_ the power of Lisp. Instead of C-like Syntax, actual JavaScript syntax. Instead of easy to learn but with weird corner cases, dangerous gotchas, and problems at scale, we fix the corner cases, remove the gotchas, and provide the tools that let you scale.\n\n**TJS is also a runtime.** A runtime that remembers your function declarations and can check whether parameter types are what they ought to be. It can guarantee safety by default, and speed when it's needed (including inline WASM).\n\n**AJS is another language.** It's a language for safe evaluation with injected capabilities and a gas limit. It's the language tjs allows you to Eval and use to create a SafeFunction. It also has its own VM and runtime to allow you to build **universal endpoints**. It's a language that's easy for agents to write and comprehend. It can be converted into an AST and run remotely.\n\n**TJS is also a toolchain.** It transpiles itself into JavaScript. It transpiles TypeScript into itself and then into JS. It turns function definitions into runtime contracts, documentation, and simple tests. It uses types both as contracts and examples. It allows inline tests of private module internals that disappear at runtime. It compresses transpilation, linting, testing, and documentation generation into a single fast pass. As for bundling? It allows it but it targets an unbundled web.\n\n\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\nTypeScript is also _difficult to transpile_. Your browser can run entire [full virtual machines](https://infinitemac.org/) in JavaScript, but most TypeScript playgrounds either fake transpilation 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**: shipping a 200MB Linux OS just to run a 1KB function 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) // MonadicError: Expected string for 'greet.name', got number\n```\n\n**Why it matters:**\n\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\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(\n agent,\n { topic: 'Agents' },\n {\n fuel: 500, // Strict CPU budget\n capabilities: { fetch: http }, // Allow ONLY http, block everything else\n }\n)\n```\n\n**Why it matters:**\n\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\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\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| | TypeScript | TJS | AJS |\n| --------------- | ------------------------------- | ------------------------------------------------ | ----------------- |\n| **Purpose** | Write your platform | Write your platform | Write your agents |\n| **Trust level** | Your code | Your code | Anyone's code |\n| **Compiles to** | JavaScript + `.d.ts` | JavaScript (with runtime checks + introspection) | JSON AST |\n| **Runs in** | Browser, Node, Bun | Browser, Node, Bun | Sandboxed VM |\n| **Types** | Static only (erased at runtime) | Examples → runtime validation | Schemas for I/O |\n| **Errors** | Exceptions | Monadic (values, not exceptions) | Monadic |\n| **Build step** | `tsc` → JS + `.d.ts` | Runs tests, builds docs, produces JS | None |\n\n> **Note:** TJS can transpile TypeScript into JS (via TJS) using `tjs convert`, giving your existing TS code\n> runtime type checks and introspection. You can even add inline tests using `/*test ...*/` comments\n> that run automatically during the build.\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 | 218 KB | **66 KB** |\n| + Batteries (LLM, vector) | 14 KB | 5 KB |\n| + Transpiler | 5 KB | 2 KB |\n| Full (with TS support) | 230 KB | 71 KB |\n\n**Dependencies:** `acorn` (JS parser), `tosijs-schema` (validation). Both have zero transitive dependencies.\n\n## Documentation\n\n- **[TJS Language Guide](https://github.com/tonioloewald/tjs-lang/blob/main/DOCS-TJS.md)** — Types, syntax, runtime\n- **[AJS Runtime Guide](https://github.com/tonioloewald/tjs-lang/blob/main/DOCS-AJS.md)** — VM, atoms, capabilities\n- **[WASM Quick Start](https://github.com/tonioloewald/tjs-lang/blob/main/docs/WASM-QUICKSTART.md)** — Build WASM-accelerated libraries with zero toolchain setup\n- **[Architecture Deep Dive](https://github.com/tonioloewald/tjs-lang/blob/main/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"
|
|
9
|
+
"text": "<!--{\"section\": \"home\", \"order\": 0, \"navTitle\": \"Home\"}-->\n\n# TJS Platform\n\n<center>\n <tosi-lottie style=\"width: 40vmin; height: 40vmin;\" src=\"./tosi-platform.json\"><img alt=\"tjs-lang logo\" style=\"width: 40vmin; height: 40vmin\" src=\"tjs-lang.svg\"></tosi-lottie>\n</center>\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## What is TJS?\n\n**TJS is a language.** It's what JavaScript always promised, but never quite delivered. Indeed it's what Apple's Dylan promised and never delivered. Instead of \"a lot of the power of Lisp\", _all_ the power of Lisp. Instead of C-like Syntax, actual JavaScript syntax. Instead of easy to learn but with weird corner cases, dangerous gotchas, and problems at scale, we fix the corner cases, remove the gotchas, and provide the tools that let you scale.\n\n**TJS is also a runtime.** A runtime that remembers your function declarations and can check whether parameter types are what they ought to be. It can guarantee safety by default, and speed when it's needed (including inline WASM).\n\n**AJS is another language.** It's a language for safe evaluation with injected capabilities and a gas limit. It's the language tjs allows you to Eval and use to create a SafeFunction. It also has its own VM and runtime to allow you to build **universal endpoints**. It's a language that's easy for agents to write and comprehend. It can be converted into an AST and run remotely.\n\n**TJS is also a toolchain.** It transpiles itself into JavaScript. It transpiles TypeScript into itself and then into JS. It turns function definitions into runtime contracts, documentation, and simple tests. It uses types both as contracts and examples. It allows inline tests of private module internals that disappear at runtime. It compresses transpilation, linting, testing, and documentation generation into a single fast pass. As for bundling? It allows it but it targets an unbundled web.\n\n\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\nTypeScript is also _difficult to transpile_. Your browser can run entire [full virtual machines](https://infinitemac.org/) in JavaScript, but most TypeScript playgrounds either fake transpilation 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**: shipping a 200MB Linux OS just to run a 1KB function 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) // MonadicError: Expected string for 'greet.name', got number\n```\n\n**Why it matters:**\n\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\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(\n agent,\n { topic: 'Agents' },\n {\n fuel: 500, // Strict CPU budget\n capabilities: { fetch: http }, // Allow ONLY http, block everything else\n }\n)\n```\n\n**Why it matters:**\n\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\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\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| | TypeScript | TJS | AJS |\n| --------------- | ------------------------------- | ------------------------------------------------ | ----------------- |\n| **Purpose** | Write your platform | Write your platform | Write your agents |\n| **Trust level** | Your code | Your code | Anyone's code |\n| **Compiles to** | JavaScript + `.d.ts` | JavaScript (with runtime checks + introspection) | JSON AST |\n| **Runs in** | Browser, Node, Bun | Browser, Node, Bun | Sandboxed VM |\n| **Types** | Static only (erased at runtime) | Examples → runtime validation | Schemas for I/O |\n| **Errors** | Exceptions | Monadic (values, not exceptions) | Monadic |\n| **Build step** | `tsc` → JS + `.d.ts` | Runs tests, builds docs, produces JS | None |\n\n> **Note:** TJS can transpile TypeScript into JS (via TJS) using `tjs convert`, giving your existing TS code\n> runtime type checks and introspection. You can even add inline tests using `/*test ...*/` comments\n> that run automatically during the build.\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 | 218 KB | **66 KB** |\n| + Batteries (LLM, vector) | 14 KB | 5 KB |\n| + Transpiler | 5 KB | 2 KB |\n| Full (with TS support) | 230 KB | 71 KB |\n\n**Dependencies:** `acorn` (JS parser), `tosijs-schema` (validation). Both have zero transitive dependencies.\n\n## Documentation\n\n- **[TJS Language Guide](https://github.com/tonioloewald/tjs-lang/blob/main/DOCS-TJS.md)** — Types, syntax, runtime\n- **[AJS Runtime Guide](https://github.com/tonioloewald/tjs-lang/blob/main/DOCS-AJS.md)** — VM, atoms, capabilities\n- **[WASM Quick Start](https://github.com/tonioloewald/tjs-lang/blob/main/docs/WASM-QUICKSTART.md)** — Build WASM-accelerated libraries with zero toolchain setup\n- **[Architecture Deep Dive](https://github.com/tonioloewald/tjs-lang/blob/main/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"
|
|
10
10
|
},
|
|
11
11
|
{
|
|
12
12
|
"title": "Applied Laziness: The \"Zero-Infrastructure\" Stack",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"section": "meta",
|
|
16
16
|
"order": 1,
|
|
17
17
|
"navTitle": "For Builders",
|
|
18
|
-
"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'): '' {
|
|
18
|
+
"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'): '' {\n return `Hello, ${name}!`\n}\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"
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
"title": "Governance: Safe Execution of Untrusted Logic",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"section": "tjs",
|
|
34
34
|
"type": "examples",
|
|
35
35
|
"pin": "top",
|
|
36
|
-
"text": "<!--{\"section\": \"tjs\", \"type\": \"examples\", \"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))
|
|
36
|
+
"text": "<!--{\"section\": \"tjs\", \"type\": \"examples\", \"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## Unbundled Integration\n\nTJS runs directly in the browser with no build step. Compare React (which requires bundling) with tosijs (which doesn't):\n\n### React Todo App\n\nThis would be easier with JSX, but we're not supporting JSX build magic (yet).\n\n```javascript\nimport { useState, createElement as h } from 'react'\nimport { createRoot } from 'react-dom/client'\n\n// Without JSX, we use createElement (aliased as h for brevity)\n// h(type, props, ...children)\n\nfunction TodoApp() {\n const [items, setItems] = useState(['bathe the cat', 'buy milk'])\n const [newItem, setNewItem] = useState('')\n\n const addItem = () => {\n if (newItem !== '') {\n setItems([...items, newItem])\n setNewItem('')\n }\n }\n\n return h(\n 'div',\n null,\n h('h1', null, 'To Do'),\n h(\n 'ul',\n null,\n items.map((item, i) => h('li', { key: i }, item))\n ),\n h(\n 'label',\n null,\n 'New item',\n h('input', {\n placeholder: 'enter thing to do',\n value: newItem,\n onChange: (e) => setNewItem(e.target.value),\n }),\n h(\n 'button',\n {\n disabled: !newItem,\n onClick: addItem,\n },\n 'Add'\n )\n )\n )\n}\n\nconst root = document.createElement('div')\nroot.setAttribute('id', 'root')\ndocument.body.append(root)\ncreateRoot(root).render(h(TodoApp))\n```\n\n### tosijs Todo App\n\n```javascript\nimport { elements, tosi } from 'tosijs'\n\nconst { todoApp } = tosi({\n todoApp: {\n items: ['bathe the cat', 'buy milk'],\n newItem: '',\n addItem() {\n if (todoApp.newItem !== '') {\n todoApp.items.push(String(todoApp.newItem))\n todoApp.newItem = ''\n }\n },\n },\n})\n\nconst { h1, ul, template, li, label, input, button } = elements\n\ndocument.body.append(\n h1('To Do'),\n ul(\n {\n bindList: {\n value: todoApp.items,\n },\n },\n template(li({ bindText: '^' }))\n ),\n label(\n 'New item',\n input({ placeholder: 'enter thing to do', bindValue: todoApp.newItem }),\n button({ bindEnabled: todoApp.newItem, onClick: todoApp.addItem }, 'Add')\n )\n)\n```\n\nThe tosijs version:\n\n- No JSX, no transpilation, no bundler\n- Reactive bindings built-in\n- Runs directly in browser via ES modules\n- ~30KB total (vs React's 140KB+ minified)\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"
|
|
37
37
|
},
|
|
38
38
|
{
|
|
39
39
|
"title": "The Universal Endpoint",
|
|
@@ -199,7 +199,7 @@
|
|
|
199
199
|
"group": "docs",
|
|
200
200
|
"order": 0,
|
|
201
201
|
"navTitle": "Documentation",
|
|
202
|
-
"text": "<!--{\"section\": \"tjs\", \"group\": \"docs\", \"order\": 0, \"navTitle\": \"Documentation\"}-->\n\n# TJS: Typed JavaScript\n\n_Types as Examples. Zero Build. Runtime Metadata._\n\n---\n\n## What is TJS?\n\nTJS is a typed superset of JavaScript where **types are concrete values**, not abstract annotations.\n\n```typescript\n// TypeScript: abstract type annotation\nfunction greet(name: string): string\n\n// TJS: concrete example value\nfunction greet(name: 'World'): '' { return `Hello, ${name}!` }\n```\n\nThe example `'World'` tells TJS that `name` is a string. The example `''` tells TJS the return type is a string. Types are inferred from the examples you provide.\n\nTJS transpiles to JavaScript with embedded `__tjs` metadata, enabling runtime type checking, autocomplete from live objects, and documentation generation.\n\n---\n\n## TJS is JavaScript\n\nTJS is **purely additive**. It adds type annotations, runtime validation, and metadata on top of standard JavaScript. It does not replace, intercept, or modify any existing JavaScript semantics.\n\n**Everything you know about JavaScript still works:**\n\n- **Proxies** — fully supported. TJS never intercepts property access. `__tjs` metadata is a plain property assignment on the function object; it doesn't interfere with Proxy traps. The `[tjsEquals]` symbol protocol is specifically designed for Proxy-friendly custom equality.\n- **WeakMap, WeakSet, Map, Set** — all unchanged. TJS doesn't wrap or validate collection internals.\n- **Closures, Promises, async/await** — work identically to JS.\n- **Prototype chains** — preserved. `wrapClass()` uses a Proxy only on the class constructor (to allow calling without `new`), not on instances.\n- **Module semantics** — TJS preserves ES module `import`/`export` exactly. Lazy getters, circular dependencies, and re-exports work the same as in JS.\n- **`this` binding** — unchanged. Arrow functions, `.bind()`, `.call()`, `.apply()` all work normally.\n- **Regular expressions, JSON, Math, Date** — all standard built-ins are available and unmodified (though `Date` is banned by default in native TJS via `TjsDate` in favor of safer alternatives like `Timestamp`/`LegalDate`; use `TjsCompat` to restore it).\n\n**What TJS adds (and when): **\n\n| Addition | When | Overhead |\n| ---------------------- | ----------------------------------------------- | ----------------------------- |\n| Parameter validation | Function entry (unless `!` unsafe) | ~1.15-1.3x on that function |\n| Return type validation | Function exit (only with `safety all`) | ~1.15-1.3x on that function |\n| `__tjs` metadata | Transpile time | Zero runtime cost |\n| `wrapClass` Proxy | Class declaration (on by default in native TJS) | One-time, on constructor only |\n| Structural equality | `==`/`!=` (on by default in native TJS) | Per-comparison |\n\nIf TJS doesn't understand something in your code, it passes it through unchanged. There is no \"TJS runtime\" that interposes between your code and the JS engine — just the inline checks you can see in the transpiled output.\n\n---\n\n## The Compiler\n\nTJS compiles in the browser. No webpack, no node_modules, no build server.\n\n```typescript\nimport { tjs } from 'tjs-lang'\n\nconst code = tjs`\n function add(a: 0, b: 0): 0 {\n return a + b\n }\n`\n\n// Returns transpiled JavaScript with __tjs metadata\n```\n\nYou can also use the CLI:\n\n```bash\nbun src/cli/tjs.ts check file.tjs # Parse and type check\nbun src/cli/tjs.ts run file.tjs # Transpile and execute\nbun src/cli/tjs.ts emit file.tjs # Output transpiled JS\nbun src/cli/tjs.ts types file.tjs # Output type metadata\n```\n\n---\n\n## Syntax\n\n### Parameter Types (Colon Syntax)\n\n> **Not TypeScript.** TJS colon syntax looks like TypeScript but has different\n> semantics. The value after `:` is a **concrete example**, not a type name.\n> Write `name: 'Alice'` (example value), not `name: string` (type name).\n> TJS infers the type from the example: `'Alice'` → string, `0` → integer,\n> `true` → boolean.\n\nRequired parameters use colon syntax with an example value:\n\n```typescript\nfunction greet(name: 'Alice') {} // name is required, type: string\nfunction calculate(value: 0) {} // value is required, type: integer\nfunction measure(rate: 0.0) {} // rate is required, type: number (float)\nfunction count(n: +0) {} // n is required, type: non-negative integer\nfunction toggle(flag: true) {} // flag is required, type: boolean\n```\n\n### Numeric Types\n\nTJS distinguishes three numeric types using valid JavaScript syntax:\n\n```typescript\nfunction process(\n rate: 3.14, // number (float) -- has a decimal point\n count: 42, // integer -- whole number, no decimal\n index: +0 // non-negative integer -- prefixed with +\n) {}\n```\n\n| You Write | Type Inferred | Runtime Validation |\n| --------- | ---------------------- | ------------------------------- |\n| `3.14` | `number` (float) | `typeof x === 'number'` |\n| `0.0` | `number` (float) | `typeof x === 'number'` |\n| `42` | `integer` | `Number.isInteger(x)` |\n| `0` | `integer` | `Number.isInteger(x)` |\n| `+20` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n| `+0` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n| `-5` | `integer` | `Number.isInteger(x)` |\n| `-3.5` | `number` (float) | `typeof x === 'number'` |\n\nAll of these are valid JavaScript expressions. TJS reads the syntax more\ncarefully to give you finer-grained type checking than JS or TypeScript\nprovide natively.\n\n### Optional Parameters (Default Values)\n\nOptional parameters use `=` with a default value:\n\n```typescript\nfunction greet(name = 'World') {} // name is optional, defaults to 'World'\nfunction calculate(value = 0) {} // value is optional, defaults to 0 (integer)\n```\n\n### TypeScript-Style Optional (`?:`)\n\nTJS supports `?:` for compatibility, but consider it a migration aid rather than idiomatic TJS:\n\n```typescript\nfunction greet(name?: '') {} // same as name = ''\n```\n\n**Why `?:` is an antipattern.** In TypeScript, `?:` creates a three-state parameter\n(`value | undefined | missing`) that forces every function body to handle the absent case:\n\n```typescript\n// TypeScript — every caller and callee must reason about undefined\nfunction greet(name?: string) {\n const safeName = name ?? 'World' // defensive check required\n return `Hello, ${safeName}!`\n}\n```\n\nTJS offers two better alternatives:\n\n**1. Safe defaults** — the parameter always has a value, no branching needed:\n\n```typescript\nfunction greet(name = 'World') {\n return `Hello, ${name}!` // name is always a string\n}\n```\n\n**2. Polymorphic functions** — separate signatures for separate behavior:\n\n```typescript\nfunction greet() {\n return 'Hello, World!'\n}\nfunction greet(name: '') {\n return `Hello, ${name}!`\n}\n```\n\nBoth approaches eliminate the `undefined` state entirely. The function body\nnever needs a null check because the type system guarantees a valid value\nat every call site. This is simpler to write, simpler to read, and produces\ntighter runtime validation.\n\n### Object Parameters\n\nObject shapes are defined by example:\n\n```typescript\nfunction createUser(user: { name: ''; age: 0 }) {}\n// user must be an object with string name and number age\n```\n\n### Nullable Types\n\nUse `|` for union with null:\n\n```typescript\nfunction find(id: 0 | null) {} // number or null\n```\n\n### Rest Parameters\n\nRest params use `:` with an array example. The annotation is stripped from\nthe JS output (JS doesn't allow defaults on rest params) but captured in\n`__tjs` metadata:\n\n```typescript\nfunction sum(...nums: [1, 2, 3]): 6 {\n return nums.reduce((a = 0, b: 0) => a + b, 0)\n}\n\nfunction mean(...values: [1.0, 2.0, 3.0, 2.0]): 2.0 {\n return values.length\n ? values.reduce((sum = 0.0, x: 1.0) => sum + x) / values.length\n : 0.0\n}\n```\n\nSignature tests work with rest params — the example array elements are\nspread as individual arguments. `mean(1.0, 2.0, 3.0, 2.0)` is called\nand the result is checked against the `: 2.0` expected return using\nexact value comparison (deepEqual).\n\nThe array example tells TJS the element type. `[0]` means \"array of\nintegers\", `[1.0, 2.0]` means \"array of numbers (floats)\".\n\n**Heterogeneous arrays** infer a union item type:\n\n```typescript\nfunction log(...args: ['info', 42, true]) {}\n// args type: array<string | integer | boolean>\n```\n\n### Return Types (Colon Syntax)\n\nReturn types use `:`:\n\n```typescript\nfunction add(a: 0, b: 0): 0 {\n return a + b\n}\n\nfunction getUser(id: 0): { name: '', age: 0 } {\n return { name: 'Alice', age: 30 }\n}\n```\n\n### Array Types\n\nArrays use bracket syntax with an example element:\n\n```typescript\nfunction sum(numbers: [0]): 0 { // array of numbers\n return numbers.reduce((a, b) => a + b, 0)\n}\n\nfunction names(users: [{ name: '' }]) { // array of objects\n return users.map(u => u.name)\n}\n```\n\n---\n\n## Safety Markers\n\n### Unsafe Functions\n\nSkip validation for hot paths:\n\n```typescript\nfunction fastAdd(! a: 0, b: 0) { return a + b }\n```\n\nThe `!` marker after the function name skips input validation.\n\n### Safe Functions\n\nExplicit validation (for emphasis): \n\n```typescript\nfunction safeAdd(? a: 0, b: 0) { return a + b }\n```\n\n### Unsafe Blocks\n\nSkip validation for a block of code:\n\n```typescript\nunsafe {\n fastPath(data)\n anotherHotFunction(moreData)\n}\n```\n\n### Module Safety Directive\n\nSet the default validation level for an entire file:\n\n```typescript\nsafety none // No validation (metadata only)\nsafety inputs // Validate function inputs (default)\nsafety all // Validate everything (debug mode)\n```\n\n---\n\n## Type System\n\n### Type()\n\nDefine named types with predicates:\n\n```typescript\n// Simple type from example\nType Name 'Alice'\n\n// Type with description\nType User {\n description: 'a user object'\n example: { name: '', age: 0 }\n}\n\n// Type with predicate\nType PositiveNumber {\n description: 'a positive number'\n example: 1\n predicate(x) { return x > 0 }\n}\n```\n\nTypes can be used in function signatures:\n\n```typescript\nfunction greet(name: Name): '' {\n return `Hello, ${name}!`\n}\n```\n\n### Generic()\n\nRuntime-checkable generics:\n\n```typescript\nGeneric Box<T> {\n description: 'a boxed value'\n predicate(x, T) {\n return typeof x === 'object' && x !== null && 'value' in x && T(x.value)\n }\n}\n\n// With default type parameter\nGeneric Container<T, U = ''> {\n description: 'container with label'\n predicate(obj, T, U) {\n return T(obj.item) && U(obj.label)\n }\n}\n```\n\n#### Declaration Blocks (for TypeScript Consumers)\n\nGenerics can include an optional `declaration` block that specifies the\nTypeScript interface to emit in `.d.ts` output. This is metadata for TS\nconsumers — it has no effect on runtime behavior.\n\n```typescript\nGeneric BoxedProxy<T> {\n description: 'typed reactive proxy'\n predicate(x, T) {\n return typeof x === 'object' && 'value' in x && T(x.value)\n }\n declaration {\n value: T\n path: string\n observe(cb: (path: string) => void): void\n touch(): void\n }\n}\n```\n\nWhen emitting `.d.ts` via `tjs emit --dts`, this produces:\n\n```typescript\nexport interface BoxedProxy<T> {\n value: T\n path: string\n observe(cb: (path: string) => void): void\n touch(): void\n}\n```\n\nThe declaration content is raw TypeScript syntax — it's emitted verbatim\ninto the `.d.ts` file. This lets TJS libraries provide proper TypeScript\ninterfaces while keeping the TJS source as the single source of truth.\n\nWithout a `declaration` block, Generics emit an `any`-based factory stub\nthat provides basic IDE hints without false type errors.\n\n### Union()\n\nDiscriminated unions:\n\n```typescript\nconst Shape = Union('kind', {\n circle: { radius: 0 },\n rectangle: { width: 0, height: 0 }\n})\n\nfunction area(shape: Shape): 0 {\n if (shape.kind === 'circle') {\n return Math.PI * shape.radius ** 2\n }\n return shape.width * shape.height\n}\n```\n\n### Enum()\n\nString or numeric enums:\n\n```typescript\nconst Status = Enum(['pending', 'active', 'completed'])\nconst Priority = Enum({ low: 1, medium: 2, high: 3 })\n\nfunction setStatus(status: Status) {}\n```\n\n---\n\n## Structural Equality: Is / IsNot\n\nJavaScript's `==` is broken (type coercion). TJS provides structural equality:\n\n```typescript\n// Structural comparison - no coercion\n[1, 2] Is [1, 2] // true\n5 Is \"5\" // false (different types)\n{ a: 1 } Is { a: 1 } // true\n\n// Arrays compared element-by-element\n[1, [2, 3]] Is [1, [2, 3]] // true\n\n// Negation\n5 IsNot \"5\" // true\n```\n\n### Custom Equality\n\nObjects can define custom equality in two ways:\n\n**1. `[tjsEquals]` symbol protocol** (preferred for Proxies and advanced use): \n\n```typescript\nimport { tjsEquals } from 'tjs-lang/lang'\n\n// A proxy that delegates equality to its target\nconst target = { x: 1, y: 2 }\nconst proxy = new Proxy({\n [tjsEquals](other) { return target Is other }\n}, {})\n\nproxy == { x: 1, y: 2 } // true — delegates to target\n```\n\n**2. `.Equals` method** (simple, works on any object or class): \n\n```typescript\nclass Point {\n constructor(x: 0, y: 0) { this.x = x; this.y = y }\n Equals(other) { return this.x === other.x && this.y === other.y }\n}\n\nPoint(1, 2) Is Point(1, 2) // true (uses .Equals)\n```\n\n**Priority:** `[tjsEquals]` symbol > `.Equals` method > structural comparison.\n\nThe symbol is `Symbol.for('tjs.equals')`, so it works across realms. Access it\nvia `import { tjsEquals } from 'tjs-lang/lang'` or `__tjs.tjsEquals` at runtime.\n\n---\n\n## Classes\n\n### Callable Without `new`\n\nIn native TJS, classes are automatically wrapped so they can be called\nwithout `new` (the `TjsClass` mode is on by default). For TS-originated\ncode, add the `TjsClass` directive to enable this:\n\n```typescript\nclass User {\n constructor(name: '') {\n this.name = name\n }\n}\n\n// Both work identically:\nconst u1 = User('Alice') // TJS way - clean\nconst u2 = new User('Alice') // Also works (linter warns)\n```\n\nThe wrapping uses a Proxy on the constructor that intercepts bare calls\nand forwards them to `Reflect.construct`. This means `User('Alice')`\nand `new User('Alice')` always produce the same result — an instance.\n\n**What gets wrapped:** Only `class` declarations in your `.tjs` file.\nSpecifically:\n\n- `class Foo { }` in native TJS (or with `TjsClass` directive) → wrapped\n- Built-in globals (`Boolean`, `Number`, `String`, `Array`) → **never touched**\n- Old-style constructor functions (`function Foo() { }` with `Foo.prototype`) → **never touched**\n\n**Why not built-ins:** JavaScript's built-in constructors have dual\nbehavior — `Boolean(0)` returns the primitive `false` (type coercion),\nwhile `new Boolean(0)` returns a `Boolean` object wrapping `false`\n(which is truthy!). If TJS wrapped `Boolean`, then `Boolean(0)` would\nsilently become `new Boolean(0)` — a truthy object instead of `false`.\nThe same applies to `Number()`, `String()`, and `Array()`.\n\n**Why not old-style constructors:** If you're using `function` +\n`prototype` to build a class manually, you may intentionally want\n`Foo(x)` to behave differently from `new Foo(x)` — the same dual\nbehavior pattern as the built-ins. TJS respects this by only wrapping\nthe `class` keyword, where calling without `new` has no existing\nmeaning in JavaScript (it throws `TypeError`).\n\n### Private Fields\n\nUse `#` for private fields:\n\n```typescript\nclass Counter {\n #count = 0\n\n increment() {\n this.#count++\n }\n get value() {\n return this.#count\n }\n}\n```\n\nWhen converting from TypeScript, `private foo` becomes `#foo`.\n\n### Getters and Setters\n\nAsymmetric types are captured:\n\n```typescript\nclass Timestamp {\n #value\n\n constructor(initial: '' | 0 | null) {\n this.#value = initial === null ? new Date() : new Date(initial)\n }\n\n set value(v: '' | 0 | null) {\n this.#value = v === null ? new Date() : new Date(v)\n }\n\n get value() {\n return this.#value\n }\n}\n\nconst ts = Timestamp('2024-01-15')\nts.value = 0 // SET accepts: string | number | null\nts.value // GET returns: Date\n```\n\n---\n\n## Polymorphic Functions\n\nMultiple function declarations with the same name are automatically merged into a dispatcher that routes by argument count and type:\n\n```typescript\nfunction describe(value: 0) {\n return 'number: ' + value\n}\nfunction describe(value: '') {\n return 'string: ' + value\n}\nfunction describe(value: { name: '' }) {\n return 'object: ' + value.name\n}\n\ndescribe(42) // 'number: 42'\ndescribe('hello') // 'string: hello'\ndescribe({ name: 'world' }) // 'object: world'\ndescribe(true) // MonadicError: no matching overload\n```\n\n### Dispatch Order\n\n1. **Arity** first (number of arguments)\n2. **Type specificity** within same arity: `integer` > `number` > `any`; objects before primitives\n3. **Declaration order** as tiebreaker\n\n### Polymorphic Constructors\n\nClasses can have multiple constructor signatures (enabled by default in native TJS via `TjsClass`). The first becomes the real JS constructor; additional variants become factory functions:\n\n```typescript\nclass Point {\n constructor(x: 0.0, y: 0.0) {\n this.x = x\n this.y = y\n }\n constructor(coords: { x: 0.0; y: 0.0 }) {\n this.x = coords.x\n this.y = coords.y\n }\n}\n\nPoint(3, 4) // variant 1: two numbers\nPoint({ x: 10, y: 20 }) // variant 2: object\n```\n\nAll variants produce correct `instanceof` results.\n\n### Compile-Time Validation\n\nTJS catches these errors at transpile time:\n\n- **Ambiguous signatures**: Two variants with identical types at every position\n- **Rest parameters**: `...args` not supported in polymorphic functions\n- **Mixed async/sync**: All variants must agree\n\n---\n\n## Local Class Extensions\n\nAdd methods to built-in types without polluting prototypes:\n\n```typescript\nextend String {\n capitalize() {\n return this[0].toUpperCase() + this.slice(1)\n }\n words() {\n return this.split(/\\s+/)\n }\n}\n\n'hello world'.capitalize() // 'Hello world'\n'foo bar baz'.words() // ['foo', 'bar', 'baz']\n```\n\n### How It Works\n\nFor known-type receivers (literals, typed variables), calls are rewritten at transpile time to `.call()` — zero runtime overhead:\n\n```javascript\n// TJS source:\n'hello'.capitalize()\n\n// Generated JS:\n__ext_String.capitalize.call('hello')\n```\n\nFor unknown types, a runtime registry (`registerExtension` / `resolveExtension`) provides fallback dispatch.\n\n### Supported Types\n\nExtensions work on any type: `String`, `Number`, `Array`, `Boolean`, custom classes, and DOM classes like `HTMLElement`. Multiple `extend` blocks for the same type merge left-to-right (later declarations can override earlier methods).\n\n### Rules\n\n- Arrow functions are **not allowed** in extend blocks (they don't bind `this`)\n- Extensions are **file-local** — they don't leak across modules\n- Prototypes are **never modified** — `String.prototype.capitalize` remains `undefined`\n\n---\n\n## Runtime Features\n\n### `__tjs` Metadata\n\nEvery TJS function carries its type information:\n\n```typescript\nfunction createUser(input: { name: '', age: 0 }): { id: 0 } {\n return { id: 123 }\n}\n\nconsole.log(createUser.__tjs)\n// {\n// params: {\n// input: { type: { kind: 'object', shape: { name: 'string', age: 'number' } } }\n// },\n// returns: { kind: 'object', shape: { id: 'number' } }\n// }\n```\n\nThis enables:\n\n- Autocomplete from live objects\n- Runtime type validation\n- Automatic documentation generation\n- JSON Schema generation (see below)\n\n### JSON Schema\n\nTJS types and function signatures can be exported as standard JSON Schema. Instead of writing schemas and inferring types (Zod), you write typed functions and get schemas out.\n\n#### From Types\n\n```typescript\nType User {\n example: { name: '', age: 0, email: '' }\n}\n\nUser.toJSONSchema()\n// {\n// type: 'object',\n// properties: {\n// name: { type: 'string' },\n// age: { type: 'integer' },\n// email: { type: 'string' }\n// },\n// required: ['name', 'age', 'email'],\n// additionalProperties: false\n// }\n\nUser.check({ name: 'Alice', age: 30, email: 'a@b.com' }) // true\nUser.strip({ name: 'Alice', age: 30, secret: 'pw' })\n// { name: 'Alice', age: 30 } — extra fields removed\n```\n\n#### From Function Signatures\n\n```typescript\nimport { functionMetaToJSONSchema } from 'tjs-lang/lang'\n\nfunction createUser(name: '', age: 0): { id: 0, name: '' } {\n return { id: 1, name }\n}\n\nconst { input, output } = functionMetaToJSONSchema(createUser.__tjs)\n// input: { type: 'object', properties: { name: { type: 'string' }, age: { type: 'integer' } }, required: ['name', 'age'] }\n// output: { type: 'object', properties: { id: { type: 'integer' }, name: { type: 'string' } }, ... }\n```\n\nWhen the shared runtime is installed (`installRuntime()`), `.schema()` is also available directly on the metadata:\n\n```typescript\ncreateUser.__tjs.schema() // same { input, output } result\n```\n\n#### Unions and Enums\n\n```typescript\nconst Direction = Union('direction', ['up', 'down', 'left', 'right'])\nDirection.toJSONSchema() // { enum: ['up', 'down', 'left', 'right'] }\n\nconst Status = Enum('status', { Active: 1, Inactive: 0 })\nStatus.toJSONSchema() // { enum: [1, 0] }\n```\n\nThis gives you OpenAPI-ready API contracts from your function signatures — no extra schema definitions needed.\n\n### Monadic Errors\n\nType validation failures return `MonadicError` instances (extends `Error`),\nnot thrown exceptions:\n\n```typescript\nimport { isMonadicError } from 'tjs-lang/lang'\n\nconst result = createUser({ name: 123 }) // wrong type\n// MonadicError: Expected string for 'createUser.name', got number\n\nif (isMonadicError(result)) {\n console.log(result.message) // \"Expected string for 'createUser.name', got number\"\n console.log(result.path) // \"createUser.name\"\n console.log(result.expected) // \"string\"\n console.log(result.actual) // \"number\"\n}\n```\n\nNo try/catch gambling. The host survives invalid inputs.\n\nFor general-purpose error values (not type errors), use the `error()` helper\nwhich returns plain `{ $error: true, message }` objects checkable with `isError()`.\n\n### Error History\n\nSince monadic errors don't throw, they can silently vanish if nobody checks the return value. TJS tracks recent type errors in a ring buffer so you can catch these:\n\n```typescript\n// Errors are tracked automatically (on by default, zero cost on happy path)\ngreet(42) // returns MonadicError, caller ignores it\nprocessOrder('bad') // same\n\n// Check what failed recently\nconst recent = __tjs.errors() // → recent MonadicErrors (newest last, max 64)\nfor (const err of recent) {\n console.log(err.message, err.path)\n}\n\n// Testing workflow: clear → run → check for surprises\n__tjs.clearErrors()\nrunMyCode()\nexpect(__tjs.errors()).toEqual([]) // no unexpected type errors\n\n// Total count survives ring buffer wrapping\n__tjs.getErrorCount() // → total since last clear\n```\n\n### Runtime Configuration\n\n```typescript\nimport { configure } from 'tjs-lang/lang'\n\n// Log type errors to console when they occur\nconfigure({ logTypeErrors: true })\n\n// Throw type errors instead of returning them (for debugging)\nconfigure({ throwTypeErrors: true })\n\n// Enable call stack tracking (off by default — ~2x overhead)\n// Useful for server-side logging and agent debugging without devtools\nconfigure({ callStacks: true })\n\n// Disable error history tracking (on by default, zero cost on happy path)\nconfigure({ trackErrors: false })\n```\n\nBoth `logTypeErrors` and `throwTypeErrors` work on the shared runtime and isolated `createRuntime()` instances.\n\n### Inline Tests\n\nTests live next to code:\n\n```typescript\nfunction double(x: 0): 0 { return x * 2 }\n\ntest('doubles numbers') {\n expect(double(5)).toBe(10)\n expect(double(-3)).toBe(-6)\n}\n```\n\nTests are extracted at compile time and can be:\n\n- Run during transpilation\n- Stripped in production builds\n- Used for documentation generation\n\n### WASM Blocks\n\nDrop into WebAssembly for compute-heavy code:\n\n```typescript\nfunction vectorDot(a: [0], b: [0]): 0 {\n let sum = 0\n wasm {\n for (let i = 0; i < a.length; i++) {\n sum = sum + a[i] * b[i]\n }\n }\n return sum\n}\n```\n\nVariables are captured automatically. Falls back to JS if WASM unavailable.\n\n#### SIMD Intrinsics (f32x4)\n\nFor compute-heavy workloads, use f32x4 SIMD intrinsics to process 4 float32 values per instruction:\n\n```typescript\nconst scale = wasm (arr: Float32Array, len: 0, factor: 0.0): 0 {\n let s = f32x4_splat(factor)\n for (let i = 0; i < len; i += 4) {\n let off = i * 4\n let v = f32x4_load(arr, off)\n f32x4_store(arr, off, f32x4_mul(v, s))\n }\n} fallback {\n for (let i = 0; i < len; i++) arr[i] *= factor\n}\n```\n\nAvailable intrinsics:\n\n| Intrinsic | Description |\n| ----------------------------------- | ------------------------------------ |\n| `f32x4_load(ptr, byteOffset)` | Load 4 floats from memory into v128 |\n| `f32x4_store(ptr, byteOffset, vec)` | Store v128 as 4 floats to memory |\n| `f32x4_splat(scalar)` | Fill all 4 lanes with a scalar value |\n| `f32x4_extract_lane(vec, N)` | Extract float from lane 0-3 |\n| `f32x4_replace_lane(vec, N, val)` | Replace one lane, return new v128 |\n| `f32x4_add(a, b)` | Lane-wise addition |\n| `f32x4_sub(a, b)` | Lane-wise subtraction |\n| `f32x4_mul(a, b)` | Lane-wise multiplication |\n| `f32x4_div(a, b)` | Lane-wise division |\n| `f32x4_neg(v)` | Negate all lanes |\n| `f32x4_sqrt(v)` | Square root of all lanes |\n\nThis mirrors C/C++ SIMD intrinsics (`_mm_add_ps`, etc.) — explicit, predictable, no auto-vectorization magic.\n\n#### Zero-Copy Arrays: `wasmBuffer()`\n\nBy default, typed arrays passed to WASM blocks are copied into WASM memory before the call and copied back out after. For large arrays called frequently, this overhead can negate WASM's speed advantage.\n\n`wasmBuffer(Constructor, length)` allocates typed arrays directly in WASM linear memory. These arrays work like normal typed arrays from JavaScript, but when passed to a `wasm {}` block, they're zero-copy — the data is already there.\n\n```typescript\n// Allocate particle positions in WASM memory\nconst starX = wasmBuffer(Float32Array, 50000)\nconst starY = wasmBuffer(Float32Array, 50000)\n\n// Use from JS like normal arrays\nfor (let i = 0; i < 50000; i++) {\n starX[i] = (Math.random() - 0.5) * 2000\n starY[i] = (Math.random() - 0.5) * 2000\n}\n\n// Zero-copy SIMD processing\nfunction moveParticles(! xs: Float32Array, ys: Float32Array, len: 0, dx: 0.0, dy: 0.0) {\n wasm {\n let vdx = f32x4_splat(dx)\n let vdy = f32x4_splat(dy)\n for (let i = 0; i < len; i += 4) {\n let off = i * 4\n f32x4_store(xs, off, f32x4_add(f32x4_load(xs, off), vdx))\n f32x4_store(ys, off, f32x4_add(f32x4_load(ys, off), vdy))\n }\n } fallback {\n for (let i = 0; i < len; i++) { xs[i] += dx; ys[i] += dy }\n }\n}\n\n// After WASM runs, JS sees the mutations immediately\nmoveParticles(starX, starY, 50000, 1.0, 0.5)\nconsole.log(starX[0]) // updated in place, no copy\n```\n\nKey points:\n\n- Supported constructors: `Float32Array`, `Float64Array`, `Int32Array`, `Uint8Array`\n- Uses a bump allocator — allocations persist for program lifetime\n- All WASM blocks in a file share one 64MB memory\n- Regular typed arrays still work (copy in/out as before)\n- Use `!` (unsafe) on hot-path functions to skip runtime type checks\n\n---\n\n## Module System\n\nTJS preserves standard ES module semantics exactly. `import` and `export` statements pass through to the output unchanged — TJS does not have its own module resolver.\n\n### Importing .tjs Files\n\nIn **Bun** (with the TJS plugin from `bunfig.toml`): \n\n```typescript\n// .tjs files are transpiled automatically on import\nimport { processOrder } from './orders.tjs'\nimport { validateUser } from './users.ts' // TS files also work\n```\n\nIn the **browser playground**, local modules are resolved from the playground's module store. Relative imports are looked up by name:\n\n```typescript\nimport { formatDate } from './date-utils' // resolves from saved modules\n```\n\nIn **production builds** (`tjs emit`), TJS transpiles `.tjs` → `.js`. Your bundler (esbuild, Rollup, etc.) handles resolution of the output `.js` files normally.\n\n### Importing JS/TS Libraries\n\nTJS files can import any JavaScript or TypeScript library. The imported code runs without TJS validation — it's just normal JS. Add a TJS wrapper at the boundary if you want type safety:\n\n```typescript\nimport { rawGeocode } from 'legacy-geo-pkg'\n\n// Wrap at the boundary — rawGeocode is unchecked, geocode validates its output\nfunction geocode(addr: ''): { lat: 0.0, lon: 0.0 } {\n return rawGeocode(addr)\n}\n```\n\n### CDN Imports\n\nURL imports work with any ESM CDN:\n\n```typescript\nimport lodash from 'https://esm.sh/lodash@4.17.21'\n```\n\n### Circular Dependencies\n\nTJS doesn't interfere with JS module loading. Circular imports work the same way as in standard ES modules — use lazy getters or late binding if you need to break cycles, exactly as you would in plain JS.\n\n### TypeScript Declaration Files (.d.ts)\n\nTJS can generate `.d.ts` files so TypeScript consumers can use TJS-authored libraries with autocomplete and tooltips:\n\n```bash\nbun src/cli/tjs.ts emit --dts src/lib.tjs -o dist/lib.js\n# Generates dist/lib.js + dist/lib.d.ts\n```\n\nFrom code:\n\n```typescript\nimport { tjs, generateDTS } from 'tjs-lang'\n\nconst result = tjs(source)\nconst dts = generateDTS(result, source)\n```\n\nFunctions get full type declarations. Classes, generics, and predicate-based types get `any`-based stubs that provide IDE hints (parameter names, object shapes) without generating false lint errors for types that TJS validates at runtime.\n\n---\n\n## TypeScript Compatibility\n\n### TS → TJS Converter\n\nConvert existing TypeScript:\n\n```bash\nbun src/cli/tjs.ts convert file.ts\n```\n\n```typescript\n// TypeScript\nfunction greet(name: string, age?: number): string { ... }\n\n// Converts to TJS\nfunction greet(name: '', age = 0): '' { ... }\n```\n\n### What Gets Converted\n\n| TypeScript | TJS |\n| -------------------------- | ----------------------- |\n| `name: string` | `name: ''` |\n| `age: number` | `age: 0.0` |\n| `flag: boolean` | `flag: false` |\n| `items: string[]` | `items: ['']` |\n| `age?: number` | `age: 0.0 \\| undefined` |\n| `private foo` | `#foo` |\n| `interface User` | `Type User` |\n| `type Status = 'a' \\| 'b'` | `Union(['a', 'b'])` |\n| `enum Color` | `Enum(...)` |\n\n> **Optional params:** TypeScript `x?: boolean` becomes TJS `x: false | undefined`.\n> This preserves the three-state semantics (`true` / `false` / `undefined`)\n> using a union type. The param is required but explicitly accepts `undefined`.\n\n---\n\n## Performance\n\n| Mode | Overhead | Use Case |\n| --------------- | -------------- | ---------------------------- |\n| `safety none` | **1.0x** | Metadata only, no validation |\n| `safety inputs` | **~1.15-1.3x** | Production |\n| `(!) unsafe` | **1.0x** | Hot paths |\n| `wasm {}` | **<1.0x** | Compute-heavy code |\n\n### Why ~1.15x, Not 25x\n\nMost validators interpret schemas at runtime (~25x overhead). TJS generates inline checks at transpile time:\n\n```typescript\n// Generated (JIT-friendly)\nif (\n typeof input !== 'object' ||\n input === null ||\n typeof input.name !== 'string' ||\n typeof input.age !== 'number'\n) {\n return { $error: true, message: 'Invalid input', path: 'fn.input' }\n}\n```\n\nNo schema interpretation. No object iteration. The JIT inlines these completely.\n\n---\n\n## Bare Assignments\n\nUppercase identifiers automatically get `const`:\n\n```typescript\nFoo = Type('test', 'example') // becomes: const Foo = Type(...)\nMyConfig = { debug: true } // becomes: const MyConfig = { ... }\n```\n\n---\n\n## Limitations\n\n### What TJS Doesn't Do\n\n- **No gradual typing** — types are all-or-nothing per function\n- **No complex type inference** — you provide examples, not constraints\n- **No type-level computation** — no conditional types, mapped types, etc.\n\n### What TJS Intentionally Avoids\n\n- Build steps beyond transpilation\n- External type checkers\n- Complex tooling configuration\n- Separation of types from runtime\n\n---\n\n## Troubleshooting\n\n### Common Transpilation Errors\n\n**\"Unexpected token\"** — Usually means TJS-specific syntax (`:` params, `:` returns, `Type`, `Generic`) wasn't recognized. Check:\n\n- Is the file being parsed as TJS (not plain JS)?\n- Are `Type`/`Generic`/`Union` declarations at the top level (not inside functions)?\n- Is the `: returnType` before the function body `{`?\n\n**\"Type is not defined\" / \"Generic is not defined\"** — These become `const Name = Type(...)` / `const Name = Generic(...)` after preprocessing. If you see this at runtime, the TJS runtime (`createRuntime()`) wasn't installed, or the file wasn't transpiled through TJS.\n\n**Signature test failures** — TJS runs your function with its example values at transpile time. If the function fails with its own examples, transpilation reports an error. Fix the function or choose better examples:\n\n```typescript\n// BAD: example 0 causes division by zero\nfunction inverse(x: 0): 0.0 { return 1 / x }\n\n// GOOD: example 1 works\nfunction inverse(x: 1): 0.0 { return 1 / x }\n```\n\n**Monadic errors instead of exceptions** — TJS validation returns `MonadicError` objects (with `$error: true`), it doesn't throw. Check with `isMonadicError(result)`, not `try/catch`:\n\n```typescript\nimport { isMonadicError } from 'tjs-lang'\n\nconst result = myFunction(badInput)\nif (isMonadicError(result)) {\n console.log(result.message) // \"type mismatch: expected string, got number\"\n}\n```\n\n### Debugging Type Checks\n\nEvery transpiled function has `.__tjs` metadata you can inspect:\n\n```typescript\nconsole.log(myFunction.__tjs)\n// { params: { name: { type: { kind: 'string' }, required: true } },\n// returns: { kind: 'string' } }\n```\n\nThe transpiled JS is readable — look at the generated code to see exactly what checks run:\n\n```bash\nbun src/cli/tjs.ts emit myfile.tjs # see the generated JS\n```\n\n### When to Use `!` (Unsafe)\n\nMark functions unsafe when:\n\n- The data source is already validated (e.g., internal helper called only from a validated wrapper)\n- You're in a hot loop and profiling shows the checks matter\n- You're calling a function millions of times with known-good data\n\nDon't use `!` at system boundaries (API handlers, user input, external data).\n\n---\n\n## Learn More\n\n- [AJS Documentation](DOCS-AJS.md) — The agent runtime\n- [Builder's Manifesto](MANIFESTO-BUILDER.md) — Why TJS is fun\n- [Enterprise Guide](MANIFESTO-ENTERPRISE.md) — Why TJS is safe\n- [Technical Context](CONTEXT.md) — Architecture deep dive\n"
|
|
202
|
+
"text": "<!--{\"section\": \"tjs\", \"group\": \"docs\", \"order\": 0, \"navTitle\": \"Documentation\"}-->\n\n# TJS: Typed JavaScript\n\n_Types as Examples. Zero Build. Runtime Metadata._\n\n---\n\n## What is TJS?\n\nTJS is a typed superset of JavaScript where **types are concrete values**, not abstract annotations.\n\n```typescript\n// TypeScript: abstract type annotation\nfunction greet(name: string): string\n\n// TJS: concrete example value\nfunction greet(name: 'World'): '' {\n return `Hello, ${name}!`\n}\n```\n\nThe example `'World'` tells TJS that `name` is a string. The example `''` tells TJS the return type is a string. Types are inferred from the examples you provide.\n\nTJS transpiles to JavaScript with embedded `__tjs` metadata, enabling runtime type checking, autocomplete from live objects, and documentation generation.\n\n---\n\n## TJS is JavaScript\n\nTJS is **purely additive**. It adds type annotations, runtime validation, and metadata on top of standard JavaScript. It does not replace, intercept, or modify any existing JavaScript semantics.\n\n**Everything you know about JavaScript still works:**\n\n- **Proxies** — fully supported. TJS never intercepts property access. `__tjs` metadata is a plain property assignment on the function object; it doesn't interfere with Proxy traps. The `[tjsEquals]` symbol protocol is specifically designed for Proxy-friendly custom equality.\n- **WeakMap, WeakSet, Map, Set** — all unchanged. TJS doesn't wrap or validate collection internals.\n- **Closures, Promises, async/await** — work identically to JS.\n- **Prototype chains** — preserved. `wrapClass()` uses a Proxy only on the class constructor (to allow calling without `new`), not on instances.\n- **Module semantics** — TJS preserves ES module `import`/`export` exactly. Lazy getters, circular dependencies, and re-exports work the same as in JS.\n- **`this` binding** — unchanged. Arrow functions, `.bind()`, `.call()`, `.apply()` all work normally.\n- **Regular expressions, JSON, Math, Date** — all standard built-ins are available and unmodified (though `Date` is banned by default in native TJS via `TjsDate` in favor of safer alternatives like `Timestamp`/`LegalDate`; use `TjsCompat` to restore it).\n\n**What TJS adds (and when): **\n\n| Addition | When | Overhead |\n| ---------------------- | ----------------------------------------------- | ----------------------------- |\n| Parameter validation | Function entry (unless `!` unsafe) | ~1.15-1.3x on that function |\n| Return type validation | Function exit (only with `safety all`) | ~1.15-1.3x on that function |\n| `__tjs` metadata | Transpile time | Zero runtime cost |\n| `wrapClass` Proxy | Class declaration (on by default in native TJS) | One-time, on constructor only |\n| Structural equality | `==`/`!=` (on by default in native TJS) | Per-comparison |\n\nIf TJS doesn't understand something in your code, it passes it through unchanged. There is no \"TJS runtime\" that interposes between your code and the JS engine — just the inline checks you can see in the transpiled output.\n\n---\n\n## The Compiler\n\nTJS compiles in the browser. No webpack, no node_modules, no build server.\n\n```typescript\nimport { tjs } from 'tjs-lang'\n\nconst code = tjs`\n function add(a: 0, b: 0): 0 {\n return a + b\n }\n`\n\n// Returns transpiled JavaScript with __tjs metadata\n```\n\nYou can also use the CLI:\n\n```bash\nbun src/cli/tjs.ts check file.tjs # Parse and type check\nbun src/cli/tjs.ts run file.tjs # Transpile and execute\nbun src/cli/tjs.ts emit file.tjs # Output transpiled JS\nbun src/cli/tjs.ts types file.tjs # Output type metadata\n```\n\n---\n\n## Syntax\n\n### Parameter Types (Colon Syntax)\n\n> **Not TypeScript.** TJS colon syntax looks like TypeScript but has different\n> semantics. The value after `:` is a **concrete example**, not a type name.\n> Write `name: 'Alice'` (example value), not `name: string` (type name).\n> TJS infers the type from the example: `'Alice'` → string, `0` → integer,\n> `true` → boolean.\n\nRequired parameters use colon syntax with an example value:\n\n```typescript\nfunction greet(name: 'Alice') {} // name is required, type: string\nfunction calculate(value: 0) {} // value is required, type: integer\nfunction measure(rate: 0.0) {} // rate is required, type: number (float)\nfunction count(n: +0) {} // n is required, type: non-negative integer\nfunction toggle(flag: true) {} // flag is required, type: boolean\n```\n\n### Numeric Types\n\nTJS distinguishes three numeric types using valid JavaScript syntax:\n\n```typescript\nfunction process(\n rate: 3.14, // number (float) -- has a decimal point\n count: 42, // integer -- whole number, no decimal\n index: +0 // non-negative integer -- prefixed with +\n) {}\n```\n\n| You Write | Type Inferred | Runtime Validation |\n| --------- | ---------------------- | ------------------------------- |\n| `3.14` | `number` (float) | `typeof x === 'number'` |\n| `0.0` | `number` (float) | `typeof x === 'number'` |\n| `42` | `integer` | `Number.isInteger(x)` |\n| `0` | `integer` | `Number.isInteger(x)` |\n| `+20` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n| `+0` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n| `-5` | `integer` | `Number.isInteger(x)` |\n| `-3.5` | `number` (float) | `typeof x === 'number'` |\n\nAll of these are valid JavaScript expressions. TJS reads the syntax more\ncarefully to give you finer-grained type checking than JS or TypeScript\nprovide natively.\n\n### Optional Parameters (Default Values)\n\nOptional parameters use `=` with a default value:\n\n```typescript\nfunction greet(name = 'World') {} // name is optional, defaults to 'World'\nfunction calculate(value = 0) {} // value is optional, defaults to 0 (integer)\n```\n\n### TypeScript-Style Optional (`?:`)\n\nTJS supports `?:` for compatibility, but consider it a migration aid rather than idiomatic TJS:\n\n```typescript\nfunction greet(name?: '') {} // same as name = ''\n```\n\n**Why `?:` is an antipattern.** In TypeScript, `?:` creates a three-state parameter\n(`value | undefined | missing`) that forces every function body to handle the absent case:\n\n```typescript\n// TypeScript — every caller and callee must reason about undefined\nfunction greet(name?: string) {\n const safeName = name ?? 'World' // defensive check required\n return `Hello, ${safeName}!`\n}\n```\n\nTJS offers two better alternatives:\n\n**1. Safe defaults** — the parameter always has a value, no branching needed:\n\n```typescript\nfunction greet(name = 'World') {\n return `Hello, ${name}!` // name is always a string\n}\n```\n\n**2. Polymorphic functions** — separate signatures for separate behavior:\n\n```typescript\nfunction greet() {\n return 'Hello, World!'\n}\nfunction greet(name: '') {\n return `Hello, ${name}!`\n}\n```\n\nBoth approaches eliminate the `undefined` state entirely. The function body\nnever needs a null check because the type system guarantees a valid value\nat every call site. This is simpler to write, simpler to read, and produces\ntighter runtime validation.\n\n### Object Parameters\n\nObject shapes are defined by example:\n\n```typescript\nfunction createUser(user: { name: ''; age: 0 }) {}\n// user must be an object with string name and number age\n```\n\n### Nullable Types\n\nUse `|` for union with null:\n\n```typescript\nfunction find(id: 0 | null) {} // number or null\n```\n\n### Rest Parameters\n\nRest params use `:` with an array example. The annotation is stripped from\nthe JS output (JS doesn't allow defaults on rest params) but captured in\n`__tjs` metadata:\n\n```typescript\nfunction sum(...nums: [1, 2, 3]): 6 {\n return nums.reduce((a = 0, b: 0) => a + b, 0)\n}\n\nfunction mean(...values: [1.0, 2.0, 3.0, 2.0]): 2.0 {\n return values.length\n ? values.reduce((sum = 0.0, x: 1.0) => sum + x) / values.length\n : 0.0\n}\n```\n\nSignature tests work with rest params — the example array elements are\nspread as individual arguments. `mean(1.0, 2.0, 3.0, 2.0)` is called\nand the result is checked against the `: 2.0` expected return using\nexact value comparison (deepEqual).\n\nThe array example tells TJS the element type. `[0]` means \"array of\nintegers\", `[1.0, 2.0]` means \"array of numbers (floats)\".\n\n**Heterogeneous arrays** infer a union item type:\n\n```typescript\nfunction log(...args: ['info', 42, true]) {}\n// args type: array<string | integer | boolean>\n```\n\n### Return Types (Colon Syntax)\n\nReturn types use `:`:\n\n```typescript\nfunction add(a: 0, b: 0): 0 {\n return a + b\n}\n\nfunction getUser(id: 0): { name: ''; age: 0 } {\n return { name: 'Alice', age: 30 }\n}\n```\n\n### Array Types\n\nArrays use bracket syntax with an example element:\n\n```typescript\nfunction sum(numbers: [0]): 0 {\n // array of numbers\n return numbers.reduce((a, b) => a + b, 0)\n}\n\nfunction names(users: [{ name: '' }]) {\n // array of objects\n return users.map((u) => u.name)\n}\n```\n\n---\n\n## Safety Markers\n\n### Unsafe Functions\n\nSkip validation for hot paths:\n\n```typescript\nfunction fastAdd(! a: 0, b: 0) { return a + b }\n```\n\nThe `!` marker after the function name skips input validation.\n\n### Safe Functions\n\nExplicit validation (for emphasis):\n\n```typescript\nfunction safeAdd(? a: 0, b: 0) { return a + b }\n```\n\n### Unsafe Blocks\n\nSkip validation for a block of code:\n\n```typescript\nunsafe {\n fastPath(data)\n anotherHotFunction(moreData)\n}\n```\n\n### Module Safety Directive\n\nSet the default validation level for an entire file:\n\n```typescript\nsafety none // No validation (metadata only)\nsafety inputs // Validate function inputs (default)\nsafety all // Validate everything (debug mode)\n```\n\n---\n\n## Type System\n\n### Type()\n\nDefine named types with predicates:\n\n```typescript\n// Simple type from example\nType Name 'Alice'\n\n// Type with description\nType User {\n description: 'a user object'\n example: { name: '', age: 0 }\n}\n\n// Type with predicate\nType PositiveNumber {\n description: 'a positive number'\n example: 1\n predicate(x) { return x > 0 }\n}\n```\n\nTypes can be used in function signatures:\n\n```typescript\nfunction greet(name: Name): '' {\n return `Hello, ${name}!`\n}\n```\n\n### Generic()\n\nRuntime-checkable generics:\n\n```typescript\nGeneric Box<T> {\n description: 'a boxed value'\n predicate(x, T) {\n return typeof x === 'object' && x !== null && 'value' in x && T(x.value)\n }\n}\n\n// With default type parameter\nGeneric Container<T, U = ''> {\n description: 'container with label'\n predicate(obj, T, U) {\n return T(obj.item) && U(obj.label)\n }\n}\n```\n\n#### Declaration Blocks (for TypeScript Consumers)\n\nGenerics can include an optional `declaration` block that specifies the\nTypeScript interface to emit in `.d.ts` output. This is metadata for TS\nconsumers — it has no effect on runtime behavior.\n\n```typescript\nGeneric BoxedProxy<T> {\n description: 'typed reactive proxy'\n predicate(x, T) {\n return typeof x === 'object' && 'value' in x && T(x.value)\n }\n declaration {\n value: T\n path: string\n observe(cb: (path: string) => void): void\n touch(): void\n }\n}\n```\n\nWhen emitting `.d.ts` via `tjs emit --dts`, this produces:\n\n```typescript\nexport interface BoxedProxy<T> {\n value: T\n path: string\n observe(cb: (path: string) => void): void\n touch(): void\n}\n```\n\nThe declaration content is raw TypeScript syntax — it's emitted verbatim\ninto the `.d.ts` file. This lets TJS libraries provide proper TypeScript\ninterfaces while keeping the TJS source as the single source of truth.\n\nWithout a `declaration` block, Generics emit an `any`-based factory stub\nthat provides basic IDE hints without false type errors.\n\n### Union()\n\nDiscriminated unions:\n\n```typescript\nconst Shape = Union('kind', {\n circle: { radius: 0 },\n rectangle: { width: 0, height: 0 },\n})\n\nfunction area(shape: Shape): 0 {\n if (shape.kind === 'circle') {\n return Math.PI * shape.radius ** 2\n }\n return shape.width * shape.height\n}\n```\n\n### Enum()\n\nString or numeric enums:\n\n```typescript\nconst Status = Enum(['pending', 'active', 'completed'])\nconst Priority = Enum({ low: 1, medium: 2, high: 3 })\n\nfunction setStatus(status: Status) {}\n```\n\n---\n\n## Structural Equality: Is / IsNot\n\nJavaScript's `==` is broken (type coercion). TJS provides structural equality:\n\n```typescript\n// Structural comparison - no coercion\n[1, 2] Is [1, 2] // true\n5 Is \"5\" // false (different types)\n{ a: 1 } Is { a: 1 } // true\n\n// Arrays compared element-by-element\n[1, [2, 3]] Is [1, [2, 3]] // true\n\n// Negation\n5 IsNot \"5\" // true\n```\n\n### Custom Equality\n\nObjects can define custom equality in two ways:\n\n**1. `[tjsEquals]` symbol protocol** (preferred for Proxies and advanced use):\n\n```typescript\nimport { tjsEquals } from 'tjs-lang/lang'\n\n// A proxy that delegates equality to its target\nconst target = { x: 1, y: 2 }\nconst proxy = new Proxy({\n [tjsEquals](other) { return target Is other }\n}, {})\n\nproxy == { x: 1, y: 2 } // true — delegates to target\n```\n\n**2. `.Equals` method** (simple, works on any object or class):\n\n```typescript\nclass Point {\n constructor(x: 0, y: 0) { this.x = x; this.y = y }\n Equals(other) { return this.x === other.x && this.y === other.y }\n}\n\nPoint(1, 2) Is Point(1, 2) // true (uses .Equals)\n```\n\n**Priority:** `[tjsEquals]` symbol > `.Equals` method > structural comparison.\n\nThe symbol is `Symbol.for('tjs.equals')`, so it works across realms. Access it\nvia `import { tjsEquals } from 'tjs-lang/lang'` or `__tjs.tjsEquals` at runtime.\n\n---\n\n## Classes\n\n### Callable Without `new`\n\nIn native TJS, classes are automatically wrapped so they can be called\nwithout `new` (the `TjsClass` mode is on by default). For TS-originated\ncode, add the `TjsClass` directive to enable this:\n\n```typescript\nclass User {\n constructor(name: '') {\n this.name = name\n }\n}\n\n// Both work identically:\nconst u1 = User('Alice') // TJS way - clean\nconst u2 = new User('Alice') // Also works (linter warns)\n```\n\nThe wrapping uses a Proxy on the constructor that intercepts bare calls\nand forwards them to `Reflect.construct`. This means `User('Alice')`\nand `new User('Alice')` always produce the same result — an instance.\n\n**What gets wrapped:** Only `class` declarations in your `.tjs` file.\nSpecifically:\n\n- `class Foo { }` in native TJS (or with `TjsClass` directive) → wrapped\n- Built-in globals (`Boolean`, `Number`, `String`, `Array`) → **never touched**\n- Old-style constructor functions (`function Foo() { }` with `Foo.prototype`) → **never touched**\n\n**Why not built-ins:** JavaScript's built-in constructors have dual\nbehavior — `Boolean(0)` returns the primitive `false` (type coercion),\nwhile `new Boolean(0)` returns a `Boolean` object wrapping `false`\n(which is truthy!). If TJS wrapped `Boolean`, then `Boolean(0)` would\nsilently become `new Boolean(0)` — a truthy object instead of `false`.\nThe same applies to `Number()`, `String()`, and `Array()`.\n\n**Why not old-style constructors:** If you're using `function` +\n`prototype` to build a class manually, you may intentionally want\n`Foo(x)` to behave differently from `new Foo(x)` — the same dual\nbehavior pattern as the built-ins. TJS respects this by only wrapping\nthe `class` keyword, where calling without `new` has no existing\nmeaning in JavaScript (it throws `TypeError`).\n\n### Private Fields\n\nUse `#` for private fields:\n\n```typescript\nclass Counter {\n #count = 0\n\n increment() {\n this.#count++\n }\n get value() {\n return this.#count\n }\n}\n```\n\nWhen converting from TypeScript, `private foo` becomes `#foo`.\n\n### Getters and Setters\n\nAsymmetric types are captured:\n\n```typescript\nclass Timestamp {\n #value\n\n constructor(initial: '' | 0 | null) {\n this.#value = initial === null ? new Date() : new Date(initial)\n }\n\n set value(v: '' | 0 | null) {\n this.#value = v === null ? new Date() : new Date(v)\n }\n\n get value() {\n return this.#value\n }\n}\n\nconst ts = Timestamp('2024-01-15')\nts.value = 0 // SET accepts: string | number | null\nts.value // GET returns: Date\n```\n\n---\n\n## Polymorphic Functions\n\nMultiple function declarations with the same name are automatically merged into a dispatcher that routes by argument count and type:\n\n```typescript\nfunction describe(value: 0) {\n return 'number: ' + value\n}\nfunction describe(value: '') {\n return 'string: ' + value\n}\nfunction describe(value: { name: '' }) {\n return 'object: ' + value.name\n}\n\ndescribe(42) // 'number: 42'\ndescribe('hello') // 'string: hello'\ndescribe({ name: 'world' }) // 'object: world'\ndescribe(true) // MonadicError: no matching overload\n```\n\n### Dispatch Order\n\n1. **Arity** first (number of arguments)\n2. **Type specificity** within same arity: `integer` > `number` > `any`; objects before primitives\n3. **Declaration order** as tiebreaker\n\n### Polymorphic Constructors\n\nClasses can have multiple constructor signatures (enabled by default in native TJS via `TjsClass`). The first becomes the real JS constructor; additional variants become factory functions:\n\n```typescript\nclass Point {\n constructor(x: 0.0, y: 0.0) {\n this.x = x\n this.y = y\n }\n constructor(coords: { x: 0.0; y: 0.0 }) {\n this.x = coords.x\n this.y = coords.y\n }\n}\n\nPoint(3, 4) // variant 1: two numbers\nPoint({ x: 10, y: 20 }) // variant 2: object\n```\n\nAll variants produce correct `instanceof` results.\n\n### Compile-Time Validation\n\nTJS catches these errors at transpile time:\n\n- **Ambiguous signatures**: Two variants with identical types at every position\n- **Rest parameters**: `...args` not supported in polymorphic functions\n- **Mixed async/sync**: All variants must agree\n\n---\n\n## Local Class Extensions\n\nAdd methods to built-in types without polluting prototypes:\n\n```typescript\nextend String {\n capitalize() {\n return this[0].toUpperCase() + this.slice(1)\n }\n words() {\n return this.split(/\\s+/)\n }\n}\n\n'hello world'.capitalize() // 'Hello world'\n'foo bar baz'.words() // ['foo', 'bar', 'baz']\n```\n\n### How It Works\n\nFor known-type receivers (literals, typed variables), calls are rewritten at transpile time to `.call()` — zero runtime overhead:\n\n```javascript\n// TJS source:\n'hello'.capitalize()\n\n// Generated JS:\n__ext_String.capitalize.call('hello')\n```\n\nFor unknown types, a runtime registry (`registerExtension` / `resolveExtension`) provides fallback dispatch.\n\n### Supported Types\n\nExtensions work on any type: `String`, `Number`, `Array`, `Boolean`, custom classes, and DOM classes like `HTMLElement`. Multiple `extend` blocks for the same type merge left-to-right (later declarations can override earlier methods).\n\n### Rules\n\n- Arrow functions are **not allowed** in extend blocks (they don't bind `this`)\n- Extensions are **file-local** — they don't leak across modules\n- Prototypes are **never modified** — `String.prototype.capitalize` remains `undefined`\n\n---\n\n## Runtime Features\n\n### `__tjs` Metadata\n\nEvery TJS function carries its type information:\n\n```typescript\nfunction createUser(input: { name: ''; age: 0 }): { id: 0 } {\n return { id: 123 }\n}\n\nconsole.log(createUser.__tjs)\n// {\n// params: {\n// input: { type: { kind: 'object', shape: { name: 'string', age: 'number' } } }\n// },\n// returns: { kind: 'object', shape: { id: 'number' } }\n// }\n```\n\nThis enables:\n\n- Autocomplete from live objects\n- Runtime type validation\n- Automatic documentation generation\n- JSON Schema generation (see below)\n\n### JSON Schema\n\nTJS types and function signatures can be exported as standard JSON Schema. Instead of writing schemas and inferring types (Zod), you write typed functions and get schemas out.\n\n#### From Types\n\n```typescript\nType User {\n example: { name: '', age: 0, email: '' }\n}\n\nUser.toJSONSchema()\n// {\n// type: 'object',\n// properties: {\n// name: { type: 'string' },\n// age: { type: 'integer' },\n// email: { type: 'string' }\n// },\n// required: ['name', 'age', 'email'],\n// additionalProperties: false\n// }\n\nUser.check({ name: 'Alice', age: 30, email: 'a@b.com' }) // true\nUser.strip({ name: 'Alice', age: 30, secret: 'pw' })\n// { name: 'Alice', age: 30 } — extra fields removed\n```\n\n#### From Function Signatures\n\n```typescript\nimport { functionMetaToJSONSchema } from 'tjs-lang/lang'\n\nfunction createUser(name: '', age: 0): { id: 0; name: '' } {\n return { id: 1, name }\n}\n\nconst { input, output } = functionMetaToJSONSchema(createUser.__tjs)\n// input: { type: 'object', properties: { name: { type: 'string' }, age: { type: 'integer' } }, required: ['name', 'age'] }\n// output: { type: 'object', properties: { id: { type: 'integer' }, name: { type: 'string' } }, ... }\n```\n\nWhen the shared runtime is installed (`installRuntime()`), `.schema()` is also available directly on the metadata:\n\n```typescript\ncreateUser.__tjs.schema() // same { input, output } result\n```\n\n#### Unions and Enums\n\n```typescript\nconst Direction = Union('direction', ['up', 'down', 'left', 'right'])\nDirection.toJSONSchema() // { enum: ['up', 'down', 'left', 'right'] }\n\nconst Status = Enum('status', { Active: 1, Inactive: 0 })\nStatus.toJSONSchema() // { enum: [1, 0] }\n```\n\nThis gives you OpenAPI-ready API contracts from your function signatures — no extra schema definitions needed.\n\n### Monadic Errors\n\nType validation failures return `MonadicError` instances (extends `Error`),\nnot thrown exceptions:\n\n```typescript\nimport { isMonadicError } from 'tjs-lang/lang'\n\nconst result = createUser({ name: 123 }) // wrong type\n// MonadicError: Expected string for 'createUser.name', got number\n\nif (isMonadicError(result)) {\n console.log(result.message) // \"Expected string for 'createUser.name', got number\"\n console.log(result.path) // \"createUser.name\"\n console.log(result.expected) // \"string\"\n console.log(result.actual) // \"number\"\n}\n```\n\nNo try/catch gambling. The host survives invalid inputs.\n\nFor general-purpose error values (not type errors), use the `error()` helper\nwhich returns plain `{ $error: true, message }` objects checkable with `isError()`.\n\n### Error History\n\nSince monadic errors don't throw, they can silently vanish if nobody checks the return value. TJS tracks recent type errors in a ring buffer so you can catch these:\n\n```typescript\n// Errors are tracked automatically (on by default, zero cost on happy path)\ngreet(42) // returns MonadicError, caller ignores it\nprocessOrder('bad') // same\n\n// Check what failed recently\nconst recent = __tjs.errors() // → recent MonadicErrors (newest last, max 64)\nfor (const err of recent) {\n console.log(err.message, err.path)\n}\n\n// Testing workflow: clear → run → check for surprises\n__tjs.clearErrors()\nrunMyCode()\nexpect(__tjs.errors()).toEqual([]) // no unexpected type errors\n\n// Total count survives ring buffer wrapping\n__tjs.getErrorCount() // → total since last clear\n```\n\n### Runtime Configuration\n\n```typescript\nimport { configure } from 'tjs-lang/lang'\n\n// Log type errors to console when they occur\nconfigure({ logTypeErrors: true })\n\n// Throw type errors instead of returning them (for debugging)\nconfigure({ throwTypeErrors: true })\n\n// Enable call stack tracking (off by default — ~2x overhead)\n// Useful for server-side logging and agent debugging without devtools\nconfigure({ callStacks: true })\n\n// Disable error history tracking (on by default, zero cost on happy path)\nconfigure({ trackErrors: false })\n```\n\nBoth `logTypeErrors` and `throwTypeErrors` work on the shared runtime and isolated `createRuntime()` instances.\n\n### Inline Tests\n\nTests live next to code:\n\n```typescript\nfunction double(x: 0): 0 { return x * 2 }\n\ntest('doubles numbers') {\n expect(double(5)).toBe(10)\n expect(double(-3)).toBe(-6)\n}\n```\n\nTests are extracted at compile time and can be:\n\n- Run during transpilation\n- Stripped in production builds\n- Used for documentation generation\n\n### WASM Blocks\n\nDrop into WebAssembly for compute-heavy code:\n\n```typescript\nfunction vectorDot(a: [0], b: [0]): 0 {\n let sum = 0\n wasm {\n for (let i = 0; i < a.length; i++) {\n sum = sum + a[i] * b[i]\n }\n }\n return sum\n}\n```\n\nVariables are captured automatically. Falls back to JS if WASM unavailable.\n\n#### SIMD Intrinsics (f32x4)\n\nFor compute-heavy workloads, use f32x4 SIMD intrinsics to process 4 float32 values per instruction:\n\n```typescript\nconst scale = wasm (arr: Float32Array, len: 0, factor: 0.0): 0 {\n let s = f32x4_splat(factor)\n for (let i = 0; i < len; i += 4) {\n let off = i * 4\n let v = f32x4_load(arr, off)\n f32x4_store(arr, off, f32x4_mul(v, s))\n }\n} fallback {\n for (let i = 0; i < len; i++) arr[i] *= factor\n}\n```\n\nAvailable intrinsics:\n\n| Intrinsic | Description |\n| ----------------------------------- | ------------------------------------ |\n| `f32x4_load(ptr, byteOffset)` | Load 4 floats from memory into v128 |\n| `f32x4_store(ptr, byteOffset, vec)` | Store v128 as 4 floats to memory |\n| `f32x4_splat(scalar)` | Fill all 4 lanes with a scalar value |\n| `f32x4_extract_lane(vec, N)` | Extract float from lane 0-3 |\n| `f32x4_replace_lane(vec, N, val)` | Replace one lane, return new v128 |\n| `f32x4_add(a, b)` | Lane-wise addition |\n| `f32x4_sub(a, b)` | Lane-wise subtraction |\n| `f32x4_mul(a, b)` | Lane-wise multiplication |\n| `f32x4_div(a, b)` | Lane-wise division |\n| `f32x4_neg(v)` | Negate all lanes |\n| `f32x4_sqrt(v)` | Square root of all lanes |\n\nThis mirrors C/C++ SIMD intrinsics (`_mm_add_ps`, etc.) — explicit, predictable, no auto-vectorization magic.\n\n#### Zero-Copy Arrays: `wasmBuffer()`\n\nBy default, typed arrays passed to WASM blocks are copied into WASM memory before the call and copied back out after. For large arrays called frequently, this overhead can negate WASM's speed advantage.\n\n`wasmBuffer(Constructor, length)` allocates typed arrays directly in WASM linear memory. These arrays work like normal typed arrays from JavaScript, but when passed to a `wasm {}` block, they're zero-copy — the data is already there.\n\n```typescript\n// Allocate particle positions in WASM memory\nconst starX = wasmBuffer(Float32Array, 50000)\nconst starY = wasmBuffer(Float32Array, 50000)\n\n// Use from JS like normal arrays\nfor (let i = 0; i < 50000; i++) {\n starX[i] = (Math.random() - 0.5) * 2000\n starY[i] = (Math.random() - 0.5) * 2000\n}\n\n// Zero-copy SIMD processing\nfunction moveParticles(! xs: Float32Array, ys: Float32Array, len: 0, dx: 0.0, dy: 0.0) {\n wasm {\n let vdx = f32x4_splat(dx)\n let vdy = f32x4_splat(dy)\n for (let i = 0; i < len; i += 4) {\n let off = i * 4\n f32x4_store(xs, off, f32x4_add(f32x4_load(xs, off), vdx))\n f32x4_store(ys, off, f32x4_add(f32x4_load(ys, off), vdy))\n }\n } fallback {\n for (let i = 0; i < len; i++) { xs[i] += dx; ys[i] += dy }\n }\n}\n\n// After WASM runs, JS sees the mutations immediately\nmoveParticles(starX, starY, 50000, 1.0, 0.5)\nconsole.log(starX[0]) // updated in place, no copy\n```\n\nKey points:\n\n- Supported constructors: `Float32Array`, `Float64Array`, `Int32Array`, `Uint8Array`\n- Uses a bump allocator — allocations persist for program lifetime\n- All WASM blocks in a file share one 64MB memory\n- Regular typed arrays still work (copy in/out as before)\n- Use `!` (unsafe) on hot-path functions to skip runtime type checks\n\n---\n\n## Module System\n\nTJS preserves standard ES module semantics exactly. `import` and `export` statements pass through to the output unchanged — TJS does not have its own module resolver.\n\n### Importing .tjs Files\n\nIn **Bun** (with the TJS plugin from `bunfig.toml`):\n\n```typescript\n// .tjs files are transpiled automatically on import\nimport { processOrder } from './orders.tjs'\nimport { validateUser } from './users.ts' // TS files also work\n```\n\nIn the **browser playground**, local modules are resolved from the playground's module store. Relative imports are looked up by name:\n\n```typescript\nimport { formatDate } from './date-utils' // resolves from saved modules\n```\n\nIn **production builds** (`tjs emit`), TJS transpiles `.tjs` → `.js`. Your bundler (esbuild, Rollup, etc.) handles resolution of the output `.js` files normally.\n\n### Importing JS/TS Libraries\n\nTJS files can import any JavaScript or TypeScript library. The imported code runs without TJS validation — it's just normal JS. Add a TJS wrapper at the boundary if you want type safety:\n\n```typescript\nimport { rawGeocode } from 'legacy-geo-pkg'\n\n// Wrap at the boundary — rawGeocode is unchecked, geocode validates its output\nfunction geocode(addr: ''): { lat: 0.0; lon: 0.0 } {\n return rawGeocode(addr)\n}\n```\n\n### CDN Imports\n\nURL imports work with any ESM CDN:\n\n```typescript\nimport lodash from 'https://esm.sh/lodash@4.17.21'\n```\n\n### Circular Dependencies\n\nTJS doesn't interfere with JS module loading. Circular imports work the same way as in standard ES modules — use lazy getters or late binding if you need to break cycles, exactly as you would in plain JS.\n\n### TypeScript Declaration Files (.d.ts)\n\nTJS can generate `.d.ts` files so TypeScript consumers can use TJS-authored libraries with autocomplete and tooltips:\n\n```bash\nbun src/cli/tjs.ts emit --dts src/lib.tjs -o dist/lib.js\n# Generates dist/lib.js + dist/lib.d.ts\n```\n\nFrom code:\n\n```typescript\nimport { tjs, generateDTS } from 'tjs-lang'\n\nconst result = tjs(source)\nconst dts = generateDTS(result, source)\n```\n\nFunctions get full type declarations. Classes, generics, and predicate-based types get `any`-based stubs that provide IDE hints (parameter names, object shapes) without generating false lint errors for types that TJS validates at runtime.\n\n---\n\n## TypeScript Compatibility\n\n### TS → TJS Converter\n\nConvert existing TypeScript:\n\n```bash\nbun src/cli/tjs.ts convert file.ts\n```\n\n```typescript\n// TypeScript\nfunction greet(name: string, age?: number): string { ... }\n\n// Converts to TJS\nfunction greet(name: '', age = 0): '' { ... }\n```\n\n### What Gets Converted\n\n| TypeScript | TJS |\n| -------------------------- | ----------------------- |\n| `name: string` | `name: ''` |\n| `age: number` | `age: 0.0` |\n| `flag: boolean` | `flag: false` |\n| `items: string[]` | `items: ['']` |\n| `age?: number` | `age: 0.0 \\| undefined` |\n| `private foo` | `#foo` |\n| `interface User` | `Type User` |\n| `type Status = 'a' \\| 'b'` | `Union(['a', 'b'])` |\n| `enum Color` | `Enum(...)` |\n\n> **Optional params:** TypeScript `x?: boolean` becomes TJS `x: false | undefined`.\n> This preserves the three-state semantics (`true` / `false` / `undefined`)\n> using a union type. The param is required but explicitly accepts `undefined`.\n\n---\n\n## Performance\n\n| Mode | Overhead | Use Case |\n| --------------- | -------------- | ---------------------------- |\n| `safety none` | **1.0x** | Metadata only, no validation |\n| `safety inputs` | **~1.15-1.3x** | Production |\n| `(!) unsafe` | **1.0x** | Hot paths |\n| `wasm {}` | **<1.0x** | Compute-heavy code |\n\n### Why ~1.15x, Not 25x\n\nMost validators interpret schemas at runtime (~25x overhead). TJS generates inline checks at transpile time:\n\n```typescript\n// Generated (JIT-friendly)\nif (\n typeof input !== 'object' ||\n input === null ||\n typeof input.name !== 'string' ||\n typeof input.age !== 'number'\n) {\n return { $error: true, message: 'Invalid input', path: 'fn.input' }\n}\n```\n\nNo schema interpretation. No object iteration. The JIT inlines these completely.\n\n---\n\n## Bare Assignments\n\nUppercase identifiers automatically get `const`:\n\n```typescript\nFoo = Type('test', 'example') // becomes: const Foo = Type(...)\nMyConfig = { debug: true } // becomes: const MyConfig = { ... }\n```\n\n---\n\n## Limitations\n\n### What TJS Doesn't Do\n\n- **No gradual typing** — types are all-or-nothing per function\n- **No complex type inference** — you provide examples, not constraints\n- **No type-level computation** — no conditional types, mapped types, etc.\n\n### What TJS Intentionally Avoids\n\n- Build steps beyond transpilation\n- External type checkers\n- Complex tooling configuration\n- Separation of types from runtime\n\n---\n\n## Troubleshooting\n\n### Common Transpilation Errors\n\n**\"Unexpected token\"** — Usually means TJS-specific syntax (`:` params, `:` returns, `Type`, `Generic`) wasn't recognized. Check:\n\n- Is the file being parsed as TJS (not plain JS)?\n- Are `Type`/`Generic`/`Union` declarations at the top level (not inside functions)?\n- Is the `: returnType` before the function body `{`?\n\n**\"Type is not defined\" / \"Generic is not defined\"** — These become `const Name = Type(...)` / `const Name = Generic(...)` after preprocessing. If you see this at runtime, the TJS runtime (`createRuntime()`) wasn't installed, or the file wasn't transpiled through TJS.\n\n**Signature test failures** — TJS runs your function with its example values at transpile time. If the function fails with its own examples, transpilation reports an error. Fix the function or choose better examples:\n\n```typescript\n// BAD: example 0 causes division by zero\nfunction inverse(x: 0): 0.0 {\n return 1 / x\n}\n\n// GOOD: example 1 works\nfunction inverse(x: 1): 0.0 {\n return 1 / x\n}\n```\n\n**Monadic errors instead of exceptions** — TJS validation returns `MonadicError` objects (with `$error: true`), it doesn't throw. Check with `isMonadicError(result)`, not `try/catch`:\n\n```typescript\nimport { isMonadicError } from 'tjs-lang'\n\nconst result = myFunction(badInput)\nif (isMonadicError(result)) {\n console.log(result.message) // \"type mismatch: expected string, got number\"\n}\n```\n\n### Debugging Type Checks\n\nEvery transpiled function has `.__tjs` metadata you can inspect:\n\n```typescript\nconsole.log(myFunction.__tjs)\n// { params: { name: { type: { kind: 'string' }, required: true } },\n// returns: { kind: 'string' } }\n```\n\nThe transpiled JS is readable — look at the generated code to see exactly what checks run:\n\n```bash\nbun src/cli/tjs.ts emit myfile.tjs # see the generated JS\n```\n\n### When to Use `!` (Unsafe)\n\nMark functions unsafe when:\n\n- The data source is already validated (e.g., internal helper called only from a validated wrapper)\n- You're in a hot loop and profiling shows the checks matter\n- You're calling a function millions of times with known-good data\n\nDon't use `!` at system boundaries (API handlers, user input, external data).\n\n---\n\n## Learn More\n\n- [AJS Documentation](DOCS-AJS.md) — The agent runtime\n- [Builder's Manifesto](MANIFESTO-BUILDER.md) — Why TJS is fun\n- [Enterprise Guide](MANIFESTO-ENTERPRISE.md) — Why TJS is safe\n- [Technical Context](CONTEXT.md) — Architecture deep dive\n"
|
|
203
203
|
},
|
|
204
204
|
{
|
|
205
205
|
"title": "Starfield",
|
|
@@ -735,18 +735,18 @@
|
|
|
735
735
|
"path": ".compat-tests/effect/.changeset/fix-openrouter-referer-header.md",
|
|
736
736
|
"text": "---\n\"@effect/ai-openrouter\": patch\n---\n\nFix typo in HTTP header name: `HTTP-Referrer` → `HTTP-Referer`. The HTTP spec spells it \"Referer\" (single r), and OpenRouter expects this exact header name for app attribution.\n"
|
|
737
737
|
},
|
|
738
|
-
{
|
|
739
|
-
"title": "---",
|
|
740
|
-
"filename": "feature_request.md",
|
|
741
|
-
"path": ".compat-tests/ts-pattern/.github/ISSUE_TEMPLATE/feature_request.md",
|
|
742
|
-
"text": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: enhancement\nassignees: ''\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
|
|
743
|
-
},
|
|
744
738
|
{
|
|
745
739
|
"title": "---",
|
|
746
740
|
"filename": "bug_report.md",
|
|
747
741
|
"path": ".compat-tests/ts-pattern/.github/ISSUE_TEMPLATE/bug_report.md",
|
|
748
742
|
"text": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**TypeScript playground with a minimal reproduction case**\n\nExample: [Playground](https://www.typescriptlang.org/play/?#code/JYWwDg9gTgLgBAbziAhjAxgCwDRwApwC+cAZlBCHAOQwDOAtGGjAKZQB2VAUFwPS9wA6lGCs4ATwgBXKHCgsw5OOhS0WAOi5A)\n\n**Versions**\n\n- TypeScript version: x.x.x\n- ts-pattern version: x.x.x\n- environment: browser + version / node version / deno version\n"
|
|
749
743
|
},
|
|
744
|
+
{
|
|
745
|
+
"title": "---",
|
|
746
|
+
"filename": "feature_request.md",
|
|
747
|
+
"path": ".compat-tests/ts-pattern/.github/ISSUE_TEMPLATE/feature_request.md",
|
|
748
|
+
"text": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: enhancement\nassignees: ''\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
|
|
749
|
+
},
|
|
750
750
|
{
|
|
751
751
|
"title": "[Read the docs →](https://zod.dev/api)",
|
|
752
752
|
"filename": "README.md",
|
|
@@ -1165,7 +1165,7 @@
|
|
|
1165
1165
|
"title": "AJS (.ajs) - A Better JavaScript for AI Agents",
|
|
1166
1166
|
"filename": "ajs.md",
|
|
1167
1167
|
"path": "guides/ajs.md",
|
|
1168
|
-
"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 colon 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"
|
|
1168
|
+
"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: {\n type: 'number',\n required: false,\n default: 10,\n description: 'Max results to return',\n },\n },\n returns: {\n type: 'array',\n items: { type: 'object', shape: { title: 'string', url: 'string' } },\n },\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 colon 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"
|
|
1169
1169
|
},
|
|
1170
1170
|
{
|
|
1171
1171
|
"title": "AJS LLM System Prompt",
|
|
@@ -1203,18 +1203,18 @@
|
|
|
1203
1203
|
"filename": "bootstrap.test.ts",
|
|
1204
1204
|
"path": "src/use-cases/bootstrap.test.ts"
|
|
1205
1205
|
},
|
|
1206
|
-
{
|
|
1207
|
-
"title": "Changelog",
|
|
1208
|
-
"filename": "CHANGELOG.md",
|
|
1209
|
-
"path": ".compat-tests/zod/packages/docs-v3/CHANGELOG.md",
|
|
1210
|
-
"text": "# Changelog\n\n## Release notes are now stored in Github Releases: https://github.com/colinhacks/zod/releases\n\n## Previous Releases\n\n### 3.10\n\n- New parser that allows parsing to continue after non-fatal errors have occurred. This allows Zod to surface more errors to the user at once.\n\n### 3.9\n\n- Custom error messages in schemas\n\n```ts\nconst name = z.string({\n invalid_type_error: 'Name must be string',\n required_error: 'Name is required',\n})\n```\n\nUnder the hood, this creates a custom error map that's bound to the schema. You can also pass a custom error map explicitly.\n\n```ts\nconst name = z.string({ errorMap: myErrorMap })\n```\n\n- Rest parameters for tuples\n\n```ts\nconst myTuple = z.tuple([z.string(), z.number()]).rest(z.boolean())\ntype t1 = z.output<typeof myTuple> // [string, number, ...boolean[]]\n```\n\n- Selective `.partial`\n\nYou can specify certain fields to make optional with the `ZodObject.partial` method.\n\n```ts\nconst user = z.object({\n name: z.string(),\n age: z.number(),\n})\n\nconst optionalNameUser = user.partial({ name: true })\n// { name?: string; age: number; }\n```\n\n- Specify key schema in ZodRecord\n\nPreviously, `z.record` only accepted a single schema:\n\n```ts\nz.record(z.boolean()) // Record<string, boolean>;\n```\n\nNow `z.record` has been overloaded to support two schemas. The first validates the _keys_ of the record, and the second validates the _values_.\n\n```ts\nconst schema = z.record(z.number(), z.boolean())\ntype schema = z.infer<typeof schema> // Record<number, boolean>\n\nconst schema = z.record(z.enum(['Tuna', 'Trout']), z.boolean())\ntype schema = z.infer<typeof schema> // Record<\"Tuna\" | \"Trout\", boolean>\n```\n\n### 3.8\n\n- Add `z.preprocess`\n- Implement CUID validation on ZodString (`z.string().cuid()`)\n- Improved `.deepPartial()`: now recursively operates on arrays, tuples, optionals, and nullables (in addition to objects)\n\n### 3.7\n\n- Eliminate `ZodNonEmptyArray`, add `Cardinality` to `ZodArray`\n- Add optional error message to `ZodArray.nonempty`\n- Add `.gt/.gte/.lt/.lte` to `ZodNumber`\n\n### 3.6\n\n- Add IE11 support\n- `ZodError.flatten` now optionally accepts a map function for customizing the output\n- `.void()` now only accepts undefined, not null.\n- `z.enum` now supports `Readonly` string tuples\n\n### 3.5\n\n- Add discriminator to all first-party schema defs\n\n### 3.4\n\n- `unknown` and `any` schemas are always interpreted as optional. Reverts change from 3.3.\n\n### 3.3\n\n- HUGE speed improvements\n- Added benchmarking: `yarn benchmark`\n- Type signature of `ZodType#_parse` has changed. This will affects users who have implemented custom subclasses of `ZodType`.\n- [reverted] Object fields of type `unknown` are no longer inferred as optional.\n\n### 3.2\n\n- Certain methods (`.or`, `.transform`) now return a new instance that wrap the current instance, instead of trying to avoid additional nesting. For example:\n\n```ts\nz.union([z.string(), z.number()]).or(z.boolean())\n// previously\n// => ZodUnion<[ZodString, ZodNumber, ZodBoolean]>\n\n// now\n// => ZodUnion<[ZodUnion<[ZodString, ZodNumber]>, ZodBoolean]>\n```\n\nThis change was made due to recursion limitations in TypeScript 4.3 that made it impossible to properly type these methods.\n\n### 3.0.0-beta.1\n\n- Moved default value logic into ZodDefault. Implemented `.nullish()` method.\n\n### 3.0.0-alpha.33\n\n- Added `.returnType` and `.parameters` methods to ZodFunction\n\n### 3.0.0-alpha.32\n\n- Added `.required()` method to ZodObject\n\n### 3.0.0-alpha.30\n\n- Added Rollup for bundling ESM module\n\n### zod@3.0.0-alpha.24\n\n- Added back ZodIntersection\n- Added .and() method to base class\n\n### zod@3.0.0-alpha.9\n\n- Added `z.strictCreate`\n\n### zod@3.0.0-alpha.8\n\n- Allowing optional default values on ZodOptional\n\n### zod@3.0.0-alpha.5\n\nMarch 17, 2021\n\n- Refactored parsing logic into individual subclass methods\n- Eliminated ZodTypes to enable custom ZodType subclasses\n- Removed ZodIntersection\n- Added ZodEffects as a container for refinement and transform logic\n- Added `or` method to `ZodType`\n- Added `format` method to `ZodError`\n- Added `unwrap` method to `ZodOptional` and `ZodNullable`\n- Added new `default` method and moved default functionality into ZodOptional\n- Implemented `z.setErrorMap`\n- Exporting `z` variable from `index.ts` to enable `import { z } from 'zod';`\n\n### zod@3.0.0-alpha.4\n\nJan 25, 2021\n\n- New implementation of transformers\n- Removed type guards\n\n### zod@2\n\n- Added ZodTransformer\n- Async refinements\n\n### zod@1.11\n\n- Introduced `.safeParse` option\n- Introduced .regex method on string schemas\n- Implemented `.primitives()` and `.nonprimitives()` on object schemas\n- Implemented `z.nativeEnum()` for creating schemas from TypeScript `enum`s\n- Switched to `new URL()` constructor to check valid URLs\n\n### zod@1.10\n\n- Dropping support for TypeScript 3.2\n\n### zod@1.9\n\n- Added z.instanceof() and z.custom()\n- Implemented ZodSchema.array() method\n\n### zod@1.8\n\n- Introduced z.void()\n- Major overhaul to error handling system, including the introduction of custom error maps\n- Wrote new [error handling guide](./ERROR_HANDLING.md)\n\n### zod@1.7\n\n- Added several built-in validators to string, number, and array schemas\n- Calls to `.refine` now return new instance\n\n### zod@1.5\n\n- Introduces ZodAny and ZodUnknown\n\n### zod@1.4\n\n- Refinement types (`.refine`)\n- Parsing no longer returns deep clone\n\n### zod@1.3\n\n- Promise schemas\n\n### zod@1.2.6\n\n- `.parse` accepts `unknown`\n- `bigint` schemas\n\n### zod@1.2.5\n\n- `.partial` and `.deepPartial` on object schemas\n\n### zod@1.2.3\n\n- Added ZodDate\n\n### zod@1.2.0\n\n- Added `.pick`, `.omit`, and `.extend` on object schemas\n\n### zod@1.1.0\n\n- Added ZodRecord\n\n### zod@1.0.11\n\n- Added `.nonstrict`\n\n### zod@1.0.10\n\n- Added type assertions with `.check`\n\n### zod@1.0.4\n\n- Support for empty tuples\n\n### zod@1.0.2\n\n- Added type assertions\n- Added ZodLiteral\n- Added ZodEnum\n- Improved error reporting\n\n### zod@1.0.0\n\n- Initial release\n"
|
|
1211
|
-
},
|
|
1212
1206
|
{
|
|
1213
1207
|
"title": "Changelog",
|
|
1214
1208
|
"filename": "Changelog.md",
|
|
1215
1209
|
"path": ".compat-tests/superstruct/Changelog.md",
|
|
1216
1210
|
"text": "# Changelog\n\nThis document maintains a list of major changes to Superstruct with each new release.\n\n### `2.0.0` - July 3, 2024\n\nSome changes in Superstruct v2.0 are _potentially_ breaking if you were using the library in unusual and/or undocumented ways. Since it has been almost 2 years since the last significant release, we want to make sure that we don't ruin someone's day by surprising them with a fix that changes [buggy behavior they were relying on](https://xkcd.com/1172/). For this reason, some changes that would ordinarily be considered a fix are marked as breaking.\n\n**:rocket: For the absolute majority of users, this should be a smooth upgrade that will not require any changes.**\n\n#### Breaking\n\n- Validation now correctly fails when arrays are passed to `object()`, `type()`, and `record()` structs.\n- When coercing an `object()` (via `mask()`, `create()` or `validate()` with the `coerce: true` option), arrays will no longer be automatically converted to objects with indexes as keys. [See this PR comment](https://github.com/ianstormtaylor/superstruct/pull/1196#issuecomment-1858924264).\n\n#### New\n\n- The library and its TypeScript typings are now compatible with NodeNext/Node16 module resolution. This means that if you are using the library with TypeScript and ECMAScript modules at the same time, you should no longer run into issues!\n\n#### Fixed\n\n- Using `mask()` with `union()` now correctly masks union members instead of incorrectly failing validation.\n\n#### Deprecations\n\n- Use with Node.js v14 is now deprecated. Due to incompatible tooling, we are no longer able to test Superstruct on this version of Node.js. Use at your own risk.\n\n#### New Maintainers & Next Steps\n\nProject maintenance has moved to a new volunteer team: [@arturmuller](https://github.com/arturmuller) and [@yeoffrey](https://github.com/yeoffrey). Hello there! :wave: We are currently going through the existing issues and PRs, trying to resolve or close the backlog. This might take a little while so please bear with us.\n\nIf you are interested in **contributing** — or helping us process the backlog — we would love your help. Don't hesitate to help us triage, open an issue, or submit a PR. You can also join our Superstruct maintainers Discord: https://discord.gg/pdHrQBjQ96.\n\nIf you have **questions, suggestions, or are just not sure about something** related to Superstruct, head over to GitHub Discussions! We have recently enabled this feature to help us differentiate between actual issues/bugs and everything else. We hope this will be a great new place where Superstruct users can get quick help from us — the maintainers — but also from the community as a whole.\n\nSee you at the next release! :v:\n\n### `1.0.0` — November 17, 2022\n\n###### NEW\n\n**Added an optional `message` argument to override error messages.** You can now pass in a `message` argument to all of the error checking functions which will override any error message with your own message. If you do, Superstruct's original descriptive message will still be accessible via [`error.cause`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause).\n\n```ts\nassert(data, User, 'The user is invalid!')\n// StructError: The user is invalid!\n```\n\n### BREAKING\n\n**No breaking changes.** This just marks Superstruct being stable for a long time, so better to move to a non `0.*` numbering scheme.\n\n### `0.16.0` — June 6, 2022\n\n###### BREAKING\n\n**Refinement functions are now called with valid, but potentially unrefined values.** Previously the functions passed in to `refine` would always be called with sub-elements (eg. when using objects or arrays) that were completely valid **and** refined. However, this prevented collecting all the refinement errors from subelements in a validation in one go, which is common when validating forms. _Note: this should not have any affect on almost all use cases, so you're safe to upgrade._\n\n### `0.15.0` — March 25, 2021\n\n###### FIXED\n\n**Unions can now be coerced.** Previously unions created a barrier in coercion such that structs nested inside unions would not have their coercion logic triggered, but this has been fixed.\n\n**Assigning preserves `type` structs.** Previously using the `assign` helper would implicitly convert `type` structs into `object` structs which wasn't expected and confusing, but this has been fixed.\n\n### `0.14.0` — January 26, 2021\n\n###### BREAKING\n\n**The `mask` helper now works for nested objects.** Previously it would only mask the properties at the top-level of a struct, however now it acts deeply. You can use it to define object structs once, but use them either strictly or loosely.\n\n**The `masked` coercion has been removed.** This previously allowed you to mix in masking to a specific struct, but the `mask` helper is a more robust way to do this, and it doesn't force you to maintain two separate structs.\n\n### `0.13.0` — December 11, 2020\n\n###### NEW\n\n**Structs can now define an `entries` iterator for nested values.** Previously iterating through nested values was defined in a one-off manner inside certain structs, but this led to non-uniform support. Now, any struct can define an `entries` iterator that will cause nested values to be automatically coerced and validated.\n\n**Coercion receives `context` objects and supports nested values.** Previously context objects were only passed to the validations and refinements. But now the same context is passed to coercions too so you can implement more complex logic. And coercions are automatically applied to nested values thanks to the addition of `entries`.\n\n**Iteration logic has gotten simpler, and more performant.** The addition of the `entries` logic has enabled us to only ever iterate through a tree of values one time for coercion and validation, instead of once each. This should speed up most standard use cases.\n\n###### BREAKING\n\n**The `ctx.fail()` function has been removed.** Previously you'd use it to return more information about a failure inside a struct. Now you can simply return a partial failure object.\n\n**The `ctx.check()` function has been removed.** Previously you'd use it to validate nested objects in more complex struct shapes. Now you can use the new `entries` property for this instead.\n\n**The `context.struct` and `context.value` properties have been removed.** These properties were previously available, but unnecessary since anywhere you have the context object you will also know the `value` and the specific struct that is being validated. Keeping them around required extra unnecessary plumbing in the library that made composing structs much more difficult so they were removed.\n\n### `0.12.0` — November 24, 2020\n\n###### NEW\n\n**New `Describe` utility type.** This new utility lets you define a struct from an existing TypeScript type and ensure that the struct's validation matches it, otherwise TypeScript's compiler will error. For example:\n\n```ts\ntype User = {\n id: number\n name: string\n}\n\nconst User: Describe<User> = object({\n id: string(), // This mistake will fail to pass type checking!\n name: string(),\n})\n```\n\n###### BREAKING\n\n**The `coerce` helper has changed to be more type-safe!** Previously `coerce` functions were called with `value: unknown` because they ran before all validation. However, now they take a new second argument that is another struct to narrow the cases where coercions occurs. This means the `value` for coercion will now be type-safe.\n\n```ts\n// Previously\nconst MyNumber = coerce(number(), (value) => {\n return typeof value === 'string' ? parseFloat(value) : value\n})\n\n// Now\nconst MyNumber = coerce(number(), string(), (value) => {\n return parseFloat(value)\n})\n```\n\n### `0.11.0` — November 20, 2020\n\n###### NEW\n\n**New `assign`, `pick`, and `omit` object utilities.** These utilities make composing object structs together possible, which should make re-using structs in your codebase easier.\n\n```ts\n// Combine two structs with `assign`:\nconst a = object({ id: number() })\nconst b = object({ name: string() })\nconst c = assign([a, b])\n\n// Pick out specific properties with `pick`:\nconst a2 = pick(c, ['id'])\n\n// Omit specific properties with `omit`:\nconst a3 = omit(c, ['name'])\n```\n\n**New `unknown` struct.** This is the same as the existing `any` struct, but it will ensure that in TypeScript the value is of the more restrictive `unknown` type so it encourages better type safety.\n\n```ts\nconst Shape = type({\n id: number(),\n name: string(),\n other: unknown(),\n})\n```\n\n**New `integer`, `regexp`, and `func` structs.** These are just simple additions for common use cases of ensuring a value is an integer, a regular expression object (not a string!), or a function.\n\n```ts\nconst Shape = type({\n id: integer(),\n matches: regexp(),\n send: func(),\n})\n```\n\n**New `max/min` refinements.** For refining `number` (or `integer`) or `date` structs to ensure they are greater than or less than a specific threshold. The third argument can indicate whether to make the threshold exclusive (instead of the default inclusive).\n\n```ts\nconst Index = min(number(), 0)\nconst PastOrPresent = max(date(), new Date())\nconst Past = max(date(), new Date(), { exclusive: true })\n```\n\n**Even more information on errors.** Errors now expose the `error.refinement` property when the failure originated in a refinement validation. And they also now have an `error.key` property which is the key for the failure in the case of complex values like arrays/objects. (Previously the key was retrievable by checking `error.path`, but this will make the 90% case easier.)\n\n###### BREAKING\n\n**The `coerce` helper has been renamed to `create`.** This will hopefully make it more clear that it's fully coercing and validating a value against a struct, throwing errors if the value was invalid. This has caused confusion for people who though it would just coerce the value and return the unvalidated-but-coerced version.\n\n```ts\n// Previously\nconst user = coerce(data, User)\n\n// Now\nconst user = create(data, User)\n```\n\n**The `struct`, `refinement` and `coercion` factories have been renamed.** This renaming is purely for keeping things slightly cleaner and easier to understand. The new names are `define`, `refine`, and `coerce`. Separating them slightly from the noun-based names used for the types themselves.\n\n```ts\n// Previously\nconst Email = struct('email', isEmail)\nconst Positive = refinement('positive', number(), n => n > 0)\nconst Trimmed = coercion(string(), s => s.trim()\n\n// Now\nconst Email = define('email', isEmail)\nconst Positive = refine(number(), 'positive', n => n > 0)\nconst Trimmed = coerce(string(), s => s.trim())\n```\n\n_Note that the order of `refine` arguments has changed to be slightly more natural, and encourage scoped refinement names._\n\n**The `length` refinement has been renamed to `size`.** This is to match with the expansion of it's abilities from purely strings and arrays to also now include numbers, maps, and sets. In addition you can also omit the `max` argument to specify an exact size:\n\n```ts\n// Previously\nconst Name = length(string(), 1, 100)\nconst MyArray = length(array(string()), 3, 3)\n\n// Now\nconst Name = size(string(), 1, 100)\nconst MyArray = size(array(string()), 3)\nconst Id = size(integer(), 1, Infinity)\nconst MySet = size(set(), 1, 9)\n```\n\n**The `StructType` inferring helper has been renamed to `Infer`.** This just makes it slightly easier to read what's going on when you're inferring a type.\n\n```ts\n// Previously\ntype User = StructType<typeof User>\n\n// Now\ntype User = Infer<typeof User>\n```\n\n**The `error.type` property has been standardized.** Previously it was a human-readable description that sort of incorporated the schema. Now it is simple the plain lowercase name of the struct in question, making it something you can use programmatically when formatting errors.\n\n```ts\n// Previously\n'Array<string>'\n'[string,number]'\n'Map<string,number>'\n\n// Now\n'array'\n'tuple'\n'map'\n```\n\n### `0.10.0` — June 6, 2020\n\nThe `0.10` version is a complete overhaul with the goal of making Superstruct much simpler and easier to understand, and with complete support for runtime type signatures TypeScript.\n\nThis makes it much more powerful, however the core architecture has had to change to make it happen. It will still look very similar, but migrating between the versions _will be more work than usual_. There's no requirement to upgrade, although if you're using Superstruct in concert with TypeScript you will have a much better experience.\n\n###### BREAKING\n\n**All types are created from factories.** Previously depending on whether the type was a complex type or a scalar type they'd be defined different. Complex types used factories, whereas scalars used strings. Now all types are exposed as factories.\n\nFor example, previously:\n\n```ts\nimport { struct } from 'superstruct'\n\nconst User = struct.object({\n name: 'string',\n age: 'number',\n})\n```\n\nNow becomes:\n\n```ts\nimport { object, string, number } from 'superstruct'\n\nconst User = object({\n name: string(),\n age: number(),\n})\n```\n\n**Custom scalars are no longer pre-defined as strings.** Previously, you would define all of your \"custom\" types in a single place in your codebase and then refer to them in structs later on with a string value. This worked, but added a layer of unnecessary indirection, and made it impossible to accomodate runtime type signatures.\n\nIn the new version, custom types are defined extremely similarly to non-custom types. And this has the added benefit that you can easily trace the custom type definitions by just following `import` statements.\n\nHere's how it used to work:\n\n```ts\nimport { superstruct } from 'superstruct'\nimport isEmail from 'is-email'\n\nconst struct = superstruct({\n types: {\n email: isEmail,\n },\n})\n\nconst Email = struct('email')\n```\n\nAnd here's what it would look like now:\n\n```ts\nimport { struct } from 'superstruct'\nimport isEmail from 'is-email'\n\nconst Email = struct('email', isEmail)\n```\n\n**Validation logic has been moved to helper functions.** Previously the `assert` and `is` helpers lived on the struct objects themselves. Now, these functions have been extracted into separate helpers. This was unfortunately necessary to work around limitations in TypeScript's `asserts` keyword.\n\nFor example, before:\n\n```ts\nUser.assert(data)\n```\n\nNow would be:\n\n```ts\nimport { assert } from 'superstruct'\n\nassert(data, User)\n```\n\n**Coercion is now separate from validation.** Previously there was native logic for handling default values for structs when validating them. This has been abstracted into the ability to define _any_ custom coercion logic for structs, and it has been separate from validation to make it very clear when data can change and when it cannot.\n\nFor example, previously:\n\n```ts\nconst output = User.assert(input)\n```\n\nWould now be:\n\n```ts\nconst input = coerce(input, User)\n```\n\nThe `coerce` step is the only time that data will be transformed at all by coercion logic, and the `assert` step no longer needs to return any values. This makes it easy to do things like:\n\n```ts\nif (is(input, User)) {\n // ...\n}\n```\n\n**Validation context is now a dictionary of properties.** Previously when performing complex validation logic that was dependent on other properties on the root object, you could use the second `branch` argument to the validation function. This argument has been changed to be a `context` dictionary with more information. The same branch argument can now be accessed as `context.branch`, along with the new information.\n\n**Unknown properties of objects now have a `'never'` type.** Previously unknown properties would throw errors with `type === null`, however the newly introduced `'never'` type is now used instead.\n\n**Defaults are now defined with a separate coercion helper.** Previously all structs took a second argument that defined the default value to use if an `undefined` value was present. This has been pulled out into a separate helper now to clearly distinguish coercion logic.\n\nFor example, previously you'd do:\n\n```ts\nconst Article = struct.object(\n {\n title: 'string',\n },\n {\n title: 'Untitled',\n }\n)\n```\n\nWhereas now you'd do:\n\n```ts\nconst Article = defaulted(\n object({\n title: string(),\n }),\n {\n title: 'Untitled',\n }\n)\n```\n\n**Optional arguments are now defined with a seperate factory.** Similarly to defaults, there is a new `optional` factory for defined values that can also be `undefined`.\n\nPreviously you'd do:\n\n```ts\nconst Flag = struct('string?')\n```\n\nNow you'd do:\n\n```ts\nconst Flag = optional(string())\n```\n\n**Several structs have been renamed.** This was necessary because structs are now exposed directly as variables, which runs afoul of reserved words. So the following renames have been applied:\n\n- `interface` -> `type`\n- `enum` -> `enums`\n- `function` -> `func`\n\n### `0.8.0` — October 8, 2019\n\n###### BREAKING\n\n**Several structs have been renamed!** Superstruct tries to mimic established naming schemes whenever possible for its API, and TypeScript is one of our main comparisons. To make things easier for people, we've renamed a few structs to more closely match their TypeScript counterparts:\n\n- The `list` struct is now called `array`.\n- The `partial` struct is now called `pick`.\n- The `dict` struct is now called `record`.\n\nHopefully this will make them easier to understand at a glance!\n\n**The `enums` struct has been removed!** This was special-cased in the API previously, but you can get the exact same behavior by creating an using the `array` and `enum` structs:\n\n```js\nstruct.array(struct.enum(['red', 'blue', 'green']))\n```\n\n**The `any` struct has been removed! (Not the scalar though.)** Previously `struct.any()` was exposed that did the same thing as `struct()`, allowing you to use shorthands for common structs. But this was confusingly named because it has nothing to do with the `'any'` scalar type. And since it was redundant it has been removed.\n\n**The `interface` struct now returns the original, unaltered value!** In an effort to make things more familiar, the `interface` struct now always returns the object that it is called with when it passes validation. So if the object was a function, a function will be returned. This makes it match more closely with the idea of \"structural typing\" that TypeScript and other typing systems are based on. \\_If you want the old behavior, use the `pick` struct.\n\n**Computed values function signatures have changed!** Previously a computed value would be called with a signature of `(value, root)` in some cases and `(value, parent)` in others. This was confusing, and the cause for the inconsistency was complex. This logic has been simplified, and now computed values are called with `(value, branch, path)` in all cases.\n\n```js\nstruct.dynamic((value, branch, path) => {\n value === branch[branch.length - 1] // you can get the value...\n const parent = branch[branch.length - 2] // ...and the parent...\n const key = path[path.length - 1] // ...and the key...\n value === parent[key]\n const root = branch[0] // ...and the root!\n})\n```\n\nThe `path` is an array of keys representing the nested value's location in the root value. And the `branch` is an array of all of the sub values along the path to get to the current one. This allows you to always be able to receive both the **parent** and the **root** values from any location—as well as any value in between.\n\n**The `error.errors` property has been renamed `error.failures`, and isn't cyclical.** It being cyclical caused lots of issues whenever an `StructError` object was attempted to be serialized. And the `errors` property was slightly confusing because the elements of the array weren't full error objects. The new structure is easier to understand and work with.\n\n**The `error.reason` property is no longer special-cased.** Previously you could return a \"reason\" string from validator functions and it would be added to error objects. However, now you must return an error properties object (with a `reason` property if you'd like), and all of the properties will be added to the error object. This makes Superstruct even more flexible as far as custom error details go.\n\n**The `type` property of structs have been rewritten to be more clear.** This is an implementation mostly, but the `struct.type` string which shows up in error messages have been tweaked to be slightly more clear exactly what type they are checking for.\n\n###### NEW\n\n**Superstruct is now written in TypeScript.** It was rewritten from the ground up to make use of types, and to have better inline documented if you use a TypeScript-compatible IDE. There are probably improvements that can be made, so if you'd like to contribute please do!\n\n**A new `partial` struct mimics TypeScript's `Partial` utility.** The new struct validates that its input partially matches an object defined as a set of properties with associated types. All of the properties of the object are optional.\n\n**A new `size` struct allows validating array and string lengths.** The new struct validates that its input has a certain size, by checking its `length` property. This works strings or arrays.\n\n**You can now provide a custom `Error` setting.** By passing in your own constructor when configuring Superstruct you can have complete control over the exact errors that are generated by structs that fail validation.\n\n### `0.7.0` — September 21, 2019\n\n###### BREAKING\n\n- **The build process now outputs ES5 code.** Previously it was outputting ES6 code, which posed problems for some builders. This change shouldn't really affect anyone negatively, but it's being released as a breaking version just in case.\n\n---\n\n### `0.6.0` — September 13, 2018\n\n###### BREAKING\n\n- **Invalid `Date` objects are now considered invalid.** Previously using the built-in `'date'` validator would only check that the object was a `Date` instance, and not that it was a valid one. This has been fixed, and although it is technically a breaking change, most everyone would have expected this behavior to begin with.\n\n---\n\n### `0.5.0` — December 21, 2017\n\n###### BREAKING\n\n- **Validators must now return `true`, `false` or an error reason string.** Previously any truthy value would be considered valid. Now you can provide more information for the thrown errors by providing a string which will be attached as `error.reason`. However, this means that truthy string values now equate to invalid, not valid.\n\n- **Property validators now receive `data` as their second argument.** Previously you only had access to the property `value`, but now you also have access to the entire object's `data`.\n\n###### NEW\n\n- **Errors can now contain reason information.** Validator functions can now return string instead of a boolean, denoting the reason a value was invalid. This can then be used to create more helpful error messages.\n\n---\n\n### `0.4.0` — December 1, 2017\n\n###### BREAKING\n\n- **`object` structs are no longer optional-ish.** Previously object struct types would not throw if `undefined` was passed and no properties were required. This was not only confusing, but complex to maintain. Now if you want an object struct to be optional, use the `struct.optional(...)` helper.\n\n- **Removed the `Struct.default` method.** If you need to get the default value, use the `Struct.validate` or `Struct.assert` methods's return value instead.\n\n###### NEW\n\n- **Added the `dict`, `enum`, `intersection`, `union` and `tuple` structs.** These are all available as `struct.dict`, `struct.enum`, etc.\n\n---\n\n### `0.3.0` — November 30, 2017\n\n###### BREAKING\n\n- **The `validate()` method now returns `[ error, result ]`.** Previously it only had a single return value, which necessitated extra type checking to see if the value was an error or a result. Now you can just destructure the array to get either return value, for easier coding.\n\n- **Errors have been simplified, removing \"codes\".** Previously there were multiple types of errors that were thrown and you could differentiate between them with the `error.code` property. But the other properties of the error already let you infer the code, so having multiple types of errors made for a larger API surface without much benefit.\n\n---\n\n### `0.2.0` — November 30, 2017\n\n###### BREAKING\n\n- **Structs are now functions again.** :smile: They are built on the same underlying schema classes underneath though, since that helps the code structure. But to allow for the `struct = Struct({ ... })` syntax the structs themselves have changed to be function.\n\n###### NEW\n\n- **The basic case is now `Struct(data)`.** Previously you had to use `Struct.assert(data)`. Although the `assert` method (and others) are still there, the basic case is a bit terser and more similar to the struct-initializing APIs in other languages.\n\n---\n\n### `0.1.0` — November 29, 2017\n\n###### BREAKING\n\n- **Structs are now classes instead of functions.** This is better in terms of the API being a bit less magic-y. It's also useful so that we can add other helpful methods to structs besides the `assert` method. What was previously `struct(data)` is now `struct.assert(data)`.\n\n---\n\n### `0.0.0` — November 24, 2017\n\n:tada:\n"
|
|
1217
1211
|
},
|
|
1212
|
+
{
|
|
1213
|
+
"title": "Changelog",
|
|
1214
|
+
"filename": "CHANGELOG.md",
|
|
1215
|
+
"path": ".compat-tests/zod/packages/docs-v3/CHANGELOG.md",
|
|
1216
|
+
"text": "# Changelog\n\n## Release notes are now stored in Github Releases: https://github.com/colinhacks/zod/releases\n\n## Previous Releases\n\n### 3.10\n\n- New parser that allows parsing to continue after non-fatal errors have occurred. This allows Zod to surface more errors to the user at once.\n\n### 3.9\n\n- Custom error messages in schemas\n\n```ts\nconst name = z.string({\n invalid_type_error: 'Name must be string',\n required_error: 'Name is required',\n})\n```\n\nUnder the hood, this creates a custom error map that's bound to the schema. You can also pass a custom error map explicitly.\n\n```ts\nconst name = z.string({ errorMap: myErrorMap })\n```\n\n- Rest parameters for tuples\n\n```ts\nconst myTuple = z.tuple([z.string(), z.number()]).rest(z.boolean())\ntype t1 = z.output<typeof myTuple> // [string, number, ...boolean[]]\n```\n\n- Selective `.partial`\n\nYou can specify certain fields to make optional with the `ZodObject.partial` method.\n\n```ts\nconst user = z.object({\n name: z.string(),\n age: z.number(),\n})\n\nconst optionalNameUser = user.partial({ name: true })\n// { name?: string; age: number; }\n```\n\n- Specify key schema in ZodRecord\n\nPreviously, `z.record` only accepted a single schema:\n\n```ts\nz.record(z.boolean()) // Record<string, boolean>;\n```\n\nNow `z.record` has been overloaded to support two schemas. The first validates the _keys_ of the record, and the second validates the _values_.\n\n```ts\nconst schema = z.record(z.number(), z.boolean())\ntype schema = z.infer<typeof schema> // Record<number, boolean>\n\nconst schema = z.record(z.enum(['Tuna', 'Trout']), z.boolean())\ntype schema = z.infer<typeof schema> // Record<\"Tuna\" | \"Trout\", boolean>\n```\n\n### 3.8\n\n- Add `z.preprocess`\n- Implement CUID validation on ZodString (`z.string().cuid()`)\n- Improved `.deepPartial()`: now recursively operates on arrays, tuples, optionals, and nullables (in addition to objects)\n\n### 3.7\n\n- Eliminate `ZodNonEmptyArray`, add `Cardinality` to `ZodArray`\n- Add optional error message to `ZodArray.nonempty`\n- Add `.gt/.gte/.lt/.lte` to `ZodNumber`\n\n### 3.6\n\n- Add IE11 support\n- `ZodError.flatten` now optionally accepts a map function for customizing the output\n- `.void()` now only accepts undefined, not null.\n- `z.enum` now supports `Readonly` string tuples\n\n### 3.5\n\n- Add discriminator to all first-party schema defs\n\n### 3.4\n\n- `unknown` and `any` schemas are always interpreted as optional. Reverts change from 3.3.\n\n### 3.3\n\n- HUGE speed improvements\n- Added benchmarking: `yarn benchmark`\n- Type signature of `ZodType#_parse` has changed. This will affects users who have implemented custom subclasses of `ZodType`.\n- [reverted] Object fields of type `unknown` are no longer inferred as optional.\n\n### 3.2\n\n- Certain methods (`.or`, `.transform`) now return a new instance that wrap the current instance, instead of trying to avoid additional nesting. For example:\n\n```ts\nz.union([z.string(), z.number()]).or(z.boolean())\n// previously\n// => ZodUnion<[ZodString, ZodNumber, ZodBoolean]>\n\n// now\n// => ZodUnion<[ZodUnion<[ZodString, ZodNumber]>, ZodBoolean]>\n```\n\nThis change was made due to recursion limitations in TypeScript 4.3 that made it impossible to properly type these methods.\n\n### 3.0.0-beta.1\n\n- Moved default value logic into ZodDefault. Implemented `.nullish()` method.\n\n### 3.0.0-alpha.33\n\n- Added `.returnType` and `.parameters` methods to ZodFunction\n\n### 3.0.0-alpha.32\n\n- Added `.required()` method to ZodObject\n\n### 3.0.0-alpha.30\n\n- Added Rollup for bundling ESM module\n\n### zod@3.0.0-alpha.24\n\n- Added back ZodIntersection\n- Added .and() method to base class\n\n### zod@3.0.0-alpha.9\n\n- Added `z.strictCreate`\n\n### zod@3.0.0-alpha.8\n\n- Allowing optional default values on ZodOptional\n\n### zod@3.0.0-alpha.5\n\nMarch 17, 2021\n\n- Refactored parsing logic into individual subclass methods\n- Eliminated ZodTypes to enable custom ZodType subclasses\n- Removed ZodIntersection\n- Added ZodEffects as a container for refinement and transform logic\n- Added `or` method to `ZodType`\n- Added `format` method to `ZodError`\n- Added `unwrap` method to `ZodOptional` and `ZodNullable`\n- Added new `default` method and moved default functionality into ZodOptional\n- Implemented `z.setErrorMap`\n- Exporting `z` variable from `index.ts` to enable `import { z } from 'zod';`\n\n### zod@3.0.0-alpha.4\n\nJan 25, 2021\n\n- New implementation of transformers\n- Removed type guards\n\n### zod@2\n\n- Added ZodTransformer\n- Async refinements\n\n### zod@1.11\n\n- Introduced `.safeParse` option\n- Introduced .regex method on string schemas\n- Implemented `.primitives()` and `.nonprimitives()` on object schemas\n- Implemented `z.nativeEnum()` for creating schemas from TypeScript `enum`s\n- Switched to `new URL()` constructor to check valid URLs\n\n### zod@1.10\n\n- Dropping support for TypeScript 3.2\n\n### zod@1.9\n\n- Added z.instanceof() and z.custom()\n- Implemented ZodSchema.array() method\n\n### zod@1.8\n\n- Introduced z.void()\n- Major overhaul to error handling system, including the introduction of custom error maps\n- Wrote new [error handling guide](./ERROR_HANDLING.md)\n\n### zod@1.7\n\n- Added several built-in validators to string, number, and array schemas\n- Calls to `.refine` now return new instance\n\n### zod@1.5\n\n- Introduces ZodAny and ZodUnknown\n\n### zod@1.4\n\n- Refinement types (`.refine`)\n- Parsing no longer returns deep clone\n\n### zod@1.3\n\n- Promise schemas\n\n### zod@1.2.6\n\n- `.parse` accepts `unknown`\n- `bigint` schemas\n\n### zod@1.2.5\n\n- `.partial` and `.deepPartial` on object schemas\n\n### zod@1.2.3\n\n- Added ZodDate\n\n### zod@1.2.0\n\n- Added `.pick`, `.omit`, and `.extend` on object schemas\n\n### zod@1.1.0\n\n- Added ZodRecord\n\n### zod@1.0.11\n\n- Added `.nonstrict`\n\n### zod@1.0.10\n\n- Added type assertions with `.check`\n\n### zod@1.0.4\n\n- Support for empty tuples\n\n### zod@1.0.2\n\n- Added type assertions\n- Added ZodLiteral\n- Added ZodEnum\n- Improved error reporting\n\n### zod@1.0.0\n\n- Initial release\n"
|
|
1217
|
+
},
|
|
1218
1218
|
{
|
|
1219
1219
|
"title": "CLAUDE.md",
|
|
1220
1220
|
"filename": "CLAUDE.md",
|
|
@@ -1386,14 +1386,14 @@
|
|
|
1386
1386
|
{
|
|
1387
1387
|
"title": "Introduction",
|
|
1388
1388
|
"filename": "README.md",
|
|
1389
|
-
"path": ".compat-tests/effect/packages/
|
|
1390
|
-
"text": "# Introduction\n\nWelcome to your guide on testing Effect-based applications using `vitest` and the `@effect/vitest` package. This package simplifies running tests for Effect-based code with Vitest.\n\nIn this guide, we'll walk you through setting up the necessary dependencies and provide examples of how to write Effect-based tests using `@effect/vitest`.\n\n# Requirements\n\nFirst, ensure you have [`vitest`](https://vitest.dev/guide/) installed (version `1.6.0` or later).\n\n```sh\npnpm add -D vitest\n```\n\nNext, install the `@effect/vitest` package, which integrates Effect with Vitest.\n\n```sh\npnpm add -D @effect/vitest\n```\n\n# Overview\n\nThe main entry point is the following import:\n\n```ts\nimport { it } from \"@effect/vitest\"\n```\n\nThis import enhances the standard `it` function from `vitest` with several powerful features, including:\n\n| Feature | Description |\n| --------------- | ------------------------------------------------------------------------------------------------------ |\n| `it.effect` | Automatically injects a `TestContext` (e.g., `TestClock`) when running a test. |\n| `it.live` | Runs the test with the live Effect environment. |\n| `it.scoped` | Allows running an Effect program that requires a `Scope`. |\n| `it.scopedLive` | Combines the features of `scoped` and `live`, using a live Effect environment that requires a `Scope`. |\n| `it.flakyTest` | Facilitates the execution of tests that might occasionally fail. |\n\n# Writing Tests with `it.effect`\n\nHere's how to use `it.effect` to write your tests:\n\n**Syntax**\n\n```ts\nimport { it } from \"@effect/vitest\"\n\nit.effect(\"test name\", () => EffectContainingAssertions, timeout: number | TestOptions = 5_000)\n```\n\n`it.effect` automatically provides a `TestContext`, allowing access to services like [`TestClock`](#using-the-testclock).\n\n## Testing Successful Operations\n\nTo write a test, place your assertions directly within the main effect. This ensures that your assertions are evaluated as part of the test's execution.\n\n**Example** (Testing a Successful Operation)\n\nIn the following example, we test a function that divides two numbers, but fails if the divisor is zero. The goal is to check that the function returns the correct result when given valid input.\n\n```ts\nimport { it, expect } from \"@effect/vitest\"\nimport { Effect } from \"effect\"\n\n// A simple divide function that returns an Effect, failing when dividing by zero\nfunction divide(a: number, b: number) {\n if (b === 0) return Effect.fail(\"Cannot divide by zero\")\n return Effect.succeed(a / b)\n}\n\n// Testing a successful division\nit.effect(\"test success\", () =>\n Effect.gen(function* () {\n const result = yield* divide(4, 2) // Expect 4 divided by 2 to succeed\n expect(result).toBe(2) // Assert that the result is 2\n })\n)\n```\n\n## Testing Successes and Failures as `Exit`\n\nWhen you need to handle both success and failure cases in a test, you can use `Effect.exit` to capture the outcome as an `Exit` object. This allows you to verify both successful and failed results within the same test structure.\n\n**Example** (Testing Success and Failure with `Exit`)\n\n```ts\nimport { it, expect } from \"@effect/vitest\"\nimport { Effect, Exit } from \"effect\"\n\n// A function that divides two numbers and returns an Effect.\n// It fails if the divisor is zero.\nfunction divide(a: number, b: number) {\n if (b === 0) return Effect.fail(\"Cannot divide by zero\")\n return Effect.succeed(a / b)\n}\n\n// Test case for a successful division, using `Effect.exit` to capture the result\nit.effect(\"test success as Exit\", () =>\n Effect.gen(function* () {\n const result = yield* Effect.exit(divide(4, 2)) // Capture the result as an Exit\n expect(result).toStrictEqual(Exit.succeed(2)) // Expect success with the value 2\n })\n)\n\n// Test case for a failure (division by zero), using `Effect.exit`\nit.effect(\"test failure as Exit\", () =>\n Effect.gen(function* () {\n const result = yield* Effect.exit(divide(4, 0)) // Capture the result as an Exit\n expect(result).toStrictEqual(Exit.fail(\"Cannot divide by zero\")) // Expect failure with the correct message\n })\n)\n```\n\n## Using the TestClock\n\nWhen writing tests with `it.effect`, a `TestContext` is automatically provided. This context gives access to various testing services, including the [`TestClock`](https://effect.website/docs/guides/testing/testclock), which allows you to simulate the passage of time in your tests.\n\n**Note**: If you want to use the real-time clock (instead of the simulated one), you can switch to `it.live`.\n\n**Example** (Using `TestClock` and `it.live`)\n\nHere are examples that demonstrate how you can work with time in your tests using `it.effect` and `TestClock`:\n\n1. **Using `it.live` to show the current time**: This will display the actual system time, since it runs in the live environment.\n\n2. **Using `it.effect` without adjustments**: By default, the `TestClock` starts at `0`, simulating the beginning of time for your test without any time passing.\n\n3. **Using `it.effect` and adjusting time**: In this test, we simulate the passage of time by advancing the clock by 1000 milliseconds (1 second).\n\n```ts\nimport { it } from \"@effect/vitest\"\nimport { Clock, Effect, TestClock } from \"effect\"\n\n// Effect to log the current time\nconst logNow = Effect.gen(function* () {\n const now = yield* Clock.currentTimeMillis // Fetch the current time from the clock\n console.log(now) // Log the current time\n})\n\n// Example of using the real system clock with `it.live`\nit.live(\"runs the test with the live Effect environment\", () =>\n Effect.gen(function* () {\n yield* logNow // Prints the actual current time\n })\n)\n\n// Example of using `it.effect` with the default test environment\nit.effect(\"run the test with the test environment\", () =>\n Effect.gen(function* () {\n yield* logNow // Prints 0, as the test clock starts at 0\n })\n)\n\n// Example of advancing the test clock by 1000 milliseconds\nit.effect(\"run the test with the test environment and the time adjusted\", () =>\n Effect.gen(function* () {\n yield* TestClock.adjust(\"1000 millis\") // Move the clock forward by 1000 milliseconds\n yield* logNow // Prints 1000, reflecting the adjusted time\n })\n)\n```\n\n## Skipping Tests\n\nIf you need to temporarily disable a test but don't want to delete or comment out the code, you can use `it.effect.skip`. This is helpful when you're working on other parts of your test suite but want to keep the test for future execution.\n\n**Example** (Skipping a Test)\n\n```ts\nimport { it } from \"@effect/vitest\"\nimport { Effect, Exit } from \"effect\"\nimport { expect } from \"@effect/vitest\"\n\nfunction divide(a: number, b: number) {\n if (b === 0) return Effect.fail(\"Cannot divide by zero\")\n return Effect.succeed(a / b)\n}\n\n// Temporarily skip the test for dividing numbers\nit.effect.skip(\"test failure as Exit\", () =>\n Effect.gen(function* () {\n const result = yield* Effect.exit(divide(4, 0))\n expect(result).toStrictEqual(Exit.fail(\"Cannot divide by zero\"))\n })\n)\n```\n\n## Running a Single Test\n\nWhen you're developing or debugging, it's often useful to run a specific test without executing the entire test suite. You can achieve this by using `it.effect.only`, which will run just the selected test and ignore the others.\n\n**Example** (Running a Single Test)\n\n```ts\nimport { it } from \"@effect/vitest\"\nimport { Effect, Exit } from \"effect\"\nimport { expect } from \"@effect/vitest\"\n\nfunction divide(a: number, b: number) {\n if (b === 0) return Effect.fail(\"Cannot divide by zero\")\n return Effect.succeed(a / b)\n}\n\n// Run only this test, skipping all others\nit.effect.only(\"test failure as Exit\", () =>\n Effect.gen(function* () {\n const result = yield* Effect.exit(divide(4, 0))\n expect(result).toStrictEqual(Exit.fail(\"Cannot divide by zero\"))\n })\n)\n```\n\n## Expecting Tests to Fail\n\nWhen adding new failing tests, you might not be able to fix them right away. Instead of skipping them, you may want to assert it fails, so that when you fix them, you'll know and can re-enable them before it regresses.\n\n**Example** (Asserting one test fails)\n\n```ts\nimport { it } from \"@effect/vitest\"\nimport { Effect, Exit } from \"effect\"\n\nfunction divide(a: number, b: number): number {\n if (b === 0) return Effect.fail(\"Cannot divide by zero\")\n return Effect.succeed(a / b)\n}\n\n// Temporarily assert that the test for dividing by zero fails.\nit.effect.fails(\"dividing by zero special cases\", ({ expect }) =>\n Effect.gen(function* () {\n const result = yield* Effect.exit(divide(4, 0))\n expect(result).toStrictEqual(0)\n })\n)\n```\n\n## Logging\n\nBy default, `it.effect` suppresses log output, which can be useful for keeping test results clean. However, if you want to enable logging during tests, you can use `it.live` or provide a custom logger to control the output.\n\n**Example** (Controlling Logging in Tests)\n\n```ts\nimport { it } from \"@effect/vitest\"\nimport { Effect, Logger } from \"effect\"\n\n// This test won't display the log message, as logging is suppressed by default in `it.effect`\nit.effect(\"does not display a log\", () =>\n Effect.gen(function* () {\n yield* Effect.log(\"it.effect\") // Log won't be shown\n })\n)\n\n// This test will display the log because a custom logger is provided\nit.effect(\"providing a logger displays a log\", () =>\n Effect.gen(function* () {\n yield* Effect.log(\"it.effect with custom logger\") // Log will be displayed\n }).pipe(\n Effect.provide(Logger.pretty) // Providing a pretty logger for log output\n )\n)\n\n// This test runs using `it.live`, which enables logging by default\nit.live(\"it.live displays a log\", () =>\n Effect.gen(function* () {\n yield* Effect.log(\"it.live\") // Log will be displayed\n })\n)\n```\n\n# Writing Tests with `it.scoped`\n\nThe `it.scoped` method is used for tests that involve `Effect` programs needing a `Scope`. A `Scope` ensures that any resources your test acquires are managed properly, meaning they will be released when the test completes. This helps prevent resource leaks and guarantees test isolation.\n\n**Example** (Using `it.scoped` to Manage Resource Lifecycle)\n\n```ts\nimport { it } from \"@effect/vitest\"\nimport { Console, Effect } from \"effect\"\n\n// Simulating the acquisition and release of a resource with console logging\nconst acquire = Console.log(\"acquire resource\")\nconst release = Console.log(\"release resource\")\n\n// Defining a resource that requires proper management\nconst resource = Effect.acquireRelease(acquire, () => release)\n\n// Incorrect usage: This will result in a type error because it lacks a scope\nit.effect(\"run with scope\", () =>\n Effect.gen(function* () {\n yield* resource\n })\n)\n\n// Correct usage: Using 'it.scoped' to manage the scope correctly\nit.scoped(\"run with scope\", () =>\n Effect.gen(function* () {\n yield* resource\n })\n)\n```\n\n# Writing Tests with `it.flakyTest`\n\n`it.flakyTest` is a utility designed to manage tests that may not succeed consistently on the first attempt. These tests, often referred to as \"flaky,\" can fail due to factors like timing issues, external dependencies, or randomness. `it.flakyTest` allows for retrying these tests until they pass or a specified timeout is reached.\n\n**Example** (Handling Flaky Tests with Retries)\n\nLet's start by setting up a basic test scenario that has the potential to fail randomly:\n\n```ts\nimport { it } from \"@effect/vitest\"\nimport { Effect, Random } from \"effect\"\n\n// Simulating a flaky effect\nconst flaky = Effect.gen(function* () {\n const random = yield* Random.nextBoolean\n if (random) {\n return yield* Effect.fail(\"Failed due to randomness\")\n }\n})\n\n// Standard test that may fail intermittently\nit.effect(\"possibly failing test\", () => flaky)\n```\n\nIn this test, the outcome is random, so the test might fail depending on the result of `Random.nextBoolean`.\n\nTo handle this flakiness, we use `it.flakyTest` to retry the test until it passes, or until a defined timeout expires:\n\n```ts\n// Retrying the flaky test with a 5-second timeout\nit.effect(\"retrying until success or timeout\", () =>\n it.flakyTest(flaky, \"5 seconds\")\n)\n```\n"
|
|
1389
|
+
"path": ".compat-tests/effect/packages/platform/README.md",
|
|
1390
|
+
"text": "# Introduction\n\nWelcome to the documentation for `@effect/platform`, a library designed for creating platform-independent abstractions (Node.js, Bun, browsers).\n\n> [!WARNING]\n> This documentation focuses on **unstable modules**. For stable modules, refer to the [official website documentation](https://effect.website/docs/guides/platform/introduction).\n\n# Running Your Main Program with runMain\n\nDocs for `runMain` have been moved to the [official website](https://effect.website/docs/platform/runtime/).\n\n# HTTP API\n\n## Overview\n\nThe `HttpApi*` modules offer a flexible and declarative way to define HTTP APIs.\n\nTo define an API, create a set of `HttpEndpoint`s. Each endpoint is described by a path, a method, and schemas for the request and response.\n\nCollections of endpoints are grouped in an `HttpApiGroup`, and multiple groups can be merged into a complete `HttpApi`.\n\n```\nHttpApi\n├── HttpGroup\n│ ├── HttpEndpoint\n│ └── HttpEndpoint\n└── HttpGroup\n ├── HttpEndpoint\n ├── HttpEndpoint\n └── HttpEndpoint\n```\n\nOnce your API is defined, the same definition can be reused for multiple purposes:\n\n- **Starting a Server**: Use the API definition to implement and serve endpoints.\n- **Generating Documentation**: Create a Swagger page to document the API.\n- **Deriving a Client**: Generate a fully-typed client for your API.\n\nBenefits of a Single API Definition:\n\n- **Consistency**: A single definition ensures the server, documentation, and client remain aligned.\n- **Reduced Maintenance**: Changes to the API are reflected across all related components.\n- **Simplified Workflow**: Avoids duplication by consolidating API details in one place.\n\n## Hello World\n\n### Defining and Implementing an API\n\nThis example demonstrates how to define and implement a simple API with a single endpoint that returns a string response. The structure of the API is as follows:\n\n```\nHttpApi (\"MyApi)\n└── HttpGroup (\"Greetings\")\n └── HttpEndpoint (\"hello-world\")\n```\n\n**Example** (Hello World Definition)\n\n```ts\nimport {\n HttpApi,\n HttpApiBuilder,\n HttpApiEndpoint,\n HttpApiGroup\n} from \"@effect/platform\"\nimport { NodeHttpServer, NodeRuntime } from \"@effect/platform-node\"\nimport { Effect, Layer, Schema } from \"effect\"\nimport { createServer } from \"node:http\"\n\n// Define our API with one group named \"Greetings\" and one endpoint called \"hello-world\"\nconst MyApi = HttpApi.make(\"MyApi\").add(\n HttpApiGroup.make(\"Greetings\").add(\n HttpApiEndpoint.get(\"hello-world\")`/`.addSuccess(Schema.String)\n )\n)\n\n// Implement the \"Greetings\" group\nconst GreetingsLive = HttpApiBuilder.group(MyApi, \"Greetings\", (handlers) =>\n handlers.handle(\"hello-world\", () => Effect.succeed(\"Hello, World!\"))\n)\n\n// Provide the implementation for the API\nconst MyApiLive = HttpApiBuilder.api(MyApi).pipe(Layer.provide(GreetingsLive))\n\n// Set up the server using NodeHttpServer on port 3000\nconst ServerLive = HttpApiBuilder.serve().pipe(\n Layer.provide(MyApiLive),\n Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 }))\n)\n\n// Launch the server\nLayer.launch(ServerLive).pipe(NodeRuntime.runMain)\n```\n\nAfter running the code, open a browser and navigate to http://localhost:3000. The server will respond with:\n\n```\nHello, World!\n```\n\n### Serving The Auto Generated Swagger Documentation\n\nYou can enhance your API by adding auto-generated Swagger documentation using the `HttpApiSwagger` module. This makes it easier for developers to explore and interact with your API.\n\nTo include Swagger in your server setup, provide the `HttpApiSwagger.layer` when configuring the server.\n\n**Example** (Serving Swagger Documentation)\n\n```ts\nimport {\n HttpApi,\n HttpApiBuilder,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiSwagger\n} from \"@effect/platform\"\nimport { NodeHttpServer, NodeRuntime } from \"@effect/platform-node\"\nimport { Effect, Layer, Schema } from \"effect\"\nimport { createServer } from \"node:http\"\n\nconst MyApi = HttpApi.make(\"MyApi\").add(\n HttpApiGroup.make(\"Greetings\").add(\n HttpApiEndpoint.get(\"hello-world\")`/`.addSuccess(Schema.String)\n )\n)\n\nconst GreetingsLive = HttpApiBuilder.group(MyApi, \"Greetings\", (handlers) =>\n handlers.handle(\"hello-world\", () => Effect.succeed(\"Hello, World!\"))\n)\n\nconst MyApiLive = HttpApiBuilder.api(MyApi).pipe(Layer.provide(GreetingsLive))\n\nconst ServerLive = HttpApiBuilder.serve().pipe(\n // Provide the Swagger layer so clients can access auto-generated docs\n Layer.provide(HttpApiSwagger.layer()),\n Layer.provide(MyApiLive),\n Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 }))\n)\n\nLayer.launch(ServerLive).pipe(NodeRuntime.runMain)\n```\n\nAfter running the server, open your browser and navigate to http://localhost:3000/docs.\n\nThis URL will display the Swagger documentation, allowing you to explore the API's endpoints, request parameters, and response structures interactively.\n\n\n\n### Deriving a Client\n\nOnce you have defined your API, you can generate a client to interact with it using the `HttpApiClient` module. This allows you to call your API endpoints without manually handling HTTP requests.\n\n**Example** (Deriving and Using a Client)\n\n```ts\nimport {\n FetchHttpClient,\n HttpApi,\n HttpApiBuilder,\n HttpApiClient,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiSwagger\n} from \"@effect/platform\"\nimport { NodeHttpServer, NodeRuntime } from \"@effect/platform-node\"\nimport { Effect, Layer, Schema } from \"effect\"\nimport { createServer } from \"node:http\"\n\nconst MyApi = HttpApi.make(\"MyApi\").add(\n HttpApiGroup.make(\"Greetings\").add(\n HttpApiEndpoint.get(\"hello-world\")`/`.addSuccess(Schema.String)\n )\n)\n\nconst GreetingsLive = HttpApiBuilder.group(MyApi, \"Greetings\", (handlers) =>\n handlers.handle(\"hello-world\", () => Effect.succeed(\"Hello, World!\"))\n)\n\nconst MyApiLive = HttpApiBuilder.api(MyApi).pipe(Layer.provide(GreetingsLive))\n\nconst ServerLive = HttpApiBuilder.serve().pipe(\n Layer.provide(HttpApiSwagger.layer()),\n Layer.provide(MyApiLive),\n Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 }))\n)\n\nLayer.launch(ServerLive).pipe(NodeRuntime.runMain)\n\n// Create a program that derives and uses the client\nconst program = Effect.gen(function* () {\n // Derive the client\n const client = yield* HttpApiClient.make(MyApi, {\n baseUrl: \"http://localhost:3000\"\n })\n // Call the \"hello-world\" endpoint\n const hello = yield* client.Greetings[\"hello-world\"]()\n console.log(hello)\n})\n\n// Provide a Fetch-based HTTP client and run the program\nEffect.runFork(program.pipe(Effect.provide(FetchHttpClient.layer)))\n// Output: Hello, World!\n```\n\n## Defining a HttpApiEndpoint\n\nAn `HttpApiEndpoint` represents a single endpoint in your API. Each endpoint is defined with a name, path, HTTP method, and optional schemas for requests and responses. This allows you to describe the structure and behavior of your API.\n\nBelow is an example of a simple CRUD API for managing users, which includes the following endpoints:\n\n- `GET /users` - Retrieve all users.\n- `GET /users/:userId` - Retrieve a specific user by ID.\n- `POST /users` - Create a new user.\n- `DELETE /users/:userId` - Delete a user by ID.\n- `PATCH /users/:userId` - Update a user by ID.\n\n### GET\n\nThe `HttpApiEndpoint.get` method allows you to define a GET endpoint by specifying its name, path, and optionally, a schema for the response.\n\nTo define the structure of successful responses, use the `.addSuccess` method. If no schema is provided, the default response status is `204 No Content`.\n\n**Example** (Defining a GET Endpoint to Retrieve All Users)\n\n```ts\nimport { HttpApiEndpoint } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\n// Define a schema representing a User entity\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\n// Define the \"getUsers\" endpoint, returning a list of users\nconst getUsers = HttpApiEndpoint\n // ┌─── Endpoint name\n // │ ┌─── Endpoint path\n // ▼ ▼\n .get(\"getUsers\", \"/users\")\n // Define the success schema for the response (optional).\n // If no response schema is specified, the default response is `204 No Content`.\n .addSuccess(Schema.Array(User))\n```\n\n### Path Parameters\n\nPath parameters allow you to include dynamic segments in your endpoint's path. There are two ways to define path parameters in your API.\n\n#### Using setPath\n\nThe `setPath` method allows you to explicitly define path parameters by associating them with a schema.\n\n**Example** (Defining Parameters with setPath)\n\n```ts\nimport { HttpApiEndpoint } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\n// Define a GET endpoint with a path parameter \":id\"\nconst getUser = HttpApiEndpoint.get(\"getUser\", \"/user/:id\")\n .setPath(\n Schema.Struct({\n // Define a schema for the \"id\" path parameter\n id: Schema.NumberFromString\n })\n )\n .addSuccess(User)\n```\n\n#### Using Template Strings\n\nYou can also define path parameters by embedding them in a template string with the help of `HttpApiSchema.param`.\n\n**Example** (Defining Parameters using a Template String)\n\n```ts\nimport { HttpApiEndpoint, HttpApiSchema } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\n// Create a path parameter using HttpApiSchema.param\nconst idParam = HttpApiSchema.param(\"id\", Schema.NumberFromString)\n\n// Define the GET endpoint using a template string\nconst getUser = HttpApiEndpoint.get(\"getUser\")`/user/${idParam}`.addSuccess(\n User\n)\n```\n\n### POST\n\nThe `HttpApiEndpoint.post` method is used to define an endpoint for creating resources. You can specify a schema for the request body (payload) and a schema for the successful response.\n\n**Example** (Defining a POST Endpoint with Payload and Success Schemas)\n\n```ts\nimport { HttpApiEndpoint } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\n// Define a schema for the user object\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\n// Define a POST endpoint for creating a new user\nconst createUser = HttpApiEndpoint.post(\"createUser\", \"/users\")\n // Define the request body schema (payload)\n .setPayload(\n Schema.Struct({\n name: Schema.String\n })\n )\n // Define the schema for a successful response\n .addSuccess(User)\n```\n\n### DELETE\n\nThe `HttpApiEndpoint.del` method is used to define an endpoint for deleting a resource.\n\n**Example** (Defining a DELETE Endpoint with Path Parameters)\n\n```ts\nimport { HttpApiEndpoint, HttpApiSchema } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\n// Define a path parameter for the user ID\nconst idParam = HttpApiSchema.param(\"id\", Schema.NumberFromString)\n\n// Define a DELETE endpoint to delete a user by ID\nconst deleteUser = HttpApiEndpoint.del(\"deleteUser\")`/users/${idParam}`\n```\n\n### PATCH\n\nThe `HttpApiEndpoint.patch` method is used to define an endpoint for partially updating a resource. This method allows you to specify a schema for the request payload and a schema for the successful response.\n\n**Example** (Defining a PATCH Endpoint for Updating a User)\n\n```ts\nimport { HttpApiEndpoint, HttpApiSchema } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\n// Define a schema for the user object\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\n// Define a path parameter for the user ID\nconst idParam = HttpApiSchema.param(\"id\", Schema.NumberFromString)\n\n// Define a PATCH endpoint to update a user's name by ID\nconst updateUser = HttpApiEndpoint.patch(\"updateUser\")`/users/${idParam}`\n // Specify the schema for the request payload\n .setPayload(\n Schema.Struct({\n name: Schema.String // Only the name can be updated\n })\n )\n // Specify the schema for a successful response\n .addSuccess(User)\n```\n\n### Catch-All Endpoints\n\nThe path can also be `\"*\"` to match any incoming path. This is useful for defining a catch-all endpoint to handle unmatched routes or provide a fallback response.\n\n**Example** (Defining a Catch-All Endpoint)\n\n```ts\nimport { HttpApiEndpoint } from \"@effect/platform\"\n\nconst catchAll = HttpApiEndpoint.get(\"catchAll\", \"*\")\n```\n\n### Setting URL Parameters\n\nThe `setUrlParams` method allows you to define the structure of URL parameters for an endpoint. You can specify the schema for each parameter and include metadata such as descriptions to provide additional context.\n\n**Example** (Defining URL Parameters with Metadata)\n\n```ts\nimport { HttpApiEndpoint } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\nconst getUsers = HttpApiEndpoint.get(\"getUsers\", \"/users\")\n // Specify the URL parameters schema\n .setUrlParams(\n Schema.Struct({\n // Parameter \"page\" for pagination\n page: Schema.NumberFromString,\n // Parameter \"sort\" for sorting options with an added description\n sort: Schema.String.annotations({\n description: \"Sorting criteria (e.g., 'name', 'date')\"\n })\n })\n )\n .addSuccess(Schema.Array(User))\n```\n\n#### Defining an Array of Values for a URL Parameter\n\nWhen defining a URL parameter that accepts multiple values, you can use the `Schema.Array` combinator. This allows the parameter to handle an array of items, with each item adhering to a specified schema.\n\n**Example** (Defining an Array of String Values for a URL Parameter)\n\n```ts\nimport { HttpApi, HttpApiEndpoint, HttpApiGroup } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst api = HttpApi.make(\"myApi\").add(\n HttpApiGroup.make(\"group\").add(\n HttpApiEndpoint.get(\"get\", \"/\")\n .setUrlParams(\n Schema.Struct({\n // Define \"a\" as an array of strings\n a: Schema.Array(Schema.String)\n })\n )\n .addSuccess(Schema.String)\n )\n)\n```\n\nYou can test this endpoint by passing an array of values in the query string. For example:\n\n```sh\ncurl \"http://localhost:3000/?a=1&a=2\"\n```\n\nThe query string sends two values (`1` and `2`) for the `a` parameter. The server will process and validate these values according to the schema.\n\n### Status Codes\n\nBy default, the success status code is `200 OK`. You can change it by annotating the schema with a custom status.\n\n**Example** (Defining a GET Endpoint with a custom status code)\n\n```ts\nimport { HttpApiEndpoint } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\nconst getUsers = HttpApiEndpoint.get(\"getUsers\", \"/users\")\n // Override the default success status\n .addSuccess(Schema.Array(User), { status: 206 })\n```\n\n### Handling Multipart Requests\n\nTo support file uploads, you can use the `HttpApiSchema.Multipart` API. This allows you to define an endpoint's payload schema as a multipart request, specifying the structure of the data, including file uploads, with the `Multipart` module.\n\n**Example** (Defining an Endpoint for File Uploads)\n\nIn this example, the `HttpApiSchema.Multipart` function marks the payload as a multipart request. The `files` field uses `Multipart.FilesSchema` to handle uploaded file data automatically.\n\n```ts\nimport { HttpApiEndpoint, HttpApiSchema, Multipart } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst upload = HttpApiEndpoint.post(\"upload\", \"/users/upload\").setPayload(\n // Specify that the payload is a multipart request\n HttpApiSchema.Multipart(\n Schema.Struct({\n // Define a \"files\" field to handle file uploads\n files: Multipart.FilesSchema\n })\n ).addSuccess(Schema.String)\n)\n```\n\nYou can test this endpoint by sending a multipart request with a file upload. For example:\n\n```sh\necho \"Sample file content\" | curl -X POST -F \"files=@-\" http://localhost:3000/users/upload\n```\n\n### Changing the Request Encoding\n\nBy default, API requests are encoded as JSON. If your application requires a different format, you can customize the request encoding using the `HttpApiSchema.withEncoding` method. This allows you to define the encoding type and content type of the request.\n\n**Example** (Customizing Request Encoding)\n\n```ts\nimport { HttpApiEndpoint, HttpApiSchema } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst createUser = HttpApiEndpoint.post(\"createUser\", \"/users\")\n // Set the request payload as a string encoded with URL parameters\n .setPayload(\n Schema.Struct({\n a: Schema.String // Parameter \"a\" must be a string\n })\n // Specify the encoding as URL parameters\n .pipe(HttpApiSchema.withEncoding({ kind: \"UrlParams\" }))\n )\n```\n\n### Changing the Response Encoding\n\nBy default, API responses are encoded as JSON. If your application requires a different format, you can customize the encoding using the `HttpApiSchema.withEncoding` API. This method lets you define the type and content type of the response.\n\n**Example** (Returning Data as `text/csv`)\n\n```ts\nimport { HttpApiEndpoint, HttpApiSchema } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst csv = HttpApiEndpoint.get(\"csv\")`/users/csv`\n // Set the success response as a string with CSV encoding\n .addSuccess(\n Schema.String.pipe(\n HttpApiSchema.withEncoding({\n // Specify the type of the response\n kind: \"Text\",\n // Define the content type as text/csv\n contentType: \"text/csv\"\n })\n )\n )\n```\n\n### Setting Request Headers\n\nUse `HttpApiEndpoint.setHeaders` to declare a single, cumulative schema that describes all expected request headers.\nProvide one struct schema where each header name maps to its validator, and you can attach metadata such as descriptions.\n\n> [!IMPORTANT]\n> All headers are normalized to lowercase. Always use lowercase keys in the headers schema.\n\n**Example** (Describe and validate custom headers)\n\n```ts\nimport { HttpApiEndpoint } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\n// Model for successful responses\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\nconst getUsers = HttpApiEndpoint.get(\"getUsers\", \"/users\")\n // Describe the headers the endpoint expects\n .setHeaders(\n // Declare a single struct schema for all headers\n // Header keys MUST be lowercase in the schema\n Schema.Struct({\n // This header must be a string\n \"x-api-key\": Schema.String,\n\n // A human-friendly description is useful for generated docs (e.g. OpenAPI)\n \"x-request-id\": Schema.String.annotations({\n description: \"Unique identifier for the request\"\n })\n })\n )\n // Successful response: an array of User\n .addSuccess(Schema.Array(User))\n```\n\nYou can test the endpoint by sending the headers:\n\n```sh\ncurl -H \"X-API-Key: 1234567890\" -H \"X-Request-ID: 1234567890\" http://localhost:3000/users\n```\n\nThe server validates these headers against the declared schema before handling the request.\n\n## Defining a HttpApiGroup\n\nYou can group related endpoints under a single entity by using `HttpApiGroup.make`. This can help organize your code and provide a clearer structure for your API.\n\n**Example** (Creating a Group for User-Related Endpoints)\n\n```ts\nimport { HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\nconst idParam = HttpApiSchema.param(\"id\", Schema.NumberFromString)\n\nconst getUsers = HttpApiEndpoint.get(\"getUsers\", \"/users\").addSuccess(\n Schema.Array(User)\n)\n\nconst getUser = HttpApiEndpoint.get(\"getUser\")`/user/${idParam}`.addSuccess(\n User\n)\n\nconst createUser = HttpApiEndpoint.post(\"createUser\", \"/users\")\n .setPayload(\n Schema.Struct({\n name: Schema.String\n })\n )\n .addSuccess(User)\n\nconst deleteUser = HttpApiEndpoint.del(\"deleteUser\")`/users/${idParam}`\n\nconst updateUser = HttpApiEndpoint.patch(\"updateUser\")`/users/${idParam}`\n .setPayload(\n Schema.Struct({\n name: Schema.String\n })\n )\n .addSuccess(User)\n\n// Group all user-related endpoints\nconst usersGroup = HttpApiGroup.make(\"users\")\n .add(getUsers)\n .add(getUser)\n .add(createUser)\n .add(deleteUser)\n .add(updateUser)\n```\n\nIf you would like to create a more opaque type for the group, you can extend `HttpApiGroup` with a class.\n\n**Example** (Creating a Group with an Opaque Type)\n\n```ts\n// Create an opaque class extending HttpApiGroup\nclass UsersGroup extends HttpApiGroup.make(\"users\").add(getUsers).add(getUser) {\n // Additional endpoints or methods can be added here\n}\n```\n\n## Creating the Top-Level HttpApi\n\nAfter defining your groups, you can combine them into one `HttpApi` representing your entire set of endpoints.\n\n**Example** (Combining Groups into a Top-Level API)\n\n```ts\nimport {\n HttpApi,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiSchema\n} from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\nconst idParam = HttpApiSchema.param(\"id\", Schema.NumberFromString)\n\nconst getUsers = HttpApiEndpoint.get(\"getUsers\", \"/users\").addSuccess(\n Schema.Array(User)\n)\n\nconst getUser = HttpApiEndpoint.get(\"getUser\")`/user/${idParam}`.addSuccess(\n User\n)\n\nconst createUser = HttpApiEndpoint.post(\"createUser\", \"/users\")\n .setPayload(\n Schema.Struct({\n name: Schema.String\n })\n )\n .addSuccess(User)\n\nconst deleteUser = HttpApiEndpoint.del(\"deleteUser\")`/users/${idParam}`\n\nconst updateUser = HttpApiEndpoint.patch(\"updateUser\")`/users/${idParam}`\n .setPayload(\n Schema.Struct({\n name: Schema.String\n })\n )\n .addSuccess(User)\n\nconst usersGroup = HttpApiGroup.make(\"users\")\n .add(getUsers)\n .add(getUser)\n .add(createUser)\n .add(deleteUser)\n .add(updateUser)\n\n// Combine the groups into one API\nconst api = HttpApi.make(\"myApi\").add(usersGroup)\n\n// Alternatively, create an opaque class for your API\nclass MyApi extends HttpApi.make(\"myApi\").add(usersGroup) {}\n```\n\n## Adding errors\n\nError responses allow your API to handle different failure scenarios. These responses can be defined at various levels:\n\n- **Endpoint-level errors**: Use `HttpApiEndpoint.addError` to add errors specific to an endpoint.\n- **Group-level errors**: Use `HttpApiGroup.addError` to add errors applicable to all endpoints in a group.\n- **API-level errors**: Use `HttpApi.addError` to define errors that apply to every endpoint in the API.\n\nGroup-level and API-level errors are useful for handling shared issues like authentication failures, especially when managed through middleware.\n\n**Example** (Defining Error Responses for Endpoints and Groups)\n\n```ts\nimport { HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\nconst idParam = HttpApiSchema.param(\"id\", Schema.NumberFromString)\n\n// Define error schemas\nclass UserNotFound extends Schema.TaggedError<UserNotFound>()(\n \"UserNotFound\",\n {}\n) {}\n\nclass Unauthorized extends Schema.TaggedError<Unauthorized>()(\n \"Unauthorized\",\n {}\n) {}\n\nconst getUsers = HttpApiEndpoint.get(\"getUsers\", \"/users\").addSuccess(\n Schema.Array(User)\n)\n\nconst getUser = HttpApiEndpoint.get(\"getUser\")`/user/${idParam}`\n .addSuccess(User)\n // Add a 404 error response for this endpoint\n .addError(UserNotFound, { status: 404 })\n\nconst usersGroup = HttpApiGroup.make(\"users\")\n .add(getUsers)\n .add(getUser)\n // ...etc...\n // Add a 401 error response for the entire group\n .addError(Unauthorized, { status: 401 })\n```\n\nYou can assign multiple error responses to a single endpoint by calling `HttpApiEndpoint.addError` multiple times. This is useful when different types of errors might occur for a single operation.\n\n**Example** (Adding Multiple Errors to an Endpoint)\n\n```ts\nconst deleteUser = HttpApiEndpoint.del(\"deleteUser\")`/users/${idParam}`\n // Add a 404 error response for when the user is not found\n .addError(UserNotFound, { status: 404 })\n // Add a 401 error response for unauthorized access\n .addError(Unauthorized, { status: 401 })\n```\n\n### Predefined Empty Error Types\n\nThe `HttpApiError` module provides a set of predefined empty error types that you can use in your endpoints. These error types help standardize common HTTP error responses, such as `404 Not Found` or `401 Unauthorized`. Using these predefined types simplifies error handling and ensures consistency across your API.\n\n**Example** (Adding a Predefined Error to an Endpoint)\n\n```ts\nimport { HttpApiEndpoint, HttpApiError, HttpApiSchema } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\nconst idParam = HttpApiSchema.param(\"id\", Schema.NumberFromString)\n\nconst getUser = HttpApiEndpoint.get(\"getUser\")`/user/${idParam}`\n .addSuccess(User)\n .addError(HttpApiError.NotFound)\n```\n\n| Name | Status | Description |\n| --------------------- | ------ | -------------------------------------------------------------------------------------------------- |\n| `HttpApiDecodeError` | 400 | Represents an error where the request did not match the expected schema. Includes detailed issues. |\n| `BadRequest` | 400 | Indicates that the request was malformed or invalid. |\n| `Unauthorized` | 401 | Indicates that authentication is required but missing or invalid. |\n| `Forbidden` | 403 | Indicates that the client does not have permission to access the requested resource. |\n| `NotFound` | 404 | Indicates that the requested resource could not be found. |\n| `MethodNotAllowed` | 405 | Indicates that the HTTP method used is not allowed for the requested resource. |\n| `NotAcceptable` | 406 | Indicates that the requested resource cannot be delivered in a format acceptable to the client. |\n| `RequestTimeout` | 408 | Indicates that the server timed out waiting for the client request. |\n| `Conflict` | 409 | Indicates a conflict in the request, such as conflicting data. |\n| `Gone` | 410 | Indicates that the requested resource is no longer available and will not return. |\n| `InternalServerError` | 500 | Indicates an unexpected server error occurred. |\n| `NotImplemented` | 501 | Indicates that the requested functionality is not implemented on the server. |\n| `ServiceUnavailable` | 503 | Indicates that the server is temporarily unavailable, often due to maintenance or overload. |\n\n## Prefixing\n\nPrefixes can be added to endpoints, groups, or an entire API to simplify the management of common paths. This is especially useful when defining multiple related endpoints that share a common base URL.\n\n**Example** (Using Prefixes for Common Path Management)\n\n```ts\nimport { HttpApi, HttpApiEndpoint, HttpApiGroup } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst api = HttpApi.make(\"api\")\n .add(\n HttpApiGroup.make(\"group\")\n .add(\n HttpApiEndpoint.get(\"getRoot\", \"/\")\n .addSuccess(Schema.String)\n // Prefix for this endpoint\n .prefix(\"/endpointPrefix\")\n )\n .add(HttpApiEndpoint.get(\"getA\", \"/a\").addSuccess(Schema.String))\n // Prefix for all endpoints in the group\n .prefix(\"/groupPrefix\")\n )\n // Prefix for the entire API\n .prefix(\"/apiPrefix\")\n```\n\n## Implementing a Server\n\nAfter defining your API, you can implement a server to handle its endpoints. The `HttpApiBuilder` module provides tools to help you connect your API's structure to the logic that serves requests.\n\nHere, we will create a simple example with a `getUser` endpoint organized within a `users` group.\n\n**Example** (Defining the `users` Group and API)\n\n```ts\nimport {\n HttpApi,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiSchema\n} from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\nconst idParam = HttpApiSchema.param(\"id\", Schema.NumberFromString)\n\nconst usersGroup = HttpApiGroup.make(\"users\").add(\n HttpApiEndpoint.get(\"getUser\")`/user/${idParam}`.addSuccess(User)\n)\n\nconst api = HttpApi.make(\"myApi\").add(usersGroup)\n```\n\n### Implementing a HttpApiGroup\n\nThe `HttpApiBuilder.group` API is used to implement a specific group of endpoints within an `HttpApi` definition. It requires the following inputs:\n\n| Input | Description |\n| --------------------------------- | ----------------------------------------------------------------------- |\n| The complete `HttpApi` definition | The overall API structure that includes the group you are implementing. |\n| The name of the group | The specific group you are focusing on within the API. |\n| A function to add handlers | A function that defines how each endpoint in the group is handled. |\n\nEach endpoint in the group is connected to its logic using the `HttpApiBuilder.handle` method, which maps the endpoint's definition to its corresponding implementation.\n\nThe `HttpApiBuilder.group` API produces a `Layer` that can later be provided to the server implementation.\n\n**Example** (Implementing a Group with Endpoint Logic)\n\n```ts\nimport {\n HttpApi,\n HttpApiBuilder,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiSchema\n} from \"@effect/platform\"\nimport { DateTime, Effect, Schema } from \"effect\"\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\nconst idParam = HttpApiSchema.param(\"id\", Schema.NumberFromString)\n\nconst usersGroup = HttpApiGroup.make(\"users\").add(\n HttpApiEndpoint.get(\"getUser\")`/user/${idParam}`.addSuccess(User)\n)\n\nconst api = HttpApi.make(\"myApi\").add(usersGroup)\n\n// --------------------------------------------\n// Implementation\n// --------------------------------------------\n\n// ┌─── Layer<HttpApiGroup.ApiGroup<\"myApi\", \"users\">>\n// ▼\nconst usersGroupLive =\n // ┌─── The Whole API\n // │ ┌─── The Group you are implementing\n // ▼ ▼\n HttpApiBuilder.group(api, \"users\", (handlers) =>\n handlers.handle(\n // ┌─── The Endpoint you are implementing\n // ▼\n \"getUser\",\n // Provide the handler logic for the endpoint.\n // The parameters & payload are passed to the handler function.\n ({ path: { id } }) =>\n Effect.succeed(\n // Return a mock user object with the provided ID\n {\n id,\n name: \"John Doe\",\n createdAt: DateTime.unsafeNow()\n }\n )\n )\n )\n```\n\nUsing `HttpApiBuilder.group`, you connect the structure of your API to its logic, enabling you to focus on each endpoint's functionality in isolation. Each handler receives the parameters and payload for the request, making it easy to process input and generate a response.\n\n### Using Services Inside a HttpApiGroup\n\nIf your handlers need to use services, you can easily integrate them because the `HttpApiBuilder.group` API allows you to return an `Effect`. This ensures that external services can be accessed and utilized directly within your handlers.\n\n**Example** (Using Services in a Group Implementation)\n\n```ts\nimport {\n HttpApi,\n HttpApiBuilder,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiSchema\n} from \"@effect/platform\"\nimport { Context, Effect, Schema } from \"effect\"\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\nconst idParam = HttpApiSchema.param(\"id\", Schema.NumberFromString)\n\nconst usersGroup = HttpApiGroup.make(\"users\").add(\n HttpApiEndpoint.get(\"getUser\")`/user/${idParam}`.addSuccess(User)\n)\n\nconst api = HttpApi.make(\"myApi\").add(usersGroup)\n\n// --------------------------------------------\n// Implementation\n// --------------------------------------------\n\ntype User = typeof User.Type\n\n// Define the UsersRepository service\nclass UsersRepository extends Context.Tag(\"UsersRepository\")<\n UsersRepository,\n {\n readonly findById: (id: number) => Effect.Effect<User>\n }\n>() {}\n\n// Implement the `users` group with access to the UsersRepository service\n//\n// ┌─── Layer<HttpApiGroup.ApiGroup<\"myApi\", \"users\">, never, UsersRepository>\n// ▼\nconst usersGroupLive = HttpApiBuilder.group(api, \"users\", (handlers) =>\n Effect.gen(function* () {\n // Access the UsersRepository service\n const repository = yield* UsersRepository\n return handlers.handle(\"getUser\", ({ path: { id } }) =>\n repository.findById(id)\n )\n })\n)\n```\n\n### Implementing a HttpApi\n\nOnce all your groups are implemented, you can create a top-level implementation to combine them into a unified API. This is done using the `HttpApiBuilder.api` API, which generates a `Layer`. You then use `Layer.provide` to include the implementations of all the groups into the top-level `HttpApi`.\n\n**Example** (Combining Group Implementations into a Top-Level API)\n\n```ts\nimport {\n HttpApi,\n HttpApiBuilder,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiSchema\n} from \"@effect/platform\"\nimport { DateTime, Effect, Layer, Schema } from \"effect\"\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\nconst idParam = HttpApiSchema.param(\"id\", Schema.NumberFromString)\n\nconst usersGroup = HttpApiGroup.make(\"users\").add(\n HttpApiEndpoint.get(\"getUser\")`/user/${idParam}`.addSuccess(User)\n)\n\nconst api = HttpApi.make(\"myApi\").add(usersGroup)\n\nconst usersGroupLive = HttpApiBuilder.group(api, \"users\", (handlers) =>\n handlers.handle(\"getUser\", ({ path: { id } }) =>\n Effect.succeed({\n id,\n name: \"John Doe\",\n createdAt: DateTime.unsafeNow()\n })\n )\n)\n\n// Combine all group implementations into the top-level API\n//\n// ┌─── Layer<HttpApi.Api, never, never>\n// ▼\nconst MyApiLive = HttpApiBuilder.api(api).pipe(Layer.provide(usersGroupLive))\n```\n\n### Serving the API\n\nYou can serve your API using the `HttpApiBuilder.serve` function. This utility builds an `HttpApp` from an `HttpApi` instance and uses an `HttpServer` to handle requests. Middleware can be added to customize or enhance the server's behavior.\n\n**Example** (Setting Up and Serving an API with Middleware)\n\n```ts\nimport {\n HttpApi,\n HttpApiBuilder,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiSchema,\n HttpMiddleware,\n HttpServer\n} from \"@effect/platform\"\nimport { NodeHttpServer, NodeRuntime } from \"@effect/platform-node\"\nimport { DateTime, Effect, Layer, Schema } from \"effect\"\nimport { createServer } from \"node:http\"\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\nconst idParam = HttpApiSchema.param(\"id\", Schema.NumberFromString)\n\nconst usersGroup = HttpApiGroup.make(\"users\").add(\n HttpApiEndpoint.get(\"getUser\")`/user/${idParam}`.addSuccess(User)\n)\n\nconst api = HttpApi.make(\"myApi\").add(usersGroup)\n\nconst usersGroupLive = HttpApiBuilder.group(api, \"users\", (handlers) =>\n handlers.handle(\"getUser\", ({ path: { id } }) =>\n Effect.succeed({\n id,\n name: \"John Doe\",\n createdAt: DateTime.unsafeNow()\n })\n )\n)\n\nconst MyApiLive = HttpApiBuilder.api(api).pipe(Layer.provide(usersGroupLive))\n\n// Configure and serve the API\nconst HttpLive = HttpApiBuilder.serve(HttpMiddleware.logger).pipe(\n // Add CORS middleware to handle cross-origin requests\n Layer.provide(HttpApiBuilder.middlewareCors()),\n // Provide the API implementation\n Layer.provide(MyApiLive),\n // Log the server's listening address\n HttpServer.withLogAddress,\n // Set up the Node.js HTTP server\n Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 }))\n)\n\n// Launch the server\nLayer.launch(HttpLive).pipe(NodeRuntime.runMain)\n```\n\n### Accessing the HttpServerRequest\n\nIn some cases, you may need to access details about the incoming `HttpServerRequest` within an endpoint handler. The HttpServerRequest module provides access to the request object, allowing you to inspect properties such as the HTTP method or headers.\n\n**Example** (Accessing the Request Object in a GET Endpoint)\n\n```ts\nimport {\n HttpApi,\n HttpApiBuilder,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpMiddleware,\n HttpServer\n} from \"@effect/platform\"\nimport { NodeHttpServer, NodeRuntime } from \"@effect/platform-node\"\nimport { Effect, Layer, Schema } from \"effect\"\nimport { createServer } from \"node:http\"\n\nconst api = HttpApi.make(\"myApi\").add(\n HttpApiGroup.make(\"group\").add(\n HttpApiEndpoint.get(\"get\", \"/\").addSuccess(Schema.String)\n )\n)\n\nconst groupLive = HttpApiBuilder.group(api, \"group\", (handlers) =>\n handlers.handle(\"get\", ({ request }) =>\n Effect.gen(function* () {\n // Log the HTTP method for demonstration purposes\n console.log(request.method)\n\n // Return a response\n return \"Hello, World!\"\n })\n )\n)\n\nconst MyApiLive = HttpApiBuilder.api(api).pipe(Layer.provide(groupLive))\n\nconst HttpLive = HttpApiBuilder.serve(HttpMiddleware.logger).pipe(\n Layer.provide(HttpApiBuilder.middlewareCors()),\n Layer.provide(MyApiLive),\n HttpServer.withLogAddress,\n Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 }))\n)\n\nLayer.launch(HttpLive).pipe(NodeRuntime.runMain)\n```\n\n### Streaming Requests\n\nStreaming requests allow you to send large or continuous data streams to the server. In this example, we define an API that accepts a stream of binary data and decodes it into a string.\n\n**Example** (Handling Streaming Requests)\n\n```ts\nimport {\n HttpApi,\n HttpApiBuilder,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiSchema,\n HttpMiddleware,\n HttpServer\n} from \"@effect/platform\"\nimport { NodeHttpServer, NodeRuntime } from \"@effect/platform-node\"\nimport { Effect, Layer, Schema } from \"effect\"\nimport { createServer } from \"node:http\"\n\nconst api = HttpApi.make(\"myApi\").add(\n HttpApiGroup.make(\"group\").add(\n HttpApiEndpoint.post(\"acceptStream\", \"/stream\")\n // Define the payload as a Uint8Array with a specific encoding\n .setPayload(\n Schema.Uint8ArrayFromSelf.pipe(\n HttpApiSchema.withEncoding({\n kind: \"Uint8Array\",\n contentType: \"application/octet-stream\"\n })\n )\n )\n .addSuccess(Schema.String)\n )\n)\n\nconst groupLive = HttpApiBuilder.group(api, \"group\", (handlers) =>\n handlers.handle(\"acceptStream\", (req) =>\n // Decode the incoming binary data into a string\n Effect.succeed(new TextDecoder().decode(req.payload))\n )\n)\n\nconst MyApiLive = HttpApiBuilder.api(api).pipe(Layer.provide(groupLive))\n\nconst HttpLive = HttpApiBuilder.serve(HttpMiddleware.logger).pipe(\n Layer.provide(HttpApiBuilder.middlewareCors()),\n Layer.provide(MyApiLive),\n HttpServer.withLogAddress,\n Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 }))\n)\n\nLayer.launch(HttpLive).pipe(NodeRuntime.runMain)\n```\n\nYou can test the streaming request using `curl` or any tool that supports sending binary data. For example:\n\n```sh\necho \"abc\" | curl -X POST 'http://localhost:3000/stream' --data-binary @- -H \"Content-Type: application/octet-stream\"\n# Output: abc\n```\n\n### Streaming Responses\n\nTo handle streaming responses in your API, you can return a raw `HttpServerResponse`. The `HttpServerResponse.stream` function is designed to return a continuous stream of data as the response.\n\n**Example** (Implementing a Streaming Endpoint)\n\n```ts\nimport {\n HttpApi,\n HttpApiBuilder,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiSchema,\n HttpMiddleware,\n HttpServer,\n HttpServerResponse\n} from \"@effect/platform\"\nimport { NodeHttpServer, NodeRuntime } from \"@effect/platform-node\"\nimport { Layer, Schedule, Schema, Stream } from \"effect\"\nimport { createServer } from \"node:http\"\n\n// Define the API with a single streaming endpoint\nconst api = HttpApi.make(\"myApi\").add(\n HttpApiGroup.make(\"group\").add(\n HttpApiEndpoint.get(\"getStream\", \"/stream\").addSuccess(\n Schema.String.pipe(\n HttpApiSchema.withEncoding({\n kind: \"Text\",\n contentType: \"application/octet-stream\"\n })\n )\n )\n )\n)\n\n// Simulate a stream of data\nconst stream = Stream.make(\"a\", \"b\", \"c\").pipe(\n Stream.schedule(Schedule.spaced(\"500 millis\")),\n Stream.map((s) => new TextEncoder().encode(s))\n)\n\nconst groupLive = HttpApiBuilder.group(api, \"group\", (handlers) =>\n handlers.handle(\"getStream\", () => HttpServerResponse.stream(stream))\n)\n\nconst MyApiLive = HttpApiBuilder.api(api).pipe(Layer.provide(groupLive))\n\nconst HttpLive = HttpApiBuilder.serve(HttpMiddleware.logger).pipe(\n Layer.provide(HttpApiBuilder.middlewareCors()),\n Layer.provide(MyApiLive),\n HttpServer.withLogAddress,\n Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 }))\n)\n\nLayer.launch(HttpLive).pipe(NodeRuntime.runMain)\n```\n\nYou can test the streaming response using `curl` or any similar HTTP client that supports streaming:\n\n```sh\ncurl 'http://localhost:3000/stream' --no-buffer\n```\n\nThe response will stream data (`a`, `b`, `c`) with a 500ms interval between each item.\n\n## Middlewares\n\n### Defining Middleware\n\nThe `HttpApiMiddleware` module allows you to add middleware to your API. Middleware can enhance your API by introducing features like logging, authentication, or additional error handling.\n\nYou can define middleware using the `HttpApiMiddleware.Tag` class, which lets you specify:\n\n| Option | Description |\n| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `failure` | A schema that describes any errors the middleware might return. |\n| `provides` | A `Context.Tag` representing the resource or data the middleware will provide to subsequent handlers. |\n| `security` | Definitions from `HttpApiSecurity` that the middleware will implement, such as authentication mechanisms. |\n| `optional` | A boolean indicating whether the request should continue if the middleware fails with an expected error. When `optional` is set to `true`, the `provides` and `failure` options do not affect the final error type or handlers. |\n\n**Example** (Defining a Logger Middleware)\n\n```ts\nimport {\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiMiddleware,\n HttpApiSchema\n} from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\n// Define a schema for errors returned by the logger middleware\nclass LoggerError extends Schema.TaggedError<LoggerError>()(\n \"LoggerError\",\n {}\n) {}\n\n// Extend the HttpApiMiddleware.Tag class to define the logger middleware tag\nclass Logger extends HttpApiMiddleware.Tag<Logger>()(\"Http/Logger\", {\n // Optionally define the error schema for the middleware\n failure: LoggerError\n}) {}\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\nconst idParam = HttpApiSchema.param(\"id\", Schema.NumberFromString)\n\nconst usersGroup = HttpApiGroup.make(\"users\")\n .add(\n HttpApiEndpoint.get(\"getUser\")`/user/${idParam}`\n .addSuccess(User)\n // Apply the middleware to a single endpoint\n .middleware(Logger)\n )\n // Or apply the middleware to the entire group\n .middleware(Logger)\n```\n\n### Implementing HttpApiMiddleware\n\nOnce you have defined your `HttpApiMiddleware`, you can implement it as a `Layer`. This allows the middleware to be applied to specific API groups or endpoints, enabling modular and reusable behavior.\n\n**Example** (Implementing and Using Logger Middleware)\n\n```ts\nimport { HttpApiMiddleware, HttpServerRequest } from \"@effect/platform\"\nimport { Effect, Layer } from \"effect\"\n\nclass Logger extends HttpApiMiddleware.Tag<Logger>()(\"Http/Logger\") {}\n\nconst LoggerLive = Layer.effect(\n Logger,\n Effect.gen(function* () {\n yield* Effect.log(\"creating Logger middleware\")\n\n // Middleware implementation as an Effect\n // that can access the `HttpServerRequest` context.\n return Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest\n yield* Effect.log(`Request: ${request.method} ${request.url}`)\n })\n })\n)\n```\n\nAfter implementing the middleware, you can attach it to your API groups or specific endpoints using the `Layer` APIs.\n\n```ts\nimport {\n HttpApi,\n HttpApiBuilder,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiMiddleware,\n HttpApiSchema,\n HttpServerRequest\n} from \"@effect/platform\"\nimport { DateTime, Effect, Layer, Schema } from \"effect\"\n\n// Define a schema for errors returned by the logger middleware\nclass LoggerError extends Schema.TaggedError<LoggerError>()(\n \"LoggerError\",\n {}\n) {}\n\n// Extend the HttpApiMiddleware.Tag class to define the logger middleware tag\nclass Logger extends HttpApiMiddleware.Tag<Logger>()(\"Http/Logger\", {\n // Optionally define the error schema for the middleware\n failure: LoggerError\n}) {}\n\nconst LoggerLive = Layer.effect(\n Logger,\n Effect.gen(function* () {\n yield* Effect.log(\"creating Logger middleware\")\n\n // Middleware implementation as an Effect\n // that can access the `HttpServerRequest` context.\n return Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest\n yield* Effect.log(`Request: ${request.method} ${request.url}`)\n })\n })\n)\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\nconst idParam = HttpApiSchema.param(\"id\", Schema.NumberFromString)\n\nconst usersGroup = HttpApiGroup.make(\"users\")\n .add(\n HttpApiEndpoint.get(\"getUser\")`/user/${idParam}`\n .addSuccess(User)\n // Apply the middleware to a single endpoint\n .middleware(Logger)\n )\n // Or apply the middleware to the entire group\n .middleware(Logger)\n\nconst api = HttpApi.make(\"myApi\").add(usersGroup)\n\nconst usersGroupLive = HttpApiBuilder.group(api, \"users\", (handlers) =>\n handlers.handle(\"getUser\", (req) =>\n Effect.succeed({\n id: req.path.id,\n name: \"John Doe\",\n createdAt: DateTime.unsafeNow()\n })\n )\n).pipe(\n // Provide the Logger middleware to the group\n Layer.provide(LoggerLive)\n)\n```\n\n### Defining security middleware\n\nThe `HttpApiSecurity` module enables you to add security annotations to your API. These annotations specify the type of authorization required to access specific endpoints.\n\nSupported authorization types include:\n\n| Authorization Type | Description |\n| ------------------------ | ---------------------------------------------------------------- |\n| `HttpApiSecurity.apiKey` | API key authorization via headers, query parameters, or cookies. |\n| `HttpApiSecurity.basic` | HTTP Basic authentication. |\n| `HttpApiSecurity.bearer` | Bearer token authentication. |\n\nThese security annotations can be used alongside `HttpApiMiddleware` to create middleware that protects your API endpoints.\n\n**Example** (Defining Security Middleware)\n\n```ts\nimport {\n HttpApi,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiMiddleware,\n HttpApiSchema,\n HttpApiSecurity\n} from \"@effect/platform\"\nimport { Context, Schema } from \"effect\"\n\n// Define a schema for the \"User\"\nclass User extends Schema.Class<User>(\"User\")({ id: Schema.Number }) {}\n\n// Define a schema for the \"Unauthorized\" error\nclass Unauthorized extends Schema.TaggedError<Unauthorized>()(\n \"Unauthorized\",\n {},\n // Specify the HTTP status code for unauthorized errors\n HttpApiSchema.annotations({ status: 401 })\n) {}\n\n// Define a Context.Tag for the authenticated user\nclass CurrentUser extends Context.Tag(\"CurrentUser\")<CurrentUser, User>() {}\n\n// Create the Authorization middleware\nclass Authorization extends HttpApiMiddleware.Tag<Authorization>()(\n \"Authorization\",\n {\n // Define the error schema for unauthorized access\n failure: Unauthorized,\n // Specify the resource this middleware will provide\n provides: CurrentUser,\n // Add security definitions\n security: {\n // ┌─── Custom name for the security definition\n // ▼\n myBearer: HttpApiSecurity.bearer\n // Additional security definitions can be added here.\n // They will attempt to be resolved in the order they are defined.\n }\n }\n) {}\n\nconst api = HttpApi.make(\"api\")\n .add(\n HttpApiGroup.make(\"group\")\n .add(\n HttpApiEndpoint.get(\"get\", \"/\")\n .addSuccess(Schema.String)\n // Apply the middleware to a single endpoint\n .middleware(Authorization)\n )\n // Or apply the middleware to the entire group\n .middleware(Authorization)\n )\n // Or apply the middleware to the entire API\n .middleware(Authorization)\n```\n\n### Implementing HttpApiSecurity middleware\n\nWhen using `HttpApiSecurity` in your middleware, the implementation involves creating a `Layer` with security handlers tailored to your requirements. Below is an example demonstrating how to implement middleware for `HttpApiSecurity.bearer` authentication.\n\n**Example** (Implementing Bearer Token Authentication Middleware)\n\n```ts\nimport {\n HttpApiMiddleware,\n HttpApiSchema,\n HttpApiSecurity\n} from \"@effect/platform\"\nimport { Context, Effect, Layer, Redacted, Schema } from \"effect\"\n\nclass User extends Schema.Class<User>(\"User\")({ id: Schema.Number }) {}\n\nclass Unauthorized extends Schema.TaggedError<Unauthorized>()(\n \"Unauthorized\",\n {},\n HttpApiSchema.annotations({ status: 401 })\n) {}\n\nclass CurrentUser extends Context.Tag(\"CurrentUser\")<CurrentUser, User>() {}\n\nclass Authorization extends HttpApiMiddleware.Tag<Authorization>()(\n \"Authorization\",\n {\n failure: Unauthorized,\n provides: CurrentUser,\n security: {\n myBearer: HttpApiSecurity.bearer\n }\n }\n) {}\n\nconst AuthorizationLive = Layer.effect(\n Authorization,\n Effect.gen(function* () {\n yield* Effect.log(\"creating Authorization middleware\")\n\n // Return the security handlers for the middleware\n return {\n // Define the handler for the Bearer token\n // The Bearer token is redacted for security\n myBearer: (bearerToken) =>\n Effect.gen(function* () {\n yield* Effect.log(\n \"checking bearer token\",\n Redacted.value(bearerToken)\n )\n // Return a mock User object as the CurrentUser\n return new User({ id: 1 })\n })\n }\n })\n)\n```\n\n### Adding Descriptions to Security Definitions\n\nThe `HttpApiSecurity.annotate` function allows you to add metadata, such as a description, to your security definitions. This metadata is displayed in the Swagger documentation, making it easier for developers to understand your API's security requirements.\n\n**Example** (Adding a Description to a Bearer Token Security Definition)\n\n```ts\nimport {\n HttpApiMiddleware,\n HttpApiSchema,\n HttpApiSecurity,\n OpenApi\n} from \"@effect/platform\"\nimport { Context, Schema } from \"effect\"\n\nclass User extends Schema.Class<User>(\"User\")({ id: Schema.Number }) {}\n\nclass Unauthorized extends Schema.TaggedError<Unauthorized>()(\n \"Unauthorized\",\n {},\n HttpApiSchema.annotations({ status: 401 })\n) {}\n\nclass CurrentUser extends Context.Tag(\"CurrentUser\")<CurrentUser, User>() {}\n\nclass Authorization extends HttpApiMiddleware.Tag<Authorization>()(\n \"Authorization\",\n {\n failure: Unauthorized,\n provides: CurrentUser,\n security: {\n myBearer: HttpApiSecurity.bearer.pipe(\n // Add a description to the security definition\n HttpApiSecurity.annotate(OpenApi.Description, \"my description\")\n )\n }\n }\n) {}\n```\n\n### Setting HttpApiSecurity cookies\n\nTo set a security cookie from within a handler, you can use the `HttpApiBuilder.securitySetCookie` API. This method sets a cookie with default properties, including the `HttpOnly` and `Secure` flags, ensuring the cookie is not accessible via JavaScript and is transmitted over secure connections.\n\n**Example** (Setting a Security Cookie in a Login Handler)\n\n```ts\n// Define the security configuration for an API key stored in a cookie\nconst security = HttpApiSecurity.apiKey({\n // Specify that the API key is stored in a cookie\n in: \"cookie\"\n // Define the cookie name,\n key: \"token\"\n})\n\nconst UsersApiLive = HttpApiBuilder.group(MyApi, \"users\", (handlers) =>\n handlers.handle(\"login\", () =>\n // Set the security cookie with a redacted value\n HttpApiBuilder.securitySetCookie(security, Redacted.make(\"keep me secret\"))\n )\n)\n```\n\n## Serving Swagger documentation\n\nYou can add Swagger documentation to your API using the `HttpApiSwagger` module. This integration provides an interactive interface for developers to explore and test your API. To enable Swagger, you simply provide the `HttpApiSwagger.layer` to your server implementation.\n\n**Example** (Adding Swagger Documentation to an API)\n\n```ts\nimport {\n HttpApi,\n HttpApiBuilder,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiSchema,\n HttpApiSwagger,\n HttpMiddleware,\n HttpServer\n} from \"@effect/platform\"\nimport { NodeHttpServer, NodeRuntime } from \"@effect/platform-node\"\nimport { DateTime, Effect, Layer, Schema } from \"effect\"\nimport { createServer } from \"node:http\"\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\nconst idParam = HttpApiSchema.param(\"id\", Schema.NumberFromString)\n\nconst usersGroup = HttpApiGroup.make(\"users\").add(\n HttpApiEndpoint.get(\"getUser\")`/user/${idParam}`.addSuccess(User)\n)\n\nconst api = HttpApi.make(\"myApi\").add(usersGroup)\n\nconst usersGroupLive = HttpApiBuilder.group(api, \"users\", (handlers) =>\n handlers.handle(\"getUser\", ({ path: { id } }) =>\n Effect.succeed({\n id,\n name: \"John Doe\",\n createdAt: DateTime.unsafeNow()\n })\n )\n)\n\nconst MyApiLive = HttpApiBuilder.api(api).pipe(Layer.provide(usersGroupLive))\n\nconst HttpLive = HttpApiBuilder.serve(HttpMiddleware.logger).pipe(\n // Add the Swagger documentation layer\n Layer.provide(\n HttpApiSwagger.layer({\n // Specify the Swagger documentation path.\n // \"/docs\" is the default path.\n path: \"/docs\"\n })\n ),\n Layer.provide(HttpApiBuilder.middlewareCors()),\n Layer.provide(MyApiLive),\n HttpServer.withLogAddress,\n Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 }))\n)\n\nLayer.launch(HttpLive).pipe(NodeRuntime.runMain)\n```\n\n\n\n### Adding OpenAPI Annotations\n\nYou can add OpenAPI annotations to your API to include metadata such as titles, descriptions, and more. These annotations help generate richer API documentation.\n\n#### HttpApi\n\nBelow is a list of available annotations for a top-level `HttpApi`. They can be added using the `.annotate` method:\n\n| Annotation | Description |\n| --------------------------- | ------------------------------------------------------------------------------------------------------------------ |\n| `HttpApi.AdditionalSchemas` | Adds custom schemas to the final OpenAPI specification. Only schemas with an `identifier` annotation are included. |\n| `OpenApi.Description` | Sets a general description for the API. |\n| `OpenApi.License` | Defines the license used by the API. |\n| `OpenApi.Summary` | Provides a brief summary of the API. |\n| `OpenApi.Servers` | Lists server URLs and optional metadata such as variables. |\n| `OpenApi.Override` | Merges the supplied fields into the resulting specification. |\n| `OpenApi.Transform` | Allows you to modify the final specification with a custom function. |\n\n**Example** (Annotating the Top-Level API)\n\n```ts\nimport { HttpApi, OpenApi } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst api = HttpApi.make(\"api\")\n // Provide additional schemas\n .annotate(HttpApi.AdditionalSchemas, [\n Schema.String.annotations({ identifier: \"MyString\" })\n ])\n // Add a description\n .annotate(OpenApi.Description, \"my description\")\n // Set license information\n .annotate(OpenApi.License, { name: \"MIT\", url: \"http://example.com\" })\n // Provide a summary\n .annotate(OpenApi.Summary, \"my summary\")\n // Define servers\n .annotate(OpenApi.Servers, [\n {\n url: \"http://example.com\",\n description: \"example\",\n variables: { a: { default: \"b\", enum: [\"c\"], description: \"d\" } }\n }\n ])\n // Override parts of the generated specification\n .annotate(OpenApi.Override, {\n tags: [{ name: \"a\", description: \"a-description\" }]\n })\n // Apply a transform function to the final specification\n .annotate(OpenApi.Transform, (spec) => ({\n ...spec,\n tags: [...spec.tags, { name: \"b\", description: \"b-description\" }]\n }))\n\n// Generate the OpenAPI specification from the annotated API\nconst spec = OpenApi.fromApi(api)\n\nconsole.log(JSON.stringify(spec, null, 2))\n/*\nOutput:\n{\n \"openapi\": \"3.1.0\",\n \"info\": {\n \"title\": \"Api\",\n \"version\": \"0.0.1\",\n \"description\": \"my description\",\n \"license\": {\n \"name\": \"MIT\",\n \"url\": \"http://example.com\"\n },\n \"summary\": \"my summary\"\n },\n \"paths\": {},\n \"tags\": [\n { \"name\": \"a\", \"description\": \"a-description\" },\n { \"name\": \"b\", \"description\": \"b-description\" }\n ],\n \"components\": {\n \"schemas\": {\n \"MyString\": {\n \"type\": \"string\"\n }\n },\n \"securitySchemes\": {}\n },\n \"security\": [],\n \"servers\": [\n {\n \"url\": \"http://example.com\",\n \"description\": \"example\",\n \"variables\": {\n \"a\": {\n \"default\": \"b\",\n \"enum\": [\n \"c\"\n ],\n \"description\": \"d\"\n }\n }\n }\n ]\n}\n*/\n```\n\n#### HttpApiGroup\n\nThe following annotations can be added to an `HttpApiGroup`:\n\n| Annotation | Description |\n| ---------------------- | --------------------------------------------------------------------- |\n| `OpenApi.Description` | Sets a description for this group. |\n| `OpenApi.ExternalDocs` | Provides external documentation links for the group. |\n| `OpenApi.Override` | Merges specified fields into the resulting specification. |\n| `OpenApi.Transform` | Lets you modify the final group specification with a custom function. |\n| `OpenApi.Exclude` | Excludes the group from the final OpenAPI specification. |\n\n**Example** (Annotating a Group)\n\n```ts\nimport { HttpApi, HttpApiGroup, OpenApi } from \"@effect/platform\"\n\nconst api = HttpApi.make(\"api\")\n .add(\n HttpApiGroup.make(\"group\")\n // Add a description for the group\n .annotate(OpenApi.Description, \"my description\")\n // Provide external documentation links\n .annotate(OpenApi.ExternalDocs, {\n url: \"http://example.com\",\n description: \"example\"\n })\n // Override parts of the final output\n .annotate(OpenApi.Override, { name: \"my name\" })\n // Transform the final specification for this group\n .annotate(OpenApi.Transform, (spec) => ({\n ...spec,\n name: spec.name + \"-transformed\"\n }))\n )\n .add(\n HttpApiGroup.make(\"excluded\")\n // Exclude the group from the final specification\n .annotate(OpenApi.Exclude, true)\n )\n\n// Generate the OpenAPI spec\nconst spec = OpenApi.fromApi(api)\n\nconsole.log(JSON.stringify(spec, null, 2))\n/*\nOutput:\n{\n \"openapi\": \"3.1.0\",\n \"info\": {\n \"title\": \"Api\",\n \"version\": \"0.0.1\"\n },\n \"paths\": {},\n \"tags\": [\n {\n \"name\": \"my name-transformed\",\n \"description\": \"my description\",\n \"externalDocs\": {\n \"url\": \"http://example.com\",\n \"description\": \"example\"\n }\n }\n ],\n \"components\": {\n \"schemas\": {},\n \"securitySchemes\": {}\n },\n \"security\": []\n}\n*/\n```\n\n#### HttpApiEndpoint\n\nFor an `HttpApiEndpoint`, you can use the following annotations:\n\n| Annotation | Description |\n| ---------------------- | --------------------------------------------------------------------------- |\n| `OpenApi.Description` | Adds a description for this endpoint. |\n| `OpenApi.Summary` | Provides a short summary of the endpoint's purpose. |\n| `OpenApi.Deprecated` | Marks the endpoint as deprecated. |\n| `OpenApi.ExternalDocs` | Supplies external documentation links for the endpoint. |\n| `OpenApi.Override` | Merges specified fields into the resulting specification for this endpoint. |\n| `OpenApi.Transform` | Lets you modify the final endpoint specification with a custom function. |\n| `OpenApi.Exclude` | Excludes the endpoint from the final OpenAPI specification. |\n\n**Example** (Annotating an Endpoint)\n\n```ts\nimport {\n HttpApi,\n HttpApiEndpoint,\n HttpApiGroup,\n OpenApi\n} from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst api = HttpApi.make(\"api\").add(\n HttpApiGroup.make(\"group\")\n .add(\n HttpApiEndpoint.get(\"get\", \"/\")\n .addSuccess(Schema.String)\n // Add a description\n .annotate(OpenApi.Description, \"my description\")\n // Provide a summary\n .annotate(OpenApi.Summary, \"my summary\")\n // Mark the endpoint as deprecated\n .annotate(OpenApi.Deprecated, true)\n // Provide external documentation\n .annotate(OpenApi.ExternalDocs, {\n url: \"http://example.com\",\n description: \"example\"\n })\n )\n .add(\n HttpApiEndpoint.get(\"excluded\", \"/excluded\")\n .addSuccess(Schema.String)\n // Exclude this endpoint from the final specification\n .annotate(OpenApi.Exclude, true)\n )\n)\n\n// Generate the OpenAPI spec\nconst spec = OpenApi.fromApi(api)\n\nconsole.log(JSON.stringify(spec, null, 2))\n/*\nOutput:\n{\n \"openapi\": \"3.1.0\",\n \"info\": {\n \"title\": \"Api\",\n \"version\": \"0.0.1\"\n },\n \"paths\": {\n \"/\": {\n \"get\": {\n \"tags\": [\n \"group\"\n ],\n \"operationId\": \"my operationId-transformed\",\n \"parameters\": [],\n \"security\": [],\n \"responses\": {\n \"200\": {\n \"description\": \"a string\",\n \"content\": {\n \"application/json\": {\n \"schema\": {\n \"type\": \"string\"\n }\n }\n }\n },\n \"400\": {\n \"description\": \"The request did not match the expected schema\",\n \"content\": {\n \"application/json\": {\n \"schema\": {\n \"$ref\": \"#/components/schemas/HttpApiDecodeError\"\n }\n }\n }\n }\n },\n \"description\": \"my description\",\n \"summary\": \"my summary\",\n \"deprecated\": true,\n \"externalDocs\": {\n \"url\": \"http://example.com\",\n \"description\": \"example\"\n }\n }\n }\n },\n ...\n}\n*/\n```\n\nThe default response description is \"Success\". You can override this by annotating the schema.\n\n**Example** (Defining a custom response description)\n\n```ts\nimport {\n HttpApi,\n HttpApiEndpoint,\n HttpApiGroup,\n OpenApi\n} from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n}).annotations({ identifier: \"User\" })\n\nconst api = HttpApi.make(\"api\").add(\n HttpApiGroup.make(\"group\").add(\n HttpApiEndpoint.get(\"getUsers\", \"/users\").addSuccess(\n Schema.Array(User).annotations({\n description: \"Returns an array of users\"\n })\n )\n )\n)\n\nconst spec = OpenApi.fromApi(api)\n\nconsole.log(JSON.stringify(spec.paths, null, 2))\n/*\nOutput:\n{\n \"/users\": {\n \"get\": {\n \"tags\": [\n \"group\"\n ],\n \"operationId\": \"group.getUsers\",\n \"parameters\": [],\n \"security\": [],\n \"responses\": {\n \"200\": {\n \"description\": \"Returns an array of users\",\n \"content\": {\n \"application/json\": {\n \"schema\": {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/components/schemas/User\"\n },\n \"description\": \"Returns an array of users\"\n }\n }\n }\n },\n \"400\": {\n \"description\": \"The request did not match the expected schema\",\n \"content\": {\n \"application/json\": {\n \"schema\": {\n \"$ref\": \"#/components/schemas/HttpApiDecodeError\"\n }\n }\n }\n }\n }\n }\n }\n}\n*/\n```\n\n### Top Level Groups\n\nWhen a group is marked as `topLevel`, the operation IDs of its endpoints do not include the group name as a prefix. This is helpful when you want to group endpoints under a shared tag without adding a redundant prefix to their operation IDs.\n\n**Example** (Using a Top-Level Group)\n\n```ts\nimport {\n HttpApi,\n HttpApiEndpoint,\n HttpApiGroup,\n OpenApi\n} from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst api = HttpApi.make(\"api\").add(\n // Mark the group as top-level\n HttpApiGroup.make(\"group\", { topLevel: true }).add(\n HttpApiEndpoint.get(\"get\", \"/\").addSuccess(Schema.String)\n )\n)\n\n// Generate the OpenAPI spec\nconst spec = OpenApi.fromApi(api)\n\nconsole.log(JSON.stringify(spec.paths, null, 2))\n/*\nOutput:\n{\n \"/\": {\n \"get\": {\n \"tags\": [\n \"group\"\n ],\n \"operationId\": \"get\", // The operation ID is not prefixed with \"group\"\n \"parameters\": [],\n \"security\": [],\n \"responses\": {\n \"200\": {\n \"description\": \"a string\",\n \"content\": {\n \"application/json\": {\n \"schema\": {\n \"type\": \"string\"\n }\n }\n }\n },\n \"400\": {\n \"description\": \"The request did not match the expected schema\",\n \"content\": {\n \"application/json\": {\n \"schema\": {\n \"$ref\": \"#/components/schemas/HttpApiDecodeError\"\n }\n }\n }\n }\n }\n }\n }\n}\n*/\n```\n\n## Deriving a Client\n\nAfter defining your API, you can derive a client that interacts with the server. The `HttpApiClient` module simplifies the process by providing tools to generate a client based on your API definition.\n\n**Example** (Deriving and Using a Client)\n\nThis example demonstrates how to create a client for an API and use it to call an endpoint.\n\n```ts\nimport {\n FetchHttpClient,\n HttpApi,\n HttpApiBuilder,\n HttpApiClient,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiSchema,\n HttpApiSwagger,\n HttpMiddleware,\n HttpServer\n} from \"@effect/platform\"\nimport { NodeHttpServer, NodeRuntime } from \"@effect/platform-node\"\nimport { DateTime, Effect, Layer, Schema } from \"effect\"\nimport { createServer } from \"node:http\"\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\nconst idParam = HttpApiSchema.param(\"id\", Schema.NumberFromString)\n\nconst usersGroup = HttpApiGroup.make(\"users\").add(\n HttpApiEndpoint.get(\"getUser\")`/user/${idParam}`.addSuccess(User)\n)\n\nconst api = HttpApi.make(\"myApi\").add(usersGroup)\n\nconst usersGroupLive = HttpApiBuilder.group(api, \"users\", (handlers) =>\n handlers.handle(\"getUser\", ({ path: { id } }) =>\n Effect.succeed({\n id,\n name: \"John Doe\",\n createdAt: DateTime.unsafeNow()\n })\n )\n)\n\nconst MyApiLive = HttpApiBuilder.api(api).pipe(Layer.provide(usersGroupLive))\n\nconst HttpLive = HttpApiBuilder.serve(HttpMiddleware.logger).pipe(\n Layer.provide(HttpApiSwagger.layer()),\n Layer.provide(HttpApiBuilder.middlewareCors()),\n Layer.provide(MyApiLive),\n HttpServer.withLogAddress,\n Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 }))\n)\n\nLayer.launch(HttpLive).pipe(NodeRuntime.runMain)\n\n// Create a program that derives and uses the client\nconst program = Effect.gen(function* () {\n // Derive the client\n const client = yield* HttpApiClient.make(api, {\n baseUrl: \"http://localhost:3000\"\n })\n // Call the `getUser` endpoint\n const user = yield* client.users.getUser({ path: { id: 1 } })\n console.log(user)\n})\n\n// Provide a Fetch-based HTTP client and run the program\nEffect.runFork(program.pipe(Effect.provide(FetchHttpClient.layer)))\n/*\nExample Output:\nUser {\n id: 1,\n name: 'John Doe',\n createdAt: DateTime.Utc(2025-01-04T15:14:49.562Z)\n}\n*/\n```\n\n### Top Level Groups\n\nWhen a group is marked as `topLevel`, the methods on the client are not nested under the group name. This can simplify client usage by providing direct access to the endpoint methods.\n\n**Example** (Using a Top-Level Group in the Client)\n\n```ts\nimport {\n HttpApi,\n HttpApiClient,\n HttpApiEndpoint,\n HttpApiGroup\n} from \"@effect/platform\"\nimport { Effect, Schema } from \"effect\"\n\nconst api = HttpApi.make(\"api\").add(\n // Mark the group as top-level\n HttpApiGroup.make(\"group\", { topLevel: true }).add(\n HttpApiEndpoint.get(\"get\", \"/\").addSuccess(Schema.String)\n )\n)\n\nconst program = Effect.gen(function* () {\n const client = yield* HttpApiClient.make(api, {\n baseUrl: \"http://localhost:3000\"\n })\n // The `get` method is not nested under the \"group\" name\n const user = yield* client.get()\n console.log(user)\n})\n```\n\n## Converting to a Web Handler\n\nYou can convert your `HttpApi` implementation into a web handler using the `HttpApiBuilder.toWebHandler` API. This approach enables you to serve your API through a custom server setup.\n\n**Example** (Creating and Serving a Web Handler)\n\n```ts\nimport {\n HttpApi,\n HttpApiBuilder,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiSwagger,\n HttpServer\n} from \"@effect/platform\"\nimport { Effect, Layer, Schema } from \"effect\"\nimport * as http from \"node:http\"\n\nconst api = HttpApi.make(\"myApi\").add(\n HttpApiGroup.make(\"group\").add(\n HttpApiEndpoint.get(\"get\", \"/\").addSuccess(Schema.String)\n )\n)\n\nconst groupLive = HttpApiBuilder.group(api, \"group\", (handlers) =>\n handlers.handle(\"get\", () => Effect.succeed(\"Hello, world!\"))\n)\n\nconst MyApiLive = HttpApiBuilder.api(api).pipe(Layer.provide(groupLive))\n\nconst SwaggerLayer = HttpApiSwagger.layer().pipe(Layer.provide(MyApiLive))\n\n// Convert the API to a web handler\nconst { dispose, handler } = HttpApiBuilder.toWebHandler(\n Layer.mergeAll(MyApiLive, SwaggerLayer, HttpServer.layerContext)\n)\n\n// Serving the handler using a custom HTTP server\nhttp\n .createServer(async (req, res) => {\n const url = `http://${req.headers.host}${req.url}`\n const init: RequestInit = {\n method: req.method!\n }\n\n const response = await handler(new Request(url, init))\n\n res.writeHead(\n response.status,\n response.statusText,\n Object.fromEntries(response.headers.entries())\n )\n const responseBody = await response.arrayBuffer()\n res.end(Buffer.from(responseBody))\n })\n .listen(3000, () => {\n console.log(\"Server running at http://localhost:3000/\")\n })\n .on(\"close\", () => {\n dispose()\n })\n```\n\n# HTTP Client\n\n## Overview\n\nThe `@effect/platform/HttpClient*` modules provide a way to send HTTP requests,\nhandle responses, and abstract over the differences between platforms.\n\nThe `HttpClient` interface has a set of methods for sending requests:\n\n- `.execute` - takes a [HttpClientRequest](#httpclientrequest) and returns a `HttpClientResponse`\n- `.{get, del, head, options, patch, post, put}` - convenience methods for creating a request and\n executing it in one step\n\nTo access the `HttpClient`, you can use the `HttpClient.HttpClient` [tag](https://effect.website/docs/guides/context-management/services).\nThis will give you access to a `HttpClient` instance.\n\n**Example: Retrieving JSON Data (GET)**\n\n```ts\nimport { FetchHttpClient, HttpClient } from \"@effect/platform\"\nimport { Effect } from \"effect\"\n\nconst program = Effect.gen(function* () {\n // Access HttpClient\n const client = yield* HttpClient.HttpClient\n\n // Create and execute a GET request\n const response = yield* client.get(\n \"https://jsonplaceholder.typicode.com/posts/1\"\n )\n\n const json = yield* response.json\n\n console.log(json)\n}).pipe(\n // Provide the HttpClient\n Effect.provide(FetchHttpClient.layer)\n)\n\nEffect.runPromise(program)\n/*\nOutput:\n{\n userId: 1,\n id: 1,\n title: 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit',\n body: 'quia et suscipit\\n' +\n 'suscipit recusandae consequuntur expedita et cum\\n' +\n 'reprehenderit molestiae ut ut quas totam\\n' +\n 'nostrum rerum est autem sunt rem eveniet architecto'\n}\n*/\n```\n\n**Example: Retrieving JSON Data with accessor apis (GET)**\n\nThe `HttpClient` module also provides a set of accessor apis that allow you to\neasily send requests without first accessing the `HttpClient` service.\n\nBelow is an example of using the `get` accessor api to send a GET request:\n\n(The following examples will continue to use the `HttpClient` service approach).\n\n```ts\nimport { FetchHttpClient, HttpClient } from \"@effect/platform\"\nimport { Effect } from \"effect\"\n\nconst program = HttpClient.get(\n \"https://jsonplaceholder.typicode.com/posts/1\"\n).pipe(\n Effect.andThen((response) => response.json),\n Effect.provide(FetchHttpClient.layer)\n)\n\nEffect.runPromise(program)\n/*\nOutput:\n{\n userId: 1,\n id: 1,\n title: 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit',\n body: 'quia et suscipit\\n' +\n 'suscipit recusandae consequuntur expedita et cum\\n' +\n 'reprehenderit molestiae ut ut quas totam\\n' +\n 'nostrum rerum est autem sunt rem eveniet architecto'\n}\n*/\n```\n\n**Example: Creating and Executing a Custom Request**\n\nUsing [HttpClientRequest](#httpclientrequest), you can create and then execute a request. This is useful for customizing the request further.\n\n```ts\nimport {\n FetchHttpClient,\n HttpClient,\n HttpClientRequest\n} from \"@effect/platform\"\nimport { Effect } from \"effect\"\n\nconst program = Effect.gen(function* () {\n // Access HttpClient\n const client = yield* HttpClient.HttpClient\n\n // Create a GET request\n const req = HttpClientRequest.get(\n \"https://jsonplaceholder.typicode.com/posts/1\"\n )\n\n // Optionally customize the request\n\n // Execute the request and get the response\n const response = yield* client.execute(req)\n\n const json = yield* response.json\n\n console.log(json)\n}).pipe(\n // Provide the HttpClient\n Effect.provide(FetchHttpClient.layer)\n)\n\nEffect.runPromise(program)\n/*\nOutput:\n{\n userId: 1,\n id: 1,\n title: 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit',\n body: 'quia et suscipit\\n' +\n 'suscipit recusandae consequuntur expedita et cum\\n' +\n 'reprehenderit molestiae ut ut quas totam\\n' +\n 'nostrum rerum est autem sunt rem eveniet architecto'\n}\n*/\n```\n\n## Customize a HttpClient\n\nThe `HttpClient` module allows you to customize the client in various ways. For instance, you can log details of a request before execution using the `tapRequest` function.\n\n**Example: Tapping**\n\n```ts\nimport { FetchHttpClient, HttpClient } from \"@effect/platform\"\nimport { Console, Effect } from \"effect\"\n\nconst program = Effect.gen(function* () {\n const client = (yield* HttpClient.HttpClient).pipe(\n // Log the request before fetching\n HttpClient.tapRequest(Console.log)\n )\n\n const response = yield* client.get(\n \"https://jsonplaceholder.typicode.com/posts/1\"\n )\n\n const json = yield* response.json\n\n console.log(json)\n}).pipe(Effect.provide(FetchHttpClient.layer))\n\nEffect.runPromise(program)\n/*\nOutput:\n{\n _id: '@effect/platform/HttpClientRequest',\n method: 'GET',\n url: 'https://jsonplaceholder.typicode.com/posts/1',\n urlParams: [],\n hash: { _id: 'Option', _tag: 'None' },\n headers: Object <[Object: null prototype]> {},\n body: { _id: '@effect/platform/HttpBody', _tag: 'Empty' }\n}\n{\n userId: 1,\n id: 1,\n title: 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit',\n body: 'quia et suscipit\\n' +\n 'suscipit recusandae consequuntur expedita et cum\\n' +\n 'reprehenderit molestiae ut ut quas totam\\n' +\n 'nostrum rerum est autem sunt rem eveniet architecto'\n}\n*/\n```\n\n**Operations Summary**\n\n| Operation | Description |\n| ------------------------ | --------------------------------------------------------------------------------------- |\n| `get`,`post`,`put`... | Send a request without first accessing the `HttpClient` service. |\n| `filterOrElse` | Filters the result of a response, or runs an alternative effect if the predicate fails. |\n| `filterOrFail` | Filters the result of a response, or throws an error if the predicate fails. |\n| `filterStatus` | Filters responses by HTTP status code. |\n| `filterStatusOk` | Filters responses that return a 2xx status code. |\n| `followRedirects` | Follows HTTP redirects up to a specified number of times. |\n| `mapRequest` | Appends a transformation of the request object before sending it. |\n| `mapRequestEffect` | Appends an effectful transformation of the request object before sending it. |\n| `mapRequestInput` | Prepends a transformation of the request object before sending it. |\n| `mapRequestInputEffect` | Prepends an effectful transformation of the request object before sending it. |\n| `retry` | Retries the request based on a provided schedule or policy. |\n| `tap` | Performs an additional effect after a successful request. |\n| `tapRequest` | Performs an additional effect on the request before sending it. |\n| `withCookiesRef` | Associates a `Ref` of cookies with the client for handling cookies across requests. |\n| `withTracerDisabledWhen` | Disables tracing for specific requests based on a provided predicate. |\n| `withTracerPropagation` | Enables or disables tracing propagation for the request. |\n\n### Mapping Requests\n\nNote that `mapRequest` and `mapRequestEffect` add transformations at the end of the request chain, while `mapRequestInput` and `mapRequestInputEffect` apply transformations at the start:\n\n```ts\nimport { FetchHttpClient, HttpClient } from \"@effect/platform\"\nimport { Effect } from \"effect\"\n\nconst program = Effect.gen(function* () {\n const client = (yield* HttpClient.HttpClient).pipe(\n // Append transformation\n HttpClient.mapRequest((req) => {\n console.log(1)\n return req\n }),\n // Another append transformation\n HttpClient.mapRequest((req) => {\n console.log(2)\n return req\n }),\n // Prepend transformation, this executes first\n HttpClient.mapRequestInput((req) => {\n console.log(3)\n return req\n })\n )\n\n const response = yield* client.get(\n \"https://jsonplaceholder.typicode.com/posts/1\"\n )\n\n const json = yield* response.json\n\n console.log(json)\n}).pipe(Effect.provide(FetchHttpClient.layer))\n\nEffect.runPromise(program)\n/*\nOutput:\n3\n1\n2\n{\n userId: 1,\n id: 1,\n title: 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit',\n body: 'quia et suscipit\\n' +\n 'suscipit recusandae consequuntur expedita et cum\\n' +\n 'reprehenderit molestiae ut ut quas totam\\n' +\n 'nostrum rerum est autem sunt rem eveniet architecto'\n}\n*/\n```\n\n### Persisting Cookies\n\nYou can manage cookies across requests using the `HttpClient.withCookiesRef` function, which associates a reference to a `Cookies` object with the client.\n\n```ts\nimport { Cookies, FetchHttpClient, HttpClient } from \"@effect/platform\"\nimport { Effect, Ref } from \"effect\"\n\nconst program = Effect.gen(function* () {\n // Create a reference to store cookies\n const ref = yield* Ref.make(Cookies.empty)\n\n // Access the HttpClient and associate the cookies reference with it\n const client = (yield* HttpClient.HttpClient).pipe(\n HttpClient.withCookiesRef(ref)\n )\n\n // Make a GET request to the specified URL\n yield* client.get(\"https://www.google.com/\")\n\n // Log the keys of the cookies stored in the reference\n console.log(Object.keys((yield* ref).cookies))\n}).pipe(Effect.provide(FetchHttpClient.layer))\n\nEffect.runPromise(program)\n// Output: [ 'SOCS', 'AEC', '__Secure-ENID' ]\n```\n\n## RequestInit Options\n\nYou can customize the `FetchHttpClient` by passing `RequestInit` options to configure aspects of the HTTP requests, such as credentials, headers, and more.\n\nIn this example, we customize the `FetchHttpClient` to include credentials with every request:\n\n```ts\nimport { FetchHttpClient, HttpClient } from \"@effect/platform\"\nimport { Effect, Layer } from \"effect\"\n\nconst CustomFetchLive = FetchHttpClient.layer.pipe(\n Layer.provide(\n Layer.succeed(FetchHttpClient.RequestInit, {\n credentials: \"include\"\n })\n )\n)\n\nconst program = Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const response = yield* client.get(\n \"https://jsonplaceholder.typicode.com/posts/1\"\n )\n const json = yield* response.json\n console.log(json)\n}).pipe(Effect.provide(CustomFetchLive))\n```\n\n## Create a Custom HttpClient\n\nYou can create a custom `HttpClient` using the `HttpClient.make` function. This allows you to simulate or mock server responses within your application.\n\n```ts\nimport { HttpClient, HttpClientResponse } from \"@effect/platform\"\nimport { Effect, Layer } from \"effect\"\n\nconst myClient = HttpClient.make((req) =>\n Effect.succeed(\n HttpClientResponse.fromWeb(\n req,\n // Simulate a response from a server\n new Response(\n JSON.stringify({\n userId: 1,\n id: 1,\n title: \"title...\",\n body: \"body...\"\n })\n )\n )\n )\n)\n\nconst program = Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const response = yield* client.get(\n \"https://jsonplaceholder.typicode.com/posts/1\"\n )\n const json = yield* response.json\n console.log(json)\n}).pipe(\n // Provide the HttpClient\n Effect.provide(Layer.succeed(HttpClient.HttpClient, myClient))\n)\n\nEffect.runPromise(program)\n/*\nOutput:\n{ userId: 1, id: 1, title: 'title...', body: 'body...' }\n*/\n```\n\n## HttpClientRequest\n\n### Overview\n\nYou can create a `HttpClientRequest` using the following provided constructors:\n\n| Constructor | Description |\n| --------------------------- | ------------------------- |\n| `HttpClientRequest.del` | Create a DELETE request |\n| `HttpClientRequest.get` | Create a GET request |\n| `HttpClientRequest.head` | Create a HEAD request |\n| `HttpClientRequest.options` | Create an OPTIONS request |\n| `HttpClientRequest.patch` | Create a PATCH request |\n| `HttpClientRequest.post` | Create a POST request |\n| `HttpClientRequest.put` | Create a PUT request |\n\n### Setting Headers\n\nWhen making HTTP requests, sometimes you need to include additional information in the request headers. You can set headers using the `setHeader` function for a single header or `setHeaders` for multiple headers simultaneously.\n\n```ts\nimport { HttpClientRequest } from \"@effect/platform\"\n\nconst req = HttpClientRequest.get(\"https://api.example.com/data\").pipe(\n // Setting a single header\n HttpClientRequest.setHeader(\"Authorization\", \"Bearer your_token_here\"),\n // Setting multiple headers\n HttpClientRequest.setHeaders({\n \"Content-Type\": \"application/json; charset=UTF-8\",\n \"Custom-Header\": \"CustomValue\"\n })\n)\n\nconsole.log(JSON.stringify(req.headers, null, 2))\n/*\nOutput:\n{\n \"authorization\": \"Bearer your_token_here\",\n \"content-type\": \"application/json; charset=UTF-8\",\n \"custom-header\": \"CustomValue\"\n}\n*/\n```\n\n### basicAuth\n\nTo include basic authentication in your HTTP request, you can use the `basicAuth` method provided by `HttpClientRequest`.\n\n```ts\nimport { HttpClientRequest } from \"@effect/platform\"\n\nconst req = HttpClientRequest.get(\"https://api.example.com/data\").pipe(\n HttpClientRequest.basicAuth(\"your_username\", \"your_password\")\n)\n\nconsole.log(JSON.stringify(req.headers, null, 2))\n/*\nOutput:\n{\n \"authorization\": \"Basic eW91cl91c2VybmFtZTp5b3VyX3Bhc3N3b3Jk\"\n}\n*/\n```\n\n### bearerToken\n\nTo include a Bearer token in your HTTP request, use the `bearerToken` method provided by `HttpClientRequest`.\n\n```ts\nimport { HttpClientRequest } from \"@effect/platform\"\n\nconst req = HttpClientRequest.get(\"https://api.example.com/data\").pipe(\n HttpClientRequest.bearerToken(\"your_token\")\n)\n\nconsole.log(JSON.stringify(req.headers, null, 2))\n/*\nOutput:\n{\n \"authorization\": \"Bearer your_token\"\n}\n*/\n```\n\n### accept\n\nTo specify the media types that are acceptable for the response, use the `accept` method provided by `HttpClientRequest`.\n\n```ts\nimport { HttpClientRequest } from \"@effect/platform\"\n\nconst req = HttpClientRequest.get(\"https://api.example.com/data\").pipe(\n HttpClientRequest.accept(\"application/xml\")\n)\n\nconsole.log(JSON.stringify(req.headers, null, 2))\n/*\nOutput:\n{\n \"accept\": \"application/xml\"\n}\n*/\n```\n\n### acceptJson\n\nTo indicate that the client accepts JSON responses, use the `acceptJson` method provided by `HttpClientRequest`.\n\n```ts\nimport { HttpClientRequest } from \"@effect/platform\"\n\nconst req = HttpClientRequest.get(\"https://api.example.com/data\").pipe(\n HttpClientRequest.acceptJson\n)\n\nconsole.log(JSON.stringify(req.headers, null, 2))\n/*\nOutput:\n{\n \"accept\": \"application/json\"\n}\n*/\n```\n\n## GET\n\n### Converting the Response\n\nThe `HttpClientResponse` provides several methods to convert a response into different formats.\n\n**Example: Converting to JSON**\n\n```ts\nimport { FetchHttpClient, HttpClient } from \"@effect/platform\"\nimport { NodeRuntime } from \"@effect/platform-node\"\nimport { Console, Effect } from \"effect\"\n\nconst getPostAsJson = Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const response = yield* client.get(\n \"https://jsonplaceholder.typicode.com/posts/1\"\n )\n return yield* response.json\n}).pipe(Effect.provide(FetchHttpClient.layer))\n\ngetPostAsJson.pipe(\n Effect.andThen((post) => Console.log(typeof post, post)),\n NodeRuntime.runMain\n)\n/*\nOutput:\nobject {\n userId: 1,\n id: 1,\n title: 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit',\n body: 'quia et suscipit\\n' +\n 'suscipit recusandae consequuntur expedita et cum\\n' +\n 'reprehenderit molestiae ut ut quas totam\\n' +\n 'nostrum rerum est autem sunt rem eveniet architecto'\n}\n*/\n```\n\n**Example: Converting to Text**\n\n```ts\nimport { FetchHttpClient, HttpClient } from \"@effect/platform\"\nimport { NodeRuntime } from \"@effect/platform-node\"\nimport { Console, Effect } from \"effect\"\n\nconst getPostAsText = Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const response = yield* client.get(\n \"https://jsonplaceholder.typicode.com/posts/1\"\n )\n return yield* response.text\n}).pipe(Effect.provide(FetchHttpClient.layer))\n\ngetPostAsText.pipe(\n Effect.andThen((post) => Console.log(typeof post, post)),\n NodeRuntime.runMain\n)\n/*\nOutput:\nstring {\n userId: 1,\n id: 1,\n title: 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit',\n body: 'quia et suscipit\\n' +\n 'suscipit recusandae consequuntur expedita et cum\\n' +\n 'reprehenderit molestiae ut ut quas totam\\n' +\n 'nostrum rerum est autem sunt rem eveniet architecto'\n}\n*/\n```\n\n**Methods Summary**\n\n| Method | Description |\n| --------------- | ------------------------------------- |\n| `arrayBuffer` | Convert to `ArrayBuffer` |\n| `formData` | Convert to `FormData` |\n| `json` | Convert to JSON |\n| `stream` | Convert to a `Stream` of `Uint8Array` |\n| `text` | Convert to text |\n| `urlParamsBody` | Convert to `UrlParams` |\n\n### Decoding Data with Schemas\n\nA common use case when fetching data is to validate the received format. For this purpose, the `HttpClientResponse` module is integrated with `effect/Schema`.\n\n```ts\nimport {\n FetchHttpClient,\n HttpClient,\n HttpClientResponse\n} from \"@effect/platform\"\nimport { NodeRuntime } from \"@effect/platform-node\"\nimport { Console, Effect, Schema } from \"effect\"\n\nconst Post = Schema.Struct({\n id: Schema.Number,\n title: Schema.String\n})\n\nconst getPostAndValidate = Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const response = yield* client.get(\n \"https://jsonplaceholder.typicode.com/posts/1\"\n )\n return yield* HttpClientResponse.schemaBodyJson(Post)(response)\n}).pipe(Effect.provide(FetchHttpClient.layer))\n\ngetPostAndValidate.pipe(Effect.andThen(Console.log), NodeRuntime.runMain)\n/*\nOutput:\n{\n id: 1,\n title: 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit'\n}\n*/\n```\n\nIn this example, we define a schema for a post object with properties `id` and `title`. Then, we fetch the data and validate it against this schema using `HttpClientResponse.schemaBodyJson`. Finally, we log the validated post object.\n\n### Filtering And Error Handling\n\nIt's important to note that `HttpClient.get` doesn't consider non-`200` status codes as errors by default. This design choice allows for flexibility in handling different response scenarios. For instance, you might have a schema union where the status code serves as the discriminator, enabling you to define a schema that encompasses all possible response cases.\n\nYou can use `HttpClient.filterStatusOk` to ensure only `2xx` responses are treated as successes.\n\nIn this example, we attempt to fetch a non-existent page and don't receive any error:\n\n```ts\nimport { FetchHttpClient, HttpClient } from \"@effect/platform\"\nimport { NodeRuntime } from \"@effect/platform-node\"\nimport { Console, Effect } from \"effect\"\n\nconst getText = Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const response = yield* client.get(\n \"https://jsonplaceholder.typicode.com/non-existing-page\"\n )\n return yield* response.text\n}).pipe(Effect.provide(FetchHttpClient.layer))\n\ngetText.pipe(Effect.andThen(Console.log), NodeRuntime.runMain)\n/*\nOutput:\n{}\n*/\n```\n\nHowever, if we use `HttpClient.filterStatusOk`, an error is logged:\n\n```ts\nimport { FetchHttpClient, HttpClient } from \"@effect/platform\"\nimport { NodeRuntime } from \"@effect/platform-node\"\nimport { Console, Effect } from \"effect\"\n\nconst getText = Effect.gen(function* () {\n const client = (yield* HttpClient.HttpClient).pipe(HttpClient.filterStatusOk)\n const response = yield* client.get(\n \"https://jsonplaceholder.typicode.com/non-existing-page\"\n )\n return yield* response.text\n}).pipe(Effect.provide(FetchHttpClient.layer))\n\ngetText.pipe(Effect.andThen(Console.log), NodeRuntime.runMain)\n/*\nOutput:\n[17:37:59.923] ERROR (#0):\n ResponseError: StatusCode: non 2xx status code (404 GET https://jsonplaceholder.typicode.com/non-existing-page)\n ... stack trace ...\n*/\n```\n\n## POST\n\nTo make a POST request, you can use the `HttpClientRequest.post` function provided by the `HttpClientRequest` module. Here's an example of how to create and send a POST request:\n\n```ts\nimport {\n FetchHttpClient,\n HttpClient,\n HttpClientRequest\n} from \"@effect/platform\"\nimport { NodeRuntime } from \"@effect/platform-node\"\nimport { Console, Effect } from \"effect\"\n\nconst addPost = Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n return yield* HttpClientRequest.post(\n \"https://jsonplaceholder.typicode.com/posts\"\n ).pipe(\n HttpClientRequest.bodyJson({\n title: \"foo\",\n body: \"bar\",\n userId: 1\n }),\n Effect.flatMap(client.execute),\n Effect.flatMap((res) => res.json)\n )\n}).pipe(Effect.provide(FetchHttpClient.layer))\n\naddPost.pipe(Effect.andThen(Console.log), NodeRuntime.runMain)\n/*\nOutput:\n{ title: 'foo', body: 'bar', userId: 1, id: 101 }\n*/\n```\n\nIf you need to send data in a format other than JSON, such as plain text, you can use different APIs provided by `HttpClientRequest`.\n\nIn the following example, we send the data as text:\n\n```ts\nimport {\n FetchHttpClient,\n HttpClient,\n HttpClientRequest\n} from \"@effect/platform\"\nimport { NodeRuntime } from \"@effect/platform-node\"\nimport { Console, Effect } from \"effect\"\n\nconst addPost = Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n return yield* HttpClientRequest.post(\n \"https://jsonplaceholder.typicode.com/posts\"\n ).pipe(\n HttpClientRequest.bodyText(\n JSON.stringify({\n title: \"foo\",\n body: \"bar\",\n userId: 1\n }),\n \"application/json; charset=UTF-8\"\n ),\n client.execute,\n Effect.flatMap((res) => res.json)\n )\n}).pipe(Effect.provide(FetchHttpClient.layer))\n\naddPost.pipe(Effect.andThen(Console.log), NodeRuntime.runMain)\n/*\nOutput:\n{ title: 'foo', body: 'bar', userId: 1, id: 101 }\n*/\n```\n\n### Decoding Data with Schemas\n\nA common use case when fetching data is to validate the received format. For this purpose, the `HttpClientResponse` module is integrated with `effect/Schema`.\n\n```ts\nimport {\n FetchHttpClient,\n HttpClient,\n HttpClientRequest,\n HttpClientResponse\n} from \"@effect/platform\"\nimport { NodeRuntime } from \"@effect/platform-node\"\nimport { Console, Effect, Schema } from \"effect\"\n\nconst Post = Schema.Struct({\n id: Schema.Number,\n title: Schema.String\n})\n\nconst addPost = Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n return yield* HttpClientRequest.post(\n \"https://jsonplaceholder.typicode.com/posts\"\n ).pipe(\n HttpClientRequest.bodyText(\n JSON.stringify({\n title: \"foo\",\n body: \"bar\",\n userId: 1\n }),\n \"application/json; charset=UTF-8\"\n ),\n client.execute,\n Effect.flatMap(HttpClientResponse.schemaBodyJson(Post))\n )\n}).pipe(Effect.provide(FetchHttpClient.layer))\n\naddPost.pipe(Effect.andThen(Console.log), NodeRuntime.runMain)\n/*\nOutput:\n{ id: 101, title: 'foo' }\n*/\n```\n\n## Testing\n\n### Injecting Fetch\n\nTo test HTTP requests, you can inject a mock fetch implementation.\n\n```ts\nimport { FetchHttpClient, HttpClient } from \"@effect/platform\"\nimport { Effect, Layer } from \"effect\"\nimport * as assert from \"node:assert\"\n\n// Mock fetch implementation\nconst FetchTest = Layer.succeed(FetchHttpClient.Fetch, () =>\n Promise.resolve(new Response(\"not found\", { status: 404 }))\n)\n\nconst TestLayer = FetchHttpClient.layer.pipe(Layer.provide(FetchTest))\n\nconst program = Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n\n return yield* client\n .get(\"https://www.google.com/\")\n .pipe(Effect.flatMap((res) => res.text))\n})\n\n// Test\nEffect.gen(function* () {\n const response = yield* program\n assert.equal(response, \"not found\")\n}).pipe(Effect.provide(TestLayer), Effect.runPromise)\n```\n\n# HTTP Server\n\n## Overview\n\nThis section provides a simplified explanation of key concepts within the `@effect/platform` TypeScript library, focusing on components used to build HTTP servers. Understanding these terms and their relationships helps in structuring and managing server applications effectively.\n\n### Core Concepts\n\n- **HttpApp**: This is an `Effect` which results in a value `A`. It can utilize `ServerRequest` to produce the outcome `A`. Essentially, an `HttpApp` represents an application component that handles HTTP requests and generates responses based on those requests.\n\n- **Default** (HttpApp): A special type of `HttpApp` that specifically produces a `ServerResponse` as its output `A`. This is the most common form of application where each interaction is expected to result in an HTTP response.\n\n- **Server**: A construct that takes a `Default` app and converts it into an `Effect`. This serves as the execution layer where the `Default` app is operated, handling incoming requests and serving responses.\n\n- **Router**: A type of `Default` app where the possible error outcome is `RouteNotFound`. Routers are used to direct incoming requests to appropriate handlers based on the request path and method.\n\n- **Handler**: Another form of `Default` app, which has access to both `RouteContext` and `ServerRequest.ParsedSearchParams`. Handlers are specific functions designed to process requests and generate responses.\n\n- **Middleware**: Functions that transform a `Default` app into another `Default` app. Middleware can be used to modify requests, responses, or handle tasks like logging, authentication, and more. Middleware can be applied in two ways:\n - On a `Router` using `router.use: Handler -> Default` which applies the middleware to specific routes.\n - On a `Server` using `server.serve: () -> Layer | Middleware -> Layer` which applies the middleware globally to all routes handled by the server.\n\n### Applying Concepts\n\nThese components are designed to work together in a modular and flexible way, allowing developers to build complex server applications with reusable components. Here's how you might typically use these components in a project:\n\n1. **Create Handlers**: Define functions that process specific types of requests (e.g., GET, POST) and return responses.\n\n2. **Set Up Routers**: Organize handlers into routers, where each router manages a subset of application routes.\n\n3. **Apply Middleware**: Enhance routers or entire servers with middleware to add extra functionality like error handling or request logging.\n\n4. **Initialize the Server**: Wrap the main router with server functionality, applying any server-wide middleware, and start listening for requests.\n\n## Getting Started\n\n### Hello world example\n\nIn this example, we will create a simple HTTP server that listens on port `3000`. The server will respond with \"Hello World!\" when a request is made to the root URL (/) and return a `500` error for all other paths.\n\nNode.js Example\n\n```ts\nimport { HttpRouter, HttpServer, HttpServerResponse } from \"@effect/platform\"\nimport { NodeHttpServer, NodeRuntime } from \"@effect/platform-node\"\nimport { Layer } from \"effect\"\nimport { createServer } from \"node:http\"\n\n// Define the router with a single route for the root URL\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\"/\", HttpServerResponse.text(\"Hello World\"))\n)\n\n// Set up the application server with logging\nconst app = router.pipe(HttpServer.serve(), HttpServer.withLogAddress)\n\n// Specify the port\nconst port = 3000\n\n// Create a server layer with the specified port\nconst ServerLive = NodeHttpServer.layer(() => createServer(), { port })\n\n// Run the application\nNodeRuntime.runMain(Layer.launch(Layer.provide(app, ServerLive)))\n\n/*\nOutput:\ntimestamp=... level=INFO fiber=#0 message=\"Listening on http://localhost:3000\"\n*/\n```\n\n> [!NOTE]\n> The `HttpServer.withLogAddress` middleware logs the address and port where the server is listening, helping to confirm that the server is running correctly and accessible on the expected endpoint.\n\nBun Example\n\n```ts\nimport { HttpRouter, HttpServer, HttpServerResponse } from \"@effect/platform\"\nimport { BunHttpServer, BunRuntime } from \"@effect/platform-bun\"\nimport { Layer } from \"effect\"\n\n// Define the router with a single route for the root URL\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\"/\", HttpServerResponse.text(\"Hello World\"))\n)\n\n// Set up the application server with logging\nconst app = router.pipe(HttpServer.serve(), HttpServer.withLogAddress)\n\n// Specify the port\nconst port = 3000\n\n// Create a server layer with the specified port\nconst ServerLive = BunHttpServer.layer({ port })\n\n// Run the application\nBunRuntime.runMain(Layer.launch(Layer.provide(app, ServerLive)))\n\n/*\nOutput:\ntimestamp=... level=INFO fiber=#0 message=\"Listening on http://localhost:3000\"\n*/\n```\n\nTo avoid boilerplate code for the final server setup, we'll use a helper function from the `listen.ts` file:\n\nNode.js Example\n\n```ts\nimport type { HttpPlatform, HttpServer } from \"@effect/platform\"\nimport { NodeHttpServer, NodeRuntime } from \"@effect/platform-node\"\nimport { Layer } from \"effect\"\nimport { createServer } from \"node:http\"\n\nexport const listen = (\n app: Layer.Layer<\n never,\n never,\n HttpPlatform.HttpPlatform | HttpServer.HttpServer\n >,\n port: number\n) =>\n NodeRuntime.runMain(\n Layer.launch(\n Layer.provide(\n app,\n NodeHttpServer.layer(() => createServer(), { port })\n )\n )\n )\n```\n\nBun Example\n\n```ts\nimport type { HttpPlatform, HttpServer } from \"@effect/platform\"\nimport { BunHttpServer, BunRuntime } from \"@effect/platform-bun\"\nimport { Layer } from \"effect\"\n\nexport const listen = (\n app: Layer.Layer<\n never,\n never,\n HttpPlatform.HttpPlatform | HttpServer.HttpServer\n >,\n port: number\n) =>\n BunRuntime.runMain(\n Layer.launch(Layer.provide(app, BunHttpServer.layer({ port })))\n )\n```\n\n### Basic routing\n\nRouting refers to determining how an application responds to a client request to a particular endpoint, which is a URI (or path) and a specific HTTP request method (GET, POST, and so on).\n\nRoute definition takes the following structure:\n\n```\nrouter.pipe(HttpRouter.METHOD(PATH, HANDLER))\n```\n\nWhere:\n\n- **router** is an instance of `Router` (`import type { Router } from \"@effect/platform/Http/Router\"`).\n- **METHOD** is an HTTP request method, in lowercase (e.g., get, post, put, del).\n- **PATH** is the path on the server (e.g., \"/\", \"/user\").\n- **HANDLER** is the action that gets executed when the route is matched.\n\nThe following examples illustrate defining simple routes.\n\nRespond with `\"Hello World!\"` on the homepage:\n\n```ts\nrouter.pipe(HttpRouter.get(\"/\", HttpServerResponse.text(\"Hello World\")))\n```\n\nRespond to POST request on the root route (/), the application's home page:\n\n```ts\nrouter.pipe(HttpRouter.post(\"/\", HttpServerResponse.text(\"Got a POST request\")))\n```\n\nRespond to a PUT request to the `/user` route:\n\n```ts\nrouter.pipe(\n HttpRouter.put(\"/user\", HttpServerResponse.text(\"Got a PUT request at /user\"))\n)\n```\n\nRespond to a DELETE request to the `/user` route:\n\n```ts\nrouter.pipe(\n HttpRouter.del(\n \"/user\",\n HttpServerResponse.text(\"Got a DELETE request at /user\")\n )\n)\n```\n\n### Serving static files\n\nTo serve static files such as images, CSS files, and JavaScript files, use the `HttpServerResponse.file` built-in action.\n\n```ts\nimport { HttpRouter, HttpServer, HttpServerResponse } from \"@effect/platform\"\nimport { listen } from \"./listen.js\"\n\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\"/\", HttpServerResponse.file(\"index.html\"))\n)\n\nconst app = router.pipe(HttpServer.serve())\n\nlisten(app, 3000)\n```\n\nCreate an `index.html` file in your project directory:\n\n```html filename=\"index.html\"\n<!DOCTYPE html>\n<html>\n <head>\n <meta charset=\"utf-8\" />\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n <title>index.html</title>\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n </head>\n <body>\n index.html\n </body>\n</html>\n```\n\n## Routing\n\nRouting refers to how an application's endpoints (URIs) respond to client requests.\n\nYou define routing using methods of the `HttpRouter` object that correspond to HTTP methods; for example, `HttpRouter.get()` to handle GET requests and `HttpRouter.post` to handle POST requests. You can also use `HttpRouter.all()` to handle all HTTP methods.\n\nThese routing methods specify a `Route.Handler` called when the application receives a request to the specified route (endpoint) and HTTP method. In other words, the application “listens” for requests that match the specified route(s) and method(s), and when it detects a match, it calls the specified handler.\n\nThe following code is an example of a very basic route.\n\n```ts\n// respond with \"hello world\" when a GET request is made to the homepage\nHttpRouter.get(\"/\", HttpServerResponse.text(\"Hello World\"))\n```\n\n### Route methods\n\nA route method is derived from one of the HTTP methods, and is attached to an instance of the `HttpRouter` object.\n\nThe following code is an example of routes that are defined for the GET and the POST methods to the root of the app.\n\n```ts\n// GET method route\nHttpRouter.get(\"/\", HttpServerResponse.text(\"GET request to the homepage\"))\n\n// POST method route\nHttpRouter.post(\"/\", HttpServerResponse.text(\"POST request to the homepage\"))\n```\n\n`HttpRouter` supports methods that correspond to all HTTP request methods: `get`, `post`, and so on.\n\nThere is a special routing method, `HttpRouter.all()`, used to load middleware functions at a path for **all** HTTP request methods. For example, the following handler is executed for requests to the route “/secret” whether using GET, POST, PUT, DELETE.\n\n```ts\nHttpRouter.all(\n \"/secret\",\n HttpServerResponse.empty().pipe(\n Effect.tap(Console.log(\"Accessing the secret section ...\"))\n )\n)\n```\n\n### Route paths\n\nRoute paths, when combined with a request method, define the endpoints where requests can be made. Route paths can be specified as strings according to the following type:\n\n```ts\ntype PathInput = `/${string}` | \"*\"\n```\n\n> [!NOTE]\n> Query strings are not part of the route path.\n\nHere are some examples of route paths based on strings.\n\nThis route path will match requests to the root route, /.\n\n```ts\nHttpRouter.get(\"/\", HttpServerResponse.text(\"root\"))\n```\n\nThis route path will match requests to `/user`.\n\n```ts\nHttpRouter.get(\"/user\", HttpServerResponse.text(\"user\"))\n```\n\nThis route path matches requests to any path starting with `/user` (e.g., `/user`, `/users`, etc.)\n\n```ts\nHttpRouter.get(\n \"/user*\",\n Effect.map(HttpServerRequest.HttpServerRequest, (req) =>\n HttpServerResponse.text(req.url)\n )\n)\n```\n\n### Route parameters\n\nRoute parameters are named URL segments that are used to capture the values specified at their position in the URL. By using a schema the captured values are populated in an object, with the name of the route parameter specified in the path as their respective keys.\n\nRoute parameters are named segments in a URL that capture the values specified at those positions. These captured values are stored in an object, with the parameter names used as keys.\n\nFor example:\n\n```\nRoute path: /users/:userId/books/:bookId\nRequest URL: http://localhost:3000/users/34/books/8989\nparams: { \"userId\": \"34\", \"bookId\": \"8989\" }\n```\n\nTo define routes with parameters, include the parameter names in the path and use a schema to validate and parse these parameters, as shown below.\n\n```ts\nimport { HttpRouter, HttpServer, HttpServerResponse } from \"@effect/platform\"\nimport { Effect, Schema } from \"effect\"\nimport { listen } from \"./listen.js\"\n\n// Define the schema for route parameters\nconst Params = Schema.Struct({\n userId: Schema.String,\n bookId: Schema.String\n})\n\n// Create a router with a route that captures parameters\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\n \"/users/:userId/books/:bookId\",\n HttpRouter.schemaPathParams(Params).pipe(\n Effect.flatMap((params) => HttpServerResponse.json(params))\n )\n )\n)\n\nconst app = router.pipe(HttpServer.serve())\n\nlisten(app, 3000)\n```\n\n### Response methods\n\nThe methods on `HttpServerResponse` object in the following table can send a response to the client, and terminate the request-response cycle. If none of these methods are called from a route handler, the client request will be left hanging.\n\n| Method | Description |\n| ------------ | ------------------------------ |\n| **empty** | Sends an empty response. |\n| **formData** | Sends form data. |\n| **html** | Sends an HTML response. |\n| **raw** | Sends a raw response. |\n| **setBody** | Sets the body of the response. |\n| **stream** | Sends a streaming response. |\n| **text** | Sends a plain text response. |\n\n### Router\n\nUse the `HttpRouter` object to create modular, mountable route handlers. A `Router` instance is a complete middleware and routing system, often referred to as a \"mini-app.\"\n\nThe following example shows how to create a router as a module, define some routes, and mount the router module on a path in the main app.\n\nCreate a file named `birds.ts` in your app directory with the following content:\n\n```ts\nimport { HttpRouter, HttpServerResponse } from \"@effect/platform\"\n\nexport const birds = HttpRouter.empty.pipe(\n HttpRouter.get(\"/\", HttpServerResponse.text(\"Birds home page\")),\n HttpRouter.get(\"/about\", HttpServerResponse.text(\"About birds\"))\n)\n```\n\nIn your main application file, load the router module and mount it.\n\n```ts\nimport { HttpRouter, HttpServer } from \"@effect/platform\"\nimport { birds } from \"./birds.js\"\nimport { listen } from \"./listen.js\"\n\n// Create the main router and mount the birds router\nconst router = HttpRouter.empty.pipe(HttpRouter.mount(\"/birds\", birds))\n\nconst app = router.pipe(HttpServer.serve())\n\nlisten(app, 3000)\n```\n\nWhen you run this code, your application will be able to handle requests to `/birds` and `/birds/about`, serving the respective responses defined in the `birds` router module.\n\n## Writing Middleware\n\nIn this section, we'll build a simple \"Hello World\" application and demonstrate how to add three middleware functions: `myLogger` for logging, `requestTime` for displaying request timestamps, and `validateCookies` for validating incoming cookies.\n\n### Example Application\n\nHere is an example of a basic \"Hello World\" application with middleware.\n\n### Middleware `myLogger`\n\nThis middleware logs \"LOGGED\" whenever a request passes through it.\n\n```ts\nconst myLogger = HttpMiddleware.make((app) =>\n Effect.gen(function* () {\n console.log(\"LOGGED\")\n return yield* app\n })\n)\n```\n\nTo use the middleware, add it to the router using `HttpRouter.use()`:\n\n```ts\nimport {\n HttpMiddleware,\n HttpRouter,\n HttpServer,\n HttpServerResponse\n} from \"@effect/platform\"\nimport { Effect } from \"effect\"\nimport { listen } from \"./listen.js\"\n\nconst myLogger = HttpMiddleware.make((app) =>\n Effect.gen(function* () {\n console.log(\"LOGGED\")\n return yield* app\n })\n)\n\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\"/\", HttpServerResponse.text(\"Hello World\"))\n)\n\nconst app = router.pipe(HttpRouter.use(myLogger), HttpServer.serve())\n\nlisten(app, 3000)\n```\n\nWith this setup, every request to the app will log \"LOGGED\" to the terminal. Middleware execute in the order they are loaded.\n\n### Middleware `requestTime`\n\nNext, we'll create a middleware that records the timestamp of each HTTP request and provides it via a service called `RequestTime`.\n\n```ts\nclass RequestTime extends Context.Tag(\"RequestTime\")<RequestTime, number>() {}\n\nconst requestTime = HttpMiddleware.make((app) =>\n Effect.gen(function* () {\n return yield* app.pipe(Effect.provideService(RequestTime, Date.now()))\n })\n)\n```\n\nUpdate the app to use this middleware and display the timestamp in the response:\n\n```ts\nimport {\n HttpMiddleware,\n HttpRouter,\n HttpServer,\n HttpServerResponse\n} from \"@effect/platform\"\nimport { Context, Effect } from \"effect\"\nimport { listen } from \"./listen.js\"\n\nclass RequestTime extends Context.Tag(\"RequestTime\")<RequestTime, number>() {}\n\nconst requestTime = HttpMiddleware.make((app) =>\n Effect.gen(function* () {\n return yield* app.pipe(Effect.provideService(RequestTime, Date.now()))\n })\n)\n\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\n \"/\",\n Effect.gen(function* () {\n const requestTime = yield* RequestTime\n const responseText = `Hello World<br/><small>Requested at: ${requestTime}</small>`\n return yield* HttpServerResponse.html(responseText)\n })\n )\n)\n\nconst app = router.pipe(HttpRouter.use(requestTime), HttpServer.serve())\n\nlisten(app, 3000)\n```\n\nNow, when you make a request to the root path, the response will include the timestamp of the request.\n\n### Middleware `validateCookies`\n\nFinally, we'll create a middleware that validates incoming cookies. If the cookies are invalid, it sends a 400 response.\n\nHere's an example that validates cookies using an external service:\n\n```ts\nclass CookieError {\n readonly _tag = \"CookieError\"\n}\n\nconst externallyValidateCookie = (testCookie: string | undefined) =>\n testCookie && testCookie.length > 0\n ? Effect.succeed(testCookie)\n : Effect.fail(new CookieError())\n\nconst cookieValidator = HttpMiddleware.make((app) =>\n Effect.gen(function* () {\n const req = yield* HttpServerRequest.HttpServerRequest\n yield* externallyValidateCookie(req.cookies.testCookie)\n return yield* app\n }).pipe(\n Effect.catchTag(\"CookieError\", () =>\n HttpServerResponse.text(\"Invalid cookie\")\n )\n )\n)\n```\n\nUpdate the app to use the `cookieValidator` middleware:\n\n```ts\nimport {\n HttpMiddleware,\n HttpRouter,\n HttpServer,\n HttpServerRequest,\n HttpServerResponse\n} from \"@effect/platform\"\nimport { Effect } from \"effect\"\nimport { listen } from \"./listen.js\"\n\nclass CookieError {\n readonly _tag = \"CookieError\"\n}\n\nconst externallyValidateCookie = (testCookie: string | undefined) =>\n testCookie && testCookie.length > 0\n ? Effect.succeed(testCookie)\n : Effect.fail(new CookieError())\n\nconst cookieValidator = HttpMiddleware.make((app) =>\n Effect.gen(function* () {\n const req = yield* HttpServerRequest.HttpServerRequest\n yield* externallyValidateCookie(req.cookies.testCookie)\n return yield* app\n }).pipe(\n Effect.catchTag(\"CookieError\", () =>\n HttpServerResponse.text(\"Invalid cookie\")\n )\n )\n)\n\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\"/\", HttpServerResponse.text(\"Hello World\"))\n)\n\nconst app = router.pipe(HttpRouter.use(cookieValidator), HttpServer.serve())\n\nlisten(app, 3000)\n```\n\nTest the middleware with the following commands:\n\n```sh\ncurl -i http://localhost:3000\ncurl -i http://localhost:3000 --cookie \"testCookie=myvalue\"\ncurl -i http://localhost:3000 --cookie \"testCookie=\"\n```\n\nThis setup validates the `testCookie` and returns \"Invalid cookie\" if the validation fails, or \"Hello World\" if it passes.\n\n## Applying Middleware in Your Application\n\nMiddleware functions are powerful tools that allow you to modify the request-response cycle. Middlewares can be applied at various levels to achieve different scopes of influence:\n\n- **Route Level**: Apply middleware to individual routes.\n- **Router Level**: Apply middleware to a group of routes within a single router.\n- **Server Level**: Apply middleware across all routes managed by a server.\n\n### Applying Middleware at the Route Level\n\nAt the route level, middlewares are applied to specific endpoints, allowing for targeted modifications or enhancements such as logging, authentication, or parameter validation for a particular route.\n\n**Example**\n\nHere's a practical example showing how to apply middleware at the route level:\n\n```ts\nimport {\n HttpMiddleware,\n HttpRouter,\n HttpServer,\n HttpServerResponse\n} from \"@effect/platform\"\nimport { Effect } from \"effect\"\nimport { listen } from \"./listen.js\"\n\n// Middleware constructor that logs the name of the middleware\nconst withMiddleware = (name: string) =>\n HttpMiddleware.make((app) =>\n Effect.gen(function* () {\n console.log(name) // Log the middleware name when the route is accessed\n return yield* app // Continue with the original application flow\n })\n )\n\nconst router = HttpRouter.empty.pipe(\n // Applying middleware to route \"/a\"\n HttpRouter.get(\"/a\", HttpServerResponse.text(\"a\").pipe(withMiddleware(\"M1\"))),\n // Applying middleware to route \"/b\"\n HttpRouter.get(\"/b\", HttpServerResponse.text(\"b\").pipe(withMiddleware(\"M2\")))\n)\n\nconst app = router.pipe(HttpServer.serve())\n\nlisten(app, 3000)\n```\n\n**Testing the Middleware**\n\nYou can test the middleware by making requests to the respective routes and observing the console output:\n\n```sh\n# Test route /a\ncurl -i http://localhost:3000/a\n# Expected console output: M1\n\n# Test route /b\ncurl -i http://localhost:3000/b\n# Expected console output: M2\n```\n\n### Applying Middleware at the Router Level\n\nApplying middleware at the router level is an efficient way to manage common functionalities across multiple routes within your application. Middleware can handle tasks such as logging, authentication, and response modifications before reaching the actual route handlers.\n\n**Example**\n\nHere's how you can structure and apply middleware across different routers using the `@effect/platform` library:\n\n```ts\nimport {\n HttpMiddleware,\n HttpRouter,\n HttpServer,\n HttpServerResponse\n} from \"@effect/platform\"\nimport { Effect } from \"effect\"\nimport { listen } from \"./listen.js\"\n\n// Middleware constructor that logs the name of the middleware\nconst withMiddleware = (name: string) =>\n HttpMiddleware.make((app) =>\n Effect.gen(function* () {\n console.log(name) // Log the middleware name when a route is accessed\n return yield* app // Continue with the original application flow\n })\n )\n\n// Define Router1 with specific routes\nconst router1 = HttpRouter.empty.pipe(\n HttpRouter.get(\"/a\", HttpServerResponse.text(\"a\")), // Middleware M4, M3, M1 will apply\n HttpRouter.get(\"/b\", HttpServerResponse.text(\"b\")), // Middleware M4, M3, M1 will apply\n // Apply Middleware at the router level\n HttpRouter.use(withMiddleware(\"M1\")),\n HttpRouter.get(\"/c\", HttpServerResponse.text(\"c\")) // Middleware M4, M3 will apply\n)\n\n// Define Router2 with specific routes\nconst router2 = HttpRouter.empty.pipe(\n HttpRouter.get(\"/d\", HttpServerResponse.text(\"d\")), // Middleware M4, M2 will apply\n HttpRouter.get(\"/e\", HttpServerResponse.text(\"e\")), // Middleware M4, M2 will apply\n HttpRouter.get(\"/f\", HttpServerResponse.text(\"f\")), // Middleware M4, M2 will apply\n // Apply Middleware at the router level\n HttpRouter.use(withMiddleware(\"M2\"))\n)\n\n// Main router combining Router1 and Router2\nconst router = HttpRouter.empty.pipe(\n HttpRouter.mount(\"/r1\", router1),\n // Apply Middleware affecting all routes under /r1\n HttpRouter.use(withMiddleware(\"M3\")),\n HttpRouter.get(\"/g\", HttpServerResponse.text(\"g\")), // Only Middleware M4 will apply\n HttpRouter.mount(\"/r2\", router2),\n // Apply Middleware affecting all routes\n HttpRouter.use(withMiddleware(\"M4\"))\n)\n\n// Configure the application with the server middleware\nconst app = router.pipe(HttpServer.serve())\n\nlisten(app, 3000)\n```\n\n**Testing the Middleware**\n\nTo ensure that the middleware is working as expected, you can test it by making HTTP requests to the defined routes and checking the console output for middleware logs:\n\n```sh\n# Test route /a under router1\ncurl -i http://localhost:3000/r1/a\n# Expected console output: M4 M3 M1\n\n# Test route /c under router1\ncurl -i http://localhost:3000/r1/c\n# Expected console output: M4 M3\n\n# Test route /d under router2\ncurl -i http://localhost:3000/r2/d\n# Expected console output: M4 M2\n\n# Test route /g under the main router\ncurl -i http://localhost:3000/g\n# Expected console output: M4\n```\n\n### Applying Middleware at the Server Level\n\nApplying middleware at the server level allows you to introduce certain functionalities, such as logging, authentication, or general request processing, that affect every request handled by the server. This ensures that all incoming requests, regardless of the route, pass through the applied middleware, making it an essential feature for global error handling, logging, or authentication.\n\n**Example**\n\n```ts\nimport {\n HttpMiddleware,\n HttpRouter,\n HttpServer,\n HttpServerResponse\n} from \"@effect/platform\"\nimport { Effect } from \"effect\"\nimport { listen } from \"./listen.js\"\n\n// Middleware constructor that logs the name of the middleware\nconst withMiddleware = (name: string) =>\n HttpMiddleware.make((app) =>\n Effect.gen(function* () {\n console.log(name) // Log the middleware name when the route is accessed\n return yield* app // Continue with the original application flow\n })\n )\n\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\"/a\", HttpServerResponse.text(\"a\").pipe(withMiddleware(\"M1\"))),\n HttpRouter.get(\"/b\", HttpServerResponse.text(\"b\")),\n HttpRouter.use(withMiddleware(\"M2\")),\n HttpRouter.get(\"/\", HttpServerResponse.text(\"root\"))\n)\n\nconst app = router.pipe(HttpServer.serve(withMiddleware(\"M3\")))\n\nlisten(app, 3000)\n```\n\n**Testing the Middleware**\n\nTo confirm the middleware is functioning as intended, you can send HTTP requests to the defined routes and check the console for middleware logs:\n\n```sh\n# Test route /a and observe the middleware logs\ncurl -i http://localhost:3000/a\n# Expected console output: M3 M2 M1 - Middleware M3 (server-level), M2 (router-level), and M1 (route-level) apply.\n\n# Test route /b and observe the middleware logs\ncurl -i http://localhost:3000/b\n# Expected console output: M3 M2 - Middleware M3 (server-level) and M2 (router-level) apply.\n\n# Test route / and observe the middleware logs\ncurl -i http://localhost:3000/\n# Expected console output: M3 M2 - Middleware M3 (server-level) and M2 (router-level) apply.\n```\n\n### Applying Multiple Middlewares\n\nMiddleware functions are simply functions that transform a `Default` app into another `Default` app. This flexibility allows for stacking multiple middleware functions, much like composing functions in functional programming. The `flow` function from the `Effect` library facilitates this by enabling function composition.\n\n**Example**\n\n```ts\nimport {\n HttpMiddleware,\n HttpRouter,\n HttpServer,\n HttpServerResponse\n} from \"@effect/platform\"\nimport { Effect, flow } from \"effect\"\nimport { listen } from \"./listen.js\"\n\n// Middleware constructor that logs the middleware's name when a route is accessed\nconst withMiddleware = (name: string) =>\n HttpMiddleware.make((app) =>\n Effect.gen(function* () {\n console.log(name) // Log the middleware name\n return yield* app // Continue with the original application flow\n })\n )\n\n// Setup routes and apply multiple middlewares using flow for function composition\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\n \"/a\",\n HttpServerResponse.text(\"a\").pipe(\n flow(withMiddleware(\"M1\"), withMiddleware(\"M2\"))\n )\n ),\n HttpRouter.get(\"/b\", HttpServerResponse.text(\"b\")),\n // Apply combined middlewares to the entire router\n HttpRouter.use(flow(withMiddleware(\"M3\"), withMiddleware(\"M4\"))),\n HttpRouter.get(\"/\", HttpServerResponse.text(\"root\"))\n)\n\n// Apply combined middlewares at the server level\nconst app = router.pipe(\n HttpServer.serve(flow(withMiddleware(\"M5\"), withMiddleware(\"M6\")))\n)\n\nlisten(app, 3000)\n```\n\n**Testing the Middleware Composition**\n\nTo verify that the middleware is functioning as expected, you can send HTTP requests to the routes and check the console for the expected middleware log output:\n\n```sh\n# Test route /a to see the output from multiple middleware layers\ncurl -i http://localhost:3000/a\n# Expected console output: M6 M5 M4 M3 M2 M1\n\n# Test route /b where fewer middleware are applied\ncurl -i http://localhost:3000/b\n# Expected console output: M6 M5 M4 M3\n\n# Test the root route to confirm top-level middleware application\ncurl -i http://localhost:3000/\n# Expected console output: M6 M5\n```\n\n## Built-in middleware\n\n### Middleware Summary\n\n| Middleware | Description |\n| --------------------- | --------------------------------------------------------------------------------------------------------------------------------- |\n| **Logger** | Provides detailed logging of all requests and responses, aiding in debugging and monitoring application activities. |\n| **xForwardedHeaders** | Manages `X-Forwarded-*` headers to accurately maintain client information such as IP addresses and host names in proxy scenarios. |\n\n### logger\n\nThe `HttpMiddleware.logger` middleware enables logging for your entire application, providing insights into each request and response. Here's how to set it up:\n\n```ts\nimport {\n HttpMiddleware,\n HttpRouter,\n HttpServer,\n HttpServerResponse\n} from \"@effect/platform\"\nimport { listen } from \"./listen.js\"\n\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\"/\", HttpServerResponse.text(\"Hello World\"))\n)\n\n// Apply the logger middleware globally\nconst app = router.pipe(HttpServer.serve(HttpMiddleware.logger))\n\nlisten(app, 3000)\n/*\ncurl -i http://localhost:3000\ntimestamp=... level=INFO fiber=#0 message=\"Listening on http://0.0.0.0:3000\"\ntimestamp=... level=INFO fiber=#19 message=\"Sent HTTP response\" http.span.1=8ms http.status=200 http.method=GET http.url=/\ntimestamp=... level=INFO fiber=#20 cause=\"RouteNotFound: GET /favicon.ico not found\n at ...\n at http.server GET\" http.span.2=4ms http.status=500 http.method=GET http.url=/favicon.ico\n*/\n```\n\nTo disable the logger for specific routes, you can use `HttpMiddleware.withLoggerDisabled`:\n\n```ts\nimport {\n HttpMiddleware,\n HttpRouter,\n HttpServer,\n HttpServerResponse\n} from \"@effect/platform\"\nimport { listen } from \"./listen.js\"\n\n// Create the router with routes that will and will not have logging\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\"/\", HttpServerResponse.text(\"Hello World\")),\n HttpRouter.get(\n \"/no-logger\",\n HttpServerResponse.text(\"no-logger\").pipe(HttpMiddleware.withLoggerDisabled)\n )\n)\n\n// Apply the logger middleware globally\nconst app = router.pipe(HttpServer.serve(HttpMiddleware.logger))\n\nlisten(app, 3000)\n/*\ncurl -i http://localhost:3000/no-logger\ntimestamp=2024-05-19T09:53:29.877Z level=INFO fiber=#0 message=\"Listening on http://0.0.0.0:3000\"\n*/\n```\n\n### xForwardedHeaders\n\nThis middleware handles `X-Forwarded-*` headers, useful when your app is behind a reverse proxy or load balancer and you need to retrieve the original client's IP and host information.\n**WARNING:** The `X-Forwarded-*` headers are untrustworthy when no trusted reverse proxy or load balancer is between the client and server.\n\n```ts\nimport {\n HttpMiddleware,\n HttpRouter,\n HttpServer,\n HttpServerRequest,\n HttpServerResponse\n} from \"@effect/platform\"\nimport { Effect } from \"effect\"\nimport { listen } from \"./listen.js\"\n\n// Create a router and a route that logs request headers and remote address\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\n \"/\",\n Effect.gen(function* () {\n const req = yield* HttpServerRequest.HttpServerRequest\n console.log(req.headers)\n console.log(req.remoteAddress)\n return yield* HttpServerResponse.text(\"Hello World\")\n })\n )\n)\n\n// Set up the server with xForwardedHeaders middleware\nconst app = router.pipe(HttpServer.serve(HttpMiddleware.xForwardedHeaders))\n\nlisten(app, 3000)\n/*\ncurl -H \"X-Forwarded-Host: 192.168.1.1\" -H \"X-Forwarded-For: 192.168.1.1\" http://localhost:3000\ntimestamp=... level=INFO fiber=#0 message=\"Listening on http://0.0.0.0:3000\"\n{\n host: '192.168.1.1',\n 'user-agent': 'curl/8.6.0',\n accept: '*\\/*',\n 'x-forwarded-host': '192.168.1.1',\n 'x-forwarded-for': '192.168.1.1'\n}\n{ _id: 'Option', _tag: 'Some', value: '192.168.1.1' }\n*/\n```\n\n## Error Handling\n\n### Catching Errors\n\nBelow is an example illustrating how to catch and manage errors that occur during the execution of route handlers:\n\n```ts\nimport { HttpRouter, HttpServer, HttpServerResponse } from \"@effect/platform\"\nimport { Effect } from \"effect\"\nimport { listen } from \"./listen.js\"\n\n// Define routes that might throw errors or fail\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\n \"/throw\",\n Effect.sync(() => {\n throw new Error(\"BROKEN\") // This will intentionally throw an error\n })\n ),\n HttpRouter.get(\"/fail\", Effect.fail(\"Uh oh!\")) // This will intentionally fail\n)\n\n// Configure the application to handle different types of errors\nconst app = router.pipe(\n Effect.catchTags({\n RouteNotFound: () =>\n HttpServerResponse.text(\"Route Not Found\", { status: 404 })\n }),\n Effect.catchAllCause((cause) =>\n HttpServerResponse.text(cause.toString(), { status: 500 })\n ),\n HttpServer.serve()\n)\n\nlisten(app, 3000)\n```\n\nYou can test the error handling setup with `curl` commands by trying to access routes that trigger errors:\n\n```sh\n# Accessing a route that does not exist\ncurl -i http://localhost:3000/nonexistent\n\n# Accessing the route that throws an error\ncurl -i http://localhost:3000/throw\n\n# Accessing the route that fails\ncurl -i http://localhost:3000/fail\n```\n\n## Validations\n\nValidation is a critical aspect of handling HTTP requests to ensure that the data your server receives is as expected. We'll explore how to validate headers and cookies using the `@effect/platform` and `effect/Schema` libraries, which provide structured and robust methods for these tasks.\n\n### Headers\n\nHeaders often contain important information needed by your application, such as content types, authentication tokens, or session data. Validating these headers ensures that your application can trust and correctly process the information it receives.\n\n```ts\nimport {\n HttpRouter,\n HttpServer,\n HttpServerRequest,\n HttpServerResponse\n} from \"@effect/platform\"\nimport { Effect, Schema } from \"effect\"\nimport { listen } from \"./listen.js\"\n\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\n \"/\",\n Effect.gen(function* () {\n // Define the schema for expected headers and validate them\n const headers = yield* HttpServerRequest.schemaHeaders(\n Schema.Struct({ test: Schema.String })\n )\n return yield* HttpServerResponse.text(\"header: \" + headers.test)\n }).pipe(\n // Handle parsing errors\n Effect.catchTag(\"ParseError\", (e) =>\n HttpServerResponse.text(`Invalid header: ${e.message}`)\n )\n )\n )\n)\n\nconst app = router.pipe(HttpServer.serve())\n\nlisten(app, 3000)\n```\n\nYou can test header validation using the following `curl` commands:\n\n```sh\n# Request without the required header\ncurl -i http://localhost:3000\n\n# Request with the valid header\ncurl -i -H \"test: myvalue\" http://localhost:3000\n```\n\n### Cookies\n\nCookies are commonly used to maintain session state or user preferences. Validating cookies ensures that the data they carry is intact and as expected, enhancing security and application integrity.\n\nHere's how you can validate cookies received in HTTP requests:\n\n```ts\nimport {\n Cookies,\n HttpRouter,\n HttpServer,\n HttpServerRequest,\n HttpServerResponse\n} from \"@effect/platform\"\nimport { Effect, Schema } from \"effect\"\nimport { listen } from \"./listen.js\"\n\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\n \"/\",\n Effect.gen(function* () {\n const cookies = yield* HttpServerRequest.schemaCookies(\n Schema.Struct({ test: Schema.String })\n )\n return yield* HttpServerResponse.text(\"cookie: \" + cookies.test)\n }).pipe(\n Effect.catchTag(\"ParseError\", (e) =>\n HttpServerResponse.text(`Invalid cookie: ${e.message}`)\n )\n )\n )\n)\n\nconst app = router.pipe(HttpServer.serve())\n\nlisten(app, 3000)\n```\n\nValidate the cookie handling with the following `curl` commands:\n\n```sh\n# Request without any cookies\ncurl -i http://localhost:3000\n\n# Request with the valid cookie\ncurl -i http://localhost:3000 --cookie \"test=myvalue\"\n```\n\n## ServerRequest\n\n### How do I get the raw request?\n\nThe native request object depends on the platform you are using, and it is not directly modeled in `@effect/platform`. Instead, you need to refer to the specific platform package you are working with, such as `@effect/platform-node` or `@effect/platform-bun`.\n\nHere is an example using Node.js:\n\n```ts\nimport {\n HttpRouter,\n HttpServer,\n HttpServerRequest,\n HttpServerResponse\n} from \"@effect/platform\"\nimport { NodeHttpServer, NodeHttpServerRequest } from \"@effect/platform-node\"\nimport { Effect } from \"effect\"\nimport { listen } from \"./listen.js\"\n\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\n \"/\",\n Effect.gen(function* () {\n const req = yield* HttpServerRequest.HttpServerRequest\n const raw = NodeHttpServerRequest.toIncomingMessage(req)\n console.log(raw)\n return HttpServerResponse.empty()\n })\n )\n)\n\nlisten(HttpServer.serve(router), 3000)\n```\n\n## Conversions\n\n### toWebHandler\n\nThe `toWebHandler` function converts a `Default` (i.e. a type of `HttpApp` that specifically produces a `ServerResponse` as its output) into a web handler that can process `Request` objects and return `Response` objects.\n\n```ts\nimport { HttpApp, HttpRouter, HttpServerResponse } from \"@effect/platform\"\n\n// Define the router with some routes\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\"/\", HttpServerResponse.text(\"content 1\")),\n HttpRouter.get(\"/foo\", HttpServerResponse.text(\"content 2\"))\n)\n\n// Convert the router to a web handler\n// const handler: (request: Request) => Promise<Response>\nconst handler = HttpApp.toWebHandler(router)\n\n// Test the handler with a request\nconst response = await handler(new Request(\"http://localhost:3000/foo\"))\nconsole.log(await response.text()) // Output: content 2\n```\n\n# Url\n\nThe `Url` module provides utilities for constructing and working with `URL` objects in a functional style. It includes:\n\n- A safe constructor for parsing URLs from strings.\n- Functions for immutably updating `URL` properties like `host`, `href`, and `search`.\n- Tools for reading and modifying URL parameters using the `UrlParams` module.\n- A focus on immutability, creating new `URL` instances for every change.\n\n## Creating a URL\n\n### fromString\n\nThis function takes a string and attempts to parse it into a `URL` object. If the string is invalid, it returns an `Either.Left` containing an `IllegalArgumentException` with the error details. Otherwise, it returns an `Either.Right` containing the parsed `URL`.\n\nYou can optionally provide a `base` parameter to resolve relative URLs. When supplied, the function treats the input `url` as relative to the `base`.\n\n**Example** (Parsing a URL with Optional Base)\n\n```ts\nimport { Url } from \"@effect/platform\"\nimport { Either } from \"effect\"\n\n// Parse an absolute URL\n//\n// ┌─── Either<URL, IllegalArgumentException>\n// ▼\nconst parsed = Url.fromString(\"https://example.com/path\")\n\nif (Either.isRight(parsed)) {\n console.log(\"Parsed URL:\", parsed.right.toString())\n} else {\n console.log(\"Error:\", parsed.left.message)\n}\n// Output: Parsed URL: https://example.com/path\n\n// Parse a relative URL with a base\nconst relativeParsed = Url.fromString(\"/relative-path\", \"https://example.com\")\n\nif (Either.isRight(relativeParsed)) {\n console.log(\"Parsed relative URL:\", relativeParsed.right.toString())\n} else {\n console.log(\"Error:\", relativeParsed.left.message)\n}\n// Output: Parsed relative URL: https://example.com/relative-path\n```\n\n## Immutably Changing URL Properties\n\nThe `Url` module offers a set of functions for updating properties of a `URL` object without modifying the original instance. These functions create and return a new `URL` with the specified updates, preserving the immutability of the original.\n\n### Available Setters\n\n| Setter | Description |\n| ------------- | --------------------------------------------------------- |\n| `setHash` | Updates the hash fragment of the URL. |\n| `setHost` | Updates the host (domain and port) of the URL. |\n| `setHostname` | Updates the domain of the URL without modifying the port. |\n| `setHref` | Replaces the entire URL string. |\n| `setPassword` | Updates the password used for authentication. |\n| `setPathname` | Updates the path of the URL. |\n| `setPort` | Updates the port of the URL. |\n| `setProtocol` | Updates the protocol (e.g., `http`, `https`). |\n| `setSearch` | Updates the query string of the URL. |\n| `setUsername` | Updates the username used for authentication. |\n\n**Example** (Using Setters to Modify URL Properties)\n\n```ts\nimport { Url } from \"@effect/platform\"\nimport { pipe } from \"effect\"\n\nconst myUrl = new URL(\"https://example.com\")\n\n// Changing protocol, host, and port\nconst newUrl = pipe(\n myUrl,\n Url.setProtocol(\"http:\"),\n Url.setHost(\"google.com\"),\n Url.setPort(\"8080\")\n)\n\nconsole.log(\"Original:\", myUrl.toString())\n// Output: Original: https://example.com/\n\nconsole.log(\"New:\", newUrl.toString())\n// Output: New: http://google.com:8080/\n```\n\n### mutate\n\nFor more advanced modifications, use the `mutate` function. It clones the original `URL` object and applies a callback to the clone, allowing multiple updates at once.\n\n**Example** (Applying Multiple Changes with `mutate`)\n\n```ts\nimport { Url } from \"@effect/platform\"\n\nconst myUrl = new URL(\"https://example.com\")\n\nconst mutatedUrl = Url.mutate(myUrl, (url) => {\n url.username = \"user\"\n url.password = \"pass\"\n})\n\nconsole.log(\"Mutated:\", mutatedUrl.toString())\n// Output: Mutated: https://user:pass@example.com/\n```\n\n## Reading and Writing URL Parameters\n\nThe `Url` module provides utilities for working with URL query parameters. These utilities allow you to read existing parameters and write new ones, all while maintaining immutability. This functionality is supported by the `UrlParams` module.\n\nYou can extract the query parameters from a `URL` object using the `urlParams` function.\n\nTo modify or add query parameters, use the `setUrlParams` function. This function creates a new `URL` with the updated query string.\n\n**Example** (Reading and Writing Parameters)\n\n```ts\nimport { Url, UrlParams } from \"@effect/platform\"\n\nconst myUrl = new URL(\"https://example.com?foo=bar\")\n\n// Read parameters\nconst params = Url.urlParams(myUrl)\n\nconsole.log(params)\n// Output: [ [ 'foo', 'bar' ] ]\n\n// Write parameters\nconst updatedUrl = Url.setUrlParams(\n myUrl,\n UrlParams.fromInput([[\"key\", \"value\"]])\n)\n\nconsole.log(updatedUrl.toString())\n// Output: https://example.com/?key=value\n```\n\n### Modifying URL Parameters\n\nThe `modifyUrlParams` function allows you to read, modify, and overwrite URL parameters in a single operation.\n\n**Example** (Appending a Parameter to a URL)\n\n```ts\nimport { Url, UrlParams } from \"@effect/platform\"\n\nconst myUrl = new URL(\"https://example.com?foo=bar\")\n\nconst changedUrl = Url.modifyUrlParams(myUrl, UrlParams.append(\"key\", \"value\"))\n\nconsole.log(changedUrl.toString())\n// Output: https://example.com/?foo=bar&key=value\n```\n\n# OpenApiJsonSchema\n\nThe `OpenApiJsonSchema` module provides utilities to transform `Schema` objects into JSON schemas that comply with the OpenAPI Specification. These utilities are especially helpful for generating OpenAPI documentation or working with tools that require OpenAPI-compliant schemas.\n\n## Creating a JSON Schema from a Schema\n\nThis module enables you to convert `Schema` objects into OpenAPI-compatible JSON schemas, making it easy to integrate with tools like Swagger or other OpenAPI-based frameworks.\n\n**Example** (Generating a JSON Schema from a String Schema)\n\n```ts\nimport { OpenApiJsonSchema } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst schema = Schema.String\n\n// Convert the schema to OpenAPI JSON Schema\nconst openApiSchema = OpenApiJsonSchema.make(schema)\n\nconsole.log(JSON.stringify(openApiSchema, null, 2))\n/*\nOutput:\n{\n \"type\": \"string\"\n}\n*/\n```\n\n## Differences from JSONSchema\n\nThe `OpenApiJsonSchema` module differs from the `JSONSchema` module in several ways. These differences are tailored to align with the OpenAPI Specification.\n\n### `$schema` Property Omission\n\nOpenAPI schemas do not include the `$schema` property, while JSON schemas do.\n\n**Example** (Comparison of `$schema` Property)\n\n```ts\nimport { OpenApiJsonSchema } from \"@effect/platform\"\nimport { JSONSchema, Schema } from \"effect\"\n\nconst schema = Schema.String\n\nconst openApiSchema = OpenApiJsonSchema.make(schema)\nconst jsonSchema = JSONSchema.make(schema)\n\nconsole.log(JSON.stringify(openApiSchema, null, 2))\n/*\nOutput:\n{\n \"type\": \"string\"\n}\n*/\n\nconsole.log(JSON.stringify(jsonSchema, null, 2))\n/*\nOutput:\n{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"type\": \"string\"\n}\n*/\n```\n\n### Handling of `null` Values\n\nOpenAPI does not support `{ \"type\": \"null\" }`. Instead, it uses an `enum` containing `null` to represent nullable values.\n\n**Example** (Representation of `null` Values)\n\n```ts\nimport { OpenApiJsonSchema } from \"@effect/platform\"\nimport { JSONSchema, Schema } from \"effect\"\n\nconst schema = Schema.Null\n\nconst openApiSchema = OpenApiJsonSchema.make(schema)\nconst jsonSchema = JSONSchema.make(schema)\n\nconsole.log(JSON.stringify(openApiSchema, null, 2))\n/*\nOutput:\n{\n \"enum\": [\n null\n ]\n}\n*/\n\nconsole.log(JSON.stringify(jsonSchema, null, 2))\n/*\nOutput:\n{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"type\": \"null\"\n}\n*/\n```\n\n### Nullable Values\n\nOpenAPI uses the `nullable` property to indicate that a value can be `null`, whereas JSON schemas use an `anyOf` structure.\n\n**Example** (Nullable Property Representation)\n\n```ts\nimport { OpenApiJsonSchema } from \"@effect/platform\"\nimport { JSONSchema, Schema } from \"effect\"\n\nconst schema = Schema.NullOr(Schema.String)\n\nconst openApiSchema = OpenApiJsonSchema.make(schema)\nconst jsonSchema = JSONSchema.make(schema)\n\nconsole.log(JSON.stringify(openApiSchema, null, 2))\n/*\nOutput:\n{\n \"type\": \"string\",\n \"nullable\": true\n}\n*/\n\nconsole.log(JSON.stringify(jsonSchema, null, 2))\n/*\nOutput:\n{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"anyOf\": [\n {\n \"type\": \"string\"\n },\n {\n \"type\": \"null\"\n }\n ]\n}\n*/\n```\n\n### `contentSchema` Support\n\nOpenAPI schemas include a `contentSchema` property, which allows you to describe the structure of the content for a media type (e.g., `application/json`). This feature is not available in JSON schemas (Draft 7), making `contentSchema` particularly useful for defining structured payloads in OpenAPI documentation.\n\n**Note**: Use `contentSchema` to define the internal structure of media types like `application/json` in OpenAPI specifications. This property provides clarity and detail for tools and users interacting with the API, especially when handling structured payloads.\n\n**Example** (Defining a Schema with `contentSchema` for JSON Content)\n\n```ts\nimport { OpenApiJsonSchema } from \"@effect/platform\"\nimport { JSONSchema, Schema } from \"effect\"\n\n// Define a schema for parsing JSON content\nconst schema = Schema.parseJson(Schema.Struct({ a: Schema.String }))\n\nconst openApiSchema = OpenApiJsonSchema.make(schema)\nconst jsonSchema = JSONSchema.make(schema)\n\nconsole.log(JSON.stringify(openApiSchema, null, 2))\n/*\nOutput:\n{\n \"type\": \"string\",\n \"contentMediaType\": \"application/json\",\n \"contentSchema\": {\n \"type\": \"object\",\n \"required\": [\n \"a\"\n ],\n \"properties\": {\n \"a\": {\n \"type\": \"string\"\n }\n },\n \"additionalProperties\": false\n }\n}\n*/\n\nconsole.log(JSON.stringify(jsonSchema, null, 2))\n/*\nOutput:\n{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"type\": \"object\",\n \"required\": [\n \"a\"\n ],\n \"properties\": {\n \"a\": {\n \"type\": \"string\"\n }\n },\n \"additionalProperties\": false\n}\n*/\n```\n\n### makeWithDefs\n\nThe `makeWithDefs` function generates OpenAPI-compatible JSON schemas and collects schema definitions in a shared object. This is especially useful for consolidating multiple schemas into a single OpenAPI specification, enabling schema reuse across your API.\n\n**Example** (Generating OpenAPI Schema with Definitions)\n\n```ts\nimport { OpenApiJsonSchema } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\n// Define a schema with an identifier annotation\nconst schema = Schema.Struct({ a: Schema.String }).annotations({\n identifier: \"MyStruct\"\n})\n\n// Create a definitions object\nconst defs = {}\n\n// Generate the OpenAPI schema while collecting definitions\nconst openApiSchema = OpenApiJsonSchema.makeWithDefs(schema, { defs })\n\nconsole.log(JSON.stringify(openApiSchema, null, 2))\n/*\nOutput:\n{\n \"$ref\": \"#/components/schemas/MyStruct\"\n}\n*/\n\nconsole.log(JSON.stringify(defs, null, 2))\n/*\nOutput:\n{\n \"MyStruct\": {\n \"type\": \"object\",\n \"required\": [\n \"a\"\n ],\n \"properties\": {\n \"a\": {\n \"type\": \"string\"\n }\n },\n \"additionalProperties\": false\n }\n}\n*/\n```\n\n**Example** (Combining Multiple Schemas into One OpenAPI Specification)\n\n```ts\nimport { OpenApiJsonSchema } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\n// Define multiple schemas with unique identifiers\nconst schema1 = Schema.Struct({ a: Schema.String }).annotations({\n identifier: \"MyStruct1\"\n})\nconst schema2 = Schema.Struct({ b: Schema.Number }).annotations({\n identifier: \"MyStruct2\"\n})\n\n// Create a shared definitions object\nconst defs = {}\n\n// Use `makeWithDefs` to generate schemas for API paths\nconst paths = {\n paths: {\n \"/path1\": {\n get: {\n responses: {\n \"200\": {\n content: {\n \"application/json\": {\n schema: OpenApiJsonSchema.makeWithDefs(schema1, { defs })\n }\n }\n }\n }\n }\n },\n \"/path2\": {\n get: {\n responses: {\n \"200\": {\n content: {\n \"application/json\": {\n schema: OpenApiJsonSchema.makeWithDefs(schema2, { defs })\n }\n }\n }\n }\n }\n }\n }\n}\n\n// Combine paths and definitions into a single OpenAPI schema\nconst openApiSchema = {\n components: {\n schemas: defs\n },\n paths\n}\n\nconsole.log(JSON.stringify(openApiSchema, null, 2))\n/*\nOutput:\n{\n \"components\": {\n \"schemas\": {\n \"MyStruct1\": {\n \"type\": \"object\",\n \"required\": [\n \"a\"\n ],\n \"properties\": {\n \"a\": {\n \"type\": \"string\"\n }\n },\n \"additionalProperties\": false\n },\n \"MyStruct2\": {\n \"type\": \"object\",\n \"required\": [\n \"b\"\n ],\n \"properties\": {\n \"b\": {\n \"type\": \"number\"\n }\n },\n \"additionalProperties\": false\n }\n }\n },\n \"paths\": {\n \"paths\": {\n \"/path1\": {\n \"get\": {\n \"responses\": {\n \"200\": {\n \"content\": {\n \"application/json\": {\n \"schema\": {\n \"$ref\": \"#/components/schemas/MyStruct1\"\n }\n }\n }\n }\n }\n }\n },\n \"/path2\": {\n \"get\": {\n \"responses\": {\n \"200\": {\n \"content\": {\n \"application/json\": {\n \"schema\": {\n \"$ref\": \"#/components/schemas/MyStruct2\"\n }\n }\n }\n }\n }\n }\n }\n }\n }\n}\n*/\n```\n\n# HttpLayerRouter\n\nThe experimental `HttpLayerRouter` module provides a simplified way to create HTTP servers.\nIt aims to simplify the process of defining routes and registering other HTTP\nservices like `HttpApi` or `RpcServer`'s.\n\n## Registering routes\n\n```ts\nimport * as NodeHttpServer from \"@effect/platform-node/NodeHttpServer\"\nimport * as NodeRuntime from \"@effect/platform-node/NodeRuntime\"\nimport * as HttpLayerRouter from \"@effect/platform/HttpLayerRouter\"\nimport * as HttpServerResponse from \"@effect/platform/HttpServerResponse\"\nimport * as Effect from \"effect/Effect\"\nimport * as Layer from \"effect/Layer\"\nimport { createServer } from \"http\"\n\n// Here is how you can register a simple GET route\nconst HelloRoute = Layer.effectDiscard(\n Effect.gen(function* () {\n // First, we need to access the `HttpRouter` service\n const router = yield* HttpLayerRouter.HttpRouter\n\n // Then, we can add a new route to the router\n yield* router.add(\"GET\", \"/hello\", HttpServerResponse.text(\"Hello, World!\"))\n })\n)\n\n// You can also use the `HttpLayerRouter.use` function to register a route\nconst GoodbyeRoute = HttpLayerRouter.use(\n Effect.fn(function* (router) {\n // The `router` parameter is the `HttpRouter` service\n yield* router.add(\n \"GET\",\n \"/goodbye\",\n HttpServerResponse.text(\"Goodbye, World!\")\n )\n })\n)\n// Or use `HttpLayerRouter.add/addAll` for simple routes\nconst SimpleRoute = HttpLayerRouter.add(\n \"GET\",\n \"/simple\",\n HttpServerResponse.text(\"Simply fantastic!\")\n)\n\nconst AllRoutes = Layer.mergeAll(HelloRoute, GoodbyeRoute, SimpleRoute)\n\n// To start the server, we use `HttpLayerRouter.serve` with the routes layer\nHttpLayerRouter.serve(AllRoutes).pipe(\n Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 })),\n Layer.launch,\n NodeRuntime.runMain\n)\n```\n\n## Applying middleware\n\n```ts\nimport * as HttpLayerRouter from \"@effect/platform/HttpLayerRouter\"\nimport * as HttpMiddleware from \"@effect/platform/HttpMiddleware\"\nimport * as HttpServerResponse from \"@effect/platform/HttpServerResponse\"\nimport * as Context from \"effect/Context\"\nimport * as Effect from \"effect/Effect\"\nimport * as Layer from \"effect/Layer\"\n\n// Here is a service that we want to provide to every HTTP request\nclass CurrentSession extends Context.Tag(\"CurrentSession\")<\n CurrentSession,\n {\n readonly token: string\n }\n>() {}\n\n// Using the `HttpLayerRouter.middleware` function, we can create a middleware\n// that provides the `CurrentSession` service to every HTTP request.\nconst SessionMiddleware = HttpLayerRouter.middleware<{\n provides: CurrentSession\n}>()(\n Effect.gen(function* () {\n yield* Effect.log(\"SessionMiddleware initialized\")\n\n return (httpEffect) =>\n Effect.provideService(httpEffect, CurrentSession, {\n token: \"dummy-token\"\n })\n })\n)\n\n// And here is an example of global middleware, that modifies the HTTP response.\n// Global middleware directly returns a `Layer`.\nconst CorsMiddleware = HttpLayerRouter.middleware(HttpMiddleware.cors(), {\n global: true\n})\n// You can also use `HttpLayerRouter.cors()` to create a CORS middleware\n\nconst HelloRoute = HttpLayerRouter.add(\n \"GET\",\n \"/hello\",\n Effect.gen(function* () {\n // We can now access the `CurrentSession` service in our route handler\n const session = yield* CurrentSession\n return HttpServerResponse.text(\n `Hello, World! Your session token is: ${session.token}`\n )\n })\n).pipe(\n // We can provide the `SessionMiddleware.layer` to the `HelloRoute` layer\n Layer.provide(SessionMiddleware.layer),\n // And we can also provide the `CorsMiddleware` layer to handle CORS\n Layer.provide(CorsMiddleware)\n)\n```\n\n## Interdependent middleware\n\nIf middleware depends on another middleware, you can use the `.combine` api to\ncombine them.\n\n```ts\nimport * as HttpLayerRouter from \"@effect/platform/HttpLayerRouter\"\nimport * as HttpServerResponse from \"@effect/platform/HttpServerResponse\"\nimport * as Context from \"effect/Context\"\nimport * as Effect from \"effect/Effect\"\nimport * as Layer from \"effect/Layer\"\n\nclass CurrentSession extends Context.Tag(\"CurrentSession\")<\n CurrentSession,\n {\n readonly token: string\n }\n>() {}\n\nconst SessionMiddleware = HttpLayerRouter.middleware<{\n provides: CurrentSession\n}>()(\n Effect.gen(function* () {\n yield* Effect.log(\"SessionMiddleware initialized\")\n\n return (httpEffect) =>\n Effect.provideService(httpEffect, CurrentSession, {\n token: \"dummy-token\"\n })\n })\n)\n\n// Here is a middleware that uses the `CurrentSession` service\nconst LogMiddleware = HttpLayerRouter.middleware(\n Effect.gen(function* () {\n yield* Effect.log(\"LogMiddleware initialized\")\n\n return Effect.fn(function* (httpEffect) {\n const session = yield* CurrentSession\n yield* Effect.log(`Current session token: ${session.token}`)\n return yield* httpEffect\n })\n })\n)\n\n// We can then use the .combine method to combine the middlewares\nconst LogAndSessionMiddleware = LogMiddleware.combine(SessionMiddleware)\n\nconst HelloRoute = HttpLayerRouter.add(\n \"GET\",\n \"/hello\",\n Effect.gen(function* () {\n const session = yield* CurrentSession\n return HttpServerResponse.text(\n `Hello, World! Your session token is: ${session.token}`\n )\n })\n).pipe(Layer.provide(LogAndSessionMiddleware.layer))\n```\n\n## Registering a HttpApi\n\n```ts\nimport {\n HttpApi,\n HttpApiBuilder,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiScalar,\n HttpLayerRouter\n} from \"@effect/platform\"\nimport { NodeHttpServer, NodeRuntime } from \"@effect/platform-node\"\nimport { Effect, Layer } from \"effect\"\nimport { createServer } from \"http\"\n\n// First, we define our HttpApi\nclass MyApi extends HttpApi.make(\"api\").add(\n HttpApiGroup.make(\"users\")\n .add(HttpApiEndpoint.get(\"me\", \"/me\"))\n .prefix(\"/users\")\n) {}\n\n// Implement the handlers for the API\nconst UsersApiLayer = HttpApiBuilder.group(MyApi, \"users\", (handers) =>\n handers.handle(\"me\", () => Effect.void)\n)\n\n// Use `HttpLayerRouter.addHttpApi` to register the API with the router\nconst HttpApiRoutes = HttpLayerRouter.addHttpApi(MyApi, {\n openapiPath: \"/docs/openapi.json\"\n}).pipe(\n // Provide the api handlers layer\n Layer.provide(UsersApiLayer)\n)\n\n// Create a /docs route for the API documentation\nconst DocsRoute = HttpApiScalar.layerHttpLayerRouter({\n api: MyApi,\n path: \"/docs\"\n})\n\n// Finally, we merge all routes and serve them using the Node HTTP server\nconst AllRoutes = Layer.mergeAll(HttpApiRoutes, DocsRoute).pipe(\n Layer.provide(HttpLayerRouter.cors())\n)\n\nHttpLayerRouter.serve(AllRoutes).pipe(\n Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 })),\n Layer.launch,\n NodeRuntime.runMain\n)\n```\n\n## Registering a RpcServer\n\n```ts\nimport { HttpLayerRouter } from \"@effect/platform\"\nimport { NodeHttpServer, NodeRuntime } from \"@effect/platform-node\"\nimport { Rpc, RpcGroup, RpcSerialization, RpcServer } from \"@effect/rpc\"\nimport { Effect, Layer, Schema } from \"effect\"\nimport { createServer } from \"http\"\n\nexport class User extends Schema.Class<User>(\"User\")({\n id: Schema.String,\n name: Schema.String\n}) {}\n\n// Define a group of RPCs\nexport class UserRpcs extends RpcGroup.make(\n Rpc.make(\"UserById\", {\n success: User,\n error: Schema.String, // Indicates that errors, if any, will be returned as strings\n payload: {\n id: Schema.String\n }\n })\n) {}\n\nconst UserHandlers = UserRpcs.toLayer({\n UserById: ({ id }) => Effect.succeed(new User({ id, name: \"John Doe\" }))\n})\n\n// Use `HttpLayerRouter` to register the rpc server\nconst RpcRoute = RpcServer.layerHttpRouter({\n group: UserRpcs,\n path: \"/rpc\"\n}).pipe(\n Layer.provide(UserHandlers),\n Layer.provide(RpcSerialization.layerJson),\n Layer.provide(HttpLayerRouter.cors()) // provide CORS middleware\n)\n\n// Start the HTTP server with the RPC route\nHttpLayerRouter.serve(RpcRoute).pipe(\n Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 })),\n Layer.launch,\n NodeRuntime.runMain\n)\n```\n\n## Create a web handler\n\n```ts\nimport * as HttpLayerRouter from \"@effect/platform/HttpLayerRouter\"\nimport * as HttpServerResponse from \"@effect/platform/HttpServerResponse\"\nimport * as Effect from \"effect/Effect\"\n\nconst HelloRoute = HttpLayerRouter.use(\n Effect.fn(function* (router) {\n yield* router.add(\n \"GET\",\n \"/hello\",\n HttpServerResponse.text(\"Hellow, World!\")\n )\n })\n)\n\nconst { dispose, handler } = HttpLayerRouter.toWebHandler(HelloRoute)\n\n// When the process is interrupted, we want to clean up resources\nprocess.on(\"SIGINT\", () => {\n dispose().then(\n () => {\n process.exit(0)\n },\n () => {\n process.exit(1)\n }\n )\n})\n\n// Use the handler in your server setup\nexport { handler }\n```\n"
|
|
1391
1391
|
},
|
|
1392
1392
|
{
|
|
1393
1393
|
"title": "Introduction",
|
|
1394
1394
|
"filename": "README.md",
|
|
1395
|
-
"path": ".compat-tests/effect/packages/
|
|
1396
|
-
"text": "# Introduction\n\nWelcome to the documentation for `@effect/platform`, a library designed for creating platform-independent abstractions (Node.js, Bun, browsers).\n\n> [!WARNING]\n> This documentation focuses on **unstable modules**. For stable modules, refer to the [official website documentation](https://effect.website/docs/guides/platform/introduction).\n\n# Running Your Main Program with runMain\n\nDocs for `runMain` have been moved to the [official website](https://effect.website/docs/platform/runtime/).\n\n# HTTP API\n\n## Overview\n\nThe `HttpApi*` modules offer a flexible and declarative way to define HTTP APIs.\n\nTo define an API, create a set of `HttpEndpoint`s. Each endpoint is described by a path, a method, and schemas for the request and response.\n\nCollections of endpoints are grouped in an `HttpApiGroup`, and multiple groups can be merged into a complete `HttpApi`.\n\n```\nHttpApi\n├── HttpGroup\n│ ├── HttpEndpoint\n│ └── HttpEndpoint\n└── HttpGroup\n ├── HttpEndpoint\n ├── HttpEndpoint\n └── HttpEndpoint\n```\n\nOnce your API is defined, the same definition can be reused for multiple purposes:\n\n- **Starting a Server**: Use the API definition to implement and serve endpoints.\n- **Generating Documentation**: Create a Swagger page to document the API.\n- **Deriving a Client**: Generate a fully-typed client for your API.\n\nBenefits of a Single API Definition:\n\n- **Consistency**: A single definition ensures the server, documentation, and client remain aligned.\n- **Reduced Maintenance**: Changes to the API are reflected across all related components.\n- **Simplified Workflow**: Avoids duplication by consolidating API details in one place.\n\n## Hello World\n\n### Defining and Implementing an API\n\nThis example demonstrates how to define and implement a simple API with a single endpoint that returns a string response. The structure of the API is as follows:\n\n```\nHttpApi (\"MyApi)\n└── HttpGroup (\"Greetings\")\n └── HttpEndpoint (\"hello-world\")\n```\n\n**Example** (Hello World Definition)\n\n```ts\nimport {\n HttpApi,\n HttpApiBuilder,\n HttpApiEndpoint,\n HttpApiGroup\n} from \"@effect/platform\"\nimport { NodeHttpServer, NodeRuntime } from \"@effect/platform-node\"\nimport { Effect, Layer, Schema } from \"effect\"\nimport { createServer } from \"node:http\"\n\n// Define our API with one group named \"Greetings\" and one endpoint called \"hello-world\"\nconst MyApi = HttpApi.make(\"MyApi\").add(\n HttpApiGroup.make(\"Greetings\").add(\n HttpApiEndpoint.get(\"hello-world\")`/`.addSuccess(Schema.String)\n )\n)\n\n// Implement the \"Greetings\" group\nconst GreetingsLive = HttpApiBuilder.group(MyApi, \"Greetings\", (handlers) =>\n handlers.handle(\"hello-world\", () => Effect.succeed(\"Hello, World!\"))\n)\n\n// Provide the implementation for the API\nconst MyApiLive = HttpApiBuilder.api(MyApi).pipe(Layer.provide(GreetingsLive))\n\n// Set up the server using NodeHttpServer on port 3000\nconst ServerLive = HttpApiBuilder.serve().pipe(\n Layer.provide(MyApiLive),\n Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 }))\n)\n\n// Launch the server\nLayer.launch(ServerLive).pipe(NodeRuntime.runMain)\n```\n\nAfter running the code, open a browser and navigate to http://localhost:3000. The server will respond with:\n\n```\nHello, World!\n```\n\n### Serving The Auto Generated Swagger Documentation\n\nYou can enhance your API by adding auto-generated Swagger documentation using the `HttpApiSwagger` module. This makes it easier for developers to explore and interact with your API.\n\nTo include Swagger in your server setup, provide the `HttpApiSwagger.layer` when configuring the server.\n\n**Example** (Serving Swagger Documentation)\n\n```ts\nimport {\n HttpApi,\n HttpApiBuilder,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiSwagger\n} from \"@effect/platform\"\nimport { NodeHttpServer, NodeRuntime } from \"@effect/platform-node\"\nimport { Effect, Layer, Schema } from \"effect\"\nimport { createServer } from \"node:http\"\n\nconst MyApi = HttpApi.make(\"MyApi\").add(\n HttpApiGroup.make(\"Greetings\").add(\n HttpApiEndpoint.get(\"hello-world\")`/`.addSuccess(Schema.String)\n )\n)\n\nconst GreetingsLive = HttpApiBuilder.group(MyApi, \"Greetings\", (handlers) =>\n handlers.handle(\"hello-world\", () => Effect.succeed(\"Hello, World!\"))\n)\n\nconst MyApiLive = HttpApiBuilder.api(MyApi).pipe(Layer.provide(GreetingsLive))\n\nconst ServerLive = HttpApiBuilder.serve().pipe(\n // Provide the Swagger layer so clients can access auto-generated docs\n Layer.provide(HttpApiSwagger.layer()),\n Layer.provide(MyApiLive),\n Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 }))\n)\n\nLayer.launch(ServerLive).pipe(NodeRuntime.runMain)\n```\n\nAfter running the server, open your browser and navigate to http://localhost:3000/docs.\n\nThis URL will display the Swagger documentation, allowing you to explore the API's endpoints, request parameters, and response structures interactively.\n\n\n\n### Deriving a Client\n\nOnce you have defined your API, you can generate a client to interact with it using the `HttpApiClient` module. This allows you to call your API endpoints without manually handling HTTP requests.\n\n**Example** (Deriving and Using a Client)\n\n```ts\nimport {\n FetchHttpClient,\n HttpApi,\n HttpApiBuilder,\n HttpApiClient,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiSwagger\n} from \"@effect/platform\"\nimport { NodeHttpServer, NodeRuntime } from \"@effect/platform-node\"\nimport { Effect, Layer, Schema } from \"effect\"\nimport { createServer } from \"node:http\"\n\nconst MyApi = HttpApi.make(\"MyApi\").add(\n HttpApiGroup.make(\"Greetings\").add(\n HttpApiEndpoint.get(\"hello-world\")`/`.addSuccess(Schema.String)\n )\n)\n\nconst GreetingsLive = HttpApiBuilder.group(MyApi, \"Greetings\", (handlers) =>\n handlers.handle(\"hello-world\", () => Effect.succeed(\"Hello, World!\"))\n)\n\nconst MyApiLive = HttpApiBuilder.api(MyApi).pipe(Layer.provide(GreetingsLive))\n\nconst ServerLive = HttpApiBuilder.serve().pipe(\n Layer.provide(HttpApiSwagger.layer()),\n Layer.provide(MyApiLive),\n Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 }))\n)\n\nLayer.launch(ServerLive).pipe(NodeRuntime.runMain)\n\n// Create a program that derives and uses the client\nconst program = Effect.gen(function* () {\n // Derive the client\n const client = yield* HttpApiClient.make(MyApi, {\n baseUrl: \"http://localhost:3000\"\n })\n // Call the \"hello-world\" endpoint\n const hello = yield* client.Greetings[\"hello-world\"]()\n console.log(hello)\n})\n\n// Provide a Fetch-based HTTP client and run the program\nEffect.runFork(program.pipe(Effect.provide(FetchHttpClient.layer)))\n// Output: Hello, World!\n```\n\n## Defining a HttpApiEndpoint\n\nAn `HttpApiEndpoint` represents a single endpoint in your API. Each endpoint is defined with a name, path, HTTP method, and optional schemas for requests and responses. This allows you to describe the structure and behavior of your API.\n\nBelow is an example of a simple CRUD API for managing users, which includes the following endpoints:\n\n- `GET /users` - Retrieve all users.\n- `GET /users/:userId` - Retrieve a specific user by ID.\n- `POST /users` - Create a new user.\n- `DELETE /users/:userId` - Delete a user by ID.\n- `PATCH /users/:userId` - Update a user by ID.\n\n### GET\n\nThe `HttpApiEndpoint.get` method allows you to define a GET endpoint by specifying its name, path, and optionally, a schema for the response.\n\nTo define the structure of successful responses, use the `.addSuccess` method. If no schema is provided, the default response status is `204 No Content`.\n\n**Example** (Defining a GET Endpoint to Retrieve All Users)\n\n```ts\nimport { HttpApiEndpoint } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\n// Define a schema representing a User entity\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\n// Define the \"getUsers\" endpoint, returning a list of users\nconst getUsers = HttpApiEndpoint\n // ┌─── Endpoint name\n // │ ┌─── Endpoint path\n // ▼ ▼\n .get(\"getUsers\", \"/users\")\n // Define the success schema for the response (optional).\n // If no response schema is specified, the default response is `204 No Content`.\n .addSuccess(Schema.Array(User))\n```\n\n### Path Parameters\n\nPath parameters allow you to include dynamic segments in your endpoint's path. There are two ways to define path parameters in your API.\n\n#### Using setPath\n\nThe `setPath` method allows you to explicitly define path parameters by associating them with a schema.\n\n**Example** (Defining Parameters with setPath)\n\n```ts\nimport { HttpApiEndpoint } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\n// Define a GET endpoint with a path parameter \":id\"\nconst getUser = HttpApiEndpoint.get(\"getUser\", \"/user/:id\")\n .setPath(\n Schema.Struct({\n // Define a schema for the \"id\" path parameter\n id: Schema.NumberFromString\n })\n )\n .addSuccess(User)\n```\n\n#### Using Template Strings\n\nYou can also define path parameters by embedding them in a template string with the help of `HttpApiSchema.param`.\n\n**Example** (Defining Parameters using a Template String)\n\n```ts\nimport { HttpApiEndpoint, HttpApiSchema } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\n// Create a path parameter using HttpApiSchema.param\nconst idParam = HttpApiSchema.param(\"id\", Schema.NumberFromString)\n\n// Define the GET endpoint using a template string\nconst getUser = HttpApiEndpoint.get(\"getUser\")`/user/${idParam}`.addSuccess(\n User\n)\n```\n\n### POST\n\nThe `HttpApiEndpoint.post` method is used to define an endpoint for creating resources. You can specify a schema for the request body (payload) and a schema for the successful response.\n\n**Example** (Defining a POST Endpoint with Payload and Success Schemas)\n\n```ts\nimport { HttpApiEndpoint } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\n// Define a schema for the user object\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\n// Define a POST endpoint for creating a new user\nconst createUser = HttpApiEndpoint.post(\"createUser\", \"/users\")\n // Define the request body schema (payload)\n .setPayload(\n Schema.Struct({\n name: Schema.String\n })\n )\n // Define the schema for a successful response\n .addSuccess(User)\n```\n\n### DELETE\n\nThe `HttpApiEndpoint.del` method is used to define an endpoint for deleting a resource.\n\n**Example** (Defining a DELETE Endpoint with Path Parameters)\n\n```ts\nimport { HttpApiEndpoint, HttpApiSchema } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\n// Define a path parameter for the user ID\nconst idParam = HttpApiSchema.param(\"id\", Schema.NumberFromString)\n\n// Define a DELETE endpoint to delete a user by ID\nconst deleteUser = HttpApiEndpoint.del(\"deleteUser\")`/users/${idParam}`\n```\n\n### PATCH\n\nThe `HttpApiEndpoint.patch` method is used to define an endpoint for partially updating a resource. This method allows you to specify a schema for the request payload and a schema for the successful response.\n\n**Example** (Defining a PATCH Endpoint for Updating a User)\n\n```ts\nimport { HttpApiEndpoint, HttpApiSchema } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\n// Define a schema for the user object\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\n// Define a path parameter for the user ID\nconst idParam = HttpApiSchema.param(\"id\", Schema.NumberFromString)\n\n// Define a PATCH endpoint to update a user's name by ID\nconst updateUser = HttpApiEndpoint.patch(\"updateUser\")`/users/${idParam}`\n // Specify the schema for the request payload\n .setPayload(\n Schema.Struct({\n name: Schema.String // Only the name can be updated\n })\n )\n // Specify the schema for a successful response\n .addSuccess(User)\n```\n\n### Catch-All Endpoints\n\nThe path can also be `\"*\"` to match any incoming path. This is useful for defining a catch-all endpoint to handle unmatched routes or provide a fallback response.\n\n**Example** (Defining a Catch-All Endpoint)\n\n```ts\nimport { HttpApiEndpoint } from \"@effect/platform\"\n\nconst catchAll = HttpApiEndpoint.get(\"catchAll\", \"*\")\n```\n\n### Setting URL Parameters\n\nThe `setUrlParams` method allows you to define the structure of URL parameters for an endpoint. You can specify the schema for each parameter and include metadata such as descriptions to provide additional context.\n\n**Example** (Defining URL Parameters with Metadata)\n\n```ts\nimport { HttpApiEndpoint } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\nconst getUsers = HttpApiEndpoint.get(\"getUsers\", \"/users\")\n // Specify the URL parameters schema\n .setUrlParams(\n Schema.Struct({\n // Parameter \"page\" for pagination\n page: Schema.NumberFromString,\n // Parameter \"sort\" for sorting options with an added description\n sort: Schema.String.annotations({\n description: \"Sorting criteria (e.g., 'name', 'date')\"\n })\n })\n )\n .addSuccess(Schema.Array(User))\n```\n\n#### Defining an Array of Values for a URL Parameter\n\nWhen defining a URL parameter that accepts multiple values, you can use the `Schema.Array` combinator. This allows the parameter to handle an array of items, with each item adhering to a specified schema.\n\n**Example** (Defining an Array of String Values for a URL Parameter)\n\n```ts\nimport { HttpApi, HttpApiEndpoint, HttpApiGroup } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst api = HttpApi.make(\"myApi\").add(\n HttpApiGroup.make(\"group\").add(\n HttpApiEndpoint.get(\"get\", \"/\")\n .setUrlParams(\n Schema.Struct({\n // Define \"a\" as an array of strings\n a: Schema.Array(Schema.String)\n })\n )\n .addSuccess(Schema.String)\n )\n)\n```\n\nYou can test this endpoint by passing an array of values in the query string. For example:\n\n```sh\ncurl \"http://localhost:3000/?a=1&a=2\"\n```\n\nThe query string sends two values (`1` and `2`) for the `a` parameter. The server will process and validate these values according to the schema.\n\n### Status Codes\n\nBy default, the success status code is `200 OK`. You can change it by annotating the schema with a custom status.\n\n**Example** (Defining a GET Endpoint with a custom status code)\n\n```ts\nimport { HttpApiEndpoint } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\nconst getUsers = HttpApiEndpoint.get(\"getUsers\", \"/users\")\n // Override the default success status\n .addSuccess(Schema.Array(User), { status: 206 })\n```\n\n### Handling Multipart Requests\n\nTo support file uploads, you can use the `HttpApiSchema.Multipart` API. This allows you to define an endpoint's payload schema as a multipart request, specifying the structure of the data, including file uploads, with the `Multipart` module.\n\n**Example** (Defining an Endpoint for File Uploads)\n\nIn this example, the `HttpApiSchema.Multipart` function marks the payload as a multipart request. The `files` field uses `Multipart.FilesSchema` to handle uploaded file data automatically.\n\n```ts\nimport { HttpApiEndpoint, HttpApiSchema, Multipart } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst upload = HttpApiEndpoint.post(\"upload\", \"/users/upload\").setPayload(\n // Specify that the payload is a multipart request\n HttpApiSchema.Multipart(\n Schema.Struct({\n // Define a \"files\" field to handle file uploads\n files: Multipart.FilesSchema\n })\n ).addSuccess(Schema.String)\n)\n```\n\nYou can test this endpoint by sending a multipart request with a file upload. For example:\n\n```sh\necho \"Sample file content\" | curl -X POST -F \"files=@-\" http://localhost:3000/users/upload\n```\n\n### Changing the Request Encoding\n\nBy default, API requests are encoded as JSON. If your application requires a different format, you can customize the request encoding using the `HttpApiSchema.withEncoding` method. This allows you to define the encoding type and content type of the request.\n\n**Example** (Customizing Request Encoding)\n\n```ts\nimport { HttpApiEndpoint, HttpApiSchema } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst createUser = HttpApiEndpoint.post(\"createUser\", \"/users\")\n // Set the request payload as a string encoded with URL parameters\n .setPayload(\n Schema.Struct({\n a: Schema.String // Parameter \"a\" must be a string\n })\n // Specify the encoding as URL parameters\n .pipe(HttpApiSchema.withEncoding({ kind: \"UrlParams\" }))\n )\n```\n\n### Changing the Response Encoding\n\nBy default, API responses are encoded as JSON. If your application requires a different format, you can customize the encoding using the `HttpApiSchema.withEncoding` API. This method lets you define the type and content type of the response.\n\n**Example** (Returning Data as `text/csv`)\n\n```ts\nimport { HttpApiEndpoint, HttpApiSchema } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst csv = HttpApiEndpoint.get(\"csv\")`/users/csv`\n // Set the success response as a string with CSV encoding\n .addSuccess(\n Schema.String.pipe(\n HttpApiSchema.withEncoding({\n // Specify the type of the response\n kind: \"Text\",\n // Define the content type as text/csv\n contentType: \"text/csv\"\n })\n )\n )\n```\n\n### Setting Request Headers\n\nUse `HttpApiEndpoint.setHeaders` to declare a single, cumulative schema that describes all expected request headers.\nProvide one struct schema where each header name maps to its validator, and you can attach metadata such as descriptions.\n\n> [!IMPORTANT]\n> All headers are normalized to lowercase. Always use lowercase keys in the headers schema.\n\n**Example** (Describe and validate custom headers)\n\n```ts\nimport { HttpApiEndpoint } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\n// Model for successful responses\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\nconst getUsers = HttpApiEndpoint.get(\"getUsers\", \"/users\")\n // Describe the headers the endpoint expects\n .setHeaders(\n // Declare a single struct schema for all headers\n // Header keys MUST be lowercase in the schema\n Schema.Struct({\n // This header must be a string\n \"x-api-key\": Schema.String,\n\n // A human-friendly description is useful for generated docs (e.g. OpenAPI)\n \"x-request-id\": Schema.String.annotations({\n description: \"Unique identifier for the request\"\n })\n })\n )\n // Successful response: an array of User\n .addSuccess(Schema.Array(User))\n```\n\nYou can test the endpoint by sending the headers:\n\n```sh\ncurl -H \"X-API-Key: 1234567890\" -H \"X-Request-ID: 1234567890\" http://localhost:3000/users\n```\n\nThe server validates these headers against the declared schema before handling the request.\n\n## Defining a HttpApiGroup\n\nYou can group related endpoints under a single entity by using `HttpApiGroup.make`. This can help organize your code and provide a clearer structure for your API.\n\n**Example** (Creating a Group for User-Related Endpoints)\n\n```ts\nimport { HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\nconst idParam = HttpApiSchema.param(\"id\", Schema.NumberFromString)\n\nconst getUsers = HttpApiEndpoint.get(\"getUsers\", \"/users\").addSuccess(\n Schema.Array(User)\n)\n\nconst getUser = HttpApiEndpoint.get(\"getUser\")`/user/${idParam}`.addSuccess(\n User\n)\n\nconst createUser = HttpApiEndpoint.post(\"createUser\", \"/users\")\n .setPayload(\n Schema.Struct({\n name: Schema.String\n })\n )\n .addSuccess(User)\n\nconst deleteUser = HttpApiEndpoint.del(\"deleteUser\")`/users/${idParam}`\n\nconst updateUser = HttpApiEndpoint.patch(\"updateUser\")`/users/${idParam}`\n .setPayload(\n Schema.Struct({\n name: Schema.String\n })\n )\n .addSuccess(User)\n\n// Group all user-related endpoints\nconst usersGroup = HttpApiGroup.make(\"users\")\n .add(getUsers)\n .add(getUser)\n .add(createUser)\n .add(deleteUser)\n .add(updateUser)\n```\n\nIf you would like to create a more opaque type for the group, you can extend `HttpApiGroup` with a class.\n\n**Example** (Creating a Group with an Opaque Type)\n\n```ts\n// Create an opaque class extending HttpApiGroup\nclass UsersGroup extends HttpApiGroup.make(\"users\").add(getUsers).add(getUser) {\n // Additional endpoints or methods can be added here\n}\n```\n\n## Creating the Top-Level HttpApi\n\nAfter defining your groups, you can combine them into one `HttpApi` representing your entire set of endpoints.\n\n**Example** (Combining Groups into a Top-Level API)\n\n```ts\nimport {\n HttpApi,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiSchema\n} from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\nconst idParam = HttpApiSchema.param(\"id\", Schema.NumberFromString)\n\nconst getUsers = HttpApiEndpoint.get(\"getUsers\", \"/users\").addSuccess(\n Schema.Array(User)\n)\n\nconst getUser = HttpApiEndpoint.get(\"getUser\")`/user/${idParam}`.addSuccess(\n User\n)\n\nconst createUser = HttpApiEndpoint.post(\"createUser\", \"/users\")\n .setPayload(\n Schema.Struct({\n name: Schema.String\n })\n )\n .addSuccess(User)\n\nconst deleteUser = HttpApiEndpoint.del(\"deleteUser\")`/users/${idParam}`\n\nconst updateUser = HttpApiEndpoint.patch(\"updateUser\")`/users/${idParam}`\n .setPayload(\n Schema.Struct({\n name: Schema.String\n })\n )\n .addSuccess(User)\n\nconst usersGroup = HttpApiGroup.make(\"users\")\n .add(getUsers)\n .add(getUser)\n .add(createUser)\n .add(deleteUser)\n .add(updateUser)\n\n// Combine the groups into one API\nconst api = HttpApi.make(\"myApi\").add(usersGroup)\n\n// Alternatively, create an opaque class for your API\nclass MyApi extends HttpApi.make(\"myApi\").add(usersGroup) {}\n```\n\n## Adding errors\n\nError responses allow your API to handle different failure scenarios. These responses can be defined at various levels:\n\n- **Endpoint-level errors**: Use `HttpApiEndpoint.addError` to add errors specific to an endpoint.\n- **Group-level errors**: Use `HttpApiGroup.addError` to add errors applicable to all endpoints in a group.\n- **API-level errors**: Use `HttpApi.addError` to define errors that apply to every endpoint in the API.\n\nGroup-level and API-level errors are useful for handling shared issues like authentication failures, especially when managed through middleware.\n\n**Example** (Defining Error Responses for Endpoints and Groups)\n\n```ts\nimport { HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\nconst idParam = HttpApiSchema.param(\"id\", Schema.NumberFromString)\n\n// Define error schemas\nclass UserNotFound extends Schema.TaggedError<UserNotFound>()(\n \"UserNotFound\",\n {}\n) {}\n\nclass Unauthorized extends Schema.TaggedError<Unauthorized>()(\n \"Unauthorized\",\n {}\n) {}\n\nconst getUsers = HttpApiEndpoint.get(\"getUsers\", \"/users\").addSuccess(\n Schema.Array(User)\n)\n\nconst getUser = HttpApiEndpoint.get(\"getUser\")`/user/${idParam}`\n .addSuccess(User)\n // Add a 404 error response for this endpoint\n .addError(UserNotFound, { status: 404 })\n\nconst usersGroup = HttpApiGroup.make(\"users\")\n .add(getUsers)\n .add(getUser)\n // ...etc...\n // Add a 401 error response for the entire group\n .addError(Unauthorized, { status: 401 })\n```\n\nYou can assign multiple error responses to a single endpoint by calling `HttpApiEndpoint.addError` multiple times. This is useful when different types of errors might occur for a single operation.\n\n**Example** (Adding Multiple Errors to an Endpoint)\n\n```ts\nconst deleteUser = HttpApiEndpoint.del(\"deleteUser\")`/users/${idParam}`\n // Add a 404 error response for when the user is not found\n .addError(UserNotFound, { status: 404 })\n // Add a 401 error response for unauthorized access\n .addError(Unauthorized, { status: 401 })\n```\n\n### Predefined Empty Error Types\n\nThe `HttpApiError` module provides a set of predefined empty error types that you can use in your endpoints. These error types help standardize common HTTP error responses, such as `404 Not Found` or `401 Unauthorized`. Using these predefined types simplifies error handling and ensures consistency across your API.\n\n**Example** (Adding a Predefined Error to an Endpoint)\n\n```ts\nimport { HttpApiEndpoint, HttpApiError, HttpApiSchema } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\nconst idParam = HttpApiSchema.param(\"id\", Schema.NumberFromString)\n\nconst getUser = HttpApiEndpoint.get(\"getUser\")`/user/${idParam}`\n .addSuccess(User)\n .addError(HttpApiError.NotFound)\n```\n\n| Name | Status | Description |\n| --------------------- | ------ | -------------------------------------------------------------------------------------------------- |\n| `HttpApiDecodeError` | 400 | Represents an error where the request did not match the expected schema. Includes detailed issues. |\n| `BadRequest` | 400 | Indicates that the request was malformed or invalid. |\n| `Unauthorized` | 401 | Indicates that authentication is required but missing or invalid. |\n| `Forbidden` | 403 | Indicates that the client does not have permission to access the requested resource. |\n| `NotFound` | 404 | Indicates that the requested resource could not be found. |\n| `MethodNotAllowed` | 405 | Indicates that the HTTP method used is not allowed for the requested resource. |\n| `NotAcceptable` | 406 | Indicates that the requested resource cannot be delivered in a format acceptable to the client. |\n| `RequestTimeout` | 408 | Indicates that the server timed out waiting for the client request. |\n| `Conflict` | 409 | Indicates a conflict in the request, such as conflicting data. |\n| `Gone` | 410 | Indicates that the requested resource is no longer available and will not return. |\n| `InternalServerError` | 500 | Indicates an unexpected server error occurred. |\n| `NotImplemented` | 501 | Indicates that the requested functionality is not implemented on the server. |\n| `ServiceUnavailable` | 503 | Indicates that the server is temporarily unavailable, often due to maintenance or overload. |\n\n## Prefixing\n\nPrefixes can be added to endpoints, groups, or an entire API to simplify the management of common paths. This is especially useful when defining multiple related endpoints that share a common base URL.\n\n**Example** (Using Prefixes for Common Path Management)\n\n```ts\nimport { HttpApi, HttpApiEndpoint, HttpApiGroup } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst api = HttpApi.make(\"api\")\n .add(\n HttpApiGroup.make(\"group\")\n .add(\n HttpApiEndpoint.get(\"getRoot\", \"/\")\n .addSuccess(Schema.String)\n // Prefix for this endpoint\n .prefix(\"/endpointPrefix\")\n )\n .add(HttpApiEndpoint.get(\"getA\", \"/a\").addSuccess(Schema.String))\n // Prefix for all endpoints in the group\n .prefix(\"/groupPrefix\")\n )\n // Prefix for the entire API\n .prefix(\"/apiPrefix\")\n```\n\n## Implementing a Server\n\nAfter defining your API, you can implement a server to handle its endpoints. The `HttpApiBuilder` module provides tools to help you connect your API's structure to the logic that serves requests.\n\nHere, we will create a simple example with a `getUser` endpoint organized within a `users` group.\n\n**Example** (Defining the `users` Group and API)\n\n```ts\nimport {\n HttpApi,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiSchema\n} from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\nconst idParam = HttpApiSchema.param(\"id\", Schema.NumberFromString)\n\nconst usersGroup = HttpApiGroup.make(\"users\").add(\n HttpApiEndpoint.get(\"getUser\")`/user/${idParam}`.addSuccess(User)\n)\n\nconst api = HttpApi.make(\"myApi\").add(usersGroup)\n```\n\n### Implementing a HttpApiGroup\n\nThe `HttpApiBuilder.group` API is used to implement a specific group of endpoints within an `HttpApi` definition. It requires the following inputs:\n\n| Input | Description |\n| --------------------------------- | ----------------------------------------------------------------------- |\n| The complete `HttpApi` definition | The overall API structure that includes the group you are implementing. |\n| The name of the group | The specific group you are focusing on within the API. |\n| A function to add handlers | A function that defines how each endpoint in the group is handled. |\n\nEach endpoint in the group is connected to its logic using the `HttpApiBuilder.handle` method, which maps the endpoint's definition to its corresponding implementation.\n\nThe `HttpApiBuilder.group` API produces a `Layer` that can later be provided to the server implementation.\n\n**Example** (Implementing a Group with Endpoint Logic)\n\n```ts\nimport {\n HttpApi,\n HttpApiBuilder,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiSchema\n} from \"@effect/platform\"\nimport { DateTime, Effect, Schema } from \"effect\"\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\nconst idParam = HttpApiSchema.param(\"id\", Schema.NumberFromString)\n\nconst usersGroup = HttpApiGroup.make(\"users\").add(\n HttpApiEndpoint.get(\"getUser\")`/user/${idParam}`.addSuccess(User)\n)\n\nconst api = HttpApi.make(\"myApi\").add(usersGroup)\n\n// --------------------------------------------\n// Implementation\n// --------------------------------------------\n\n// ┌─── Layer<HttpApiGroup.ApiGroup<\"myApi\", \"users\">>\n// ▼\nconst usersGroupLive =\n // ┌─── The Whole API\n // │ ┌─── The Group you are implementing\n // ▼ ▼\n HttpApiBuilder.group(api, \"users\", (handlers) =>\n handlers.handle(\n // ┌─── The Endpoint you are implementing\n // ▼\n \"getUser\",\n // Provide the handler logic for the endpoint.\n // The parameters & payload are passed to the handler function.\n ({ path: { id } }) =>\n Effect.succeed(\n // Return a mock user object with the provided ID\n {\n id,\n name: \"John Doe\",\n createdAt: DateTime.unsafeNow()\n }\n )\n )\n )\n```\n\nUsing `HttpApiBuilder.group`, you connect the structure of your API to its logic, enabling you to focus on each endpoint's functionality in isolation. Each handler receives the parameters and payload for the request, making it easy to process input and generate a response.\n\n### Using Services Inside a HttpApiGroup\n\nIf your handlers need to use services, you can easily integrate them because the `HttpApiBuilder.group` API allows you to return an `Effect`. This ensures that external services can be accessed and utilized directly within your handlers.\n\n**Example** (Using Services in a Group Implementation)\n\n```ts\nimport {\n HttpApi,\n HttpApiBuilder,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiSchema\n} from \"@effect/platform\"\nimport { Context, Effect, Schema } from \"effect\"\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\nconst idParam = HttpApiSchema.param(\"id\", Schema.NumberFromString)\n\nconst usersGroup = HttpApiGroup.make(\"users\").add(\n HttpApiEndpoint.get(\"getUser\")`/user/${idParam}`.addSuccess(User)\n)\n\nconst api = HttpApi.make(\"myApi\").add(usersGroup)\n\n// --------------------------------------------\n// Implementation\n// --------------------------------------------\n\ntype User = typeof User.Type\n\n// Define the UsersRepository service\nclass UsersRepository extends Context.Tag(\"UsersRepository\")<\n UsersRepository,\n {\n readonly findById: (id: number) => Effect.Effect<User>\n }\n>() {}\n\n// Implement the `users` group with access to the UsersRepository service\n//\n// ┌─── Layer<HttpApiGroup.ApiGroup<\"myApi\", \"users\">, never, UsersRepository>\n// ▼\nconst usersGroupLive = HttpApiBuilder.group(api, \"users\", (handlers) =>\n Effect.gen(function* () {\n // Access the UsersRepository service\n const repository = yield* UsersRepository\n return handlers.handle(\"getUser\", ({ path: { id } }) =>\n repository.findById(id)\n )\n })\n)\n```\n\n### Implementing a HttpApi\n\nOnce all your groups are implemented, you can create a top-level implementation to combine them into a unified API. This is done using the `HttpApiBuilder.api` API, which generates a `Layer`. You then use `Layer.provide` to include the implementations of all the groups into the top-level `HttpApi`.\n\n**Example** (Combining Group Implementations into a Top-Level API)\n\n```ts\nimport {\n HttpApi,\n HttpApiBuilder,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiSchema\n} from \"@effect/platform\"\nimport { DateTime, Effect, Layer, Schema } from \"effect\"\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\nconst idParam = HttpApiSchema.param(\"id\", Schema.NumberFromString)\n\nconst usersGroup = HttpApiGroup.make(\"users\").add(\n HttpApiEndpoint.get(\"getUser\")`/user/${idParam}`.addSuccess(User)\n)\n\nconst api = HttpApi.make(\"myApi\").add(usersGroup)\n\nconst usersGroupLive = HttpApiBuilder.group(api, \"users\", (handlers) =>\n handlers.handle(\"getUser\", ({ path: { id } }) =>\n Effect.succeed({\n id,\n name: \"John Doe\",\n createdAt: DateTime.unsafeNow()\n })\n )\n)\n\n// Combine all group implementations into the top-level API\n//\n// ┌─── Layer<HttpApi.Api, never, never>\n// ▼\nconst MyApiLive = HttpApiBuilder.api(api).pipe(Layer.provide(usersGroupLive))\n```\n\n### Serving the API\n\nYou can serve your API using the `HttpApiBuilder.serve` function. This utility builds an `HttpApp` from an `HttpApi` instance and uses an `HttpServer` to handle requests. Middleware can be added to customize or enhance the server's behavior.\n\n**Example** (Setting Up and Serving an API with Middleware)\n\n```ts\nimport {\n HttpApi,\n HttpApiBuilder,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiSchema,\n HttpMiddleware,\n HttpServer\n} from \"@effect/platform\"\nimport { NodeHttpServer, NodeRuntime } from \"@effect/platform-node\"\nimport { DateTime, Effect, Layer, Schema } from \"effect\"\nimport { createServer } from \"node:http\"\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\nconst idParam = HttpApiSchema.param(\"id\", Schema.NumberFromString)\n\nconst usersGroup = HttpApiGroup.make(\"users\").add(\n HttpApiEndpoint.get(\"getUser\")`/user/${idParam}`.addSuccess(User)\n)\n\nconst api = HttpApi.make(\"myApi\").add(usersGroup)\n\nconst usersGroupLive = HttpApiBuilder.group(api, \"users\", (handlers) =>\n handlers.handle(\"getUser\", ({ path: { id } }) =>\n Effect.succeed({\n id,\n name: \"John Doe\",\n createdAt: DateTime.unsafeNow()\n })\n )\n)\n\nconst MyApiLive = HttpApiBuilder.api(api).pipe(Layer.provide(usersGroupLive))\n\n// Configure and serve the API\nconst HttpLive = HttpApiBuilder.serve(HttpMiddleware.logger).pipe(\n // Add CORS middleware to handle cross-origin requests\n Layer.provide(HttpApiBuilder.middlewareCors()),\n // Provide the API implementation\n Layer.provide(MyApiLive),\n // Log the server's listening address\n HttpServer.withLogAddress,\n // Set up the Node.js HTTP server\n Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 }))\n)\n\n// Launch the server\nLayer.launch(HttpLive).pipe(NodeRuntime.runMain)\n```\n\n### Accessing the HttpServerRequest\n\nIn some cases, you may need to access details about the incoming `HttpServerRequest` within an endpoint handler. The HttpServerRequest module provides access to the request object, allowing you to inspect properties such as the HTTP method or headers.\n\n**Example** (Accessing the Request Object in a GET Endpoint)\n\n```ts\nimport {\n HttpApi,\n HttpApiBuilder,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpMiddleware,\n HttpServer\n} from \"@effect/platform\"\nimport { NodeHttpServer, NodeRuntime } from \"@effect/platform-node\"\nimport { Effect, Layer, Schema } from \"effect\"\nimport { createServer } from \"node:http\"\n\nconst api = HttpApi.make(\"myApi\").add(\n HttpApiGroup.make(\"group\").add(\n HttpApiEndpoint.get(\"get\", \"/\").addSuccess(Schema.String)\n )\n)\n\nconst groupLive = HttpApiBuilder.group(api, \"group\", (handlers) =>\n handlers.handle(\"get\", ({ request }) =>\n Effect.gen(function* () {\n // Log the HTTP method for demonstration purposes\n console.log(request.method)\n\n // Return a response\n return \"Hello, World!\"\n })\n )\n)\n\nconst MyApiLive = HttpApiBuilder.api(api).pipe(Layer.provide(groupLive))\n\nconst HttpLive = HttpApiBuilder.serve(HttpMiddleware.logger).pipe(\n Layer.provide(HttpApiBuilder.middlewareCors()),\n Layer.provide(MyApiLive),\n HttpServer.withLogAddress,\n Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 }))\n)\n\nLayer.launch(HttpLive).pipe(NodeRuntime.runMain)\n```\n\n### Streaming Requests\n\nStreaming requests allow you to send large or continuous data streams to the server. In this example, we define an API that accepts a stream of binary data and decodes it into a string.\n\n**Example** (Handling Streaming Requests)\n\n```ts\nimport {\n HttpApi,\n HttpApiBuilder,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiSchema,\n HttpMiddleware,\n HttpServer\n} from \"@effect/platform\"\nimport { NodeHttpServer, NodeRuntime } from \"@effect/platform-node\"\nimport { Effect, Layer, Schema } from \"effect\"\nimport { createServer } from \"node:http\"\n\nconst api = HttpApi.make(\"myApi\").add(\n HttpApiGroup.make(\"group\").add(\n HttpApiEndpoint.post(\"acceptStream\", \"/stream\")\n // Define the payload as a Uint8Array with a specific encoding\n .setPayload(\n Schema.Uint8ArrayFromSelf.pipe(\n HttpApiSchema.withEncoding({\n kind: \"Uint8Array\",\n contentType: \"application/octet-stream\"\n })\n )\n )\n .addSuccess(Schema.String)\n )\n)\n\nconst groupLive = HttpApiBuilder.group(api, \"group\", (handlers) =>\n handlers.handle(\"acceptStream\", (req) =>\n // Decode the incoming binary data into a string\n Effect.succeed(new TextDecoder().decode(req.payload))\n )\n)\n\nconst MyApiLive = HttpApiBuilder.api(api).pipe(Layer.provide(groupLive))\n\nconst HttpLive = HttpApiBuilder.serve(HttpMiddleware.logger).pipe(\n Layer.provide(HttpApiBuilder.middlewareCors()),\n Layer.provide(MyApiLive),\n HttpServer.withLogAddress,\n Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 }))\n)\n\nLayer.launch(HttpLive).pipe(NodeRuntime.runMain)\n```\n\nYou can test the streaming request using `curl` or any tool that supports sending binary data. For example:\n\n```sh\necho \"abc\" | curl -X POST 'http://localhost:3000/stream' --data-binary @- -H \"Content-Type: application/octet-stream\"\n# Output: abc\n```\n\n### Streaming Responses\n\nTo handle streaming responses in your API, you can return a raw `HttpServerResponse`. The `HttpServerResponse.stream` function is designed to return a continuous stream of data as the response.\n\n**Example** (Implementing a Streaming Endpoint)\n\n```ts\nimport {\n HttpApi,\n HttpApiBuilder,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiSchema,\n HttpMiddleware,\n HttpServer,\n HttpServerResponse\n} from \"@effect/platform\"\nimport { NodeHttpServer, NodeRuntime } from \"@effect/platform-node\"\nimport { Layer, Schedule, Schema, Stream } from \"effect\"\nimport { createServer } from \"node:http\"\n\n// Define the API with a single streaming endpoint\nconst api = HttpApi.make(\"myApi\").add(\n HttpApiGroup.make(\"group\").add(\n HttpApiEndpoint.get(\"getStream\", \"/stream\").addSuccess(\n Schema.String.pipe(\n HttpApiSchema.withEncoding({\n kind: \"Text\",\n contentType: \"application/octet-stream\"\n })\n )\n )\n )\n)\n\n// Simulate a stream of data\nconst stream = Stream.make(\"a\", \"b\", \"c\").pipe(\n Stream.schedule(Schedule.spaced(\"500 millis\")),\n Stream.map((s) => new TextEncoder().encode(s))\n)\n\nconst groupLive = HttpApiBuilder.group(api, \"group\", (handlers) =>\n handlers.handle(\"getStream\", () => HttpServerResponse.stream(stream))\n)\n\nconst MyApiLive = HttpApiBuilder.api(api).pipe(Layer.provide(groupLive))\n\nconst HttpLive = HttpApiBuilder.serve(HttpMiddleware.logger).pipe(\n Layer.provide(HttpApiBuilder.middlewareCors()),\n Layer.provide(MyApiLive),\n HttpServer.withLogAddress,\n Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 }))\n)\n\nLayer.launch(HttpLive).pipe(NodeRuntime.runMain)\n```\n\nYou can test the streaming response using `curl` or any similar HTTP client that supports streaming:\n\n```sh\ncurl 'http://localhost:3000/stream' --no-buffer\n```\n\nThe response will stream data (`a`, `b`, `c`) with a 500ms interval between each item.\n\n## Middlewares\n\n### Defining Middleware\n\nThe `HttpApiMiddleware` module allows you to add middleware to your API. Middleware can enhance your API by introducing features like logging, authentication, or additional error handling.\n\nYou can define middleware using the `HttpApiMiddleware.Tag` class, which lets you specify:\n\n| Option | Description |\n| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `failure` | A schema that describes any errors the middleware might return. |\n| `provides` | A `Context.Tag` representing the resource or data the middleware will provide to subsequent handlers. |\n| `security` | Definitions from `HttpApiSecurity` that the middleware will implement, such as authentication mechanisms. |\n| `optional` | A boolean indicating whether the request should continue if the middleware fails with an expected error. When `optional` is set to `true`, the `provides` and `failure` options do not affect the final error type or handlers. |\n\n**Example** (Defining a Logger Middleware)\n\n```ts\nimport {\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiMiddleware,\n HttpApiSchema\n} from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\n// Define a schema for errors returned by the logger middleware\nclass LoggerError extends Schema.TaggedError<LoggerError>()(\n \"LoggerError\",\n {}\n) {}\n\n// Extend the HttpApiMiddleware.Tag class to define the logger middleware tag\nclass Logger extends HttpApiMiddleware.Tag<Logger>()(\"Http/Logger\", {\n // Optionally define the error schema for the middleware\n failure: LoggerError\n}) {}\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\nconst idParam = HttpApiSchema.param(\"id\", Schema.NumberFromString)\n\nconst usersGroup = HttpApiGroup.make(\"users\")\n .add(\n HttpApiEndpoint.get(\"getUser\")`/user/${idParam}`\n .addSuccess(User)\n // Apply the middleware to a single endpoint\n .middleware(Logger)\n )\n // Or apply the middleware to the entire group\n .middleware(Logger)\n```\n\n### Implementing HttpApiMiddleware\n\nOnce you have defined your `HttpApiMiddleware`, you can implement it as a `Layer`. This allows the middleware to be applied to specific API groups or endpoints, enabling modular and reusable behavior.\n\n**Example** (Implementing and Using Logger Middleware)\n\n```ts\nimport { HttpApiMiddleware, HttpServerRequest } from \"@effect/platform\"\nimport { Effect, Layer } from \"effect\"\n\nclass Logger extends HttpApiMiddleware.Tag<Logger>()(\"Http/Logger\") {}\n\nconst LoggerLive = Layer.effect(\n Logger,\n Effect.gen(function* () {\n yield* Effect.log(\"creating Logger middleware\")\n\n // Middleware implementation as an Effect\n // that can access the `HttpServerRequest` context.\n return Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest\n yield* Effect.log(`Request: ${request.method} ${request.url}`)\n })\n })\n)\n```\n\nAfter implementing the middleware, you can attach it to your API groups or specific endpoints using the `Layer` APIs.\n\n```ts\nimport {\n HttpApi,\n HttpApiBuilder,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiMiddleware,\n HttpApiSchema,\n HttpServerRequest\n} from \"@effect/platform\"\nimport { DateTime, Effect, Layer, Schema } from \"effect\"\n\n// Define a schema for errors returned by the logger middleware\nclass LoggerError extends Schema.TaggedError<LoggerError>()(\n \"LoggerError\",\n {}\n) {}\n\n// Extend the HttpApiMiddleware.Tag class to define the logger middleware tag\nclass Logger extends HttpApiMiddleware.Tag<Logger>()(\"Http/Logger\", {\n // Optionally define the error schema for the middleware\n failure: LoggerError\n}) {}\n\nconst LoggerLive = Layer.effect(\n Logger,\n Effect.gen(function* () {\n yield* Effect.log(\"creating Logger middleware\")\n\n // Middleware implementation as an Effect\n // that can access the `HttpServerRequest` context.\n return Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest\n yield* Effect.log(`Request: ${request.method} ${request.url}`)\n })\n })\n)\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\nconst idParam = HttpApiSchema.param(\"id\", Schema.NumberFromString)\n\nconst usersGroup = HttpApiGroup.make(\"users\")\n .add(\n HttpApiEndpoint.get(\"getUser\")`/user/${idParam}`\n .addSuccess(User)\n // Apply the middleware to a single endpoint\n .middleware(Logger)\n )\n // Or apply the middleware to the entire group\n .middleware(Logger)\n\nconst api = HttpApi.make(\"myApi\").add(usersGroup)\n\nconst usersGroupLive = HttpApiBuilder.group(api, \"users\", (handlers) =>\n handlers.handle(\"getUser\", (req) =>\n Effect.succeed({\n id: req.path.id,\n name: \"John Doe\",\n createdAt: DateTime.unsafeNow()\n })\n )\n).pipe(\n // Provide the Logger middleware to the group\n Layer.provide(LoggerLive)\n)\n```\n\n### Defining security middleware\n\nThe `HttpApiSecurity` module enables you to add security annotations to your API. These annotations specify the type of authorization required to access specific endpoints.\n\nSupported authorization types include:\n\n| Authorization Type | Description |\n| ------------------------ | ---------------------------------------------------------------- |\n| `HttpApiSecurity.apiKey` | API key authorization via headers, query parameters, or cookies. |\n| `HttpApiSecurity.basic` | HTTP Basic authentication. |\n| `HttpApiSecurity.bearer` | Bearer token authentication. |\n\nThese security annotations can be used alongside `HttpApiMiddleware` to create middleware that protects your API endpoints.\n\n**Example** (Defining Security Middleware)\n\n```ts\nimport {\n HttpApi,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiMiddleware,\n HttpApiSchema,\n HttpApiSecurity\n} from \"@effect/platform\"\nimport { Context, Schema } from \"effect\"\n\n// Define a schema for the \"User\"\nclass User extends Schema.Class<User>(\"User\")({ id: Schema.Number }) {}\n\n// Define a schema for the \"Unauthorized\" error\nclass Unauthorized extends Schema.TaggedError<Unauthorized>()(\n \"Unauthorized\",\n {},\n // Specify the HTTP status code for unauthorized errors\n HttpApiSchema.annotations({ status: 401 })\n) {}\n\n// Define a Context.Tag for the authenticated user\nclass CurrentUser extends Context.Tag(\"CurrentUser\")<CurrentUser, User>() {}\n\n// Create the Authorization middleware\nclass Authorization extends HttpApiMiddleware.Tag<Authorization>()(\n \"Authorization\",\n {\n // Define the error schema for unauthorized access\n failure: Unauthorized,\n // Specify the resource this middleware will provide\n provides: CurrentUser,\n // Add security definitions\n security: {\n // ┌─── Custom name for the security definition\n // ▼\n myBearer: HttpApiSecurity.bearer\n // Additional security definitions can be added here.\n // They will attempt to be resolved in the order they are defined.\n }\n }\n) {}\n\nconst api = HttpApi.make(\"api\")\n .add(\n HttpApiGroup.make(\"group\")\n .add(\n HttpApiEndpoint.get(\"get\", \"/\")\n .addSuccess(Schema.String)\n // Apply the middleware to a single endpoint\n .middleware(Authorization)\n )\n // Or apply the middleware to the entire group\n .middleware(Authorization)\n )\n // Or apply the middleware to the entire API\n .middleware(Authorization)\n```\n\n### Implementing HttpApiSecurity middleware\n\nWhen using `HttpApiSecurity` in your middleware, the implementation involves creating a `Layer` with security handlers tailored to your requirements. Below is an example demonstrating how to implement middleware for `HttpApiSecurity.bearer` authentication.\n\n**Example** (Implementing Bearer Token Authentication Middleware)\n\n```ts\nimport {\n HttpApiMiddleware,\n HttpApiSchema,\n HttpApiSecurity\n} from \"@effect/platform\"\nimport { Context, Effect, Layer, Redacted, Schema } from \"effect\"\n\nclass User extends Schema.Class<User>(\"User\")({ id: Schema.Number }) {}\n\nclass Unauthorized extends Schema.TaggedError<Unauthorized>()(\n \"Unauthorized\",\n {},\n HttpApiSchema.annotations({ status: 401 })\n) {}\n\nclass CurrentUser extends Context.Tag(\"CurrentUser\")<CurrentUser, User>() {}\n\nclass Authorization extends HttpApiMiddleware.Tag<Authorization>()(\n \"Authorization\",\n {\n failure: Unauthorized,\n provides: CurrentUser,\n security: {\n myBearer: HttpApiSecurity.bearer\n }\n }\n) {}\n\nconst AuthorizationLive = Layer.effect(\n Authorization,\n Effect.gen(function* () {\n yield* Effect.log(\"creating Authorization middleware\")\n\n // Return the security handlers for the middleware\n return {\n // Define the handler for the Bearer token\n // The Bearer token is redacted for security\n myBearer: (bearerToken) =>\n Effect.gen(function* () {\n yield* Effect.log(\n \"checking bearer token\",\n Redacted.value(bearerToken)\n )\n // Return a mock User object as the CurrentUser\n return new User({ id: 1 })\n })\n }\n })\n)\n```\n\n### Adding Descriptions to Security Definitions\n\nThe `HttpApiSecurity.annotate` function allows you to add metadata, such as a description, to your security definitions. This metadata is displayed in the Swagger documentation, making it easier for developers to understand your API's security requirements.\n\n**Example** (Adding a Description to a Bearer Token Security Definition)\n\n```ts\nimport {\n HttpApiMiddleware,\n HttpApiSchema,\n HttpApiSecurity,\n OpenApi\n} from \"@effect/platform\"\nimport { Context, Schema } from \"effect\"\n\nclass User extends Schema.Class<User>(\"User\")({ id: Schema.Number }) {}\n\nclass Unauthorized extends Schema.TaggedError<Unauthorized>()(\n \"Unauthorized\",\n {},\n HttpApiSchema.annotations({ status: 401 })\n) {}\n\nclass CurrentUser extends Context.Tag(\"CurrentUser\")<CurrentUser, User>() {}\n\nclass Authorization extends HttpApiMiddleware.Tag<Authorization>()(\n \"Authorization\",\n {\n failure: Unauthorized,\n provides: CurrentUser,\n security: {\n myBearer: HttpApiSecurity.bearer.pipe(\n // Add a description to the security definition\n HttpApiSecurity.annotate(OpenApi.Description, \"my description\")\n )\n }\n }\n) {}\n```\n\n### Setting HttpApiSecurity cookies\n\nTo set a security cookie from within a handler, you can use the `HttpApiBuilder.securitySetCookie` API. This method sets a cookie with default properties, including the `HttpOnly` and `Secure` flags, ensuring the cookie is not accessible via JavaScript and is transmitted over secure connections.\n\n**Example** (Setting a Security Cookie in a Login Handler)\n\n```ts\n// Define the security configuration for an API key stored in a cookie\nconst security = HttpApiSecurity.apiKey({\n // Specify that the API key is stored in a cookie\n in: \"cookie\"\n // Define the cookie name,\n key: \"token\"\n})\n\nconst UsersApiLive = HttpApiBuilder.group(MyApi, \"users\", (handlers) =>\n handlers.handle(\"login\", () =>\n // Set the security cookie with a redacted value\n HttpApiBuilder.securitySetCookie(security, Redacted.make(\"keep me secret\"))\n )\n)\n```\n\n## Serving Swagger documentation\n\nYou can add Swagger documentation to your API using the `HttpApiSwagger` module. This integration provides an interactive interface for developers to explore and test your API. To enable Swagger, you simply provide the `HttpApiSwagger.layer` to your server implementation.\n\n**Example** (Adding Swagger Documentation to an API)\n\n```ts\nimport {\n HttpApi,\n HttpApiBuilder,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiSchema,\n HttpApiSwagger,\n HttpMiddleware,\n HttpServer\n} from \"@effect/platform\"\nimport { NodeHttpServer, NodeRuntime } from \"@effect/platform-node\"\nimport { DateTime, Effect, Layer, Schema } from \"effect\"\nimport { createServer } from \"node:http\"\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\nconst idParam = HttpApiSchema.param(\"id\", Schema.NumberFromString)\n\nconst usersGroup = HttpApiGroup.make(\"users\").add(\n HttpApiEndpoint.get(\"getUser\")`/user/${idParam}`.addSuccess(User)\n)\n\nconst api = HttpApi.make(\"myApi\").add(usersGroup)\n\nconst usersGroupLive = HttpApiBuilder.group(api, \"users\", (handlers) =>\n handlers.handle(\"getUser\", ({ path: { id } }) =>\n Effect.succeed({\n id,\n name: \"John Doe\",\n createdAt: DateTime.unsafeNow()\n })\n )\n)\n\nconst MyApiLive = HttpApiBuilder.api(api).pipe(Layer.provide(usersGroupLive))\n\nconst HttpLive = HttpApiBuilder.serve(HttpMiddleware.logger).pipe(\n // Add the Swagger documentation layer\n Layer.provide(\n HttpApiSwagger.layer({\n // Specify the Swagger documentation path.\n // \"/docs\" is the default path.\n path: \"/docs\"\n })\n ),\n Layer.provide(HttpApiBuilder.middlewareCors()),\n Layer.provide(MyApiLive),\n HttpServer.withLogAddress,\n Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 }))\n)\n\nLayer.launch(HttpLive).pipe(NodeRuntime.runMain)\n```\n\n\n\n### Adding OpenAPI Annotations\n\nYou can add OpenAPI annotations to your API to include metadata such as titles, descriptions, and more. These annotations help generate richer API documentation.\n\n#### HttpApi\n\nBelow is a list of available annotations for a top-level `HttpApi`. They can be added using the `.annotate` method:\n\n| Annotation | Description |\n| --------------------------- | ------------------------------------------------------------------------------------------------------------------ |\n| `HttpApi.AdditionalSchemas` | Adds custom schemas to the final OpenAPI specification. Only schemas with an `identifier` annotation are included. |\n| `OpenApi.Description` | Sets a general description for the API. |\n| `OpenApi.License` | Defines the license used by the API. |\n| `OpenApi.Summary` | Provides a brief summary of the API. |\n| `OpenApi.Servers` | Lists server URLs and optional metadata such as variables. |\n| `OpenApi.Override` | Merges the supplied fields into the resulting specification. |\n| `OpenApi.Transform` | Allows you to modify the final specification with a custom function. |\n\n**Example** (Annotating the Top-Level API)\n\n```ts\nimport { HttpApi, OpenApi } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst api = HttpApi.make(\"api\")\n // Provide additional schemas\n .annotate(HttpApi.AdditionalSchemas, [\n Schema.String.annotations({ identifier: \"MyString\" })\n ])\n // Add a description\n .annotate(OpenApi.Description, \"my description\")\n // Set license information\n .annotate(OpenApi.License, { name: \"MIT\", url: \"http://example.com\" })\n // Provide a summary\n .annotate(OpenApi.Summary, \"my summary\")\n // Define servers\n .annotate(OpenApi.Servers, [\n {\n url: \"http://example.com\",\n description: \"example\",\n variables: { a: { default: \"b\", enum: [\"c\"], description: \"d\" } }\n }\n ])\n // Override parts of the generated specification\n .annotate(OpenApi.Override, {\n tags: [{ name: \"a\", description: \"a-description\" }]\n })\n // Apply a transform function to the final specification\n .annotate(OpenApi.Transform, (spec) => ({\n ...spec,\n tags: [...spec.tags, { name: \"b\", description: \"b-description\" }]\n }))\n\n// Generate the OpenAPI specification from the annotated API\nconst spec = OpenApi.fromApi(api)\n\nconsole.log(JSON.stringify(spec, null, 2))\n/*\nOutput:\n{\n \"openapi\": \"3.1.0\",\n \"info\": {\n \"title\": \"Api\",\n \"version\": \"0.0.1\",\n \"description\": \"my description\",\n \"license\": {\n \"name\": \"MIT\",\n \"url\": \"http://example.com\"\n },\n \"summary\": \"my summary\"\n },\n \"paths\": {},\n \"tags\": [\n { \"name\": \"a\", \"description\": \"a-description\" },\n { \"name\": \"b\", \"description\": \"b-description\" }\n ],\n \"components\": {\n \"schemas\": {\n \"MyString\": {\n \"type\": \"string\"\n }\n },\n \"securitySchemes\": {}\n },\n \"security\": [],\n \"servers\": [\n {\n \"url\": \"http://example.com\",\n \"description\": \"example\",\n \"variables\": {\n \"a\": {\n \"default\": \"b\",\n \"enum\": [\n \"c\"\n ],\n \"description\": \"d\"\n }\n }\n }\n ]\n}\n*/\n```\n\n#### HttpApiGroup\n\nThe following annotations can be added to an `HttpApiGroup`:\n\n| Annotation | Description |\n| ---------------------- | --------------------------------------------------------------------- |\n| `OpenApi.Description` | Sets a description for this group. |\n| `OpenApi.ExternalDocs` | Provides external documentation links for the group. |\n| `OpenApi.Override` | Merges specified fields into the resulting specification. |\n| `OpenApi.Transform` | Lets you modify the final group specification with a custom function. |\n| `OpenApi.Exclude` | Excludes the group from the final OpenAPI specification. |\n\n**Example** (Annotating a Group)\n\n```ts\nimport { HttpApi, HttpApiGroup, OpenApi } from \"@effect/platform\"\n\nconst api = HttpApi.make(\"api\")\n .add(\n HttpApiGroup.make(\"group\")\n // Add a description for the group\n .annotate(OpenApi.Description, \"my description\")\n // Provide external documentation links\n .annotate(OpenApi.ExternalDocs, {\n url: \"http://example.com\",\n description: \"example\"\n })\n // Override parts of the final output\n .annotate(OpenApi.Override, { name: \"my name\" })\n // Transform the final specification for this group\n .annotate(OpenApi.Transform, (spec) => ({\n ...spec,\n name: spec.name + \"-transformed\"\n }))\n )\n .add(\n HttpApiGroup.make(\"excluded\")\n // Exclude the group from the final specification\n .annotate(OpenApi.Exclude, true)\n )\n\n// Generate the OpenAPI spec\nconst spec = OpenApi.fromApi(api)\n\nconsole.log(JSON.stringify(spec, null, 2))\n/*\nOutput:\n{\n \"openapi\": \"3.1.0\",\n \"info\": {\n \"title\": \"Api\",\n \"version\": \"0.0.1\"\n },\n \"paths\": {},\n \"tags\": [\n {\n \"name\": \"my name-transformed\",\n \"description\": \"my description\",\n \"externalDocs\": {\n \"url\": \"http://example.com\",\n \"description\": \"example\"\n }\n }\n ],\n \"components\": {\n \"schemas\": {},\n \"securitySchemes\": {}\n },\n \"security\": []\n}\n*/\n```\n\n#### HttpApiEndpoint\n\nFor an `HttpApiEndpoint`, you can use the following annotations:\n\n| Annotation | Description |\n| ---------------------- | --------------------------------------------------------------------------- |\n| `OpenApi.Description` | Adds a description for this endpoint. |\n| `OpenApi.Summary` | Provides a short summary of the endpoint's purpose. |\n| `OpenApi.Deprecated` | Marks the endpoint as deprecated. |\n| `OpenApi.ExternalDocs` | Supplies external documentation links for the endpoint. |\n| `OpenApi.Override` | Merges specified fields into the resulting specification for this endpoint. |\n| `OpenApi.Transform` | Lets you modify the final endpoint specification with a custom function. |\n| `OpenApi.Exclude` | Excludes the endpoint from the final OpenAPI specification. |\n\n**Example** (Annotating an Endpoint)\n\n```ts\nimport {\n HttpApi,\n HttpApiEndpoint,\n HttpApiGroup,\n OpenApi\n} from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst api = HttpApi.make(\"api\").add(\n HttpApiGroup.make(\"group\")\n .add(\n HttpApiEndpoint.get(\"get\", \"/\")\n .addSuccess(Schema.String)\n // Add a description\n .annotate(OpenApi.Description, \"my description\")\n // Provide a summary\n .annotate(OpenApi.Summary, \"my summary\")\n // Mark the endpoint as deprecated\n .annotate(OpenApi.Deprecated, true)\n // Provide external documentation\n .annotate(OpenApi.ExternalDocs, {\n url: \"http://example.com\",\n description: \"example\"\n })\n )\n .add(\n HttpApiEndpoint.get(\"excluded\", \"/excluded\")\n .addSuccess(Schema.String)\n // Exclude this endpoint from the final specification\n .annotate(OpenApi.Exclude, true)\n )\n)\n\n// Generate the OpenAPI spec\nconst spec = OpenApi.fromApi(api)\n\nconsole.log(JSON.stringify(spec, null, 2))\n/*\nOutput:\n{\n \"openapi\": \"3.1.0\",\n \"info\": {\n \"title\": \"Api\",\n \"version\": \"0.0.1\"\n },\n \"paths\": {\n \"/\": {\n \"get\": {\n \"tags\": [\n \"group\"\n ],\n \"operationId\": \"my operationId-transformed\",\n \"parameters\": [],\n \"security\": [],\n \"responses\": {\n \"200\": {\n \"description\": \"a string\",\n \"content\": {\n \"application/json\": {\n \"schema\": {\n \"type\": \"string\"\n }\n }\n }\n },\n \"400\": {\n \"description\": \"The request did not match the expected schema\",\n \"content\": {\n \"application/json\": {\n \"schema\": {\n \"$ref\": \"#/components/schemas/HttpApiDecodeError\"\n }\n }\n }\n }\n },\n \"description\": \"my description\",\n \"summary\": \"my summary\",\n \"deprecated\": true,\n \"externalDocs\": {\n \"url\": \"http://example.com\",\n \"description\": \"example\"\n }\n }\n }\n },\n ...\n}\n*/\n```\n\nThe default response description is \"Success\". You can override this by annotating the schema.\n\n**Example** (Defining a custom response description)\n\n```ts\nimport {\n HttpApi,\n HttpApiEndpoint,\n HttpApiGroup,\n OpenApi\n} from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n}).annotations({ identifier: \"User\" })\n\nconst api = HttpApi.make(\"api\").add(\n HttpApiGroup.make(\"group\").add(\n HttpApiEndpoint.get(\"getUsers\", \"/users\").addSuccess(\n Schema.Array(User).annotations({\n description: \"Returns an array of users\"\n })\n )\n )\n)\n\nconst spec = OpenApi.fromApi(api)\n\nconsole.log(JSON.stringify(spec.paths, null, 2))\n/*\nOutput:\n{\n \"/users\": {\n \"get\": {\n \"tags\": [\n \"group\"\n ],\n \"operationId\": \"group.getUsers\",\n \"parameters\": [],\n \"security\": [],\n \"responses\": {\n \"200\": {\n \"description\": \"Returns an array of users\",\n \"content\": {\n \"application/json\": {\n \"schema\": {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/components/schemas/User\"\n },\n \"description\": \"Returns an array of users\"\n }\n }\n }\n },\n \"400\": {\n \"description\": \"The request did not match the expected schema\",\n \"content\": {\n \"application/json\": {\n \"schema\": {\n \"$ref\": \"#/components/schemas/HttpApiDecodeError\"\n }\n }\n }\n }\n }\n }\n }\n}\n*/\n```\n\n### Top Level Groups\n\nWhen a group is marked as `topLevel`, the operation IDs of its endpoints do not include the group name as a prefix. This is helpful when you want to group endpoints under a shared tag without adding a redundant prefix to their operation IDs.\n\n**Example** (Using a Top-Level Group)\n\n```ts\nimport {\n HttpApi,\n HttpApiEndpoint,\n HttpApiGroup,\n OpenApi\n} from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst api = HttpApi.make(\"api\").add(\n // Mark the group as top-level\n HttpApiGroup.make(\"group\", { topLevel: true }).add(\n HttpApiEndpoint.get(\"get\", \"/\").addSuccess(Schema.String)\n )\n)\n\n// Generate the OpenAPI spec\nconst spec = OpenApi.fromApi(api)\n\nconsole.log(JSON.stringify(spec.paths, null, 2))\n/*\nOutput:\n{\n \"/\": {\n \"get\": {\n \"tags\": [\n \"group\"\n ],\n \"operationId\": \"get\", // The operation ID is not prefixed with \"group\"\n \"parameters\": [],\n \"security\": [],\n \"responses\": {\n \"200\": {\n \"description\": \"a string\",\n \"content\": {\n \"application/json\": {\n \"schema\": {\n \"type\": \"string\"\n }\n }\n }\n },\n \"400\": {\n \"description\": \"The request did not match the expected schema\",\n \"content\": {\n \"application/json\": {\n \"schema\": {\n \"$ref\": \"#/components/schemas/HttpApiDecodeError\"\n }\n }\n }\n }\n }\n }\n }\n}\n*/\n```\n\n## Deriving a Client\n\nAfter defining your API, you can derive a client that interacts with the server. The `HttpApiClient` module simplifies the process by providing tools to generate a client based on your API definition.\n\n**Example** (Deriving and Using a Client)\n\nThis example demonstrates how to create a client for an API and use it to call an endpoint.\n\n```ts\nimport {\n FetchHttpClient,\n HttpApi,\n HttpApiBuilder,\n HttpApiClient,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiSchema,\n HttpApiSwagger,\n HttpMiddleware,\n HttpServer\n} from \"@effect/platform\"\nimport { NodeHttpServer, NodeRuntime } from \"@effect/platform-node\"\nimport { DateTime, Effect, Layer, Schema } from \"effect\"\nimport { createServer } from \"node:http\"\n\nconst User = Schema.Struct({\n id: Schema.Number,\n name: Schema.String,\n createdAt: Schema.DateTimeUtc\n})\n\nconst idParam = HttpApiSchema.param(\"id\", Schema.NumberFromString)\n\nconst usersGroup = HttpApiGroup.make(\"users\").add(\n HttpApiEndpoint.get(\"getUser\")`/user/${idParam}`.addSuccess(User)\n)\n\nconst api = HttpApi.make(\"myApi\").add(usersGroup)\n\nconst usersGroupLive = HttpApiBuilder.group(api, \"users\", (handlers) =>\n handlers.handle(\"getUser\", ({ path: { id } }) =>\n Effect.succeed({\n id,\n name: \"John Doe\",\n createdAt: DateTime.unsafeNow()\n })\n )\n)\n\nconst MyApiLive = HttpApiBuilder.api(api).pipe(Layer.provide(usersGroupLive))\n\nconst HttpLive = HttpApiBuilder.serve(HttpMiddleware.logger).pipe(\n Layer.provide(HttpApiSwagger.layer()),\n Layer.provide(HttpApiBuilder.middlewareCors()),\n Layer.provide(MyApiLive),\n HttpServer.withLogAddress,\n Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 }))\n)\n\nLayer.launch(HttpLive).pipe(NodeRuntime.runMain)\n\n// Create a program that derives and uses the client\nconst program = Effect.gen(function* () {\n // Derive the client\n const client = yield* HttpApiClient.make(api, {\n baseUrl: \"http://localhost:3000\"\n })\n // Call the `getUser` endpoint\n const user = yield* client.users.getUser({ path: { id: 1 } })\n console.log(user)\n})\n\n// Provide a Fetch-based HTTP client and run the program\nEffect.runFork(program.pipe(Effect.provide(FetchHttpClient.layer)))\n/*\nExample Output:\nUser {\n id: 1,\n name: 'John Doe',\n createdAt: DateTime.Utc(2025-01-04T15:14:49.562Z)\n}\n*/\n```\n\n### Top Level Groups\n\nWhen a group is marked as `topLevel`, the methods on the client are not nested under the group name. This can simplify client usage by providing direct access to the endpoint methods.\n\n**Example** (Using a Top-Level Group in the Client)\n\n```ts\nimport {\n HttpApi,\n HttpApiClient,\n HttpApiEndpoint,\n HttpApiGroup\n} from \"@effect/platform\"\nimport { Effect, Schema } from \"effect\"\n\nconst api = HttpApi.make(\"api\").add(\n // Mark the group as top-level\n HttpApiGroup.make(\"group\", { topLevel: true }).add(\n HttpApiEndpoint.get(\"get\", \"/\").addSuccess(Schema.String)\n )\n)\n\nconst program = Effect.gen(function* () {\n const client = yield* HttpApiClient.make(api, {\n baseUrl: \"http://localhost:3000\"\n })\n // The `get` method is not nested under the \"group\" name\n const user = yield* client.get()\n console.log(user)\n})\n```\n\n## Converting to a Web Handler\n\nYou can convert your `HttpApi` implementation into a web handler using the `HttpApiBuilder.toWebHandler` API. This approach enables you to serve your API through a custom server setup.\n\n**Example** (Creating and Serving a Web Handler)\n\n```ts\nimport {\n HttpApi,\n HttpApiBuilder,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiSwagger,\n HttpServer\n} from \"@effect/platform\"\nimport { Effect, Layer, Schema } from \"effect\"\nimport * as http from \"node:http\"\n\nconst api = HttpApi.make(\"myApi\").add(\n HttpApiGroup.make(\"group\").add(\n HttpApiEndpoint.get(\"get\", \"/\").addSuccess(Schema.String)\n )\n)\n\nconst groupLive = HttpApiBuilder.group(api, \"group\", (handlers) =>\n handlers.handle(\"get\", () => Effect.succeed(\"Hello, world!\"))\n)\n\nconst MyApiLive = HttpApiBuilder.api(api).pipe(Layer.provide(groupLive))\n\nconst SwaggerLayer = HttpApiSwagger.layer().pipe(Layer.provide(MyApiLive))\n\n// Convert the API to a web handler\nconst { dispose, handler } = HttpApiBuilder.toWebHandler(\n Layer.mergeAll(MyApiLive, SwaggerLayer, HttpServer.layerContext)\n)\n\n// Serving the handler using a custom HTTP server\nhttp\n .createServer(async (req, res) => {\n const url = `http://${req.headers.host}${req.url}`\n const init: RequestInit = {\n method: req.method!\n }\n\n const response = await handler(new Request(url, init))\n\n res.writeHead(\n response.status,\n response.statusText,\n Object.fromEntries(response.headers.entries())\n )\n const responseBody = await response.arrayBuffer()\n res.end(Buffer.from(responseBody))\n })\n .listen(3000, () => {\n console.log(\"Server running at http://localhost:3000/\")\n })\n .on(\"close\", () => {\n dispose()\n })\n```\n\n# HTTP Client\n\n## Overview\n\nThe `@effect/platform/HttpClient*` modules provide a way to send HTTP requests,\nhandle responses, and abstract over the differences between platforms.\n\nThe `HttpClient` interface has a set of methods for sending requests:\n\n- `.execute` - takes a [HttpClientRequest](#httpclientrequest) and returns a `HttpClientResponse`\n- `.{get, del, head, options, patch, post, put}` - convenience methods for creating a request and\n executing it in one step\n\nTo access the `HttpClient`, you can use the `HttpClient.HttpClient` [tag](https://effect.website/docs/guides/context-management/services).\nThis will give you access to a `HttpClient` instance.\n\n**Example: Retrieving JSON Data (GET)**\n\n```ts\nimport { FetchHttpClient, HttpClient } from \"@effect/platform\"\nimport { Effect } from \"effect\"\n\nconst program = Effect.gen(function* () {\n // Access HttpClient\n const client = yield* HttpClient.HttpClient\n\n // Create and execute a GET request\n const response = yield* client.get(\n \"https://jsonplaceholder.typicode.com/posts/1\"\n )\n\n const json = yield* response.json\n\n console.log(json)\n}).pipe(\n // Provide the HttpClient\n Effect.provide(FetchHttpClient.layer)\n)\n\nEffect.runPromise(program)\n/*\nOutput:\n{\n userId: 1,\n id: 1,\n title: 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit',\n body: 'quia et suscipit\\n' +\n 'suscipit recusandae consequuntur expedita et cum\\n' +\n 'reprehenderit molestiae ut ut quas totam\\n' +\n 'nostrum rerum est autem sunt rem eveniet architecto'\n}\n*/\n```\n\n**Example: Retrieving JSON Data with accessor apis (GET)**\n\nThe `HttpClient` module also provides a set of accessor apis that allow you to\neasily send requests without first accessing the `HttpClient` service.\n\nBelow is an example of using the `get` accessor api to send a GET request:\n\n(The following examples will continue to use the `HttpClient` service approach).\n\n```ts\nimport { FetchHttpClient, HttpClient } from \"@effect/platform\"\nimport { Effect } from \"effect\"\n\nconst program = HttpClient.get(\n \"https://jsonplaceholder.typicode.com/posts/1\"\n).pipe(\n Effect.andThen((response) => response.json),\n Effect.provide(FetchHttpClient.layer)\n)\n\nEffect.runPromise(program)\n/*\nOutput:\n{\n userId: 1,\n id: 1,\n title: 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit',\n body: 'quia et suscipit\\n' +\n 'suscipit recusandae consequuntur expedita et cum\\n' +\n 'reprehenderit molestiae ut ut quas totam\\n' +\n 'nostrum rerum est autem sunt rem eveniet architecto'\n}\n*/\n```\n\n**Example: Creating and Executing a Custom Request**\n\nUsing [HttpClientRequest](#httpclientrequest), you can create and then execute a request. This is useful for customizing the request further.\n\n```ts\nimport {\n FetchHttpClient,\n HttpClient,\n HttpClientRequest\n} from \"@effect/platform\"\nimport { Effect } from \"effect\"\n\nconst program = Effect.gen(function* () {\n // Access HttpClient\n const client = yield* HttpClient.HttpClient\n\n // Create a GET request\n const req = HttpClientRequest.get(\n \"https://jsonplaceholder.typicode.com/posts/1\"\n )\n\n // Optionally customize the request\n\n // Execute the request and get the response\n const response = yield* client.execute(req)\n\n const json = yield* response.json\n\n console.log(json)\n}).pipe(\n // Provide the HttpClient\n Effect.provide(FetchHttpClient.layer)\n)\n\nEffect.runPromise(program)\n/*\nOutput:\n{\n userId: 1,\n id: 1,\n title: 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit',\n body: 'quia et suscipit\\n' +\n 'suscipit recusandae consequuntur expedita et cum\\n' +\n 'reprehenderit molestiae ut ut quas totam\\n' +\n 'nostrum rerum est autem sunt rem eveniet architecto'\n}\n*/\n```\n\n## Customize a HttpClient\n\nThe `HttpClient` module allows you to customize the client in various ways. For instance, you can log details of a request before execution using the `tapRequest` function.\n\n**Example: Tapping**\n\n```ts\nimport { FetchHttpClient, HttpClient } from \"@effect/platform\"\nimport { Console, Effect } from \"effect\"\n\nconst program = Effect.gen(function* () {\n const client = (yield* HttpClient.HttpClient).pipe(\n // Log the request before fetching\n HttpClient.tapRequest(Console.log)\n )\n\n const response = yield* client.get(\n \"https://jsonplaceholder.typicode.com/posts/1\"\n )\n\n const json = yield* response.json\n\n console.log(json)\n}).pipe(Effect.provide(FetchHttpClient.layer))\n\nEffect.runPromise(program)\n/*\nOutput:\n{\n _id: '@effect/platform/HttpClientRequest',\n method: 'GET',\n url: 'https://jsonplaceholder.typicode.com/posts/1',\n urlParams: [],\n hash: { _id: 'Option', _tag: 'None' },\n headers: Object <[Object: null prototype]> {},\n body: { _id: '@effect/platform/HttpBody', _tag: 'Empty' }\n}\n{\n userId: 1,\n id: 1,\n title: 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit',\n body: 'quia et suscipit\\n' +\n 'suscipit recusandae consequuntur expedita et cum\\n' +\n 'reprehenderit molestiae ut ut quas totam\\n' +\n 'nostrum rerum est autem sunt rem eveniet architecto'\n}\n*/\n```\n\n**Operations Summary**\n\n| Operation | Description |\n| ------------------------ | --------------------------------------------------------------------------------------- |\n| `get`,`post`,`put`... | Send a request without first accessing the `HttpClient` service. |\n| `filterOrElse` | Filters the result of a response, or runs an alternative effect if the predicate fails. |\n| `filterOrFail` | Filters the result of a response, or throws an error if the predicate fails. |\n| `filterStatus` | Filters responses by HTTP status code. |\n| `filterStatusOk` | Filters responses that return a 2xx status code. |\n| `followRedirects` | Follows HTTP redirects up to a specified number of times. |\n| `mapRequest` | Appends a transformation of the request object before sending it. |\n| `mapRequestEffect` | Appends an effectful transformation of the request object before sending it. |\n| `mapRequestInput` | Prepends a transformation of the request object before sending it. |\n| `mapRequestInputEffect` | Prepends an effectful transformation of the request object before sending it. |\n| `retry` | Retries the request based on a provided schedule or policy. |\n| `tap` | Performs an additional effect after a successful request. |\n| `tapRequest` | Performs an additional effect on the request before sending it. |\n| `withCookiesRef` | Associates a `Ref` of cookies with the client for handling cookies across requests. |\n| `withTracerDisabledWhen` | Disables tracing for specific requests based on a provided predicate. |\n| `withTracerPropagation` | Enables or disables tracing propagation for the request. |\n\n### Mapping Requests\n\nNote that `mapRequest` and `mapRequestEffect` add transformations at the end of the request chain, while `mapRequestInput` and `mapRequestInputEffect` apply transformations at the start:\n\n```ts\nimport { FetchHttpClient, HttpClient } from \"@effect/platform\"\nimport { Effect } from \"effect\"\n\nconst program = Effect.gen(function* () {\n const client = (yield* HttpClient.HttpClient).pipe(\n // Append transformation\n HttpClient.mapRequest((req) => {\n console.log(1)\n return req\n }),\n // Another append transformation\n HttpClient.mapRequest((req) => {\n console.log(2)\n return req\n }),\n // Prepend transformation, this executes first\n HttpClient.mapRequestInput((req) => {\n console.log(3)\n return req\n })\n )\n\n const response = yield* client.get(\n \"https://jsonplaceholder.typicode.com/posts/1\"\n )\n\n const json = yield* response.json\n\n console.log(json)\n}).pipe(Effect.provide(FetchHttpClient.layer))\n\nEffect.runPromise(program)\n/*\nOutput:\n3\n1\n2\n{\n userId: 1,\n id: 1,\n title: 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit',\n body: 'quia et suscipit\\n' +\n 'suscipit recusandae consequuntur expedita et cum\\n' +\n 'reprehenderit molestiae ut ut quas totam\\n' +\n 'nostrum rerum est autem sunt rem eveniet architecto'\n}\n*/\n```\n\n### Persisting Cookies\n\nYou can manage cookies across requests using the `HttpClient.withCookiesRef` function, which associates a reference to a `Cookies` object with the client.\n\n```ts\nimport { Cookies, FetchHttpClient, HttpClient } from \"@effect/platform\"\nimport { Effect, Ref } from \"effect\"\n\nconst program = Effect.gen(function* () {\n // Create a reference to store cookies\n const ref = yield* Ref.make(Cookies.empty)\n\n // Access the HttpClient and associate the cookies reference with it\n const client = (yield* HttpClient.HttpClient).pipe(\n HttpClient.withCookiesRef(ref)\n )\n\n // Make a GET request to the specified URL\n yield* client.get(\"https://www.google.com/\")\n\n // Log the keys of the cookies stored in the reference\n console.log(Object.keys((yield* ref).cookies))\n}).pipe(Effect.provide(FetchHttpClient.layer))\n\nEffect.runPromise(program)\n// Output: [ 'SOCS', 'AEC', '__Secure-ENID' ]\n```\n\n## RequestInit Options\n\nYou can customize the `FetchHttpClient` by passing `RequestInit` options to configure aspects of the HTTP requests, such as credentials, headers, and more.\n\nIn this example, we customize the `FetchHttpClient` to include credentials with every request:\n\n```ts\nimport { FetchHttpClient, HttpClient } from \"@effect/platform\"\nimport { Effect, Layer } from \"effect\"\n\nconst CustomFetchLive = FetchHttpClient.layer.pipe(\n Layer.provide(\n Layer.succeed(FetchHttpClient.RequestInit, {\n credentials: \"include\"\n })\n )\n)\n\nconst program = Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const response = yield* client.get(\n \"https://jsonplaceholder.typicode.com/posts/1\"\n )\n const json = yield* response.json\n console.log(json)\n}).pipe(Effect.provide(CustomFetchLive))\n```\n\n## Create a Custom HttpClient\n\nYou can create a custom `HttpClient` using the `HttpClient.make` function. This allows you to simulate or mock server responses within your application.\n\n```ts\nimport { HttpClient, HttpClientResponse } from \"@effect/platform\"\nimport { Effect, Layer } from \"effect\"\n\nconst myClient = HttpClient.make((req) =>\n Effect.succeed(\n HttpClientResponse.fromWeb(\n req,\n // Simulate a response from a server\n new Response(\n JSON.stringify({\n userId: 1,\n id: 1,\n title: \"title...\",\n body: \"body...\"\n })\n )\n )\n )\n)\n\nconst program = Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const response = yield* client.get(\n \"https://jsonplaceholder.typicode.com/posts/1\"\n )\n const json = yield* response.json\n console.log(json)\n}).pipe(\n // Provide the HttpClient\n Effect.provide(Layer.succeed(HttpClient.HttpClient, myClient))\n)\n\nEffect.runPromise(program)\n/*\nOutput:\n{ userId: 1, id: 1, title: 'title...', body: 'body...' }\n*/\n```\n\n## HttpClientRequest\n\n### Overview\n\nYou can create a `HttpClientRequest` using the following provided constructors:\n\n| Constructor | Description |\n| --------------------------- | ------------------------- |\n| `HttpClientRequest.del` | Create a DELETE request |\n| `HttpClientRequest.get` | Create a GET request |\n| `HttpClientRequest.head` | Create a HEAD request |\n| `HttpClientRequest.options` | Create an OPTIONS request |\n| `HttpClientRequest.patch` | Create a PATCH request |\n| `HttpClientRequest.post` | Create a POST request |\n| `HttpClientRequest.put` | Create a PUT request |\n\n### Setting Headers\n\nWhen making HTTP requests, sometimes you need to include additional information in the request headers. You can set headers using the `setHeader` function for a single header or `setHeaders` for multiple headers simultaneously.\n\n```ts\nimport { HttpClientRequest } from \"@effect/platform\"\n\nconst req = HttpClientRequest.get(\"https://api.example.com/data\").pipe(\n // Setting a single header\n HttpClientRequest.setHeader(\"Authorization\", \"Bearer your_token_here\"),\n // Setting multiple headers\n HttpClientRequest.setHeaders({\n \"Content-Type\": \"application/json; charset=UTF-8\",\n \"Custom-Header\": \"CustomValue\"\n })\n)\n\nconsole.log(JSON.stringify(req.headers, null, 2))\n/*\nOutput:\n{\n \"authorization\": \"Bearer your_token_here\",\n \"content-type\": \"application/json; charset=UTF-8\",\n \"custom-header\": \"CustomValue\"\n}\n*/\n```\n\n### basicAuth\n\nTo include basic authentication in your HTTP request, you can use the `basicAuth` method provided by `HttpClientRequest`.\n\n```ts\nimport { HttpClientRequest } from \"@effect/platform\"\n\nconst req = HttpClientRequest.get(\"https://api.example.com/data\").pipe(\n HttpClientRequest.basicAuth(\"your_username\", \"your_password\")\n)\n\nconsole.log(JSON.stringify(req.headers, null, 2))\n/*\nOutput:\n{\n \"authorization\": \"Basic eW91cl91c2VybmFtZTp5b3VyX3Bhc3N3b3Jk\"\n}\n*/\n```\n\n### bearerToken\n\nTo include a Bearer token in your HTTP request, use the `bearerToken` method provided by `HttpClientRequest`.\n\n```ts\nimport { HttpClientRequest } from \"@effect/platform\"\n\nconst req = HttpClientRequest.get(\"https://api.example.com/data\").pipe(\n HttpClientRequest.bearerToken(\"your_token\")\n)\n\nconsole.log(JSON.stringify(req.headers, null, 2))\n/*\nOutput:\n{\n \"authorization\": \"Bearer your_token\"\n}\n*/\n```\n\n### accept\n\nTo specify the media types that are acceptable for the response, use the `accept` method provided by `HttpClientRequest`.\n\n```ts\nimport { HttpClientRequest } from \"@effect/platform\"\n\nconst req = HttpClientRequest.get(\"https://api.example.com/data\").pipe(\n HttpClientRequest.accept(\"application/xml\")\n)\n\nconsole.log(JSON.stringify(req.headers, null, 2))\n/*\nOutput:\n{\n \"accept\": \"application/xml\"\n}\n*/\n```\n\n### acceptJson\n\nTo indicate that the client accepts JSON responses, use the `acceptJson` method provided by `HttpClientRequest`.\n\n```ts\nimport { HttpClientRequest } from \"@effect/platform\"\n\nconst req = HttpClientRequest.get(\"https://api.example.com/data\").pipe(\n HttpClientRequest.acceptJson\n)\n\nconsole.log(JSON.stringify(req.headers, null, 2))\n/*\nOutput:\n{\n \"accept\": \"application/json\"\n}\n*/\n```\n\n## GET\n\n### Converting the Response\n\nThe `HttpClientResponse` provides several methods to convert a response into different formats.\n\n**Example: Converting to JSON**\n\n```ts\nimport { FetchHttpClient, HttpClient } from \"@effect/platform\"\nimport { NodeRuntime } from \"@effect/platform-node\"\nimport { Console, Effect } from \"effect\"\n\nconst getPostAsJson = Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const response = yield* client.get(\n \"https://jsonplaceholder.typicode.com/posts/1\"\n )\n return yield* response.json\n}).pipe(Effect.provide(FetchHttpClient.layer))\n\ngetPostAsJson.pipe(\n Effect.andThen((post) => Console.log(typeof post, post)),\n NodeRuntime.runMain\n)\n/*\nOutput:\nobject {\n userId: 1,\n id: 1,\n title: 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit',\n body: 'quia et suscipit\\n' +\n 'suscipit recusandae consequuntur expedita et cum\\n' +\n 'reprehenderit molestiae ut ut quas totam\\n' +\n 'nostrum rerum est autem sunt rem eveniet architecto'\n}\n*/\n```\n\n**Example: Converting to Text**\n\n```ts\nimport { FetchHttpClient, HttpClient } from \"@effect/platform\"\nimport { NodeRuntime } from \"@effect/platform-node\"\nimport { Console, Effect } from \"effect\"\n\nconst getPostAsText = Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const response = yield* client.get(\n \"https://jsonplaceholder.typicode.com/posts/1\"\n )\n return yield* response.text\n}).pipe(Effect.provide(FetchHttpClient.layer))\n\ngetPostAsText.pipe(\n Effect.andThen((post) => Console.log(typeof post, post)),\n NodeRuntime.runMain\n)\n/*\nOutput:\nstring {\n userId: 1,\n id: 1,\n title: 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit',\n body: 'quia et suscipit\\n' +\n 'suscipit recusandae consequuntur expedita et cum\\n' +\n 'reprehenderit molestiae ut ut quas totam\\n' +\n 'nostrum rerum est autem sunt rem eveniet architecto'\n}\n*/\n```\n\n**Methods Summary**\n\n| Method | Description |\n| --------------- | ------------------------------------- |\n| `arrayBuffer` | Convert to `ArrayBuffer` |\n| `formData` | Convert to `FormData` |\n| `json` | Convert to JSON |\n| `stream` | Convert to a `Stream` of `Uint8Array` |\n| `text` | Convert to text |\n| `urlParamsBody` | Convert to `UrlParams` |\n\n### Decoding Data with Schemas\n\nA common use case when fetching data is to validate the received format. For this purpose, the `HttpClientResponse` module is integrated with `effect/Schema`.\n\n```ts\nimport {\n FetchHttpClient,\n HttpClient,\n HttpClientResponse\n} from \"@effect/platform\"\nimport { NodeRuntime } from \"@effect/platform-node\"\nimport { Console, Effect, Schema } from \"effect\"\n\nconst Post = Schema.Struct({\n id: Schema.Number,\n title: Schema.String\n})\n\nconst getPostAndValidate = Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const response = yield* client.get(\n \"https://jsonplaceholder.typicode.com/posts/1\"\n )\n return yield* HttpClientResponse.schemaBodyJson(Post)(response)\n}).pipe(Effect.provide(FetchHttpClient.layer))\n\ngetPostAndValidate.pipe(Effect.andThen(Console.log), NodeRuntime.runMain)\n/*\nOutput:\n{\n id: 1,\n title: 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit'\n}\n*/\n```\n\nIn this example, we define a schema for a post object with properties `id` and `title`. Then, we fetch the data and validate it against this schema using `HttpClientResponse.schemaBodyJson`. Finally, we log the validated post object.\n\n### Filtering And Error Handling\n\nIt's important to note that `HttpClient.get` doesn't consider non-`200` status codes as errors by default. This design choice allows for flexibility in handling different response scenarios. For instance, you might have a schema union where the status code serves as the discriminator, enabling you to define a schema that encompasses all possible response cases.\n\nYou can use `HttpClient.filterStatusOk` to ensure only `2xx` responses are treated as successes.\n\nIn this example, we attempt to fetch a non-existent page and don't receive any error:\n\n```ts\nimport { FetchHttpClient, HttpClient } from \"@effect/platform\"\nimport { NodeRuntime } from \"@effect/platform-node\"\nimport { Console, Effect } from \"effect\"\n\nconst getText = Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const response = yield* client.get(\n \"https://jsonplaceholder.typicode.com/non-existing-page\"\n )\n return yield* response.text\n}).pipe(Effect.provide(FetchHttpClient.layer))\n\ngetText.pipe(Effect.andThen(Console.log), NodeRuntime.runMain)\n/*\nOutput:\n{}\n*/\n```\n\nHowever, if we use `HttpClient.filterStatusOk`, an error is logged:\n\n```ts\nimport { FetchHttpClient, HttpClient } from \"@effect/platform\"\nimport { NodeRuntime } from \"@effect/platform-node\"\nimport { Console, Effect } from \"effect\"\n\nconst getText = Effect.gen(function* () {\n const client = (yield* HttpClient.HttpClient).pipe(HttpClient.filterStatusOk)\n const response = yield* client.get(\n \"https://jsonplaceholder.typicode.com/non-existing-page\"\n )\n return yield* response.text\n}).pipe(Effect.provide(FetchHttpClient.layer))\n\ngetText.pipe(Effect.andThen(Console.log), NodeRuntime.runMain)\n/*\nOutput:\n[17:37:59.923] ERROR (#0):\n ResponseError: StatusCode: non 2xx status code (404 GET https://jsonplaceholder.typicode.com/non-existing-page)\n ... stack trace ...\n*/\n```\n\n## POST\n\nTo make a POST request, you can use the `HttpClientRequest.post` function provided by the `HttpClientRequest` module. Here's an example of how to create and send a POST request:\n\n```ts\nimport {\n FetchHttpClient,\n HttpClient,\n HttpClientRequest\n} from \"@effect/platform\"\nimport { NodeRuntime } from \"@effect/platform-node\"\nimport { Console, Effect } from \"effect\"\n\nconst addPost = Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n return yield* HttpClientRequest.post(\n \"https://jsonplaceholder.typicode.com/posts\"\n ).pipe(\n HttpClientRequest.bodyJson({\n title: \"foo\",\n body: \"bar\",\n userId: 1\n }),\n Effect.flatMap(client.execute),\n Effect.flatMap((res) => res.json)\n )\n}).pipe(Effect.provide(FetchHttpClient.layer))\n\naddPost.pipe(Effect.andThen(Console.log), NodeRuntime.runMain)\n/*\nOutput:\n{ title: 'foo', body: 'bar', userId: 1, id: 101 }\n*/\n```\n\nIf you need to send data in a format other than JSON, such as plain text, you can use different APIs provided by `HttpClientRequest`.\n\nIn the following example, we send the data as text:\n\n```ts\nimport {\n FetchHttpClient,\n HttpClient,\n HttpClientRequest\n} from \"@effect/platform\"\nimport { NodeRuntime } from \"@effect/platform-node\"\nimport { Console, Effect } from \"effect\"\n\nconst addPost = Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n return yield* HttpClientRequest.post(\n \"https://jsonplaceholder.typicode.com/posts\"\n ).pipe(\n HttpClientRequest.bodyText(\n JSON.stringify({\n title: \"foo\",\n body: \"bar\",\n userId: 1\n }),\n \"application/json; charset=UTF-8\"\n ),\n client.execute,\n Effect.flatMap((res) => res.json)\n )\n}).pipe(Effect.provide(FetchHttpClient.layer))\n\naddPost.pipe(Effect.andThen(Console.log), NodeRuntime.runMain)\n/*\nOutput:\n{ title: 'foo', body: 'bar', userId: 1, id: 101 }\n*/\n```\n\n### Decoding Data with Schemas\n\nA common use case when fetching data is to validate the received format. For this purpose, the `HttpClientResponse` module is integrated with `effect/Schema`.\n\n```ts\nimport {\n FetchHttpClient,\n HttpClient,\n HttpClientRequest,\n HttpClientResponse\n} from \"@effect/platform\"\nimport { NodeRuntime } from \"@effect/platform-node\"\nimport { Console, Effect, Schema } from \"effect\"\n\nconst Post = Schema.Struct({\n id: Schema.Number,\n title: Schema.String\n})\n\nconst addPost = Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n return yield* HttpClientRequest.post(\n \"https://jsonplaceholder.typicode.com/posts\"\n ).pipe(\n HttpClientRequest.bodyText(\n JSON.stringify({\n title: \"foo\",\n body: \"bar\",\n userId: 1\n }),\n \"application/json; charset=UTF-8\"\n ),\n client.execute,\n Effect.flatMap(HttpClientResponse.schemaBodyJson(Post))\n )\n}).pipe(Effect.provide(FetchHttpClient.layer))\n\naddPost.pipe(Effect.andThen(Console.log), NodeRuntime.runMain)\n/*\nOutput:\n{ id: 101, title: 'foo' }\n*/\n```\n\n## Testing\n\n### Injecting Fetch\n\nTo test HTTP requests, you can inject a mock fetch implementation.\n\n```ts\nimport { FetchHttpClient, HttpClient } from \"@effect/platform\"\nimport { Effect, Layer } from \"effect\"\nimport * as assert from \"node:assert\"\n\n// Mock fetch implementation\nconst FetchTest = Layer.succeed(FetchHttpClient.Fetch, () =>\n Promise.resolve(new Response(\"not found\", { status: 404 }))\n)\n\nconst TestLayer = FetchHttpClient.layer.pipe(Layer.provide(FetchTest))\n\nconst program = Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n\n return yield* client\n .get(\"https://www.google.com/\")\n .pipe(Effect.flatMap((res) => res.text))\n})\n\n// Test\nEffect.gen(function* () {\n const response = yield* program\n assert.equal(response, \"not found\")\n}).pipe(Effect.provide(TestLayer), Effect.runPromise)\n```\n\n# HTTP Server\n\n## Overview\n\nThis section provides a simplified explanation of key concepts within the `@effect/platform` TypeScript library, focusing on components used to build HTTP servers. Understanding these terms and their relationships helps in structuring and managing server applications effectively.\n\n### Core Concepts\n\n- **HttpApp**: This is an `Effect` which results in a value `A`. It can utilize `ServerRequest` to produce the outcome `A`. Essentially, an `HttpApp` represents an application component that handles HTTP requests and generates responses based on those requests.\n\n- **Default** (HttpApp): A special type of `HttpApp` that specifically produces a `ServerResponse` as its output `A`. This is the most common form of application where each interaction is expected to result in an HTTP response.\n\n- **Server**: A construct that takes a `Default` app and converts it into an `Effect`. This serves as the execution layer where the `Default` app is operated, handling incoming requests and serving responses.\n\n- **Router**: A type of `Default` app where the possible error outcome is `RouteNotFound`. Routers are used to direct incoming requests to appropriate handlers based on the request path and method.\n\n- **Handler**: Another form of `Default` app, which has access to both `RouteContext` and `ServerRequest.ParsedSearchParams`. Handlers are specific functions designed to process requests and generate responses.\n\n- **Middleware**: Functions that transform a `Default` app into another `Default` app. Middleware can be used to modify requests, responses, or handle tasks like logging, authentication, and more. Middleware can be applied in two ways:\n - On a `Router` using `router.use: Handler -> Default` which applies the middleware to specific routes.\n - On a `Server` using `server.serve: () -> Layer | Middleware -> Layer` which applies the middleware globally to all routes handled by the server.\n\n### Applying Concepts\n\nThese components are designed to work together in a modular and flexible way, allowing developers to build complex server applications with reusable components. Here's how you might typically use these components in a project:\n\n1. **Create Handlers**: Define functions that process specific types of requests (e.g., GET, POST) and return responses.\n\n2. **Set Up Routers**: Organize handlers into routers, where each router manages a subset of application routes.\n\n3. **Apply Middleware**: Enhance routers or entire servers with middleware to add extra functionality like error handling or request logging.\n\n4. **Initialize the Server**: Wrap the main router with server functionality, applying any server-wide middleware, and start listening for requests.\n\n## Getting Started\n\n### Hello world example\n\nIn this example, we will create a simple HTTP server that listens on port `3000`. The server will respond with \"Hello World!\" when a request is made to the root URL (/) and return a `500` error for all other paths.\n\nNode.js Example\n\n```ts\nimport { HttpRouter, HttpServer, HttpServerResponse } from \"@effect/platform\"\nimport { NodeHttpServer, NodeRuntime } from \"@effect/platform-node\"\nimport { Layer } from \"effect\"\nimport { createServer } from \"node:http\"\n\n// Define the router with a single route for the root URL\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\"/\", HttpServerResponse.text(\"Hello World\"))\n)\n\n// Set up the application server with logging\nconst app = router.pipe(HttpServer.serve(), HttpServer.withLogAddress)\n\n// Specify the port\nconst port = 3000\n\n// Create a server layer with the specified port\nconst ServerLive = NodeHttpServer.layer(() => createServer(), { port })\n\n// Run the application\nNodeRuntime.runMain(Layer.launch(Layer.provide(app, ServerLive)))\n\n/*\nOutput:\ntimestamp=... level=INFO fiber=#0 message=\"Listening on http://localhost:3000\"\n*/\n```\n\n> [!NOTE]\n> The `HttpServer.withLogAddress` middleware logs the address and port where the server is listening, helping to confirm that the server is running correctly and accessible on the expected endpoint.\n\nBun Example\n\n```ts\nimport { HttpRouter, HttpServer, HttpServerResponse } from \"@effect/platform\"\nimport { BunHttpServer, BunRuntime } from \"@effect/platform-bun\"\nimport { Layer } from \"effect\"\n\n// Define the router with a single route for the root URL\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\"/\", HttpServerResponse.text(\"Hello World\"))\n)\n\n// Set up the application server with logging\nconst app = router.pipe(HttpServer.serve(), HttpServer.withLogAddress)\n\n// Specify the port\nconst port = 3000\n\n// Create a server layer with the specified port\nconst ServerLive = BunHttpServer.layer({ port })\n\n// Run the application\nBunRuntime.runMain(Layer.launch(Layer.provide(app, ServerLive)))\n\n/*\nOutput:\ntimestamp=... level=INFO fiber=#0 message=\"Listening on http://localhost:3000\"\n*/\n```\n\nTo avoid boilerplate code for the final server setup, we'll use a helper function from the `listen.ts` file:\n\nNode.js Example\n\n```ts\nimport type { HttpPlatform, HttpServer } from \"@effect/platform\"\nimport { NodeHttpServer, NodeRuntime } from \"@effect/platform-node\"\nimport { Layer } from \"effect\"\nimport { createServer } from \"node:http\"\n\nexport const listen = (\n app: Layer.Layer<\n never,\n never,\n HttpPlatform.HttpPlatform | HttpServer.HttpServer\n >,\n port: number\n) =>\n NodeRuntime.runMain(\n Layer.launch(\n Layer.provide(\n app,\n NodeHttpServer.layer(() => createServer(), { port })\n )\n )\n )\n```\n\nBun Example\n\n```ts\nimport type { HttpPlatform, HttpServer } from \"@effect/platform\"\nimport { BunHttpServer, BunRuntime } from \"@effect/platform-bun\"\nimport { Layer } from \"effect\"\n\nexport const listen = (\n app: Layer.Layer<\n never,\n never,\n HttpPlatform.HttpPlatform | HttpServer.HttpServer\n >,\n port: number\n) =>\n BunRuntime.runMain(\n Layer.launch(Layer.provide(app, BunHttpServer.layer({ port })))\n )\n```\n\n### Basic routing\n\nRouting refers to determining how an application responds to a client request to a particular endpoint, which is a URI (or path) and a specific HTTP request method (GET, POST, and so on).\n\nRoute definition takes the following structure:\n\n```\nrouter.pipe(HttpRouter.METHOD(PATH, HANDLER))\n```\n\nWhere:\n\n- **router** is an instance of `Router` (`import type { Router } from \"@effect/platform/Http/Router\"`).\n- **METHOD** is an HTTP request method, in lowercase (e.g., get, post, put, del).\n- **PATH** is the path on the server (e.g., \"/\", \"/user\").\n- **HANDLER** is the action that gets executed when the route is matched.\n\nThe following examples illustrate defining simple routes.\n\nRespond with `\"Hello World!\"` on the homepage:\n\n```ts\nrouter.pipe(HttpRouter.get(\"/\", HttpServerResponse.text(\"Hello World\")))\n```\n\nRespond to POST request on the root route (/), the application's home page:\n\n```ts\nrouter.pipe(HttpRouter.post(\"/\", HttpServerResponse.text(\"Got a POST request\")))\n```\n\nRespond to a PUT request to the `/user` route:\n\n```ts\nrouter.pipe(\n HttpRouter.put(\"/user\", HttpServerResponse.text(\"Got a PUT request at /user\"))\n)\n```\n\nRespond to a DELETE request to the `/user` route:\n\n```ts\nrouter.pipe(\n HttpRouter.del(\n \"/user\",\n HttpServerResponse.text(\"Got a DELETE request at /user\")\n )\n)\n```\n\n### Serving static files\n\nTo serve static files such as images, CSS files, and JavaScript files, use the `HttpServerResponse.file` built-in action.\n\n```ts\nimport { HttpRouter, HttpServer, HttpServerResponse } from \"@effect/platform\"\nimport { listen } from \"./listen.js\"\n\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\"/\", HttpServerResponse.file(\"index.html\"))\n)\n\nconst app = router.pipe(HttpServer.serve())\n\nlisten(app, 3000)\n```\n\nCreate an `index.html` file in your project directory:\n\n```html filename=\"index.html\"\n<!DOCTYPE html>\n<html>\n <head>\n <meta charset=\"utf-8\" />\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n <title>index.html</title>\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n </head>\n <body>\n index.html\n </body>\n</html>\n```\n\n## Routing\n\nRouting refers to how an application's endpoints (URIs) respond to client requests.\n\nYou define routing using methods of the `HttpRouter` object that correspond to HTTP methods; for example, `HttpRouter.get()` to handle GET requests and `HttpRouter.post` to handle POST requests. You can also use `HttpRouter.all()` to handle all HTTP methods.\n\nThese routing methods specify a `Route.Handler` called when the application receives a request to the specified route (endpoint) and HTTP method. In other words, the application “listens” for requests that match the specified route(s) and method(s), and when it detects a match, it calls the specified handler.\n\nThe following code is an example of a very basic route.\n\n```ts\n// respond with \"hello world\" when a GET request is made to the homepage\nHttpRouter.get(\"/\", HttpServerResponse.text(\"Hello World\"))\n```\n\n### Route methods\n\nA route method is derived from one of the HTTP methods, and is attached to an instance of the `HttpRouter` object.\n\nThe following code is an example of routes that are defined for the GET and the POST methods to the root of the app.\n\n```ts\n// GET method route\nHttpRouter.get(\"/\", HttpServerResponse.text(\"GET request to the homepage\"))\n\n// POST method route\nHttpRouter.post(\"/\", HttpServerResponse.text(\"POST request to the homepage\"))\n```\n\n`HttpRouter` supports methods that correspond to all HTTP request methods: `get`, `post`, and so on.\n\nThere is a special routing method, `HttpRouter.all()`, used to load middleware functions at a path for **all** HTTP request methods. For example, the following handler is executed for requests to the route “/secret” whether using GET, POST, PUT, DELETE.\n\n```ts\nHttpRouter.all(\n \"/secret\",\n HttpServerResponse.empty().pipe(\n Effect.tap(Console.log(\"Accessing the secret section ...\"))\n )\n)\n```\n\n### Route paths\n\nRoute paths, when combined with a request method, define the endpoints where requests can be made. Route paths can be specified as strings according to the following type:\n\n```ts\ntype PathInput = `/${string}` | \"*\"\n```\n\n> [!NOTE]\n> Query strings are not part of the route path.\n\nHere are some examples of route paths based on strings.\n\nThis route path will match requests to the root route, /.\n\n```ts\nHttpRouter.get(\"/\", HttpServerResponse.text(\"root\"))\n```\n\nThis route path will match requests to `/user`.\n\n```ts\nHttpRouter.get(\"/user\", HttpServerResponse.text(\"user\"))\n```\n\nThis route path matches requests to any path starting with `/user` (e.g., `/user`, `/users`, etc.)\n\n```ts\nHttpRouter.get(\n \"/user*\",\n Effect.map(HttpServerRequest.HttpServerRequest, (req) =>\n HttpServerResponse.text(req.url)\n )\n)\n```\n\n### Route parameters\n\nRoute parameters are named URL segments that are used to capture the values specified at their position in the URL. By using a schema the captured values are populated in an object, with the name of the route parameter specified in the path as their respective keys.\n\nRoute parameters are named segments in a URL that capture the values specified at those positions. These captured values are stored in an object, with the parameter names used as keys.\n\nFor example:\n\n```\nRoute path: /users/:userId/books/:bookId\nRequest URL: http://localhost:3000/users/34/books/8989\nparams: { \"userId\": \"34\", \"bookId\": \"8989\" }\n```\n\nTo define routes with parameters, include the parameter names in the path and use a schema to validate and parse these parameters, as shown below.\n\n```ts\nimport { HttpRouter, HttpServer, HttpServerResponse } from \"@effect/platform\"\nimport { Effect, Schema } from \"effect\"\nimport { listen } from \"./listen.js\"\n\n// Define the schema for route parameters\nconst Params = Schema.Struct({\n userId: Schema.String,\n bookId: Schema.String\n})\n\n// Create a router with a route that captures parameters\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\n \"/users/:userId/books/:bookId\",\n HttpRouter.schemaPathParams(Params).pipe(\n Effect.flatMap((params) => HttpServerResponse.json(params))\n )\n )\n)\n\nconst app = router.pipe(HttpServer.serve())\n\nlisten(app, 3000)\n```\n\n### Response methods\n\nThe methods on `HttpServerResponse` object in the following table can send a response to the client, and terminate the request-response cycle. If none of these methods are called from a route handler, the client request will be left hanging.\n\n| Method | Description |\n| ------------ | ------------------------------ |\n| **empty** | Sends an empty response. |\n| **formData** | Sends form data. |\n| **html** | Sends an HTML response. |\n| **raw** | Sends a raw response. |\n| **setBody** | Sets the body of the response. |\n| **stream** | Sends a streaming response. |\n| **text** | Sends a plain text response. |\n\n### Router\n\nUse the `HttpRouter` object to create modular, mountable route handlers. A `Router` instance is a complete middleware and routing system, often referred to as a \"mini-app.\"\n\nThe following example shows how to create a router as a module, define some routes, and mount the router module on a path in the main app.\n\nCreate a file named `birds.ts` in your app directory with the following content:\n\n```ts\nimport { HttpRouter, HttpServerResponse } from \"@effect/platform\"\n\nexport const birds = HttpRouter.empty.pipe(\n HttpRouter.get(\"/\", HttpServerResponse.text(\"Birds home page\")),\n HttpRouter.get(\"/about\", HttpServerResponse.text(\"About birds\"))\n)\n```\n\nIn your main application file, load the router module and mount it.\n\n```ts\nimport { HttpRouter, HttpServer } from \"@effect/platform\"\nimport { birds } from \"./birds.js\"\nimport { listen } from \"./listen.js\"\n\n// Create the main router and mount the birds router\nconst router = HttpRouter.empty.pipe(HttpRouter.mount(\"/birds\", birds))\n\nconst app = router.pipe(HttpServer.serve())\n\nlisten(app, 3000)\n```\n\nWhen you run this code, your application will be able to handle requests to `/birds` and `/birds/about`, serving the respective responses defined in the `birds` router module.\n\n## Writing Middleware\n\nIn this section, we'll build a simple \"Hello World\" application and demonstrate how to add three middleware functions: `myLogger` for logging, `requestTime` for displaying request timestamps, and `validateCookies` for validating incoming cookies.\n\n### Example Application\n\nHere is an example of a basic \"Hello World\" application with middleware.\n\n### Middleware `myLogger`\n\nThis middleware logs \"LOGGED\" whenever a request passes through it.\n\n```ts\nconst myLogger = HttpMiddleware.make((app) =>\n Effect.gen(function* () {\n console.log(\"LOGGED\")\n return yield* app\n })\n)\n```\n\nTo use the middleware, add it to the router using `HttpRouter.use()`:\n\n```ts\nimport {\n HttpMiddleware,\n HttpRouter,\n HttpServer,\n HttpServerResponse\n} from \"@effect/platform\"\nimport { Effect } from \"effect\"\nimport { listen } from \"./listen.js\"\n\nconst myLogger = HttpMiddleware.make((app) =>\n Effect.gen(function* () {\n console.log(\"LOGGED\")\n return yield* app\n })\n)\n\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\"/\", HttpServerResponse.text(\"Hello World\"))\n)\n\nconst app = router.pipe(HttpRouter.use(myLogger), HttpServer.serve())\n\nlisten(app, 3000)\n```\n\nWith this setup, every request to the app will log \"LOGGED\" to the terminal. Middleware execute in the order they are loaded.\n\n### Middleware `requestTime`\n\nNext, we'll create a middleware that records the timestamp of each HTTP request and provides it via a service called `RequestTime`.\n\n```ts\nclass RequestTime extends Context.Tag(\"RequestTime\")<RequestTime, number>() {}\n\nconst requestTime = HttpMiddleware.make((app) =>\n Effect.gen(function* () {\n return yield* app.pipe(Effect.provideService(RequestTime, Date.now()))\n })\n)\n```\n\nUpdate the app to use this middleware and display the timestamp in the response:\n\n```ts\nimport {\n HttpMiddleware,\n HttpRouter,\n HttpServer,\n HttpServerResponse\n} from \"@effect/platform\"\nimport { Context, Effect } from \"effect\"\nimport { listen } from \"./listen.js\"\n\nclass RequestTime extends Context.Tag(\"RequestTime\")<RequestTime, number>() {}\n\nconst requestTime = HttpMiddleware.make((app) =>\n Effect.gen(function* () {\n return yield* app.pipe(Effect.provideService(RequestTime, Date.now()))\n })\n)\n\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\n \"/\",\n Effect.gen(function* () {\n const requestTime = yield* RequestTime\n const responseText = `Hello World<br/><small>Requested at: ${requestTime}</small>`\n return yield* HttpServerResponse.html(responseText)\n })\n )\n)\n\nconst app = router.pipe(HttpRouter.use(requestTime), HttpServer.serve())\n\nlisten(app, 3000)\n```\n\nNow, when you make a request to the root path, the response will include the timestamp of the request.\n\n### Middleware `validateCookies`\n\nFinally, we'll create a middleware that validates incoming cookies. If the cookies are invalid, it sends a 400 response.\n\nHere's an example that validates cookies using an external service:\n\n```ts\nclass CookieError {\n readonly _tag = \"CookieError\"\n}\n\nconst externallyValidateCookie = (testCookie: string | undefined) =>\n testCookie && testCookie.length > 0\n ? Effect.succeed(testCookie)\n : Effect.fail(new CookieError())\n\nconst cookieValidator = HttpMiddleware.make((app) =>\n Effect.gen(function* () {\n const req = yield* HttpServerRequest.HttpServerRequest\n yield* externallyValidateCookie(req.cookies.testCookie)\n return yield* app\n }).pipe(\n Effect.catchTag(\"CookieError\", () =>\n HttpServerResponse.text(\"Invalid cookie\")\n )\n )\n)\n```\n\nUpdate the app to use the `cookieValidator` middleware:\n\n```ts\nimport {\n HttpMiddleware,\n HttpRouter,\n HttpServer,\n HttpServerRequest,\n HttpServerResponse\n} from \"@effect/platform\"\nimport { Effect } from \"effect\"\nimport { listen } from \"./listen.js\"\n\nclass CookieError {\n readonly _tag = \"CookieError\"\n}\n\nconst externallyValidateCookie = (testCookie: string | undefined) =>\n testCookie && testCookie.length > 0\n ? Effect.succeed(testCookie)\n : Effect.fail(new CookieError())\n\nconst cookieValidator = HttpMiddleware.make((app) =>\n Effect.gen(function* () {\n const req = yield* HttpServerRequest.HttpServerRequest\n yield* externallyValidateCookie(req.cookies.testCookie)\n return yield* app\n }).pipe(\n Effect.catchTag(\"CookieError\", () =>\n HttpServerResponse.text(\"Invalid cookie\")\n )\n )\n)\n\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\"/\", HttpServerResponse.text(\"Hello World\"))\n)\n\nconst app = router.pipe(HttpRouter.use(cookieValidator), HttpServer.serve())\n\nlisten(app, 3000)\n```\n\nTest the middleware with the following commands:\n\n```sh\ncurl -i http://localhost:3000\ncurl -i http://localhost:3000 --cookie \"testCookie=myvalue\"\ncurl -i http://localhost:3000 --cookie \"testCookie=\"\n```\n\nThis setup validates the `testCookie` and returns \"Invalid cookie\" if the validation fails, or \"Hello World\" if it passes.\n\n## Applying Middleware in Your Application\n\nMiddleware functions are powerful tools that allow you to modify the request-response cycle. Middlewares can be applied at various levels to achieve different scopes of influence:\n\n- **Route Level**: Apply middleware to individual routes.\n- **Router Level**: Apply middleware to a group of routes within a single router.\n- **Server Level**: Apply middleware across all routes managed by a server.\n\n### Applying Middleware at the Route Level\n\nAt the route level, middlewares are applied to specific endpoints, allowing for targeted modifications or enhancements such as logging, authentication, or parameter validation for a particular route.\n\n**Example**\n\nHere's a practical example showing how to apply middleware at the route level:\n\n```ts\nimport {\n HttpMiddleware,\n HttpRouter,\n HttpServer,\n HttpServerResponse\n} from \"@effect/platform\"\nimport { Effect } from \"effect\"\nimport { listen } from \"./listen.js\"\n\n// Middleware constructor that logs the name of the middleware\nconst withMiddleware = (name: string) =>\n HttpMiddleware.make((app) =>\n Effect.gen(function* () {\n console.log(name) // Log the middleware name when the route is accessed\n return yield* app // Continue with the original application flow\n })\n )\n\nconst router = HttpRouter.empty.pipe(\n // Applying middleware to route \"/a\"\n HttpRouter.get(\"/a\", HttpServerResponse.text(\"a\").pipe(withMiddleware(\"M1\"))),\n // Applying middleware to route \"/b\"\n HttpRouter.get(\"/b\", HttpServerResponse.text(\"b\").pipe(withMiddleware(\"M2\")))\n)\n\nconst app = router.pipe(HttpServer.serve())\n\nlisten(app, 3000)\n```\n\n**Testing the Middleware**\n\nYou can test the middleware by making requests to the respective routes and observing the console output:\n\n```sh\n# Test route /a\ncurl -i http://localhost:3000/a\n# Expected console output: M1\n\n# Test route /b\ncurl -i http://localhost:3000/b\n# Expected console output: M2\n```\n\n### Applying Middleware at the Router Level\n\nApplying middleware at the router level is an efficient way to manage common functionalities across multiple routes within your application. Middleware can handle tasks such as logging, authentication, and response modifications before reaching the actual route handlers.\n\n**Example**\n\nHere's how you can structure and apply middleware across different routers using the `@effect/platform` library:\n\n```ts\nimport {\n HttpMiddleware,\n HttpRouter,\n HttpServer,\n HttpServerResponse\n} from \"@effect/platform\"\nimport { Effect } from \"effect\"\nimport { listen } from \"./listen.js\"\n\n// Middleware constructor that logs the name of the middleware\nconst withMiddleware = (name: string) =>\n HttpMiddleware.make((app) =>\n Effect.gen(function* () {\n console.log(name) // Log the middleware name when a route is accessed\n return yield* app // Continue with the original application flow\n })\n )\n\n// Define Router1 with specific routes\nconst router1 = HttpRouter.empty.pipe(\n HttpRouter.get(\"/a\", HttpServerResponse.text(\"a\")), // Middleware M4, M3, M1 will apply\n HttpRouter.get(\"/b\", HttpServerResponse.text(\"b\")), // Middleware M4, M3, M1 will apply\n // Apply Middleware at the router level\n HttpRouter.use(withMiddleware(\"M1\")),\n HttpRouter.get(\"/c\", HttpServerResponse.text(\"c\")) // Middleware M4, M3 will apply\n)\n\n// Define Router2 with specific routes\nconst router2 = HttpRouter.empty.pipe(\n HttpRouter.get(\"/d\", HttpServerResponse.text(\"d\")), // Middleware M4, M2 will apply\n HttpRouter.get(\"/e\", HttpServerResponse.text(\"e\")), // Middleware M4, M2 will apply\n HttpRouter.get(\"/f\", HttpServerResponse.text(\"f\")), // Middleware M4, M2 will apply\n // Apply Middleware at the router level\n HttpRouter.use(withMiddleware(\"M2\"))\n)\n\n// Main router combining Router1 and Router2\nconst router = HttpRouter.empty.pipe(\n HttpRouter.mount(\"/r1\", router1),\n // Apply Middleware affecting all routes under /r1\n HttpRouter.use(withMiddleware(\"M3\")),\n HttpRouter.get(\"/g\", HttpServerResponse.text(\"g\")), // Only Middleware M4 will apply\n HttpRouter.mount(\"/r2\", router2),\n // Apply Middleware affecting all routes\n HttpRouter.use(withMiddleware(\"M4\"))\n)\n\n// Configure the application with the server middleware\nconst app = router.pipe(HttpServer.serve())\n\nlisten(app, 3000)\n```\n\n**Testing the Middleware**\n\nTo ensure that the middleware is working as expected, you can test it by making HTTP requests to the defined routes and checking the console output for middleware logs:\n\n```sh\n# Test route /a under router1\ncurl -i http://localhost:3000/r1/a\n# Expected console output: M4 M3 M1\n\n# Test route /c under router1\ncurl -i http://localhost:3000/r1/c\n# Expected console output: M4 M3\n\n# Test route /d under router2\ncurl -i http://localhost:3000/r2/d\n# Expected console output: M4 M2\n\n# Test route /g under the main router\ncurl -i http://localhost:3000/g\n# Expected console output: M4\n```\n\n### Applying Middleware at the Server Level\n\nApplying middleware at the server level allows you to introduce certain functionalities, such as logging, authentication, or general request processing, that affect every request handled by the server. This ensures that all incoming requests, regardless of the route, pass through the applied middleware, making it an essential feature for global error handling, logging, or authentication.\n\n**Example**\n\n```ts\nimport {\n HttpMiddleware,\n HttpRouter,\n HttpServer,\n HttpServerResponse\n} from \"@effect/platform\"\nimport { Effect } from \"effect\"\nimport { listen } from \"./listen.js\"\n\n// Middleware constructor that logs the name of the middleware\nconst withMiddleware = (name: string) =>\n HttpMiddleware.make((app) =>\n Effect.gen(function* () {\n console.log(name) // Log the middleware name when the route is accessed\n return yield* app // Continue with the original application flow\n })\n )\n\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\"/a\", HttpServerResponse.text(\"a\").pipe(withMiddleware(\"M1\"))),\n HttpRouter.get(\"/b\", HttpServerResponse.text(\"b\")),\n HttpRouter.use(withMiddleware(\"M2\")),\n HttpRouter.get(\"/\", HttpServerResponse.text(\"root\"))\n)\n\nconst app = router.pipe(HttpServer.serve(withMiddleware(\"M3\")))\n\nlisten(app, 3000)\n```\n\n**Testing the Middleware**\n\nTo confirm the middleware is functioning as intended, you can send HTTP requests to the defined routes and check the console for middleware logs:\n\n```sh\n# Test route /a and observe the middleware logs\ncurl -i http://localhost:3000/a\n# Expected console output: M3 M2 M1 - Middleware M3 (server-level), M2 (router-level), and M1 (route-level) apply.\n\n# Test route /b and observe the middleware logs\ncurl -i http://localhost:3000/b\n# Expected console output: M3 M2 - Middleware M3 (server-level) and M2 (router-level) apply.\n\n# Test route / and observe the middleware logs\ncurl -i http://localhost:3000/\n# Expected console output: M3 M2 - Middleware M3 (server-level) and M2 (router-level) apply.\n```\n\n### Applying Multiple Middlewares\n\nMiddleware functions are simply functions that transform a `Default` app into another `Default` app. This flexibility allows for stacking multiple middleware functions, much like composing functions in functional programming. The `flow` function from the `Effect` library facilitates this by enabling function composition.\n\n**Example**\n\n```ts\nimport {\n HttpMiddleware,\n HttpRouter,\n HttpServer,\n HttpServerResponse\n} from \"@effect/platform\"\nimport { Effect, flow } from \"effect\"\nimport { listen } from \"./listen.js\"\n\n// Middleware constructor that logs the middleware's name when a route is accessed\nconst withMiddleware = (name: string) =>\n HttpMiddleware.make((app) =>\n Effect.gen(function* () {\n console.log(name) // Log the middleware name\n return yield* app // Continue with the original application flow\n })\n )\n\n// Setup routes and apply multiple middlewares using flow for function composition\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\n \"/a\",\n HttpServerResponse.text(\"a\").pipe(\n flow(withMiddleware(\"M1\"), withMiddleware(\"M2\"))\n )\n ),\n HttpRouter.get(\"/b\", HttpServerResponse.text(\"b\")),\n // Apply combined middlewares to the entire router\n HttpRouter.use(flow(withMiddleware(\"M3\"), withMiddleware(\"M4\"))),\n HttpRouter.get(\"/\", HttpServerResponse.text(\"root\"))\n)\n\n// Apply combined middlewares at the server level\nconst app = router.pipe(\n HttpServer.serve(flow(withMiddleware(\"M5\"), withMiddleware(\"M6\")))\n)\n\nlisten(app, 3000)\n```\n\n**Testing the Middleware Composition**\n\nTo verify that the middleware is functioning as expected, you can send HTTP requests to the routes and check the console for the expected middleware log output:\n\n```sh\n# Test route /a to see the output from multiple middleware layers\ncurl -i http://localhost:3000/a\n# Expected console output: M6 M5 M4 M3 M2 M1\n\n# Test route /b where fewer middleware are applied\ncurl -i http://localhost:3000/b\n# Expected console output: M6 M5 M4 M3\n\n# Test the root route to confirm top-level middleware application\ncurl -i http://localhost:3000/\n# Expected console output: M6 M5\n```\n\n## Built-in middleware\n\n### Middleware Summary\n\n| Middleware | Description |\n| --------------------- | --------------------------------------------------------------------------------------------------------------------------------- |\n| **Logger** | Provides detailed logging of all requests and responses, aiding in debugging and monitoring application activities. |\n| **xForwardedHeaders** | Manages `X-Forwarded-*` headers to accurately maintain client information such as IP addresses and host names in proxy scenarios. |\n\n### logger\n\nThe `HttpMiddleware.logger` middleware enables logging for your entire application, providing insights into each request and response. Here's how to set it up:\n\n```ts\nimport {\n HttpMiddleware,\n HttpRouter,\n HttpServer,\n HttpServerResponse\n} from \"@effect/platform\"\nimport { listen } from \"./listen.js\"\n\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\"/\", HttpServerResponse.text(\"Hello World\"))\n)\n\n// Apply the logger middleware globally\nconst app = router.pipe(HttpServer.serve(HttpMiddleware.logger))\n\nlisten(app, 3000)\n/*\ncurl -i http://localhost:3000\ntimestamp=... level=INFO fiber=#0 message=\"Listening on http://0.0.0.0:3000\"\ntimestamp=... level=INFO fiber=#19 message=\"Sent HTTP response\" http.span.1=8ms http.status=200 http.method=GET http.url=/\ntimestamp=... level=INFO fiber=#20 cause=\"RouteNotFound: GET /favicon.ico not found\n at ...\n at http.server GET\" http.span.2=4ms http.status=500 http.method=GET http.url=/favicon.ico\n*/\n```\n\nTo disable the logger for specific routes, you can use `HttpMiddleware.withLoggerDisabled`:\n\n```ts\nimport {\n HttpMiddleware,\n HttpRouter,\n HttpServer,\n HttpServerResponse\n} from \"@effect/platform\"\nimport { listen } from \"./listen.js\"\n\n// Create the router with routes that will and will not have logging\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\"/\", HttpServerResponse.text(\"Hello World\")),\n HttpRouter.get(\n \"/no-logger\",\n HttpServerResponse.text(\"no-logger\").pipe(HttpMiddleware.withLoggerDisabled)\n )\n)\n\n// Apply the logger middleware globally\nconst app = router.pipe(HttpServer.serve(HttpMiddleware.logger))\n\nlisten(app, 3000)\n/*\ncurl -i http://localhost:3000/no-logger\ntimestamp=2024-05-19T09:53:29.877Z level=INFO fiber=#0 message=\"Listening on http://0.0.0.0:3000\"\n*/\n```\n\n### xForwardedHeaders\n\nThis middleware handles `X-Forwarded-*` headers, useful when your app is behind a reverse proxy or load balancer and you need to retrieve the original client's IP and host information.\n**WARNING:** The `X-Forwarded-*` headers are untrustworthy when no trusted reverse proxy or load balancer is between the client and server.\n\n```ts\nimport {\n HttpMiddleware,\n HttpRouter,\n HttpServer,\n HttpServerRequest,\n HttpServerResponse\n} from \"@effect/platform\"\nimport { Effect } from \"effect\"\nimport { listen } from \"./listen.js\"\n\n// Create a router and a route that logs request headers and remote address\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\n \"/\",\n Effect.gen(function* () {\n const req = yield* HttpServerRequest.HttpServerRequest\n console.log(req.headers)\n console.log(req.remoteAddress)\n return yield* HttpServerResponse.text(\"Hello World\")\n })\n )\n)\n\n// Set up the server with xForwardedHeaders middleware\nconst app = router.pipe(HttpServer.serve(HttpMiddleware.xForwardedHeaders))\n\nlisten(app, 3000)\n/*\ncurl -H \"X-Forwarded-Host: 192.168.1.1\" -H \"X-Forwarded-For: 192.168.1.1\" http://localhost:3000\ntimestamp=... level=INFO fiber=#0 message=\"Listening on http://0.0.0.0:3000\"\n{\n host: '192.168.1.1',\n 'user-agent': 'curl/8.6.0',\n accept: '*\\/*',\n 'x-forwarded-host': '192.168.1.1',\n 'x-forwarded-for': '192.168.1.1'\n}\n{ _id: 'Option', _tag: 'Some', value: '192.168.1.1' }\n*/\n```\n\n## Error Handling\n\n### Catching Errors\n\nBelow is an example illustrating how to catch and manage errors that occur during the execution of route handlers:\n\n```ts\nimport { HttpRouter, HttpServer, HttpServerResponse } from \"@effect/platform\"\nimport { Effect } from \"effect\"\nimport { listen } from \"./listen.js\"\n\n// Define routes that might throw errors or fail\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\n \"/throw\",\n Effect.sync(() => {\n throw new Error(\"BROKEN\") // This will intentionally throw an error\n })\n ),\n HttpRouter.get(\"/fail\", Effect.fail(\"Uh oh!\")) // This will intentionally fail\n)\n\n// Configure the application to handle different types of errors\nconst app = router.pipe(\n Effect.catchTags({\n RouteNotFound: () =>\n HttpServerResponse.text(\"Route Not Found\", { status: 404 })\n }),\n Effect.catchAllCause((cause) =>\n HttpServerResponse.text(cause.toString(), { status: 500 })\n ),\n HttpServer.serve()\n)\n\nlisten(app, 3000)\n```\n\nYou can test the error handling setup with `curl` commands by trying to access routes that trigger errors:\n\n```sh\n# Accessing a route that does not exist\ncurl -i http://localhost:3000/nonexistent\n\n# Accessing the route that throws an error\ncurl -i http://localhost:3000/throw\n\n# Accessing the route that fails\ncurl -i http://localhost:3000/fail\n```\n\n## Validations\n\nValidation is a critical aspect of handling HTTP requests to ensure that the data your server receives is as expected. We'll explore how to validate headers and cookies using the `@effect/platform` and `effect/Schema` libraries, which provide structured and robust methods for these tasks.\n\n### Headers\n\nHeaders often contain important information needed by your application, such as content types, authentication tokens, or session data. Validating these headers ensures that your application can trust and correctly process the information it receives.\n\n```ts\nimport {\n HttpRouter,\n HttpServer,\n HttpServerRequest,\n HttpServerResponse\n} from \"@effect/platform\"\nimport { Effect, Schema } from \"effect\"\nimport { listen } from \"./listen.js\"\n\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\n \"/\",\n Effect.gen(function* () {\n // Define the schema for expected headers and validate them\n const headers = yield* HttpServerRequest.schemaHeaders(\n Schema.Struct({ test: Schema.String })\n )\n return yield* HttpServerResponse.text(\"header: \" + headers.test)\n }).pipe(\n // Handle parsing errors\n Effect.catchTag(\"ParseError\", (e) =>\n HttpServerResponse.text(`Invalid header: ${e.message}`)\n )\n )\n )\n)\n\nconst app = router.pipe(HttpServer.serve())\n\nlisten(app, 3000)\n```\n\nYou can test header validation using the following `curl` commands:\n\n```sh\n# Request without the required header\ncurl -i http://localhost:3000\n\n# Request with the valid header\ncurl -i -H \"test: myvalue\" http://localhost:3000\n```\n\n### Cookies\n\nCookies are commonly used to maintain session state or user preferences. Validating cookies ensures that the data they carry is intact and as expected, enhancing security and application integrity.\n\nHere's how you can validate cookies received in HTTP requests:\n\n```ts\nimport {\n Cookies,\n HttpRouter,\n HttpServer,\n HttpServerRequest,\n HttpServerResponse\n} from \"@effect/platform\"\nimport { Effect, Schema } from \"effect\"\nimport { listen } from \"./listen.js\"\n\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\n \"/\",\n Effect.gen(function* () {\n const cookies = yield* HttpServerRequest.schemaCookies(\n Schema.Struct({ test: Schema.String })\n )\n return yield* HttpServerResponse.text(\"cookie: \" + cookies.test)\n }).pipe(\n Effect.catchTag(\"ParseError\", (e) =>\n HttpServerResponse.text(`Invalid cookie: ${e.message}`)\n )\n )\n )\n)\n\nconst app = router.pipe(HttpServer.serve())\n\nlisten(app, 3000)\n```\n\nValidate the cookie handling with the following `curl` commands:\n\n```sh\n# Request without any cookies\ncurl -i http://localhost:3000\n\n# Request with the valid cookie\ncurl -i http://localhost:3000 --cookie \"test=myvalue\"\n```\n\n## ServerRequest\n\n### How do I get the raw request?\n\nThe native request object depends on the platform you are using, and it is not directly modeled in `@effect/platform`. Instead, you need to refer to the specific platform package you are working with, such as `@effect/platform-node` or `@effect/platform-bun`.\n\nHere is an example using Node.js:\n\n```ts\nimport {\n HttpRouter,\n HttpServer,\n HttpServerRequest,\n HttpServerResponse\n} from \"@effect/platform\"\nimport { NodeHttpServer, NodeHttpServerRequest } from \"@effect/platform-node\"\nimport { Effect } from \"effect\"\nimport { listen } from \"./listen.js\"\n\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\n \"/\",\n Effect.gen(function* () {\n const req = yield* HttpServerRequest.HttpServerRequest\n const raw = NodeHttpServerRequest.toIncomingMessage(req)\n console.log(raw)\n return HttpServerResponse.empty()\n })\n )\n)\n\nlisten(HttpServer.serve(router), 3000)\n```\n\n## Conversions\n\n### toWebHandler\n\nThe `toWebHandler` function converts a `Default` (i.e. a type of `HttpApp` that specifically produces a `ServerResponse` as its output) into a web handler that can process `Request` objects and return `Response` objects.\n\n```ts\nimport { HttpApp, HttpRouter, HttpServerResponse } from \"@effect/platform\"\n\n// Define the router with some routes\nconst router = HttpRouter.empty.pipe(\n HttpRouter.get(\"/\", HttpServerResponse.text(\"content 1\")),\n HttpRouter.get(\"/foo\", HttpServerResponse.text(\"content 2\"))\n)\n\n// Convert the router to a web handler\n// const handler: (request: Request) => Promise<Response>\nconst handler = HttpApp.toWebHandler(router)\n\n// Test the handler with a request\nconst response = await handler(new Request(\"http://localhost:3000/foo\"))\nconsole.log(await response.text()) // Output: content 2\n```\n\n# Url\n\nThe `Url` module provides utilities for constructing and working with `URL` objects in a functional style. It includes:\n\n- A safe constructor for parsing URLs from strings.\n- Functions for immutably updating `URL` properties like `host`, `href`, and `search`.\n- Tools for reading and modifying URL parameters using the `UrlParams` module.\n- A focus on immutability, creating new `URL` instances for every change.\n\n## Creating a URL\n\n### fromString\n\nThis function takes a string and attempts to parse it into a `URL` object. If the string is invalid, it returns an `Either.Left` containing an `IllegalArgumentException` with the error details. Otherwise, it returns an `Either.Right` containing the parsed `URL`.\n\nYou can optionally provide a `base` parameter to resolve relative URLs. When supplied, the function treats the input `url` as relative to the `base`.\n\n**Example** (Parsing a URL with Optional Base)\n\n```ts\nimport { Url } from \"@effect/platform\"\nimport { Either } from \"effect\"\n\n// Parse an absolute URL\n//\n// ┌─── Either<URL, IllegalArgumentException>\n// ▼\nconst parsed = Url.fromString(\"https://example.com/path\")\n\nif (Either.isRight(parsed)) {\n console.log(\"Parsed URL:\", parsed.right.toString())\n} else {\n console.log(\"Error:\", parsed.left.message)\n}\n// Output: Parsed URL: https://example.com/path\n\n// Parse a relative URL with a base\nconst relativeParsed = Url.fromString(\"/relative-path\", \"https://example.com\")\n\nif (Either.isRight(relativeParsed)) {\n console.log(\"Parsed relative URL:\", relativeParsed.right.toString())\n} else {\n console.log(\"Error:\", relativeParsed.left.message)\n}\n// Output: Parsed relative URL: https://example.com/relative-path\n```\n\n## Immutably Changing URL Properties\n\nThe `Url` module offers a set of functions for updating properties of a `URL` object without modifying the original instance. These functions create and return a new `URL` with the specified updates, preserving the immutability of the original.\n\n### Available Setters\n\n| Setter | Description |\n| ------------- | --------------------------------------------------------- |\n| `setHash` | Updates the hash fragment of the URL. |\n| `setHost` | Updates the host (domain and port) of the URL. |\n| `setHostname` | Updates the domain of the URL without modifying the port. |\n| `setHref` | Replaces the entire URL string. |\n| `setPassword` | Updates the password used for authentication. |\n| `setPathname` | Updates the path of the URL. |\n| `setPort` | Updates the port of the URL. |\n| `setProtocol` | Updates the protocol (e.g., `http`, `https`). |\n| `setSearch` | Updates the query string of the URL. |\n| `setUsername` | Updates the username used for authentication. |\n\n**Example** (Using Setters to Modify URL Properties)\n\n```ts\nimport { Url } from \"@effect/platform\"\nimport { pipe } from \"effect\"\n\nconst myUrl = new URL(\"https://example.com\")\n\n// Changing protocol, host, and port\nconst newUrl = pipe(\n myUrl,\n Url.setProtocol(\"http:\"),\n Url.setHost(\"google.com\"),\n Url.setPort(\"8080\")\n)\n\nconsole.log(\"Original:\", myUrl.toString())\n// Output: Original: https://example.com/\n\nconsole.log(\"New:\", newUrl.toString())\n// Output: New: http://google.com:8080/\n```\n\n### mutate\n\nFor more advanced modifications, use the `mutate` function. It clones the original `URL` object and applies a callback to the clone, allowing multiple updates at once.\n\n**Example** (Applying Multiple Changes with `mutate`)\n\n```ts\nimport { Url } from \"@effect/platform\"\n\nconst myUrl = new URL(\"https://example.com\")\n\nconst mutatedUrl = Url.mutate(myUrl, (url) => {\n url.username = \"user\"\n url.password = \"pass\"\n})\n\nconsole.log(\"Mutated:\", mutatedUrl.toString())\n// Output: Mutated: https://user:pass@example.com/\n```\n\n## Reading and Writing URL Parameters\n\nThe `Url` module provides utilities for working with URL query parameters. These utilities allow you to read existing parameters and write new ones, all while maintaining immutability. This functionality is supported by the `UrlParams` module.\n\nYou can extract the query parameters from a `URL` object using the `urlParams` function.\n\nTo modify or add query parameters, use the `setUrlParams` function. This function creates a new `URL` with the updated query string.\n\n**Example** (Reading and Writing Parameters)\n\n```ts\nimport { Url, UrlParams } from \"@effect/platform\"\n\nconst myUrl = new URL(\"https://example.com?foo=bar\")\n\n// Read parameters\nconst params = Url.urlParams(myUrl)\n\nconsole.log(params)\n// Output: [ [ 'foo', 'bar' ] ]\n\n// Write parameters\nconst updatedUrl = Url.setUrlParams(\n myUrl,\n UrlParams.fromInput([[\"key\", \"value\"]])\n)\n\nconsole.log(updatedUrl.toString())\n// Output: https://example.com/?key=value\n```\n\n### Modifying URL Parameters\n\nThe `modifyUrlParams` function allows you to read, modify, and overwrite URL parameters in a single operation.\n\n**Example** (Appending a Parameter to a URL)\n\n```ts\nimport { Url, UrlParams } from \"@effect/platform\"\n\nconst myUrl = new URL(\"https://example.com?foo=bar\")\n\nconst changedUrl = Url.modifyUrlParams(myUrl, UrlParams.append(\"key\", \"value\"))\n\nconsole.log(changedUrl.toString())\n// Output: https://example.com/?foo=bar&key=value\n```\n\n# OpenApiJsonSchema\n\nThe `OpenApiJsonSchema` module provides utilities to transform `Schema` objects into JSON schemas that comply with the OpenAPI Specification. These utilities are especially helpful for generating OpenAPI documentation or working with tools that require OpenAPI-compliant schemas.\n\n## Creating a JSON Schema from a Schema\n\nThis module enables you to convert `Schema` objects into OpenAPI-compatible JSON schemas, making it easy to integrate with tools like Swagger or other OpenAPI-based frameworks.\n\n**Example** (Generating a JSON Schema from a String Schema)\n\n```ts\nimport { OpenApiJsonSchema } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\nconst schema = Schema.String\n\n// Convert the schema to OpenAPI JSON Schema\nconst openApiSchema = OpenApiJsonSchema.make(schema)\n\nconsole.log(JSON.stringify(openApiSchema, null, 2))\n/*\nOutput:\n{\n \"type\": \"string\"\n}\n*/\n```\n\n## Differences from JSONSchema\n\nThe `OpenApiJsonSchema` module differs from the `JSONSchema` module in several ways. These differences are tailored to align with the OpenAPI Specification.\n\n### `$schema` Property Omission\n\nOpenAPI schemas do not include the `$schema` property, while JSON schemas do.\n\n**Example** (Comparison of `$schema` Property)\n\n```ts\nimport { OpenApiJsonSchema } from \"@effect/platform\"\nimport { JSONSchema, Schema } from \"effect\"\n\nconst schema = Schema.String\n\nconst openApiSchema = OpenApiJsonSchema.make(schema)\nconst jsonSchema = JSONSchema.make(schema)\n\nconsole.log(JSON.stringify(openApiSchema, null, 2))\n/*\nOutput:\n{\n \"type\": \"string\"\n}\n*/\n\nconsole.log(JSON.stringify(jsonSchema, null, 2))\n/*\nOutput:\n{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"type\": \"string\"\n}\n*/\n```\n\n### Handling of `null` Values\n\nOpenAPI does not support `{ \"type\": \"null\" }`. Instead, it uses an `enum` containing `null` to represent nullable values.\n\n**Example** (Representation of `null` Values)\n\n```ts\nimport { OpenApiJsonSchema } from \"@effect/platform\"\nimport { JSONSchema, Schema } from \"effect\"\n\nconst schema = Schema.Null\n\nconst openApiSchema = OpenApiJsonSchema.make(schema)\nconst jsonSchema = JSONSchema.make(schema)\n\nconsole.log(JSON.stringify(openApiSchema, null, 2))\n/*\nOutput:\n{\n \"enum\": [\n null\n ]\n}\n*/\n\nconsole.log(JSON.stringify(jsonSchema, null, 2))\n/*\nOutput:\n{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"type\": \"null\"\n}\n*/\n```\n\n### Nullable Values\n\nOpenAPI uses the `nullable` property to indicate that a value can be `null`, whereas JSON schemas use an `anyOf` structure.\n\n**Example** (Nullable Property Representation)\n\n```ts\nimport { OpenApiJsonSchema } from \"@effect/platform\"\nimport { JSONSchema, Schema } from \"effect\"\n\nconst schema = Schema.NullOr(Schema.String)\n\nconst openApiSchema = OpenApiJsonSchema.make(schema)\nconst jsonSchema = JSONSchema.make(schema)\n\nconsole.log(JSON.stringify(openApiSchema, null, 2))\n/*\nOutput:\n{\n \"type\": \"string\",\n \"nullable\": true\n}\n*/\n\nconsole.log(JSON.stringify(jsonSchema, null, 2))\n/*\nOutput:\n{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"anyOf\": [\n {\n \"type\": \"string\"\n },\n {\n \"type\": \"null\"\n }\n ]\n}\n*/\n```\n\n### `contentSchema` Support\n\nOpenAPI schemas include a `contentSchema` property, which allows you to describe the structure of the content for a media type (e.g., `application/json`). This feature is not available in JSON schemas (Draft 7), making `contentSchema` particularly useful for defining structured payloads in OpenAPI documentation.\n\n**Note**: Use `contentSchema` to define the internal structure of media types like `application/json` in OpenAPI specifications. This property provides clarity and detail for tools and users interacting with the API, especially when handling structured payloads.\n\n**Example** (Defining a Schema with `contentSchema` for JSON Content)\n\n```ts\nimport { OpenApiJsonSchema } from \"@effect/platform\"\nimport { JSONSchema, Schema } from \"effect\"\n\n// Define a schema for parsing JSON content\nconst schema = Schema.parseJson(Schema.Struct({ a: Schema.String }))\n\nconst openApiSchema = OpenApiJsonSchema.make(schema)\nconst jsonSchema = JSONSchema.make(schema)\n\nconsole.log(JSON.stringify(openApiSchema, null, 2))\n/*\nOutput:\n{\n \"type\": \"string\",\n \"contentMediaType\": \"application/json\",\n \"contentSchema\": {\n \"type\": \"object\",\n \"required\": [\n \"a\"\n ],\n \"properties\": {\n \"a\": {\n \"type\": \"string\"\n }\n },\n \"additionalProperties\": false\n }\n}\n*/\n\nconsole.log(JSON.stringify(jsonSchema, null, 2))\n/*\nOutput:\n{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"type\": \"object\",\n \"required\": [\n \"a\"\n ],\n \"properties\": {\n \"a\": {\n \"type\": \"string\"\n }\n },\n \"additionalProperties\": false\n}\n*/\n```\n\n### makeWithDefs\n\nThe `makeWithDefs` function generates OpenAPI-compatible JSON schemas and collects schema definitions in a shared object. This is especially useful for consolidating multiple schemas into a single OpenAPI specification, enabling schema reuse across your API.\n\n**Example** (Generating OpenAPI Schema with Definitions)\n\n```ts\nimport { OpenApiJsonSchema } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\n// Define a schema with an identifier annotation\nconst schema = Schema.Struct({ a: Schema.String }).annotations({\n identifier: \"MyStruct\"\n})\n\n// Create a definitions object\nconst defs = {}\n\n// Generate the OpenAPI schema while collecting definitions\nconst openApiSchema = OpenApiJsonSchema.makeWithDefs(schema, { defs })\n\nconsole.log(JSON.stringify(openApiSchema, null, 2))\n/*\nOutput:\n{\n \"$ref\": \"#/components/schemas/MyStruct\"\n}\n*/\n\nconsole.log(JSON.stringify(defs, null, 2))\n/*\nOutput:\n{\n \"MyStruct\": {\n \"type\": \"object\",\n \"required\": [\n \"a\"\n ],\n \"properties\": {\n \"a\": {\n \"type\": \"string\"\n }\n },\n \"additionalProperties\": false\n }\n}\n*/\n```\n\n**Example** (Combining Multiple Schemas into One OpenAPI Specification)\n\n```ts\nimport { OpenApiJsonSchema } from \"@effect/platform\"\nimport { Schema } from \"effect\"\n\n// Define multiple schemas with unique identifiers\nconst schema1 = Schema.Struct({ a: Schema.String }).annotations({\n identifier: \"MyStruct1\"\n})\nconst schema2 = Schema.Struct({ b: Schema.Number }).annotations({\n identifier: \"MyStruct2\"\n})\n\n// Create a shared definitions object\nconst defs = {}\n\n// Use `makeWithDefs` to generate schemas for API paths\nconst paths = {\n paths: {\n \"/path1\": {\n get: {\n responses: {\n \"200\": {\n content: {\n \"application/json\": {\n schema: OpenApiJsonSchema.makeWithDefs(schema1, { defs })\n }\n }\n }\n }\n }\n },\n \"/path2\": {\n get: {\n responses: {\n \"200\": {\n content: {\n \"application/json\": {\n schema: OpenApiJsonSchema.makeWithDefs(schema2, { defs })\n }\n }\n }\n }\n }\n }\n }\n}\n\n// Combine paths and definitions into a single OpenAPI schema\nconst openApiSchema = {\n components: {\n schemas: defs\n },\n paths\n}\n\nconsole.log(JSON.stringify(openApiSchema, null, 2))\n/*\nOutput:\n{\n \"components\": {\n \"schemas\": {\n \"MyStruct1\": {\n \"type\": \"object\",\n \"required\": [\n \"a\"\n ],\n \"properties\": {\n \"a\": {\n \"type\": \"string\"\n }\n },\n \"additionalProperties\": false\n },\n \"MyStruct2\": {\n \"type\": \"object\",\n \"required\": [\n \"b\"\n ],\n \"properties\": {\n \"b\": {\n \"type\": \"number\"\n }\n },\n \"additionalProperties\": false\n }\n }\n },\n \"paths\": {\n \"paths\": {\n \"/path1\": {\n \"get\": {\n \"responses\": {\n \"200\": {\n \"content\": {\n \"application/json\": {\n \"schema\": {\n \"$ref\": \"#/components/schemas/MyStruct1\"\n }\n }\n }\n }\n }\n }\n },\n \"/path2\": {\n \"get\": {\n \"responses\": {\n \"200\": {\n \"content\": {\n \"application/json\": {\n \"schema\": {\n \"$ref\": \"#/components/schemas/MyStruct2\"\n }\n }\n }\n }\n }\n }\n }\n }\n }\n}\n*/\n```\n\n# HttpLayerRouter\n\nThe experimental `HttpLayerRouter` module provides a simplified way to create HTTP servers.\nIt aims to simplify the process of defining routes and registering other HTTP\nservices like `HttpApi` or `RpcServer`'s.\n\n## Registering routes\n\n```ts\nimport * as NodeHttpServer from \"@effect/platform-node/NodeHttpServer\"\nimport * as NodeRuntime from \"@effect/platform-node/NodeRuntime\"\nimport * as HttpLayerRouter from \"@effect/platform/HttpLayerRouter\"\nimport * as HttpServerResponse from \"@effect/platform/HttpServerResponse\"\nimport * as Effect from \"effect/Effect\"\nimport * as Layer from \"effect/Layer\"\nimport { createServer } from \"http\"\n\n// Here is how you can register a simple GET route\nconst HelloRoute = Layer.effectDiscard(\n Effect.gen(function* () {\n // First, we need to access the `HttpRouter` service\n const router = yield* HttpLayerRouter.HttpRouter\n\n // Then, we can add a new route to the router\n yield* router.add(\"GET\", \"/hello\", HttpServerResponse.text(\"Hello, World!\"))\n })\n)\n\n// You can also use the `HttpLayerRouter.use` function to register a route\nconst GoodbyeRoute = HttpLayerRouter.use(\n Effect.fn(function* (router) {\n // The `router` parameter is the `HttpRouter` service\n yield* router.add(\n \"GET\",\n \"/goodbye\",\n HttpServerResponse.text(\"Goodbye, World!\")\n )\n })\n)\n// Or use `HttpLayerRouter.add/addAll` for simple routes\nconst SimpleRoute = HttpLayerRouter.add(\n \"GET\",\n \"/simple\",\n HttpServerResponse.text(\"Simply fantastic!\")\n)\n\nconst AllRoutes = Layer.mergeAll(HelloRoute, GoodbyeRoute, SimpleRoute)\n\n// To start the server, we use `HttpLayerRouter.serve` with the routes layer\nHttpLayerRouter.serve(AllRoutes).pipe(\n Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 })),\n Layer.launch,\n NodeRuntime.runMain\n)\n```\n\n## Applying middleware\n\n```ts\nimport * as HttpLayerRouter from \"@effect/platform/HttpLayerRouter\"\nimport * as HttpMiddleware from \"@effect/platform/HttpMiddleware\"\nimport * as HttpServerResponse from \"@effect/platform/HttpServerResponse\"\nimport * as Context from \"effect/Context\"\nimport * as Effect from \"effect/Effect\"\nimport * as Layer from \"effect/Layer\"\n\n// Here is a service that we want to provide to every HTTP request\nclass CurrentSession extends Context.Tag(\"CurrentSession\")<\n CurrentSession,\n {\n readonly token: string\n }\n>() {}\n\n// Using the `HttpLayerRouter.middleware` function, we can create a middleware\n// that provides the `CurrentSession` service to every HTTP request.\nconst SessionMiddleware = HttpLayerRouter.middleware<{\n provides: CurrentSession\n}>()(\n Effect.gen(function* () {\n yield* Effect.log(\"SessionMiddleware initialized\")\n\n return (httpEffect) =>\n Effect.provideService(httpEffect, CurrentSession, {\n token: \"dummy-token\"\n })\n })\n)\n\n// And here is an example of global middleware, that modifies the HTTP response.\n// Global middleware directly returns a `Layer`.\nconst CorsMiddleware = HttpLayerRouter.middleware(HttpMiddleware.cors(), {\n global: true\n})\n// You can also use `HttpLayerRouter.cors()` to create a CORS middleware\n\nconst HelloRoute = HttpLayerRouter.add(\n \"GET\",\n \"/hello\",\n Effect.gen(function* () {\n // We can now access the `CurrentSession` service in our route handler\n const session = yield* CurrentSession\n return HttpServerResponse.text(\n `Hello, World! Your session token is: ${session.token}`\n )\n })\n).pipe(\n // We can provide the `SessionMiddleware.layer` to the `HelloRoute` layer\n Layer.provide(SessionMiddleware.layer),\n // And we can also provide the `CorsMiddleware` layer to handle CORS\n Layer.provide(CorsMiddleware)\n)\n```\n\n## Interdependent middleware\n\nIf middleware depends on another middleware, you can use the `.combine` api to\ncombine them.\n\n```ts\nimport * as HttpLayerRouter from \"@effect/platform/HttpLayerRouter\"\nimport * as HttpServerResponse from \"@effect/platform/HttpServerResponse\"\nimport * as Context from \"effect/Context\"\nimport * as Effect from \"effect/Effect\"\nimport * as Layer from \"effect/Layer\"\n\nclass CurrentSession extends Context.Tag(\"CurrentSession\")<\n CurrentSession,\n {\n readonly token: string\n }\n>() {}\n\nconst SessionMiddleware = HttpLayerRouter.middleware<{\n provides: CurrentSession\n}>()(\n Effect.gen(function* () {\n yield* Effect.log(\"SessionMiddleware initialized\")\n\n return (httpEffect) =>\n Effect.provideService(httpEffect, CurrentSession, {\n token: \"dummy-token\"\n })\n })\n)\n\n// Here is a middleware that uses the `CurrentSession` service\nconst LogMiddleware = HttpLayerRouter.middleware(\n Effect.gen(function* () {\n yield* Effect.log(\"LogMiddleware initialized\")\n\n return Effect.fn(function* (httpEffect) {\n const session = yield* CurrentSession\n yield* Effect.log(`Current session token: ${session.token}`)\n return yield* httpEffect\n })\n })\n)\n\n// We can then use the .combine method to combine the middlewares\nconst LogAndSessionMiddleware = LogMiddleware.combine(SessionMiddleware)\n\nconst HelloRoute = HttpLayerRouter.add(\n \"GET\",\n \"/hello\",\n Effect.gen(function* () {\n const session = yield* CurrentSession\n return HttpServerResponse.text(\n `Hello, World! Your session token is: ${session.token}`\n )\n })\n).pipe(Layer.provide(LogAndSessionMiddleware.layer))\n```\n\n## Registering a HttpApi\n\n```ts\nimport {\n HttpApi,\n HttpApiBuilder,\n HttpApiEndpoint,\n HttpApiGroup,\n HttpApiScalar,\n HttpLayerRouter\n} from \"@effect/platform\"\nimport { NodeHttpServer, NodeRuntime } from \"@effect/platform-node\"\nimport { Effect, Layer } from \"effect\"\nimport { createServer } from \"http\"\n\n// First, we define our HttpApi\nclass MyApi extends HttpApi.make(\"api\").add(\n HttpApiGroup.make(\"users\")\n .add(HttpApiEndpoint.get(\"me\", \"/me\"))\n .prefix(\"/users\")\n) {}\n\n// Implement the handlers for the API\nconst UsersApiLayer = HttpApiBuilder.group(MyApi, \"users\", (handers) =>\n handers.handle(\"me\", () => Effect.void)\n)\n\n// Use `HttpLayerRouter.addHttpApi` to register the API with the router\nconst HttpApiRoutes = HttpLayerRouter.addHttpApi(MyApi, {\n openapiPath: \"/docs/openapi.json\"\n}).pipe(\n // Provide the api handlers layer\n Layer.provide(UsersApiLayer)\n)\n\n// Create a /docs route for the API documentation\nconst DocsRoute = HttpApiScalar.layerHttpLayerRouter({\n api: MyApi,\n path: \"/docs\"\n})\n\n// Finally, we merge all routes and serve them using the Node HTTP server\nconst AllRoutes = Layer.mergeAll(HttpApiRoutes, DocsRoute).pipe(\n Layer.provide(HttpLayerRouter.cors())\n)\n\nHttpLayerRouter.serve(AllRoutes).pipe(\n Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 })),\n Layer.launch,\n NodeRuntime.runMain\n)\n```\n\n## Registering a RpcServer\n\n```ts\nimport { HttpLayerRouter } from \"@effect/platform\"\nimport { NodeHttpServer, NodeRuntime } from \"@effect/platform-node\"\nimport { Rpc, RpcGroup, RpcSerialization, RpcServer } from \"@effect/rpc\"\nimport { Effect, Layer, Schema } from \"effect\"\nimport { createServer } from \"http\"\n\nexport class User extends Schema.Class<User>(\"User\")({\n id: Schema.String,\n name: Schema.String\n}) {}\n\n// Define a group of RPCs\nexport class UserRpcs extends RpcGroup.make(\n Rpc.make(\"UserById\", {\n success: User,\n error: Schema.String, // Indicates that errors, if any, will be returned as strings\n payload: {\n id: Schema.String\n }\n })\n) {}\n\nconst UserHandlers = UserRpcs.toLayer({\n UserById: ({ id }) => Effect.succeed(new User({ id, name: \"John Doe\" }))\n})\n\n// Use `HttpLayerRouter` to register the rpc server\nconst RpcRoute = RpcServer.layerHttpRouter({\n group: UserRpcs,\n path: \"/rpc\"\n}).pipe(\n Layer.provide(UserHandlers),\n Layer.provide(RpcSerialization.layerJson),\n Layer.provide(HttpLayerRouter.cors()) // provide CORS middleware\n)\n\n// Start the HTTP server with the RPC route\nHttpLayerRouter.serve(RpcRoute).pipe(\n Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 })),\n Layer.launch,\n NodeRuntime.runMain\n)\n```\n\n## Create a web handler\n\n```ts\nimport * as HttpLayerRouter from \"@effect/platform/HttpLayerRouter\"\nimport * as HttpServerResponse from \"@effect/platform/HttpServerResponse\"\nimport * as Effect from \"effect/Effect\"\n\nconst HelloRoute = HttpLayerRouter.use(\n Effect.fn(function* (router) {\n yield* router.add(\n \"GET\",\n \"/hello\",\n HttpServerResponse.text(\"Hellow, World!\")\n )\n })\n)\n\nconst { dispose, handler } = HttpLayerRouter.toWebHandler(HelloRoute)\n\n// When the process is interrupted, we want to clean up resources\nprocess.on(\"SIGINT\", () => {\n dispose().then(\n () => {\n process.exit(0)\n },\n () => {\n process.exit(1)\n }\n )\n})\n\n// Use the handler in your server setup\nexport { handler }\n```\n"
|
|
1395
|
+
"path": ".compat-tests/effect/packages/rpc/README.md",
|
|
1396
|
+
"text": "# Introduction\n\nThe `@effect/rpc` library facilitates the development of remote procedure call (RPC) systems in TypeScript, enhancing application scalability and maintainability. It provides a type-safe environment that reduces runtime errors by aligning with TypeScript's strong typing. This library simplifies the creation of network-exposed services, handling the intricacies of data serialization and network communication, allowing developers to concentrate on core business logic. Its features support custom serialization, error handling, and middleware, making it adaptable for diverse application needs.\n\n# Quickstart\n\n## Declaring Requests\n\nThe `RpcGroup` and `Rpc` modules can be used alongside the `Schema` module to\ndefine requests and responses.\n\nHere we are defining a request to retrieve a list of users, a request to\nretrieve a user by ID, and a request to create a new user.\n\n```ts filename=\"request.ts\"\n// request.ts\nimport { Rpc, RpcGroup } from \"@effect/rpc\"\nimport { Schema } from \"effect\"\n\n// Define a user with an ID and name\nexport class User extends Schema.Class<User>(\"User\")({\n id: Schema.String, // User's ID as a string\n name: Schema.String // User's name as a string\n}) {}\n\n// Define a group of RPCs for user management.\n// You can use the `RpcGroup.make` function to create a group of RPCs.\nexport class UserRpcs extends RpcGroup.make(\n // Request to retrieve a list of users\n Rpc.make(\"UserList\", {\n success: User, // Succeed with a stream of users\n stream: true\n }),\n Rpc.make(\"UserById\", {\n success: User,\n error: Schema.String, // Indicates that errors, if any, will be returned as strings\n payload: {\n id: Schema.String\n }\n }),\n Rpc.make(\"UserCreate\", {\n success: User,\n payload: {\n name: Schema.String\n }\n })\n) {}\n```\n\n## Implementing the handlers\n\nThis section introduces how to implement the rpc handlers, using an imaginary database setup to manage user data.\n\n```ts filename=\"handers.ts\"\n// handlers.ts\nimport type { Rpc } from \"@effect/rpc\"\nimport { Effect, Layer, Ref, Stream } from \"effect\"\nimport { User, UserRpcs } from \"./request.js\"\n\n// ---------------------------------------------\n// Imaginary Database\n// ---------------------------------------------\n\nclass UserRepository extends Effect.Service<UserRepository>()(\n \"UserRepository\",\n {\n effect: Effect.gen(function* () {\n const ref = yield* Ref.make<Array<User>>([\n new User({ id: \"1\", name: \"Alice\" }),\n new User({ id: \"2\", name: \"Bob\" })\n ])\n\n return {\n findMany: ref.get,\n findById: (id: string) =>\n Ref.get(ref).pipe(\n Effect.andThen((users) => {\n const user = users.find((user) => user.id === id)\n return user\n ? Effect.succeed(user)\n : Effect.fail(`User not found: ${id}`)\n })\n ),\n create: (name: string) =>\n Ref.updateAndGet(ref, (users) => [\n ...users,\n new User({ id: String(users.length + 1), name })\n ]).pipe(Effect.andThen((users) => users[users.length - 1]))\n }\n })\n }\n) {}\n\n// ---------------------------------------------\n// RPC handlers\n// ---------------------------------------------\n\nexport const UsersLive: Layer.Layer<\n Rpc.Handler<\"UserList\"> | Rpc.Handler<\"UserById\"> | Rpc.Handler<\"UserCreate\">\n> = UserRpcs.toLayer(\n Effect.gen(function* () {\n const db = yield* UserRepository\n\n return {\n UserList: () => Stream.fromIterableEffect(db.findMany),\n UserById: ({ id }) => db.findById(id),\n UserCreate: ({ name }) => db.create(name)\n }\n })\n).pipe(\n // Provide the UserRepository layer\n Layer.provide(UserRepository.Default)\n)\n```\n\n## Serving the API\n\nThis part explains how to serve the API using the handlers we defined earlier.\n\n```ts filename=\"server.ts\"\n// server.ts\nimport { HttpRouter } from \"@effect/platform\"\nimport { BunHttpServer, BunRuntime } from \"@effect/platform-bun\"\nimport { RpcSerialization, RpcServer } from \"@effect/rpc\"\nimport { Layer } from \"effect\"\nimport { UsersLive } from \"./handlers.js\"\nimport { UserRpcs } from \"./request.js\"\n\n// Create the RPC server layer\nconst RpcLayer = RpcServer.layer(UserRpcs).pipe(Layer.provide(UsersLive))\n\n// Choose the protocol and serialization format\nconst HttpProtocol = RpcServer.layerProtocolHttp({\n path: \"/rpc\"\n}).pipe(Layer.provide(RpcSerialization.layerNdjson))\n\n// Create the main server layer\nconst Main = HttpRouter.Default.serve().pipe(\n Layer.provide(RpcLayer),\n Layer.provide(HttpProtocol),\n Layer.provide(BunHttpServer.layer({ port: 3000 }))\n)\n\nBunRuntime.runMain(Layer.launch(Main))\n```\n\n**Testing the API with curl**\n\nUse this `curl` command to test if the API is operational:\n\n```bash\ncurl -X POST http://localhost:3000/rpc \\\n -H \"Content-Type: application/ndjson\" \\\n -d $'{\"_tag\": \"Request\", \"id\": \"123\", \"tag\": \"UserList\", \"payload\": {}, \"traceId\": \"traceId\", \"spanId\": \"spanId\", \"sampled\": true, \"headers\": [] }\\n'\n```\n\n## Using your new backend on the client\n\nLet's now move to the client-side code and embrace the power of end-to-end typesafety.\n\n```ts\n// client.ts\nimport { FetchHttpClient } from \"@effect/platform\"\nimport { RpcClient, RpcSerialization } from \"@effect/rpc\"\nimport { Chunk, Effect, Layer, Option, Stream } from \"effect\"\nimport { UserRpcs } from \"./request.js\"\n\n// Choose which protocol to use\nconst ProtocolLive = RpcClient.layerProtocolHttp({\n url: \"http://localhost:3000/rpc\"\n}).pipe(\n Layer.provide([\n // use fetch for http requests\n FetchHttpClient.layer,\n // use ndjson for serialization\n RpcSerialization.layerNdjson\n ])\n)\n\n// Use the client\nconst program = Effect.gen(function* () {\n const client = yield* RpcClient.make(UserRpcs)\n let users = yield* Stream.runCollect(client.UserList({}))\n if (Option.isNone(Chunk.findFirst(users, (user) => user.id === \"3\"))) {\n console.log(`Creating user \"Charlie\"`)\n yield* client.UserCreate({ name: \"Charlie\" })\n users = yield* Stream.runCollect(client.UserList({}))\n } else {\n console.log(`User \"Charlie\" already exists`)\n }\n return users\n}).pipe(Effect.scoped)\n\nprogram.pipe(Effect.provide(ProtocolLive), Effect.runPromise).then(console.log)\n```\n\n## Defining middleware\n\nTo add middleware to the RPC server (& optionally the client), you can use the\n`RpcMiddleware` module.\n\nThe first step is to define the middleware context tag, which is used to both\nimplement and access the middleware.\n\n```ts filename=\"middleware.ts\"\n// middleware.ts\nimport { RpcMiddleware } from \"@effect/rpc\"\nimport { Context } from \"effect\"\nimport type { User } from \"./request.js\"\n\n// A context tag which represents the current user\nexport class CurrentUser extends Context.Tag(\"CurrentUser\")<\n CurrentUser,\n User\n>() {}\n\n// The context tag for the authentication middleware\nexport class AuthMiddleware extends RpcMiddleware.Tag<AuthMiddleware>()(\n \"AuthMiddleware\",\n {\n // This middleware will provide the current user context\n provides: CurrentUser,\n // This middleware requires a client implementation too\n requiredForClient: true\n }\n) {}\n```\n\n## Implementing middleware\n\nOnce the middleware context tag is defined, you can then use it in a `RpcGroup`\nto apply it to various RPCs.\n\nWhen it has been applied, you can then implement the middleware logic and add it\nto your server and client.\n\n```ts\nimport { Headers } from \"@effect/platform\"\nimport { Rpc, RpcClient, RpcGroup, RpcMiddleware, RpcServer } from \"@effect/rpc\"\nimport { Effect, Layer, Schema } from \"effect\"\nimport { AuthMiddleware } from \"./middleware.js\"\nimport { User } from \"./request.js\"\n\nexport class UserRpcs extends RpcGroup.make(\n Rpc.make(\"UserById\", {\n success: User,\n payload: {\n id: Schema.String\n }\n })\n // apply the middleware to a single RPC\n .middleware(AuthMiddleware)\n)\n // or apply the middleware to the entire group\n .middleware(AuthMiddleware) {}\n\n// Implement the middleware for a server\nexport const AuthLive: Layer.Layer<AuthMiddleware> = Layer.succeed(\n AuthMiddleware,\n // A middleware that provides the current user.\n //\n // You can access the headers, payload, and the RPC definition when\n // implementing the middleware.\n AuthMiddleware.of(({ headers, payload, rpc }) =>\n Effect.succeed(new User({ id: \"123\", name: \"Logged in user\" }))\n )\n)\n\n// apply the middleware to a rpc server\nRpcServer.layer(UserRpcs).pipe(Layer.provide(AuthLive))\n\n// Implement the middleware for a client\n//\n// The client middleware can access the request and the RPC definition, and\n// returns a modified request.\nexport const AuthClientLive: Layer.Layer<\n RpcMiddleware.ForClient<AuthMiddleware>\n> = RpcMiddleware.layerClient(AuthMiddleware, ({ request, rpc }) =>\n Effect.succeed({\n ...request,\n headers: Headers.set(request.headers, \"authorization\", \"Bearer token\")\n })\n)\n\n// apply the middleware to a rpc client\nexport class UsersClient extends Effect.Service<UsersClient>()(\"UsersClient\", {\n scoped: RpcClient.make(UserRpcs),\n // add the middleware layer to the dependencies\n dependencies: [AuthClientLive]\n}) {}\n```\n"
|
|
1397
1397
|
},
|
|
1398
1398
|
{
|
|
1399
1399
|
"title": "Introduction",
|
|
@@ -1404,8 +1404,8 @@
|
|
|
1404
1404
|
{
|
|
1405
1405
|
"title": "Introduction",
|
|
1406
1406
|
"filename": "README.md",
|
|
1407
|
-
"path": ".compat-tests/effect/packages/
|
|
1408
|
-
"text": "# Introduction\n\nThe `@effect/rpc` library facilitates the development of remote procedure call (RPC) systems in TypeScript, enhancing application scalability and maintainability. It provides a type-safe environment that reduces runtime errors by aligning with TypeScript's strong typing. This library simplifies the creation of network-exposed services, handling the intricacies of data serialization and network communication, allowing developers to concentrate on core business logic. Its features support custom serialization, error handling, and middleware, making it adaptable for diverse application needs.\n\n# Quickstart\n\n## Declaring Requests\n\nThe `RpcGroup` and `Rpc` modules can be used alongside the `Schema` module to\ndefine requests and responses.\n\nHere we are defining a request to retrieve a list of users, a request to\nretrieve a user by ID, and a request to create a new user.\n\n```ts filename=\"request.ts\"\n// request.ts\nimport { Rpc, RpcGroup } from \"@effect/rpc\"\nimport { Schema } from \"effect\"\n\n// Define a user with an ID and name\nexport class User extends Schema.Class<User>(\"User\")({\n id: Schema.String, // User's ID as a string\n name: Schema.String // User's name as a string\n}) {}\n\n// Define a group of RPCs for user management.\n// You can use the `RpcGroup.make` function to create a group of RPCs.\nexport class UserRpcs extends RpcGroup.make(\n // Request to retrieve a list of users\n Rpc.make(\"UserList\", {\n success: User, // Succeed with a stream of users\n stream: true\n }),\n Rpc.make(\"UserById\", {\n success: User,\n error: Schema.String, // Indicates that errors, if any, will be returned as strings\n payload: {\n id: Schema.String\n }\n }),\n Rpc.make(\"UserCreate\", {\n success: User,\n payload: {\n name: Schema.String\n }\n })\n) {}\n```\n\n## Implementing the handlers\n\nThis section introduces how to implement the rpc handlers, using an imaginary database setup to manage user data.\n\n```ts filename=\"handers.ts\"\n// handlers.ts\nimport type { Rpc } from \"@effect/rpc\"\nimport { Effect, Layer, Ref, Stream } from \"effect\"\nimport { User, UserRpcs } from \"./request.js\"\n\n// ---------------------------------------------\n// Imaginary Database\n// ---------------------------------------------\n\nclass UserRepository extends Effect.Service<UserRepository>()(\n \"UserRepository\",\n {\n effect: Effect.gen(function* () {\n const ref = yield* Ref.make<Array<User>>([\n new User({ id: \"1\", name: \"Alice\" }),\n new User({ id: \"2\", name: \"Bob\" })\n ])\n\n return {\n findMany: ref.get,\n findById: (id: string) =>\n Ref.get(ref).pipe(\n Effect.andThen((users) => {\n const user = users.find((user) => user.id === id)\n return user\n ? Effect.succeed(user)\n : Effect.fail(`User not found: ${id}`)\n })\n ),\n create: (name: string) =>\n Ref.updateAndGet(ref, (users) => [\n ...users,\n new User({ id: String(users.length + 1), name })\n ]).pipe(Effect.andThen((users) => users[users.length - 1]))\n }\n })\n }\n) {}\n\n// ---------------------------------------------\n// RPC handlers\n// ---------------------------------------------\n\nexport const UsersLive: Layer.Layer<\n Rpc.Handler<\"UserList\"> | Rpc.Handler<\"UserById\"> | Rpc.Handler<\"UserCreate\">\n> = UserRpcs.toLayer(\n Effect.gen(function* () {\n const db = yield* UserRepository\n\n return {\n UserList: () => Stream.fromIterableEffect(db.findMany),\n UserById: ({ id }) => db.findById(id),\n UserCreate: ({ name }) => db.create(name)\n }\n })\n).pipe(\n // Provide the UserRepository layer\n Layer.provide(UserRepository.Default)\n)\n```\n\n## Serving the API\n\nThis part explains how to serve the API using the handlers we defined earlier.\n\n```ts filename=\"server.ts\"\n// server.ts\nimport { HttpRouter } from \"@effect/platform\"\nimport { BunHttpServer, BunRuntime } from \"@effect/platform-bun\"\nimport { RpcSerialization, RpcServer } from \"@effect/rpc\"\nimport { Layer } from \"effect\"\nimport { UsersLive } from \"./handlers.js\"\nimport { UserRpcs } from \"./request.js\"\n\n// Create the RPC server layer\nconst RpcLayer = RpcServer.layer(UserRpcs).pipe(Layer.provide(UsersLive))\n\n// Choose the protocol and serialization format\nconst HttpProtocol = RpcServer.layerProtocolHttp({\n path: \"/rpc\"\n}).pipe(Layer.provide(RpcSerialization.layerNdjson))\n\n// Create the main server layer\nconst Main = HttpRouter.Default.serve().pipe(\n Layer.provide(RpcLayer),\n Layer.provide(HttpProtocol),\n Layer.provide(BunHttpServer.layer({ port: 3000 }))\n)\n\nBunRuntime.runMain(Layer.launch(Main))\n```\n\n**Testing the API with curl**\n\nUse this `curl` command to test if the API is operational:\n\n```bash\ncurl -X POST http://localhost:3000/rpc \\\n -H \"Content-Type: application/ndjson\" \\\n -d $'{\"_tag\": \"Request\", \"id\": \"123\", \"tag\": \"UserList\", \"payload\": {}, \"traceId\": \"traceId\", \"spanId\": \"spanId\", \"sampled\": true, \"headers\": [] }\\n'\n```\n\n## Using your new backend on the client\n\nLet's now move to the client-side code and embrace the power of end-to-end typesafety.\n\n```ts\n// client.ts\nimport { FetchHttpClient } from \"@effect/platform\"\nimport { RpcClient, RpcSerialization } from \"@effect/rpc\"\nimport { Chunk, Effect, Layer, Option, Stream } from \"effect\"\nimport { UserRpcs } from \"./request.js\"\n\n// Choose which protocol to use\nconst ProtocolLive = RpcClient.layerProtocolHttp({\n url: \"http://localhost:3000/rpc\"\n}).pipe(\n Layer.provide([\n // use fetch for http requests\n FetchHttpClient.layer,\n // use ndjson for serialization\n RpcSerialization.layerNdjson\n ])\n)\n\n// Use the client\nconst program = Effect.gen(function* () {\n const client = yield* RpcClient.make(UserRpcs)\n let users = yield* Stream.runCollect(client.UserList({}))\n if (Option.isNone(Chunk.findFirst(users, (user) => user.id === \"3\"))) {\n console.log(`Creating user \"Charlie\"`)\n yield* client.UserCreate({ name: \"Charlie\" })\n users = yield* Stream.runCollect(client.UserList({}))\n } else {\n console.log(`User \"Charlie\" already exists`)\n }\n return users\n}).pipe(Effect.scoped)\n\nprogram.pipe(Effect.provide(ProtocolLive), Effect.runPromise).then(console.log)\n```\n\n## Defining middleware\n\nTo add middleware to the RPC server (& optionally the client), you can use the\n`RpcMiddleware` module.\n\nThe first step is to define the middleware context tag, which is used to both\nimplement and access the middleware.\n\n```ts filename=\"middleware.ts\"\n// middleware.ts\nimport { RpcMiddleware } from \"@effect/rpc\"\nimport { Context } from \"effect\"\nimport type { User } from \"./request.js\"\n\n// A context tag which represents the current user\nexport class CurrentUser extends Context.Tag(\"CurrentUser\")<\n CurrentUser,\n User\n>() {}\n\n// The context tag for the authentication middleware\nexport class AuthMiddleware extends RpcMiddleware.Tag<AuthMiddleware>()(\n \"AuthMiddleware\",\n {\n // This middleware will provide the current user context\n provides: CurrentUser,\n // This middleware requires a client implementation too\n requiredForClient: true\n }\n) {}\n```\n\n## Implementing middleware\n\nOnce the middleware context tag is defined, you can then use it in a `RpcGroup`\nto apply it to various RPCs.\n\nWhen it has been applied, you can then implement the middleware logic and add it\nto your server and client.\n\n```ts\nimport { Headers } from \"@effect/platform\"\nimport { Rpc, RpcClient, RpcGroup, RpcMiddleware, RpcServer } from \"@effect/rpc\"\nimport { Effect, Layer, Schema } from \"effect\"\nimport { AuthMiddleware } from \"./middleware.js\"\nimport { User } from \"./request.js\"\n\nexport class UserRpcs extends RpcGroup.make(\n Rpc.make(\"UserById\", {\n success: User,\n payload: {\n id: Schema.String\n }\n })\n // apply the middleware to a single RPC\n .middleware(AuthMiddleware)\n)\n // or apply the middleware to the entire group\n .middleware(AuthMiddleware) {}\n\n// Implement the middleware for a server\nexport const AuthLive: Layer.Layer<AuthMiddleware> = Layer.succeed(\n AuthMiddleware,\n // A middleware that provides the current user.\n //\n // You can access the headers, payload, and the RPC definition when\n // implementing the middleware.\n AuthMiddleware.of(({ headers, payload, rpc }) =>\n Effect.succeed(new User({ id: \"123\", name: \"Logged in user\" }))\n )\n)\n\n// apply the middleware to a rpc server\nRpcServer.layer(UserRpcs).pipe(Layer.provide(AuthLive))\n\n// Implement the middleware for a client\n//\n// The client middleware can access the request and the RPC definition, and\n// returns a modified request.\nexport const AuthClientLive: Layer.Layer<\n RpcMiddleware.ForClient<AuthMiddleware>\n> = RpcMiddleware.layerClient(AuthMiddleware, ({ request, rpc }) =>\n Effect.succeed({\n ...request,\n headers: Headers.set(request.headers, \"authorization\", \"Bearer token\")\n })\n)\n\n// apply the middleware to a rpc client\nexport class UsersClient extends Effect.Service<UsersClient>()(\"UsersClient\", {\n scoped: RpcClient.make(UserRpcs),\n // add the middleware layer to the dependencies\n dependencies: [AuthClientLive]\n}) {}\n```\n"
|
|
1407
|
+
"path": ".compat-tests/effect/packages/vitest/README.md",
|
|
1408
|
+
"text": "# Introduction\n\nWelcome to your guide on testing Effect-based applications using `vitest` and the `@effect/vitest` package. This package simplifies running tests for Effect-based code with Vitest.\n\nIn this guide, we'll walk you through setting up the necessary dependencies and provide examples of how to write Effect-based tests using `@effect/vitest`.\n\n# Requirements\n\nFirst, ensure you have [`vitest`](https://vitest.dev/guide/) installed (version `1.6.0` or later).\n\n```sh\npnpm add -D vitest\n```\n\nNext, install the `@effect/vitest` package, which integrates Effect with Vitest.\n\n```sh\npnpm add -D @effect/vitest\n```\n\n# Overview\n\nThe main entry point is the following import:\n\n```ts\nimport { it } from \"@effect/vitest\"\n```\n\nThis import enhances the standard `it` function from `vitest` with several powerful features, including:\n\n| Feature | Description |\n| --------------- | ------------------------------------------------------------------------------------------------------ |\n| `it.effect` | Automatically injects a `TestContext` (e.g., `TestClock`) when running a test. |\n| `it.live` | Runs the test with the live Effect environment. |\n| `it.scoped` | Allows running an Effect program that requires a `Scope`. |\n| `it.scopedLive` | Combines the features of `scoped` and `live`, using a live Effect environment that requires a `Scope`. |\n| `it.flakyTest` | Facilitates the execution of tests that might occasionally fail. |\n\n# Writing Tests with `it.effect`\n\nHere's how to use `it.effect` to write your tests:\n\n**Syntax**\n\n```ts\nimport { it } from \"@effect/vitest\"\n\nit.effect(\"test name\", () => EffectContainingAssertions, timeout: number | TestOptions = 5_000)\n```\n\n`it.effect` automatically provides a `TestContext`, allowing access to services like [`TestClock`](#using-the-testclock).\n\n## Testing Successful Operations\n\nTo write a test, place your assertions directly within the main effect. This ensures that your assertions are evaluated as part of the test's execution.\n\n**Example** (Testing a Successful Operation)\n\nIn the following example, we test a function that divides two numbers, but fails if the divisor is zero. The goal is to check that the function returns the correct result when given valid input.\n\n```ts\nimport { it, expect } from \"@effect/vitest\"\nimport { Effect } from \"effect\"\n\n// A simple divide function that returns an Effect, failing when dividing by zero\nfunction divide(a: number, b: number) {\n if (b === 0) return Effect.fail(\"Cannot divide by zero\")\n return Effect.succeed(a / b)\n}\n\n// Testing a successful division\nit.effect(\"test success\", () =>\n Effect.gen(function* () {\n const result = yield* divide(4, 2) // Expect 4 divided by 2 to succeed\n expect(result).toBe(2) // Assert that the result is 2\n })\n)\n```\n\n## Testing Successes and Failures as `Exit`\n\nWhen you need to handle both success and failure cases in a test, you can use `Effect.exit` to capture the outcome as an `Exit` object. This allows you to verify both successful and failed results within the same test structure.\n\n**Example** (Testing Success and Failure with `Exit`)\n\n```ts\nimport { it, expect } from \"@effect/vitest\"\nimport { Effect, Exit } from \"effect\"\n\n// A function that divides two numbers and returns an Effect.\n// It fails if the divisor is zero.\nfunction divide(a: number, b: number) {\n if (b === 0) return Effect.fail(\"Cannot divide by zero\")\n return Effect.succeed(a / b)\n}\n\n// Test case for a successful division, using `Effect.exit` to capture the result\nit.effect(\"test success as Exit\", () =>\n Effect.gen(function* () {\n const result = yield* Effect.exit(divide(4, 2)) // Capture the result as an Exit\n expect(result).toStrictEqual(Exit.succeed(2)) // Expect success with the value 2\n })\n)\n\n// Test case for a failure (division by zero), using `Effect.exit`\nit.effect(\"test failure as Exit\", () =>\n Effect.gen(function* () {\n const result = yield* Effect.exit(divide(4, 0)) // Capture the result as an Exit\n expect(result).toStrictEqual(Exit.fail(\"Cannot divide by zero\")) // Expect failure with the correct message\n })\n)\n```\n\n## Using the TestClock\n\nWhen writing tests with `it.effect`, a `TestContext` is automatically provided. This context gives access to various testing services, including the [`TestClock`](https://effect.website/docs/guides/testing/testclock), which allows you to simulate the passage of time in your tests.\n\n**Note**: If you want to use the real-time clock (instead of the simulated one), you can switch to `it.live`.\n\n**Example** (Using `TestClock` and `it.live`)\n\nHere are examples that demonstrate how you can work with time in your tests using `it.effect` and `TestClock`:\n\n1. **Using `it.live` to show the current time**: This will display the actual system time, since it runs in the live environment.\n\n2. **Using `it.effect` without adjustments**: By default, the `TestClock` starts at `0`, simulating the beginning of time for your test without any time passing.\n\n3. **Using `it.effect` and adjusting time**: In this test, we simulate the passage of time by advancing the clock by 1000 milliseconds (1 second).\n\n```ts\nimport { it } from \"@effect/vitest\"\nimport { Clock, Effect, TestClock } from \"effect\"\n\n// Effect to log the current time\nconst logNow = Effect.gen(function* () {\n const now = yield* Clock.currentTimeMillis // Fetch the current time from the clock\n console.log(now) // Log the current time\n})\n\n// Example of using the real system clock with `it.live`\nit.live(\"runs the test with the live Effect environment\", () =>\n Effect.gen(function* () {\n yield* logNow // Prints the actual current time\n })\n)\n\n// Example of using `it.effect` with the default test environment\nit.effect(\"run the test with the test environment\", () =>\n Effect.gen(function* () {\n yield* logNow // Prints 0, as the test clock starts at 0\n })\n)\n\n// Example of advancing the test clock by 1000 milliseconds\nit.effect(\"run the test with the test environment and the time adjusted\", () =>\n Effect.gen(function* () {\n yield* TestClock.adjust(\"1000 millis\") // Move the clock forward by 1000 milliseconds\n yield* logNow // Prints 1000, reflecting the adjusted time\n })\n)\n```\n\n## Skipping Tests\n\nIf you need to temporarily disable a test but don't want to delete or comment out the code, you can use `it.effect.skip`. This is helpful when you're working on other parts of your test suite but want to keep the test for future execution.\n\n**Example** (Skipping a Test)\n\n```ts\nimport { it } from \"@effect/vitest\"\nimport { Effect, Exit } from \"effect\"\nimport { expect } from \"@effect/vitest\"\n\nfunction divide(a: number, b: number) {\n if (b === 0) return Effect.fail(\"Cannot divide by zero\")\n return Effect.succeed(a / b)\n}\n\n// Temporarily skip the test for dividing numbers\nit.effect.skip(\"test failure as Exit\", () =>\n Effect.gen(function* () {\n const result = yield* Effect.exit(divide(4, 0))\n expect(result).toStrictEqual(Exit.fail(\"Cannot divide by zero\"))\n })\n)\n```\n\n## Running a Single Test\n\nWhen you're developing or debugging, it's often useful to run a specific test without executing the entire test suite. You can achieve this by using `it.effect.only`, which will run just the selected test and ignore the others.\n\n**Example** (Running a Single Test)\n\n```ts\nimport { it } from \"@effect/vitest\"\nimport { Effect, Exit } from \"effect\"\nimport { expect } from \"@effect/vitest\"\n\nfunction divide(a: number, b: number) {\n if (b === 0) return Effect.fail(\"Cannot divide by zero\")\n return Effect.succeed(a / b)\n}\n\n// Run only this test, skipping all others\nit.effect.only(\"test failure as Exit\", () =>\n Effect.gen(function* () {\n const result = yield* Effect.exit(divide(4, 0))\n expect(result).toStrictEqual(Exit.fail(\"Cannot divide by zero\"))\n })\n)\n```\n\n## Expecting Tests to Fail\n\nWhen adding new failing tests, you might not be able to fix them right away. Instead of skipping them, you may want to assert it fails, so that when you fix them, you'll know and can re-enable them before it regresses.\n\n**Example** (Asserting one test fails)\n\n```ts\nimport { it } from \"@effect/vitest\"\nimport { Effect, Exit } from \"effect\"\n\nfunction divide(a: number, b: number): number {\n if (b === 0) return Effect.fail(\"Cannot divide by zero\")\n return Effect.succeed(a / b)\n}\n\n// Temporarily assert that the test for dividing by zero fails.\nit.effect.fails(\"dividing by zero special cases\", ({ expect }) =>\n Effect.gen(function* () {\n const result = yield* Effect.exit(divide(4, 0))\n expect(result).toStrictEqual(0)\n })\n)\n```\n\n## Logging\n\nBy default, `it.effect` suppresses log output, which can be useful for keeping test results clean. However, if you want to enable logging during tests, you can use `it.live` or provide a custom logger to control the output.\n\n**Example** (Controlling Logging in Tests)\n\n```ts\nimport { it } from \"@effect/vitest\"\nimport { Effect, Logger } from \"effect\"\n\n// This test won't display the log message, as logging is suppressed by default in `it.effect`\nit.effect(\"does not display a log\", () =>\n Effect.gen(function* () {\n yield* Effect.log(\"it.effect\") // Log won't be shown\n })\n)\n\n// This test will display the log because a custom logger is provided\nit.effect(\"providing a logger displays a log\", () =>\n Effect.gen(function* () {\n yield* Effect.log(\"it.effect with custom logger\") // Log will be displayed\n }).pipe(\n Effect.provide(Logger.pretty) // Providing a pretty logger for log output\n )\n)\n\n// This test runs using `it.live`, which enables logging by default\nit.live(\"it.live displays a log\", () =>\n Effect.gen(function* () {\n yield* Effect.log(\"it.live\") // Log will be displayed\n })\n)\n```\n\n# Writing Tests with `it.scoped`\n\nThe `it.scoped` method is used for tests that involve `Effect` programs needing a `Scope`. A `Scope` ensures that any resources your test acquires are managed properly, meaning they will be released when the test completes. This helps prevent resource leaks and guarantees test isolation.\n\n**Example** (Using `it.scoped` to Manage Resource Lifecycle)\n\n```ts\nimport { it } from \"@effect/vitest\"\nimport { Console, Effect } from \"effect\"\n\n// Simulating the acquisition and release of a resource with console logging\nconst acquire = Console.log(\"acquire resource\")\nconst release = Console.log(\"release resource\")\n\n// Defining a resource that requires proper management\nconst resource = Effect.acquireRelease(acquire, () => release)\n\n// Incorrect usage: This will result in a type error because it lacks a scope\nit.effect(\"run with scope\", () =>\n Effect.gen(function* () {\n yield* resource\n })\n)\n\n// Correct usage: Using 'it.scoped' to manage the scope correctly\nit.scoped(\"run with scope\", () =>\n Effect.gen(function* () {\n yield* resource\n })\n)\n```\n\n# Writing Tests with `it.flakyTest`\n\n`it.flakyTest` is a utility designed to manage tests that may not succeed consistently on the first attempt. These tests, often referred to as \"flaky,\" can fail due to factors like timing issues, external dependencies, or randomness. `it.flakyTest` allows for retrying these tests until they pass or a specified timeout is reached.\n\n**Example** (Handling Flaky Tests with Retries)\n\nLet's start by setting up a basic test scenario that has the potential to fail randomly:\n\n```ts\nimport { it } from \"@effect/vitest\"\nimport { Effect, Random } from \"effect\"\n\n// Simulating a flaky effect\nconst flaky = Effect.gen(function* () {\n const random = yield* Random.nextBoolean\n if (random) {\n return yield* Effect.fail(\"Failed due to randomness\")\n }\n})\n\n// Standard test that may fail intermittently\nit.effect(\"possibly failing test\", () => flaky)\n```\n\nIn this test, the outcome is random, so the test might fail depending on the result of `Random.nextBoolean`.\n\nTo handle this flakiness, we use `it.flakyTest` to retry the test until it passes, or until a defined timeout expires:\n\n```ts\n// Retrying the flaky test with a 5-second timeout\nit.effect(\"retrying until success or timeout\", () =>\n it.flakyTest(flaky, \"5 seconds\")\n)\n```\n"
|
|
1409
1409
|
},
|
|
1410
1410
|
{
|
|
1411
1411
|
"title": "Join the discussion ⠀⠀⠀⠀⠀⠀⠀",
|
|
@@ -1525,7 +1525,7 @@
|
|
|
1525
1525
|
"title": "TJS Roadmap",
|
|
1526
1526
|
"filename": "PLAN.md",
|
|
1527
1527
|
"path": "PLAN.md",
|
|
1528
|
-
"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.15-1.3x** | Production with validation |\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\nInline validation = **~1.15x overhead** on real-world functions with full runtime type checking. Trivial functions (e.g. `x * 2`) show ~1.3x because the `typeof`/`instanceof` checks dominate.\n\n### Why Inline Validation Wins\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({\n name,\n email,\n age,\n}: {\n name: string\n email: string\n age: number\n}) {\n return save({ name, email, age })\n}\n```\n\nTJS generates inline type checks at transpile time:\n\n```javascript\nif (\n typeof input !== 'object' ||\n input === null ||\n typeof input.name !== 'string' ||\n typeof input.email !== 'string' ||\n typeof input.age !== 'number'\n) {\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.15x overhead on real functions\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\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\n```\nPlain function call: 1.2ms / 1M calls (baseline)\nsafety: 'none': 1.2ms / 1M calls (~1.0x) - no wrapper\nsafety: 'inputs': 1.4ms / 1M calls (~1.15x) - inline validation*\nsafety: 'all': ~14x / 1M calls - validates args + return\n\n* Inline typeof/instanceof checks, no try/finally, no schema interpretation\n ~1.15x for functions with real work, ~1.3x for trivial functions\n```\n\n**Why inline validation is fast:**\n\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 (\n typeof input !== 'object' ||\n input === null ||\n typeof input.x !== 'number' ||\n typeof input.y !== 'number' ||\n typeof input.name !== 'string'\n) {\n return { $error: true, message: 'Invalid input', path: 'process.input' }\n}\n```\n\nThis makes `safety: 'inputs'` viable for **production**.\n\n**Why `safety: 'none'` is free:**\n\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\n```typescript\nfunction hot(! x: 0): 0 { return x * 2 }\n```\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\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\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 fully implemented:\n\n- Parser extracts `wasm { }` blocks with automatic variable capture\n- Compiler generates valid WebAssembly binary embedded as base64 in output\n- SIMD intrinsics (`f32x4_*`) for 4x float throughput\n- `wasmBuffer()` for zero-copy typed array sharing between JS and WASM\n- `fallback { }` provides JS path when WASM unavailable\n- See the [WASM Quick Start](https://github.com/tonioloewald/tjs-lang/blob/main/docs/WASM-QUICKSTART.md) for details\n\n### Debugging\n\n**Source locations in errors:**\n\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\n```typescript\nconfigure({ debug: true })\n// Errors now include full call stacks\n```\n\n**The `--debug` flag (planned):**\n\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\n```typescript\n// Types ARE examples - self-documenting\nfunction greet(name: 'World', times: 3): '' {\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\n```typescript\nsafety none // This module skips validation\n\nfunction hot(x: 0): 0 {\n return x * 2 // No wrapper, but autocomplete still works\n}\n```\n\n**Escape hatches:**\n\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\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\n```typescript\nconst result = riskyOperation()\nif (isMonadicError(result)) {\n // Error is a value, not an exception\n // Agent can inspect and handle gracefully\n}\n```\n\n**Fuel metering:**\n\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: 'World', times = 1) { ... }\nfunction delay(ms = 1000) { ... }\nfunction fetch(url: '', 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 | ✅ | MonadicError with path, expected, actual, callStack |\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 | ✅ | Eq/NotEq + Is/IsNot, on by default in native TJS, honest typeof |\n| 17 | WASM blocks | ✅ | Full: SIMD intrinsics, wasmBuffer, fallback, base64 embed |\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` — proven on Zod, Effect, Radash, etc. |\n| 21 | Docs generation | ✅ | Auto-generated with emit, --no-docs, --docs-dir |\n| 22 | Class support | ✅ | TS→TJS class conversion, private→#, Proxy wrap |\n| 23 | JSON Schema | ✅ | Type.toJSONSchema(), Type.strip(), fn.\\_\\_tjs.schema() |\n| 24 | Error history | ✅ | Ring buffer of recent MonadicErrors, on by default |\n| 25 | Polymorphic functions | ✅ | Multiple same-name declarations merge into dispatcher |\n| 26 | Local class extensions | ✅ | `extend String { }` without prototype pollution |\n| 27 | `const!` | ✅ | Compile-time immutability, zero runtime cost |\n| 28 | FunctionPredicate | ✅ | First-class function types with params/returns/contract |\n| 29 | `.d.ts` generation | ✅ | `generateDTS()` from TJS transpilation results |\n| 30 | `@tjs` annotations | ✅ | `/* @tjs ... */` comments enrich TS→TJS output |\n\n### fromTS Transpiler Compatibility\n\nProven against real-world TypeScript libraries (zero test regressions):\n\n| Library | Files | LOC | Tests | Status |\n| ----------- | ----- | ---- | ---------- | ------------- |\n| Radash | 10 | ~3K | 340/340 | ✅ 100% |\n| Superstruct | 8 | 1.8K | 225/225 | ✅ 100% |\n| ts-pattern | 17 | 5.5K | 453/453 | ✅ 100% |\n| Zod | 114 | ~30K | 1842/1842 | ✅ 100% |\n| Kysely | 279 | ~20K | (DB req'd) | ✅ transpiles |\n| Effect | 363 | 120K | (not run) | ✅ transpiles |\n\nScripts: `bun scripts/compat-radash.ts`, `compat-superstruct.ts`, `compat-ts-pattern.ts`, `compat-zod.ts`, `compat-kysely.ts`, `compat-effect.ts`\n\n## Next Up\n\n| Priority | Feature | Why |\n| -------- | -------------------------- | ---------------------------------------------------------- |\n| 1 | **JSON Schema from types** | Full schema emission for API contracts, OpenAPI generation |\n| 2 | **Tacit proxies** | Transpiler-assisted implicit namespaces (see Ideas) |\n| 3 | **target()** | Conditional compilation for build flags |\n| 4 | **Multi-target emission** | LLVM, SwiftUI, Android (long-term) |\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: 0): 0 { return x * 2 }\nfunction process(x: 0): 0 { 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: 0): 0 { return x * 2 }\nconst nums = [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: 0): 0 { 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 inline validation: ~1.15-1.3x overhead\n- With type flow: ~1.0x overhead (skip checks when types proven)\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.__tjs\n// {\n// params: {\n// name: { type: { kind: 'string' }, required: true },\n// age: { type: { kind: 'integer' }, required: false, default: 17 }\n// },\n// returns: { type: { kind: 'object' } },\n// source: 'users.tjs:42'\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.__tjs.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\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\n\nJavaScript's `==` is broken (type coercion chaos). In native TJS, `==`/`!=` use honest equality by default (no coercion, unwraps boxed primitives). TS-originated code retains JS semantics unless `TjsEquals` or `TjsStrict` is added. `Is`/`IsNot` provide explicit deep structural comparison:\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- ✅ `Eq()`/`NotEq()` honest equality (== and != — enabled by default in native TJS)\n- ✅ `Is()`/`IsNot()` deep structural comparison\n- ✅ Infix syntax transformation (`a Is b` → `Is(a, b)`)\n- ✅ Custom equality protocol (`[tjsEquals]` symbol and `.Equals` method)\n- ✅ Honest `typeof` (`typeof null` → `'null'` — enabled by default in native TJS)\n- ✅ TS-originated code retains JS semantics unless `TjsEquals` or `TjsStrict` added\n\n## 16. Death to Semicolons (`TjsStandard`)\n\nIn native TJS, `TjsStandard` is on by default, so newlines are meaningful. TS-originated code retains JS semantics unless `TjsStandard` or `TjsStrict` is added. 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\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() {\n return '<button>v1</button>'\n }\n}\n\n// Later redefinition (hot reload, live coding)\nclass MyButton extends HTMLElement {\n render() {\n return '<button>v2</button>'\n }\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### Tacit Proxies — Implicit Namespaces\n\nA `tacit` directive that tells the transpiler: \"unresolved identifiers matching this pattern should be looked up on this object at runtime.\" No Proxy needed at runtime — pure transpile-time rewriting.\n\n```typescript\ntacit elements as UPPERCASE\n\n// LABEL is undefined, but matches the UPPERCASE pattern\n// Transpiler rewrites to: elements.label(...)\nLABEL('Edit me', INPUT({ placeholder: 'foo' }))\n```\n\nThis gives you JSX-like ergonomics without special syntax, without build magic, and it's general-purpose — DOM elements, SQL builders, test DSLs, anything where you want a namespace of functions without explicit imports.\n\n**Key design constraints:**\n\n- Pattern matching (e.g., UPPERCASE) provides visual signal — you see `LABEL` and know it's tacit\n- File-scoped only — no cross-module leaking (same as `extend` blocks)\n- Zero runtime cost — transpiler rewrites to property access at compile time\n- Falls back to normal resolution — if an identifier IS defined, it takes precedence\n\n**Related prior art:** Python's `from module import *`, Ruby's `method_missing`, Kotlin's scope functions. TJS version is explicit (you declare the pattern) and compile-time (no runtime dispatch).\n\n### JSON Schema Deepening\n\nCurrent: `Type.toJSONSchema()`, `fn.__tjs.schema()`, `functionMetaToJSONSchema()`.\n\nNext steps:\n\n- CLI command: `tjs schema <file>` dumps JSON Schema for all exported types and functions\n- OpenAPI generation from function signatures\n- Schema validation at API boundaries (request/response matching)\n- Round-trip: JSON Schema → TJS Type (for consuming external schemas)\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"
|
|
1528
|
+
"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.15-1.3x** | Production with validation |\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\nInline validation = **~1.15x overhead** on real-world functions with full runtime type checking. Trivial functions (e.g. `x * 2`) show ~1.3x because the `typeof`/`instanceof` checks dominate.\n\n### Why Inline Validation Wins\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({\n name,\n email,\n age,\n}: {\n name: string\n email: string\n age: number\n}) {\n return save({ name, email, age })\n}\n```\n\nTJS generates inline type checks at transpile time:\n\n```javascript\nif (\n typeof input !== 'object' ||\n input === null ||\n typeof input.name !== 'string' ||\n typeof input.email !== 'string' ||\n typeof input.age !== 'number'\n) {\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.15x overhead on real functions\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\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\n```\nPlain function call: 1.2ms / 1M calls (baseline)\nsafety: 'none': 1.2ms / 1M calls (~1.0x) - no wrapper\nsafety: 'inputs': 1.4ms / 1M calls (~1.15x) - inline validation*\nsafety: 'all': ~14x / 1M calls - validates args + return\n\n* Inline typeof/instanceof checks, no try/finally, no schema interpretation\n ~1.15x for functions with real work, ~1.3x for trivial functions\n```\n\n**Why inline validation is fast:**\n\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 (\n typeof input !== 'object' ||\n input === null ||\n typeof input.x !== 'number' ||\n typeof input.y !== 'number' ||\n typeof input.name !== 'string'\n) {\n return { $error: true, message: 'Invalid input', path: 'process.input' }\n}\n```\n\nThis makes `safety: 'inputs'` viable for **production**.\n\n**Why `safety: 'none'` is free:**\n\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\n```typescript\nfunction hot(! x: 0): 0 { return x * 2 }\n```\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\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\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 fully implemented:\n\n- Parser extracts `wasm { }` blocks with automatic variable capture\n- Compiler generates valid WebAssembly binary embedded as base64 in output\n- SIMD intrinsics (`f32x4_*`) for 4x float throughput\n- `wasmBuffer()` for zero-copy typed array sharing between JS and WASM\n- `fallback { }` provides JS path when WASM unavailable\n- See the [WASM Quick Start](https://github.com/tonioloewald/tjs-lang/blob/main/docs/WASM-QUICKSTART.md) for details\n\n### Debugging\n\n**Source locations in errors:**\n\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\n```typescript\nconfigure({ debug: true })\n// Errors now include full call stacks\n```\n\n**The `--debug` flag (planned):**\n\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\n```typescript\n// Types ARE examples - self-documenting\nfunction greet(name: 'World', times: 3): '' {\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\n```typescript\nsafety none // This module skips validation\n\nfunction hot(x: 0): 0 {\n return x * 2 // No wrapper, but autocomplete still works\n}\n```\n\n**Escape hatches:**\n\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\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\n```typescript\nconst result = riskyOperation()\nif (isMonadicError(result)) {\n // Error is a value, not an exception\n // Agent can inspect and handle gracefully\n}\n```\n\n**Fuel metering:**\n\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: 'World', times = 1) { ... }\nfunction delay(ms = 1000) { ... }\nfunction fetch(url: '', 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 | ✅ | MonadicError with path, expected, actual, callStack |\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 | ✅ | Eq/NotEq + Is/IsNot, on by default in native TJS, honest typeof |\n| 17 | WASM blocks | ✅ | Full: SIMD intrinsics, wasmBuffer, fallback, base64 embed |\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` — proven on Zod, Effect, Radash, etc. |\n| 21 | Docs generation | ✅ | Auto-generated with emit, --no-docs, --docs-dir |\n| 22 | Class support | ✅ | TS→TJS class conversion, private→#, Proxy wrap |\n| 23 | JSON Schema | ✅ | Type.toJSONSchema(), Type.strip(), fn.\\_\\_tjs.schema() |\n| 24 | Error history | ✅ | Ring buffer of recent MonadicErrors, on by default |\n| 25 | Polymorphic functions | ✅ | Multiple same-name declarations merge into dispatcher |\n| 26 | Local class extensions | ✅ | `extend String { }` without prototype pollution |\n| 27 | `const!` | ✅ | Compile-time immutability, zero runtime cost |\n| 28 | FunctionPredicate | ✅ | First-class function types with params/returns/contract |\n| 29 | `.d.ts` generation | ✅ | `generateDTS()` from TJS transpilation results |\n| 30 | `@tjs` annotations | ✅ | `/* @tjs ... */` comments enrich TS→TJS output |\n\n### fromTS Transpiler Compatibility\n\nProven against real-world TypeScript libraries (zero test regressions):\n\n| Library | Files | LOC | Tests | Status |\n| ----------- | ----- | ---- | ---------- | ------------- |\n| Radash | 10 | ~3K | 340/340 | ✅ 100% |\n| Superstruct | 8 | 1.8K | 225/225 | ✅ 100% |\n| ts-pattern | 17 | 5.5K | 453/453 | ✅ 100% |\n| Zod | 114 | ~30K | 1842/1842 | ✅ 100% |\n| Kysely | 279 | ~20K | (DB req'd) | ✅ transpiles |\n| Effect | 363 | 120K | (not run) | ✅ transpiles |\n\nScripts: `bun scripts/compat-radash.ts`, `compat-superstruct.ts`, `compat-ts-pattern.ts`, `compat-zod.ts`, `compat-kysely.ts`, `compat-effect.ts`\n\n## Next Up\n\n| Priority | Feature | Why |\n| -------- | -------------------------- | ---------------------------------------------------------- |\n| 1 | **JSON Schema from types** | Full schema emission for API contracts, OpenAPI generation |\n| 2 | **Tacit proxies** | Transpiler-assisted implicit namespaces (see Ideas) |\n| 3 | **target()** | Conditional compilation for build flags |\n| 4 | **Multi-target emission** | LLVM, SwiftUI, Android (long-term) |\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: 0): 0 {\n return x * 2\n}\nfunction process(x: 0): 0 {\n return x + 1\n}\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: 0): 0 {\n return x * 2\n}\nconst nums = [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(\n 'positive integer',\n (n) => Number.isInteger(n) && n > 0\n)\nfunction increment(x: 0): 0 {\n return x + 1\n}\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 inline validation: ~1.15-1.3x overhead\n- With type flow: ~1.0x overhead (skip checks when types proven)\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.__tjs\n// {\n// params: {\n// name: { type: { kind: 'string' }, required: true },\n// age: { type: { kind: 'integer' }, required: false, default: 17 }\n// },\n// returns: { type: { kind: 'object' } },\n// source: 'users.tjs:42'\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.__tjs.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\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\n\nJavaScript's `==` is broken (type coercion chaos). In native TJS, `==`/`!=` use honest equality by default (no coercion, unwraps boxed primitives). TS-originated code retains JS semantics unless `TjsEquals` or `TjsStrict` is added. `Is`/`IsNot` provide explicit deep structural comparison:\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- ✅ `Eq()`/`NotEq()` honest equality (== and != — enabled by default in native TJS)\n- ✅ `Is()`/`IsNot()` deep structural comparison\n- ✅ Infix syntax transformation (`a Is b` → `Is(a, b)`)\n- ✅ Custom equality protocol (`[tjsEquals]` symbol and `.Equals` method)\n- ✅ Honest `typeof` (`typeof null` → `'null'` — enabled by default in native TJS)\n- ✅ TS-originated code retains JS semantics unless `TjsEquals` or `TjsStrict` added\n\n## 16. Death to Semicolons (`TjsStandard`)\n\nIn native TJS, `TjsStandard` is on by default, so newlines are meaningful. TS-originated code retains JS semantics unless `TjsStandard` or `TjsStrict` is added. 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\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() {\n return '<button>v1</button>'\n }\n}\n\n// Later redefinition (hot reload, live coding)\nclass MyButton extends HTMLElement {\n render() {\n return '<button>v2</button>'\n }\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### Tacit Proxies — Implicit Namespaces\n\nA `tacit` directive that tells the transpiler: \"unresolved identifiers matching this pattern should be looked up on this object at runtime.\" No Proxy needed at runtime — pure transpile-time rewriting.\n\n```typescript\ntacit elements as UPPERCASE\n\n// LABEL is undefined, but matches the UPPERCASE pattern\n// Transpiler rewrites to: elements.label(...)\nLABEL('Edit me', INPUT({ placeholder: 'foo' }))\n```\n\nThis gives you JSX-like ergonomics without special syntax, without build magic, and it's general-purpose — DOM elements, SQL builders, test DSLs, anything where you want a namespace of functions without explicit imports.\n\n**Key design constraints:**\n\n- Pattern matching (e.g., UPPERCASE) provides visual signal — you see `LABEL` and know it's tacit\n- File-scoped only — no cross-module leaking (same as `extend` blocks)\n- Zero runtime cost — transpiler rewrites to property access at compile time\n- Falls back to normal resolution — if an identifier IS defined, it takes precedence\n\n**Related prior art:** Python's `from module import *`, Ruby's `method_missing`, Kotlin's scope functions. TJS version is explicit (you declare the pattern) and compile-time (no runtime dispatch).\n\n### JSON Schema Deepening\n\nCurrent: `Type.toJSONSchema()`, `fn.__tjs.schema()`, `functionMetaToJSONSchema()`.\n\nNext steps:\n\n- CLI command: `tjs schema <file>` dumps JSON Schema for all exported types and functions\n- OpenAPI generation from function signatures\n- Schema validation at API boundaries (request/response matching)\n- Round-trip: JSON Schema → TJS Type (for consuming external schemas)\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"
|
|
1529
1529
|
},
|
|
1530
1530
|
{
|
|
1531
1531
|
"title": "TJS Syntax Reference",
|
|
@@ -1549,7 +1549,7 @@
|
|
|
1549
1549
|
"title": "TJS: Typed JavaScript",
|
|
1550
1550
|
"filename": "tjs.md",
|
|
1551
1551
|
"path": "guides/tjs.md",
|
|
1552
|
-
"text": "# TJS: Typed JavaScript\n\nTJS is a typed superset of JavaScript where **types are examples**.\n\n```javascript\nfunction greet(name: 'World', times: 3): '' {\n let result = ''\n let i = 0\n while (i < times) {\n result = result + `Hello, ${name}! `\n i = i + 1\n }\n return result.trim()\n}\n```\n\n## Philosophy\n\nTJS takes a different approach to typing than TypeScript:\n\n| Aspect | TypeScript | TJS |\n| -------------- | --------------------------------------- | ----------------------------- |\n| Types | Abstract declarations | Concrete examples |\n| Runtime | Erased completely | Preserved as metadata |\n| Validation | Compile-time only | Runtime optional |\n| Learning curve | Learn type syntax | Use values you know |\n| Error messages | \"Type 'string' is not assignable to...\" | \"Expected string, got number\" |\n\n### Why Examples?\n\nConsider how you'd explain a function to another developer:\n\n> \"This function takes a name like 'World' and a count like 3, and returns a greeting string\"\n\nThat's exactly how TJS works. The example _is_ the type:\n\n```javascript\n// TypeScript\nfunction greet(name: string, times: number): string\n\n// TJS - the example IS the documentation\nfunction greet(name: 'World', times: 3): ''\n```\n\n## Core Concepts\n\n### 1. Types by Example\n\nInstead of abstract type names, use example values:\n\n```javascript\n// Strings\nname: '' // any string\nname: 'default' // string with default value\n\n// Numbers\ncount: 0 // any number\nport: 8080 // number with default\n\n// Booleans\nenabled: true // boolean (default true)\ndisabled: false // boolean (default false)\n\n// Arrays\nitems: [''] // array of strings\nnumbers: [0] // array of numbers\nmixed: [0, ''] // tuple: number, string\n\n// Objects\nuser: { name: '', age: 0 } // object with shape\n\n// Null/Undefined\nnullable: null\noptional: undefined\n```\n\n### 2. Required vs Optional (`:` vs `=`)\n\nThe colon `:` means required, equals `=` means optional:\n\n```javascript\nfunction createUser(\n name: 'Anonymous', // required string\n email: 'user@example.com', // required string\n age = 0, // optional number (defaults to 0)\n role = 'user' // optional string (defaults to 'user')\n): { id: '', name: '', email: '', age: 0, role: '' } {\n return {\n id: crypto.randomUUID(),\n name,\n email,\n age,\n role\n }\n}\n\n// Valid calls:\ncreateUser('Alice', 'alice@example.com')\ncreateUser('Bob', 'bob@example.com', 30)\ncreateUser('Carol', 'carol@example.com', 25, 'admin')\n\n// Invalid - missing required params:\ncreateUser('Dave') // Error: missing required parameter 'email'\n```\n\n### 3. Return Type Annotation\n\nUse `:` to declare the return type:\n\n```javascript\nfunction add(a: 0, b: 0): 0 {\n return a + b\n}\n\nfunction getUser(id: ''): { name: '', email: '' } | null {\n // Returns user object or null\n}\n```\n\n### 4. Union Types\n\nUse `|` for unions (same as TypeScript):\n\n```javascript\nfunction parseInput(value: '' | 0 | null): '' {\n if (value === null) return 'null'\n if (typeof value === 'number') return `number: ${value}`\n return `string: ${value}`\n}\n```\n\n### 5. The `any` Type\n\nWhen you genuinely don't know the type:\n\n```javascript\nfunction identity(x: any): any {\n return x\n}\n```\n\nGenerics from TypeScript become `any` but preserve metadata:\n\n```javascript\n// TypeScript: function identity<T>(x: T): T\n// TJS: any, but __tjs.typeParams captures the generic info\nfunction identity(x: any): any {\n return x\n}\n// identity.__tjs.typeParams = { T: {} }\n```\n\n### 6. Type Declarations\n\nDefine reusable types with the `Type` keyword:\n\n```javascript\n// Type with default value (= syntax)\nType Name = 'Alice'\nType Count = 0\nType Age = +18 // positive number\n\n// Type with description and example\nType User {\n description: 'a registered user'\n example: { name: '', age: 0 }\n}\n\n// Type with predicate (auto-generates type guard from example)\nType EvenNumber {\n example: 2\n predicate(x) { return x % 2 === 0 }\n}\n\n// Complex validation with predicate\nType Email {\n example: 'test@example.com'\n predicate(x) { return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(x) }\n}\n```\n\n**Default vs Example:**\n\n- `= value` sets a **default** for instantiation\n- `example:` in block sets an **example** for testing/documentation\n- When both are present, they serve different purposes\n\nWhen `example` and `predicate` are provided, the type guard auto-checks the example's shape, then your predicate refines it.\n\n### 7. Generic Declarations\n\nDefine parameterized types with the `Generic` keyword:\n\n```javascript\n// Simple generic\nGeneric Box<T> {\n description: 'a boxed value'\n predicate(x, T) {\n return typeof x === 'object' && x !== null && 'value' in x && T(x.value)\n }\n}\n\n// Generic with default type parameter\nGeneric Container<T, U = ''> {\n description: 'container with label'\n predicate(obj, T, U) {\n return T(obj.item) && U(obj.label)\n }\n}\n```\n\nIn the predicate, `T` and `U` are type-checking functions that validate values against the provided type parameters.\n\n### 8. Bare Assignments\n\nUppercase identifiers automatically get `const`:\n\n```javascript\n// These are equivalent:\nFoo = Type('test', 'example')\nconst Foo = Type('test', 'example')\n\n// Works for any uppercase identifier\nMyConfig = { debug: true }\nconst MyConfig = { debug: true }\n```\n\n## Runtime Features\n\n### Monadic Error Handling\n\nTJS functions propagate errors automatically:\n\n```javascript\n// If any input is an error, it passes through\nconst result = processData(maybeError)\n// If maybeError is an error, result is that error (processData not called)\n\n// Check for errors\nif (isError(result)) {\n console.log(result.message)\n}\n```\n\n### Error History\n\nSince monadic errors don't throw, they can silently vanish. TJS tracks recent type errors in a ring buffer so you always know what failed:\n\n```javascript\ngreet(42) // returns MonadicError, caller ignores it\n\n// Find it later\n__tjs.errors() // → recent errors (newest last, max 64)\n__tjs.clearErrors() // → clear and return them\n__tjs.getErrorCount() // → total since last clear\n\n// Testing pattern: clear → run → check\n__tjs.clearErrors()\nrunMyCode()\nif (__tjs.errors().length > 0) {\n console.log('Unexpected type errors!')\n}\n```\n\nOn by default. Zero cost on the happy path — only writes when an error occurs.\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 colon 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| `safety none` | 1.0x | Metadata only, no wrappers |\n| `safety inputs` | ~1.15-1.3x | Production with validation |\n| `safety all` | ~14x | Debug — validates inputs and outputs |\n| `(!)` function | 1.0x | Hot paths — explicit opt-out |\n| `unsafe {}` block | 1.0x | Hot loops within validated functions |\n\nUse `(!)` for internal functions that are called frequently with known-good data. Keep public APIs safe.\n\n### SafeFunction and Eval\n\nSafe replacements for `new Function()` and `eval()` with typed inputs/outputs:\n\n```javascript\n// SafeFunction - create a typed async function from code\nconst add = await SafeFunction({\n inputs: { a: 0, b: 0 }, // typed parameters\n output: 0, // typed return\n body: 'return a + b',\n})\nawait add(1, 2) // 3\nawait add('x', 2) // Error: invalid input 'a'\n\n// Eval - evaluate code once with typed result\nconst result = await Eval({\n code: 'a + b',\n context: { a: 1, b: 2 },\n output: 0,\n}) // 3\n```\n\n**Key safety features:**\n\n- **Typed inputs/outputs** - validated at runtime\n- **Async execution** - can timeout, won't block\n- **Explicit context** - no implicit scope access\n- **Injectable capabilities** - fetch, console, etc. must be provided\n\n```javascript\n// With capabilities and timeout\nconst fetcher = await SafeFunction({\n inputs: { url: '' },\n output: { data: [] },\n body: 'return await fetch(url).then(r => r.json())',\n capabilities: { fetch: globalThis.fetch },\n timeoutMs: 10000,\n})\n\nconst data = await Eval({\n code: 'await fetch(url).then(r => r.json())',\n context: { url: 'https://api.example.com' },\n output: { items: [] },\n capabilities: { fetch: globalThis.fetch },\n})\n```\n\nBoth functions return errors as values (monadic) rather than throwing.\n\n## Testing\n\n### Compile-Time Tests\n\nTests run at **transpile time** and are stripped from output:\n\n```javascript\nType Email {\n example: 'test@example.com'\n predicate(x) { return x.includes('@') }\n}\n\n// This test runs during transpilation\ntest 'email validation' {\n if (!Email.check('user@example.com')) {\n throw new Error('valid email should pass')\n }\n if (Email.check('invalid')) {\n throw new Error('invalid email should fail')\n }\n}\n\nfunction sendEmail(to: Email) {\n // ...\n}\n```\n\nThe transpiled output contains only:\n\n```javascript\nconst Email = Type('Email', ...)\nfunction sendEmail(to) { ... }\n```\n\nThe test code **evaporates** - it verified correctness at build time.\n\n### Implicit Type Tests\n\nTypes with `example` have implicit tests - the example must pass the type check:\n\n```javascript\nType PositiveInt {\n example: 42\n predicate(x) { return Number.isInteger(x) && x > 0 }\n}\n// Implicit test: PositiveInt.check(42) must be true\n```\n\nIf the example fails the predicate, transpilation fails.\n\n### Skip Tests Flag\n\nFor debugging or speed, skip test execution:\n\n```bash\ntjs emit file.tjs --dangerously-skip-tests\n```\n\nTests are still stripped from output, but not executed.\n\n### Legacy Inline Tests\n\nFor runtime tests (e.g., integration tests), use standard test frameworks:\n\n```javascript\ntest('async operations work') {\n const data = await fetchData()\n expect(data).toBeDefined()\n}\n```\n\n## Differences from JavaScript\n\n### Removed/Discouraged\n\n| Feature | Reason |\n| -------- | ------------------------------------------------------------- |\n| `var` | Use `let` or `const` (`TjsNoVar` makes `var` an error) |\n| `new` | Classes are callable without `new` (`TjsClass` on by default) |\n| `throw` | Return errors as values (monadic errors) |\n| `eval()` | Use `Eval()` or `SafeFunction()` (`TjsNoeval` bans it) |\n| `Date` | With `TjsDate`, use `Timestamp`/`LegalDate` instead |\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\nType User {\n example: { name: '', age: 0, email: '' }\n}\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"
|
|
1552
|
+
"text": "# TJS: Typed JavaScript\n\nTJS is a typed superset of JavaScript where **types are examples**.\n\n```javascript\nfunction greet(name: 'World', times: 3): '' {\n let result = ''\n let i = 0\n while (i < times) {\n result = result + `Hello, ${name}! `\n i = i + 1\n }\n return result.trim()\n}\n```\n\n## Philosophy\n\nTJS takes a different approach to typing than TypeScript:\n\n| Aspect | TypeScript | TJS |\n| -------------- | --------------------------------------- | ----------------------------- |\n| Types | Abstract declarations | Concrete examples |\n| Runtime | Erased completely | Preserved as metadata |\n| Validation | Compile-time only | Runtime optional |\n| Learning curve | Learn type syntax | Use values you know |\n| Error messages | \"Type 'string' is not assignable to...\" | \"Expected string, got number\" |\n\n### Why Examples?\n\nConsider how you'd explain a function to another developer:\n\n> \"This function takes a name like 'World' and a count like 3, and returns a greeting string\"\n\nThat's exactly how TJS works. The example _is_ the type:\n\n```javascript\n// TypeScript\nfunction greet(name: string, times: number): string\n\n// TJS - the example IS the documentation\nfunction greet(name: 'World', times: 3): ''\n```\n\n## Core Concepts\n\n### 1. Types by Example\n\nInstead of abstract type names, use example values:\n\n```javascript\n// Strings\nname: '' // any string\nname: 'default' // string with default value\n\n// Numbers\ncount: 0 // any number\nport: 8080 // number with default\n\n// Booleans\nenabled: true // boolean (default true)\ndisabled: false // boolean (default false)\n\n// Arrays\nitems: [''] // array of strings\nnumbers: [0] // array of numbers\nmixed: [0, ''] // tuple: number, string\n\n// Objects\nuser: { name: '', age: 0 } // object with shape\n\n// Null/Undefined\nnullable: null\noptional: undefined\n```\n\n### 2. Required vs Optional (`:` vs `=`)\n\nThe colon `:` means required, equals `=` means optional:\n\n```javascript\nfunction createUser(\n name: 'Anonymous', // required string\n email: 'user@example.com', // required string\n age = 0, // optional number (defaults to 0)\n role = 'user' // optional string (defaults to 'user')\n): { id: '', name: '', email: '', age: 0, role: '' } {\n return {\n id: crypto.randomUUID(),\n name,\n email,\n age,\n role,\n }\n}\n\n// Valid calls:\ncreateUser('Alice', 'alice@example.com')\ncreateUser('Bob', 'bob@example.com', 30)\ncreateUser('Carol', 'carol@example.com', 25, 'admin')\n\n// Invalid - missing required params:\ncreateUser('Dave') // Error: missing required parameter 'email'\n```\n\n### 3. Return Type Annotation\n\nUse `:` to declare the return type:\n\n```javascript\nfunction add(a: 0, b: 0): 0 {\n return a + b\n}\n\nfunction getUser(id: ''): { name: '', email: '' } | null {\n // Returns user object or null\n}\n```\n\n### 4. Union Types\n\nUse `|` for unions (same as TypeScript):\n\n```javascript\nfunction parseInput(value: '' | 0 | null): '' {\n if (value === null) return 'null'\n if (typeof value === 'number') return `number: ${value}`\n return `string: ${value}`\n}\n```\n\n### 5. The `any` Type\n\nWhen you genuinely don't know the type:\n\n```javascript\nfunction identity(x: any): any {\n return x\n}\n```\n\nGenerics from TypeScript become `any` but preserve metadata:\n\n```javascript\n// TypeScript: function identity<T>(x: T): T\n// TJS: any, but __tjs.typeParams captures the generic info\nfunction identity(x: any): any {\n return x\n}\n// identity.__tjs.typeParams = { T: {} }\n```\n\n### 6. Type Declarations\n\nDefine reusable types with the `Type` keyword:\n\n```javascript\n// Type with default value (= syntax)\nType Name = 'Alice'\nType Count = 0\nType Age = +18 // positive number\n\n// Type with description and example\nType User {\n description: 'a registered user'\n example: { name: '', age: 0 }\n}\n\n// Type with predicate (auto-generates type guard from example)\nType EvenNumber {\n example: 2\n predicate(x) { return x % 2 === 0 }\n}\n\n// Complex validation with predicate\nType Email {\n example: 'test@example.com'\n predicate(x) { return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(x) }\n}\n```\n\n**Default vs Example:**\n\n- `= value` sets a **default** for instantiation\n- `example:` in block sets an **example** for testing/documentation\n- When both are present, they serve different purposes\n\nWhen `example` and `predicate` are provided, the type guard auto-checks the example's shape, then your predicate refines it.\n\n### 7. Generic Declarations\n\nDefine parameterized types with the `Generic` keyword:\n\n```javascript\n// Simple generic\nGeneric Box<T> {\n description: 'a boxed value'\n predicate(x, T) {\n return typeof x === 'object' && x !== null && 'value' in x && T(x.value)\n }\n}\n\n// Generic with default type parameter\nGeneric Container<T, U = ''> {\n description: 'container with label'\n predicate(obj, T, U) {\n return T(obj.item) && U(obj.label)\n }\n}\n```\n\nIn the predicate, `T` and `U` are type-checking functions that validate values against the provided type parameters.\n\n### 8. Bare Assignments\n\nUppercase identifiers automatically get `const`:\n\n```javascript\n// These are equivalent:\nFoo = Type('test', 'example')\nconst Foo = Type('test', 'example')\n\n// Works for any uppercase identifier\nMyConfig = { debug: true }\nconst MyConfig = { debug: true }\n```\n\n## Runtime Features\n\n### Monadic Error Handling\n\nTJS functions propagate errors automatically:\n\n```javascript\n// If any input is an error, it passes through\nconst result = processData(maybeError)\n// If maybeError is an error, result is that error (processData not called)\n\n// Check for errors\nif (isError(result)) {\n console.log(result.message)\n}\n```\n\n### Error History\n\nSince monadic errors don't throw, they can silently vanish. TJS tracks recent type errors in a ring buffer so you always know what failed:\n\n```javascript\ngreet(42) // returns MonadicError, caller ignores it\n\n// Find it later\n__tjs.errors() // → recent errors (newest last, max 64)\n__tjs.clearErrors() // → clear and return them\n__tjs.getErrorCount() // → total since last clear\n\n// Testing pattern: clear → run → check\n__tjs.clearErrors()\nrunMyCode()\nif (__tjs.errors().length > 0) {\n console.log('Unexpected type errors!')\n}\n```\n\nOn by default. Zero cost on the happy path — only writes when an error occurs.\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 colon 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| `safety none` | 1.0x | Metadata only, no wrappers |\n| `safety inputs` | ~1.15-1.3x | Production with validation |\n| `safety all` | ~14x | Debug — validates inputs and outputs |\n| `(!)` function | 1.0x | Hot paths — explicit opt-out |\n| `unsafe {}` block | 1.0x | Hot loops within validated functions |\n\nUse `(!)` for internal functions that are called frequently with known-good data. Keep public APIs safe.\n\n### SafeFunction and Eval\n\nSafe replacements for `new Function()` and `eval()` with typed inputs/outputs:\n\n```javascript\n// SafeFunction - create a typed async function from code\nconst add = await SafeFunction({\n inputs: { a: 0, b: 0 }, // typed parameters\n output: 0, // typed return\n body: 'return a + b',\n})\nawait add(1, 2) // 3\nawait add('x', 2) // Error: invalid input 'a'\n\n// Eval - evaluate code once with typed result\nconst result = await Eval({\n code: 'a + b',\n context: { a: 1, b: 2 },\n output: 0,\n}) // 3\n```\n\n**Key safety features:**\n\n- **Typed inputs/outputs** - validated at runtime\n- **Async execution** - can timeout, won't block\n- **Explicit context** - no implicit scope access\n- **Injectable capabilities** - fetch, console, etc. must be provided\n\n```javascript\n// With capabilities and timeout\nconst fetcher = await SafeFunction({\n inputs: { url: '' },\n output: { data: [] },\n body: 'return await fetch(url).then(r => r.json())',\n capabilities: { fetch: globalThis.fetch },\n timeoutMs: 10000,\n})\n\nconst data = await Eval({\n code: 'await fetch(url).then(r => r.json())',\n context: { url: 'https://api.example.com' },\n output: { items: [] },\n capabilities: { fetch: globalThis.fetch },\n})\n```\n\nBoth functions return errors as values (monadic) rather than throwing.\n\n## Testing\n\n### Compile-Time Tests\n\nTests run at **transpile time** and are stripped from output:\n\n```javascript\nType Email {\n example: 'test@example.com'\n predicate(x) { return x.includes('@') }\n}\n\n// This test runs during transpilation\ntest 'email validation' {\n if (!Email.check('user@example.com')) {\n throw new Error('valid email should pass')\n }\n if (Email.check('invalid')) {\n throw new Error('invalid email should fail')\n }\n}\n\nfunction sendEmail(to: Email) {\n // ...\n}\n```\n\nThe transpiled output contains only:\n\n```javascript\nconst Email = Type('Email', ...)\nfunction sendEmail(to) { ... }\n```\n\nThe test code **evaporates** - it verified correctness at build time.\n\n### Implicit Type Tests\n\nTypes with `example` have implicit tests - the example must pass the type check:\n\n```javascript\nType PositiveInt {\n example: 42\n predicate(x) { return Number.isInteger(x) && x > 0 }\n}\n// Implicit test: PositiveInt.check(42) must be true\n```\n\nIf the example fails the predicate, transpilation fails.\n\n### Skip Tests Flag\n\nFor debugging or speed, skip test execution:\n\n```bash\ntjs emit file.tjs --dangerously-skip-tests\n```\n\nTests are still stripped from output, but not executed.\n\n### Legacy Inline Tests\n\nFor runtime tests (e.g., integration tests), use standard test frameworks:\n\n```javascript\ntest('async operations work') {\n const data = await fetchData()\n expect(data).toBeDefined()\n}\n```\n\n## Differences from JavaScript\n\n### Removed/Discouraged\n\n| Feature | Reason |\n| -------- | ------------------------------------------------------------- |\n| `var` | Use `let` or `const` (`TjsNoVar` makes `var` an error) |\n| `new` | Classes are callable without `new` (`TjsClass` on by default) |\n| `throw` | Return errors as values (monadic errors) |\n| `eval()` | Use `Eval()` or `SafeFunction()` (`TjsNoeval` bans it) |\n| `Date` | With `TjsDate`, use `Timestamp`/`LegalDate` instead |\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\nType User {\n example: { name: '', age: 0, email: '' }\n}\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: {\n type: 'object',\n shape: { id: 'string', name: 'string', age: 'number' },\n },\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"
|
|
1553
1553
|
},
|
|
1554
1554
|
{
|
|
1555
1555
|
"text": "# Example\n\n\\`\\`\\`javascript\nexport function add(a: 0, b: 0): 0 {\n return a + b\n}\n\\`\\`\\`",
|
|
@@ -1578,13 +1578,13 @@
|
|
|
1578
1578
|
{
|
|
1579
1579
|
"title": "Zod perftesting",
|
|
1580
1580
|
"filename": "README.md",
|
|
1581
|
-
"path": ".compat-tests/zod/packages/
|
|
1581
|
+
"path": ".compat-tests/zod/packages/resolution/README.md",
|
|
1582
1582
|
"text": "# Zod perftesting\n\n`node generateRandomSchemas.js` to generate some random Zod schemas (pregenerated ones already exist in `src/index.ts`)\n\n`npm run build-bench` to run `tsc` with `extendedDiagnostics`\n\nEither modify zod's typings in `node_modules/zod` or `npm link` a local copy and do modifications there. Remember to build `zod` in between!\n"
|
|
1583
1583
|
},
|
|
1584
1584
|
{
|
|
1585
1585
|
"title": "Zod perftesting",
|
|
1586
1586
|
"filename": "README.md",
|
|
1587
|
-
"path": ".compat-tests/zod/packages/
|
|
1587
|
+
"path": ".compat-tests/zod/packages/tsc/README.md",
|
|
1588
1588
|
"text": "# Zod perftesting\n\n`node generateRandomSchemas.js` to generate some random Zod schemas (pregenerated ones already exist in `src/index.ts`)\n\n`npm run build-bench` to run `tsc` with `extendedDiagnostics`\n\nEither modify zod's typings in `node_modules/zod` or `npm link` a local copy and do modifications there. Remember to build `zod` in between!\n"
|
|
1589
1589
|
},
|
|
1590
1590
|
{
|
|
@@ -1607,7 +1607,7 @@
|
|
|
1607
1607
|
"group": "docs",
|
|
1608
1608
|
"order": 0,
|
|
1609
1609
|
"navTitle": "TJS for JS Devs",
|
|
1610
|
-
"text": "<!--{\"section\": \"tjs-for-js\", \"group\": \"docs\", \"order\": 0, \"navTitle\": \"TJS for JS Devs\"}-->\n\n# TJS for JavaScript Programmers\n\n_Everything you already know, plus the things you always wished JavaScript had._\n\n---\n\n## The One-Sentence Pitch\n\nTJS is JavaScript with types that actually exist at runtime, equality that actually works, and errors that don't crash your program.\n\n---\n\n## What Stays the Same\n\nTJS is a superset of JavaScript. All of this works exactly as you expect:\n\n```javascript\nconst x = 42\nlet name = 'Alice'\nconst items = [1, 2, 3].map((n) => n * 2)\nconst user = { name: 'Bob', age: 30 }\nconst { name: userName, age } = user\nconst merged = { ...defaults, ...overrides }\nconst greeting = `Hello, ${name}!`\n\nfor (const item of items) {\n console.log(item)\n}\nwhile (condition) {\n /* ... */\n}\nif (x > 0) {\n /* ... */\n} else {\n /* ... */\n}\n\ntry {\n riskyThing()\n} catch (e) {\n handleError(e)\n}\n\nclass Dog {\n #name\n constructor(name) {\n this.#name = name\n }\n get name() {\n return this.#name\n }\n}\n\nimport { something } from './module.js'\nexport function myFunction() {\n /* ... */\n}\n```\n\nIf you don't use any TJS features, your code is just JavaScript. No lock-in.\n\nThis extends to advanced patterns too: **Proxies**, **WeakMap/WeakSet**, **Symbols**, **generators**, **async iterators**, **`Object.defineProperty`** — all work identically. TJS adds type checks at function boundaries; it doesn't wrap or intercept any JS runtime behavior.\n\n---\n\n## What's Different\n\n### 1. Types Are Example Values\n\nIn JavaScript, there are no types. In TJS, you annotate parameters with\nan example of a valid value:\n\n```javascript\n// JavaScript\nfunction greet(name) {\n return `Hello, ${name}!`\n}\n\n// TJS - name is required and must be a string (like 'World')\nfunction greet(name: 'World'): '' {\n return `Hello, ${name}!`\n}\n```\n\nThe `: 'World'` means \"required, must be a string, here's an example.\" The `: ''` means \"returns a string.\" These aren't abstract type annotations -- they're concrete values the system can use for testing, documentation, and validation.\n\n| You Write | TJS Infers |\n| ----------------- | ----------------------------- |\n| `name: 'Alice'` | Required string |\n| `count: 42` | Required integer |\n| `rate: 3.14` | Required number (float) |\n| `age: +20` | Required non-negative integer |\n| `flag: true` | Required boolean |\n| `items: [0]` | Required integer[] |\n| `values: [0.0]` | Required number[] |\n| `user: { n: '' }` | Required object |\n| `id: 0 \\|\\| null` | integer or null |\n\nAll of these are valid JavaScript expressions. `42` vs `42.0` vs `+42` are\nall legal JS -- TJS leverages this to distinguish integer, float, and\nnon-negative integer types at the syntax level.\n\nNote: `|| null` means the value accepts the base type _or_ `null`, but not\n`undefined`. TJS treats `null` and `undefined` as distinct types\n(`typeOf(null) === 'null'`, `typeOf(undefined) === 'undefined'`).\n\n**Optional** parameters use `=` (just like JS default values):\n\n```javascript\nfunction greet(name = 'World') {\n /* ... */\n} // optional, defaults to 'World'\nfunction retry(count = 3) {\n /* ... */\n} // optional, defaults to 3 (integer)\n```\n\n**The difference:** `: value` means required. `= value` means optional with a default. In plain JS, both would be written as `= value`.\n\n### 2. Numeric Type Narrowing\n\nTJS distinguishes three numeric types using valid JavaScript syntax:\n\n```javascript\nfunction process(\n rate: 3.14, // number (float) -- has a decimal point\n count: 42, // integer -- whole number, no decimal\n index: +0 // non-negative integer -- prefixed with +\n) { /* ... */ }\n```\n\n| You Write | Type Inferred | Validates |\n| --------- | ---------------------- | ------------------------------- |\n| `3.14` | `number` (float) | Any number |\n| `0.0` | `number` (float) | Any number |\n| `42` | `integer` | `Number.isInteger(x)` |\n| `0` | `integer` | `Number.isInteger(x)` |\n| `+20` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n| `+0` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n| `-5` | `integer` | `Number.isInteger(x)` |\n| `-3.5` | `number` (float) | Any number |\n\nThese are all valid JavaScript expressions -- TJS just reads the syntax more\ncarefully than JS does. At runtime, passing `3.14` to a parameter typed as\ninteger returns a monadic error.\n\n### 3. Return Type Annotations\n\nTJS uses `:` for return types (same as TypeScript):\n\n```javascript\n// Returns an integer\nfunction add(a: 0, b: 0): 0 {\n return a + b\n}\n\n// Returns an object with specific shape\nfunction getUser(id: 0): { name: '', age: 0 } {\n return { name: 'Alice', age: 30 }\n}\n```\n\nThe return type example doubles as an automatic test. When you write `: 0`, TJS will call `add(0, 0)` at transpile time and verify the result is a number.\n\n### 4. Equality That Works\n\nJavaScript's `==` is notoriously broken (type coercion). Its `===` doesn't do structural comparison. TJS fixes this:\n\n```javascript\n// JavaScript\n[1, 2] === [1, 2] // false (different references)\n{ a: 1 } === { a: 1 } // false (different references)\n\n// TJS (structural equality is on by default)\n[1, 2] == [1, 2] // true (same structure)\n{ a: 1 } == { a: 1 } // true (same structure)\n[1, 2] === [1, 2] // false (different references, identity check)\n```\n\nTJS redefines the operators:\n\n| Operator | JavaScript | TJS |\n| -------- | ------------------- | --------------------- |\n| `==` | Coercive equality | Structural equality |\n| `!=` | Coercive inequality | Structural inequality |\n| `===` | Strict equality | Identity (same ref) |\n| `!==` | Strict inequality | Not same reference |\n\nYou can also use the explicit forms `Is` and `IsNot`:\n\n```javascript\nuser Is expectedUser // structural deep equality\nresult IsNot errorValue // structural deep inequality\n```\n\nClasses can define custom equality:\n\n```javascript\nclass Point {\n constructor(x: 0, y: 0) { this.x = x; this.y = y }\n Equals(other) { return this.x === other.x && this.y === other.y }\n}\n\nPoint(1, 2) Is Point(1, 2) // true, uses .Equals\n```\n\nNote: structural equality does not handle circular references. Use `===`\nfor objects that might be circular, or define an `.Equals` method.\n\n### 5. Errors Are Values, Not Exceptions\n\nIn JavaScript, type errors crash your program. In TJS, they're values:\n\n```javascript\nfunction double(x: 0): 0 {\n return x * 2\n}\n\ndouble(5) // 10\ndouble('oops') // { $error: true, message: \"Expected number for 'double.x', got string\" }\n```\n\nNo `try`/`catch`. No crashes. The caller gets a clear error value they can inspect:\n\n```javascript\nconst result = double(input)\nif (result?.$error) {\n console.log(result.message) // handle gracefully\n} else {\n useResult(result)\n}\n```\n\nErrors propagate automatically through function calls. If you pass a monadic error to another TJS function, it passes through without executing:\n\n```javascript\nconst a = step1(badInput) // MonadicError\nconst b = step2(a) // skips execution, returns the same error\nconst c = step3(b) // skips again -- error flows to the surface\n```\n\nThis is sometimes called \"railway-oriented programming.\"\n\n### 6. Classes Without `new`\n\nTJS classes are callable as functions:\n\n```javascript\n// JavaScript\nconst p = new Point(10, 20)\n\n// TJS - both work, but the clean form is preferred\nconst p1 = Point(10, 20) // works\nconst p2 = new Point(10, 20) // also works (linter warns)\n```\n\nThis is cleaner and more consistent with functional style. Under the hood, TJS uses a Proxy to auto-construct when you call a class as a function.\n\n### 7. `const` for Free\n\nUppercase identifiers automatically get `const`:\n\n```javascript\n// TJS\nConfig = { debug: true, version: '1.0' }\nMaxRetries = 3\n\n// Transpiles to\nconst Config = { debug: true, version: '1.0' }\nconst MaxRetries = 3\n```\n\nLowercase identifiers behave normally.\n\n---\n\n## The Type System\n\nJavaScript has no type system. Libraries like Zod and Ajv bolt one on,\nbut they're separate from your function signatures -- a second source of\ntruth that drifts. TJS's `Type()` built-in gives you as much narrowing\nand specificity as you want, in one place, using plain functions you\nalready know how to write.\n\n### From Simple to Sophisticated\n\n```javascript\n// Simple -- infer type from an example value\nType Name 'Alice' // any string\n\n// Descriptive -- add documentation\nType User {\n description: 'a registered user'\n example: { name: '', age: 0, email: '' }\n}\n\n// Constrained -- add a predicate for narrowing\nType PositiveNumber {\n description: 'a number greater than zero'\n example: 1\n predicate(x) { return typeof x === 'number' && x > 0 }\n}\n\n// Domain-specific -- real business rules\nType USZipCode {\n description: '5-digit US zip code'\n example: '90210'\n predicate(v) { return typeof v === 'string' && /^\\d{5}$/.test(v) }\n}\n\nType Email {\n description: 'email address'\n example: 'user@example.com'\n predicate(v) { return typeof v === 'string' && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(v) }\n}\n```\n\nThe key insight: a `predicate` is just a function that returns `true` or\n`false`. If you can express a constraint as a boolean check, you can make\nit a type. No schema DSL to learn, no special syntax -- it's JavaScript\nall the way down.\n\nTypes work in function signatures:\n\n```javascript\nfunction sendWelcome(email: Email, name: Name): '' {\n return `Welcome, ${name}! Confirmation sent to ${email}.`\n}\n\nsendWelcome('alice@example.com', 'Alice') // works\nsendWelcome('not-an-email', 'Alice') // MonadicError\n```\n\nTJS also ships common types out of the box: `TString`, `TNumber`,\n`TBoolean`, `TInteger`, `TPositiveInt`, `TNonEmptyString`, `TEmail`,\n`TUrl`, `TUuid`, `Timestamp`, `LegalDate`. No imports from a validation\nlibrary needed.\n\n### Combinators\n\nCompose types from other types:\n\n```javascript\nType OptionalEmail Nullable(Email) // Email | null\nType UserIds TArray(TPositiveInt) // array of positive integers\n```\n\n### Unions and Enums\n\n```javascript\n// Union of string literals\nUnion Status 'task status' 'pending' | 'active' | 'done'\n\n// Enum with named members\nEnum Color 'CSS color' {\n Red = 'red'\n Green = 'green'\n Blue = 'blue'\n}\n\n// Discriminated union -- like tagged unions or sum types\nconst Shape = Union('kind', {\n circle: { radius: 0 },\n rectangle: { width: 0, height: 0 }\n})\n```\n\n### Generics\n\nRuntime-checkable generic types:\n\n```javascript\nGeneric Box<T> {\n description: 'a boxed value'\n predicate(x, T) {\n return typeof x === 'object' && x !== null && 'value' in x && T(x.value)\n }\n}\n\n// Instantiate with a concrete type\nconst NumberBox = Box(TNumber)\nNumberBox.check({ value: 42 }) // true\nNumberBox.check({ value: 'nope' }) // false\n```\n\n---\n\n## Runtime Metadata\n\nEvery TJS function carries its type information at runtime via `__tjs`:\n\n```javascript\nfunction createUser(input: { name: '', age: 0 }): { id: 0 } {\n return { id: 123 }\n}\n\ncreateUser.__tjs\n// {\n// params: { input: { type: { kind: 'object', shape: {...} }, required: true } },\n// returns: { type: { kind: 'object', shape: { id: { kind: 'number' } } } }\n// }\n```\n\nThis metadata enables autocomplete from live objects, automatic documentation\ngeneration, and runtime reflection -- things that require build tools and\nexternal libraries in vanilla JavaScript.\n\n---\n\n## Safety Levels\n\nYou control how much validation TJS applies:\n\n```javascript\n// Per-module (top of file)\nsafety none // Metadata only -- no runtime checks (fastest)\nsafety inputs // Validate inputs only (default)\nsafety all // Validate everything (debug mode)\n\n// Per-function\nfunction fastPath(! x: 0) { /* ... */ } // Skip validation\nfunction safePath(? x: 0) { /* ... */ } // Force validation\n```\n\nUse `!` (skip validation) only in hot loops where every microsecond counts\nand the data source is already trusted. In all other cases, the ~1.5x\noverhead of `safety inputs` is negligible compared to the bugs it catches.\n\n### Unsafe Blocks\n\nSkip validation for a hot inner loop:\n\n```javascript\nunsafe {\n for (let i = 0; i < million; i++) {\n hotFunction(data[i])\n }\n}\n```\n\n---\n\n## Inline Tests\n\nTests live next to the code they test and run at transpile time:\n\n```javascript\nfunction isPrime(n: 2): true {\n if (n < 2) return false\n for (let i = 2; i * i <= n; i++) {\n if (n % i === 0) return false\n }\n return true\n}\n\ntest 'prime detection' {\n expect(isPrime(2)).toBe(true)\n expect(isPrime(4)).toBe(false)\n expect(isPrime(17)).toBe(true)\n}\n```\n\nTests are stripped from production output. They're also generated\nautomatically from return type annotations -- `: true` means TJS will call\n`isPrime(2)` and verify it returns a boolean.\n\n---\n\n## WASM Blocks\n\nFor compute-heavy code, drop into WebAssembly:\n\n```javascript\nconst add = wasm (a: i32, b: i32): i32 {\n local.get $a\n local.get $b\n i32.add\n}\n\nadd(1, 2) // 3, runs as native WASM\n```\n\nWASM is compiled at transpile time and embedded as base64 in the output.\nNo separate `.wasm` files.\n\n---\n\n## Safe Eval\n\nThe killer feature for many use cases. Run untrusted code safely:\n\n```javascript\nimport { Eval, SafeFunction } from 'tjs-lang/eval'\n\n// One-shot evaluation\nconst { result } = await Eval({\n code: 'return items.filter(x => x > threshold)',\n context: { items: [1, 5, 10, 15], threshold: 7 },\n fuel: 1000,\n})\n// result: [10, 15]\n\n// Reusable safe function\nconst transform = await SafeFunction({\n body: 'return x * multiplier',\n params: ['x'],\n fuel: 500,\n})\n\nawait transform(21) // { result: 42, fuelUsed: 8 }\n```\n\n- Gas-limited (fuel runs out = execution stops, no infinite loops)\n- Capability-based (no I/O unless you grant it)\n- No `eval()`, no CSP violations, no containers\n\n---\n\n## What You Give Up\n\nTJS is opinionated. Here's what changes:\n\n| JavaScript | TJS | Why |\n| -------------------------- | ----------------------------- | ------------------------------- |\n| `==` (type coercion) | `==` (structural equality) | Coercion is a bug factory |\n| Exceptions for type bugs | Monadic error values | Exceptions escape, values don't |\n| `new ClassName()` | `ClassName()` preferred | Cleaner, more functional |\n| No runtime types | `__tjs` metadata on functions | Types should exist at runtime |\n| `typeof null === 'object'` | `typeOf(null) === 'null'` | JS got this wrong in 1995 |\n\nNothing is taken away. `new` still works. `===` still works. You can write\nplain JavaScript in a `.tjs` file and it works. The type-related additions\nuse explicit syntax (`:` annotations, `:` return types, `Type` declarations).\nBehavioral modes like structural equality, callable classes, and honest\n`typeof` are enabled by default in native TJS files. Use `TjsCompat` at the\ntop of a file to disable all modes for gradual migration or JS interop.\n\n---\n\n## Getting Started\n\n```bash\nnpm install tjs-lang\n```\n\n### Try It in the Browser\n\nThe playground runs the full TJS compiler in your browser -- no backend needed:\n\n**[tjs-platform.web.app](https://tjs-platform.web.app)**\n\n### CLI\n\n```bash\nbun src/cli/tjs.ts check file.tjs # Parse and type check\nbun src/cli/tjs.ts run file.tjs # Transpile and execute\nbun src/cli/tjs.ts emit file.tjs # Output transpiled JS\nbun src/cli/tjs.ts test file.tjs # Run inline tests\n```\n\n### From Code\n\n```javascript\nimport { tjs } from 'tjs-lang'\n\nconst output = tjs`\n function add(a: 0, b: 0): 0 {\n return a + b\n }\n`\n```\n\n---\n\n## Runtime Traceability\n\nEvery TJS function carries its source identity in `__tjs` metadata.\nWhen validation fails, the error tells you exactly which function and\nparameter failed, in which source file:\n\n```javascript\nfunction add(a: 0, b: 0): 0 { return a + b }\n\nadd.__tjs.source // \"mymodule.tjs:3\"\nadd('oops', 1) // MonadicError { path: \"mymodule.tjs:3:add.a\", expected: \"integer\", actual: \"string\" }\n```\n\nNo source maps. No build artifacts. The function _knows where it came from_.\n\n---\n\n## Learn More\n\n- [TJS Language Reference](DOCS-TJS.md) -- Full syntax and features\n- [AJS Agent Language](DOCS-AJS.md) -- The sandboxed agent VM\n- [TJS for TypeScript Programmers](TJS-FOR-TS.md) -- Coming from TypeScript?\n- [Playground](https://tjs-platform.web.app) -- Try it live\n"
|
|
1610
|
+
"text": "<!--{\"section\": \"tjs-for-js\", \"group\": \"docs\", \"order\": 0, \"navTitle\": \"TJS for JS Devs\"}-->\n\n# TJS for JavaScript Programmers\n\n_Everything you already know, plus the things you always wished JavaScript had._\n\n---\n\n## The One-Sentence Pitch\n\nTJS is JavaScript with types that actually exist at runtime, equality that actually works, and errors that don't crash your program.\n\n---\n\n## What Stays the Same\n\nTJS is a superset of JavaScript. All of this works exactly as you expect:\n\n```javascript\nconst x = 42\nlet name = 'Alice'\nconst items = [1, 2, 3].map((n) => n * 2)\nconst user = { name: 'Bob', age: 30 }\nconst { name: userName, age } = user\nconst merged = { ...defaults, ...overrides }\nconst greeting = `Hello, ${name}!`\n\nfor (const item of items) {\n console.log(item)\n}\nwhile (condition) {\n /* ... */\n}\nif (x > 0) {\n /* ... */\n} else {\n /* ... */\n}\n\ntry {\n riskyThing()\n} catch (e) {\n handleError(e)\n}\n\nclass Dog {\n #name\n constructor(name) {\n this.#name = name\n }\n get name() {\n return this.#name\n }\n}\n\nimport { something } from './module.js'\nexport function myFunction() {\n /* ... */\n}\n```\n\nIf you don't use any TJS features, your code is just JavaScript. No lock-in.\n\nThis extends to advanced patterns too: **Proxies**, **WeakMap/WeakSet**, **Symbols**, **generators**, **async iterators**, **`Object.defineProperty`** — all work identically. TJS adds type checks at function boundaries; it doesn't wrap or intercept any JS runtime behavior.\n\n---\n\n## What's Different\n\n### 1. Types Are Example Values\n\nIn JavaScript, there are no types. In TJS, you annotate parameters with\nan example of a valid value:\n\n```javascript\n// JavaScript\nfunction greet(name) {\n return `Hello, ${name}!`\n}\n\n// TJS - name is required and must be a string (like 'World')\nfunction greet(name: 'World'): '' {\n return `Hello, ${name}!`\n}\n```\n\nThe `: 'World'` means \"required, must be a string, here's an example.\" The `: ''` means \"returns a string.\" These aren't abstract type annotations -- they're concrete values the system can use for testing, documentation, and validation.\n\n| You Write | TJS Infers |\n| ----------------- | ----------------------------- |\n| `name: 'Alice'` | Required string |\n| `count: 42` | Required integer |\n| `rate: 3.14` | Required number (float) |\n| `age: +20` | Required non-negative integer |\n| `flag: true` | Required boolean |\n| `items: [0]` | Required integer[] |\n| `values: [0.0]` | Required number[] |\n| `user: { n: '' }` | Required object |\n| `id: 0 \\|\\| null` | integer or null |\n\nAll of these are valid JavaScript expressions. `42` vs `42.0` vs `+42` are\nall legal JS -- TJS leverages this to distinguish integer, float, and\nnon-negative integer types at the syntax level.\n\nNote: `|| null` means the value accepts the base type _or_ `null`, but not\n`undefined`. TJS treats `null` and `undefined` as distinct types\n(`typeOf(null) === 'null'`, `typeOf(undefined) === 'undefined'`).\n\n**Optional** parameters use `=` (just like JS default values):\n\n```javascript\nfunction greet(name = 'World') {\n /* ... */\n} // optional, defaults to 'World'\nfunction retry(count = 3) {\n /* ... */\n} // optional, defaults to 3 (integer)\n```\n\n**The difference:** `: value` means required. `= value` means optional with a default. In plain JS, both would be written as `= value`.\n\n### 2. Numeric Type Narrowing\n\nTJS distinguishes three numeric types using valid JavaScript syntax:\n\n```javascript\nfunction process(\n rate: 3.14, // number (float) -- has a decimal point\n count: 42, // integer -- whole number, no decimal\n index: +0 // non-negative integer -- prefixed with +\n) { /* ... */ }\n```\n\n| You Write | Type Inferred | Validates |\n| --------- | ---------------------- | ------------------------------- |\n| `3.14` | `number` (float) | Any number |\n| `0.0` | `number` (float) | Any number |\n| `42` | `integer` | `Number.isInteger(x)` |\n| `0` | `integer` | `Number.isInteger(x)` |\n| `+20` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n| `+0` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n| `-5` | `integer` | `Number.isInteger(x)` |\n| `-3.5` | `number` (float) | Any number |\n\nThese are all valid JavaScript expressions -- TJS just reads the syntax more\ncarefully than JS does. At runtime, passing `3.14` to a parameter typed as\ninteger returns a monadic error.\n\n### 3. Return Type Annotations\n\nTJS uses `:` for return types (same as TypeScript):\n\n```javascript\n// Returns an integer\nfunction add(a: 0, b: 0): 0 {\n return a + b\n}\n\n// Returns an object with specific shape\nfunction getUser(id: 0): { name: '', age: 0 } {\n return { name: 'Alice', age: 30 }\n}\n```\n\nThe return type example doubles as an automatic test. When you write `: 0`, TJS will call `add(0, 0)` at transpile time and verify the result is a number.\n\n### 4. Equality That Works\n\nJavaScript's `==` is notoriously broken (type coercion). Its `===` doesn't do structural comparison. TJS fixes this:\n\n```javascript\n// JavaScript\n[1, 2] === [1, 2] // false (different references)\n{ a: 1 } === { a: 1 } // false (different references)\n\n// TJS (structural equality is on by default)\n[1, 2] == [1, 2] // true (same structure)\n{ a: 1 } == { a: 1 } // true (same structure)\n[1, 2] === [1, 2] // false (different references, identity check)\n```\n\nTJS redefines the operators:\n\n| Operator | JavaScript | TJS |\n| -------- | ------------------- | --------------------- |\n| `==` | Coercive equality | Structural equality |\n| `!=` | Coercive inequality | Structural inequality |\n| `===` | Strict equality | Identity (same ref) |\n| `!==` | Strict inequality | Not same reference |\n\nYou can also use the explicit forms `Is` and `IsNot`:\n\n```javascript\nuser Is expectedUser // structural deep equality\nresult IsNot errorValue // structural deep inequality\n```\n\nClasses can define custom equality:\n\n```javascript\nclass Point {\n constructor(x: 0, y: 0) { this.x = x; this.y = y }\n Equals(other) { return this.x === other.x && this.y === other.y }\n}\n\nPoint(1, 2) Is Point(1, 2) // true, uses .Equals\n```\n\nNote: structural equality does not handle circular references. Use `===`\nfor objects that might be circular, or define an `.Equals` method.\n\n### 5. Errors Are Values, Not Exceptions\n\nIn JavaScript, type errors crash your program. In TJS, they're values:\n\n```javascript\nfunction double(x: 0): 0 {\n return x * 2\n}\n\ndouble(5) // 10\ndouble('oops') // { $error: true, message: \"Expected number for 'double.x', got string\" }\n```\n\nNo `try`/`catch`. No crashes. The caller gets a clear error value they can inspect:\n\n```javascript\nconst result = double(input)\nif (result?.$error) {\n console.log(result.message) // handle gracefully\n} else {\n useResult(result)\n}\n```\n\nErrors propagate automatically through function calls. If you pass a monadic error to another TJS function, it passes through without executing:\n\n```javascript\nconst a = step1(badInput) // MonadicError\nconst b = step2(a) // skips execution, returns the same error\nconst c = step3(b) // skips again -- error flows to the surface\n```\n\nThis is sometimes called \"railway-oriented programming.\"\n\n### 6. Classes Without `new`\n\nTJS classes are callable as functions:\n\n```javascript\n// JavaScript\nconst p = new Point(10, 20)\n\n// TJS - both work, but the clean form is preferred\nconst p1 = Point(10, 20) // works\nconst p2 = new Point(10, 20) // also works (linter warns)\n```\n\nThis is cleaner and more consistent with functional style. Under the hood, TJS uses a Proxy to auto-construct when you call a class as a function.\n\n### 7. `const` for Free\n\nUppercase identifiers automatically get `const`:\n\n```javascript\n// TJS\nConfig = { debug: true, version: '1.0' }\nMaxRetries = 3\n\n// Transpiles to\nconst Config = { debug: true, version: '1.0' }\nconst MaxRetries = 3\n```\n\nLowercase identifiers behave normally.\n\n---\n\n## The Type System\n\nJavaScript has no type system. Libraries like Zod and Ajv bolt one on,\nbut they're separate from your function signatures -- a second source of\ntruth that drifts. TJS's `Type()` built-in gives you as much narrowing\nand specificity as you want, in one place, using plain functions you\nalready know how to write.\n\n### From Simple to Sophisticated\n\n```javascript\n// Simple -- infer type from an example value\nType Name 'Alice' // any string\n\n// Descriptive -- add documentation\nType User {\n description: 'a registered user'\n example: { name: '', age: 0, email: '' }\n}\n\n// Constrained -- add a predicate for narrowing\nType PositiveNumber {\n description: 'a number greater than zero'\n example: 1\n predicate(x) { return typeof x === 'number' && x > 0 }\n}\n\n// Domain-specific -- real business rules\nType USZipCode {\n description: '5-digit US zip code'\n example: '90210'\n predicate(v) { return typeof v === 'string' && /^\\d{5}$/.test(v) }\n}\n\nType Email {\n description: 'email address'\n example: 'user@example.com'\n predicate(v) { return typeof v === 'string' && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(v) }\n}\n```\n\nThe key insight: a `predicate` is just a function that returns `true` or\n`false`. If you can express a constraint as a boolean check, you can make\nit a type. No schema DSL to learn, no special syntax -- it's JavaScript\nall the way down.\n\nTypes work in function signatures:\n\n```javascript\nfunction sendWelcome(email: Email, name: Name): '' {\n return `Welcome, ${name}! Confirmation sent to ${email}.`\n}\n\nsendWelcome('alice@example.com', 'Alice') // works\nsendWelcome('not-an-email', 'Alice') // MonadicError\n```\n\nTJS also ships common types out of the box: `TString`, `TNumber`,\n`TBoolean`, `TInteger`, `TPositiveInt`, `TNonEmptyString`, `TEmail`,\n`TUrl`, `TUuid`, `Timestamp`, `LegalDate`. No imports from a validation\nlibrary needed.\n\n### Combinators\n\nCompose types from other types:\n\n```javascript\nType OptionalEmail Nullable(Email) // Email | null\nType UserIds TArray(TPositiveInt) // array of positive integers\n```\n\n### Unions and Enums\n\n```javascript\n// Union of string literals\nUnion Status 'task status' 'pending' | 'active' | 'done'\n\n// Enum with named members\nEnum Color 'CSS color' {\n Red = 'red'\n Green = 'green'\n Blue = 'blue'\n}\n\n// Discriminated union -- like tagged unions or sum types\nconst Shape = Union('kind', {\n circle: { radius: 0 },\n rectangle: { width: 0, height: 0 }\n})\n```\n\n### Generics\n\nRuntime-checkable generic types:\n\n```javascript\nGeneric Box<T> {\n description: 'a boxed value'\n predicate(x, T) {\n return typeof x === 'object' && x !== null && 'value' in x && T(x.value)\n }\n}\n\n// Instantiate with a concrete type\nconst NumberBox = Box(TNumber)\nNumberBox.check({ value: 42 }) // true\nNumberBox.check({ value: 'nope' }) // false\n```\n\n---\n\n## Runtime Metadata\n\nEvery TJS function carries its type information at runtime via `__tjs`:\n\n```javascript\nfunction createUser(input: { name: '', age: 0 }): { id: 0 } {\n return { id: 123 }\n}\n\ncreateUser.__tjs\n// {\n// params: { input: { type: { kind: 'object', shape: {...} }, required: true } },\n// returns: { type: { kind: 'object', shape: { id: { kind: 'number' } } } }\n// }\n```\n\nThis metadata enables autocomplete from live objects, automatic documentation\ngeneration, and runtime reflection -- things that require build tools and\nexternal libraries in vanilla JavaScript.\n\n---\n\n## Safety Levels\n\nYou control how much validation TJS applies:\n\n```javascript\n// Per-module (top of file)\nsafety none // Metadata only -- no runtime checks (fastest)\nsafety inputs // Validate inputs only (default)\nsafety all // Validate everything (debug mode)\n\n// Per-function\nfunction fastPath(! x: 0) { /* ... */ } // Skip validation\nfunction safePath(? x: 0) { /* ... */ } // Force validation\n```\n\nUse `!` (skip validation) only in hot loops where every microsecond counts\nand the data source is already trusted. In all other cases, the ~1.5x\noverhead of `safety inputs` is negligible compared to the bugs it catches.\n\n### Unsafe Blocks\n\nSkip validation for a hot inner loop:\n\n```javascript\nunsafe {\n for (let i = 0; i < million; i++) {\n hotFunction(data[i])\n }\n}\n```\n\n---\n\n## Inline Tests\n\nTests live next to the code they test and run at transpile time:\n\n```javascript\nfunction isPrime(n: 2): true {\n if (n < 2) return false\n for (let i = 2; i * i <= n; i++) {\n if (n % i === 0) return false\n }\n return true\n}\n\ntest 'prime detection' {\n expect(isPrime(2)).toBe(true)\n expect(isPrime(4)).toBe(false)\n expect(isPrime(17)).toBe(true)\n}\n```\n\nTests are stripped from production output. They're also generated\nautomatically from return type annotations -- `: true` means TJS will call\n`isPrime(2)` and verify it returns a boolean.\n\n---\n\n## WASM Blocks\n\nFor compute-heavy code, drop into WebAssembly:\n\n```javascript\nconst add = wasm (a: i32, b: i32): i32 {\n local.get $a\n local.get $b\n i32.add\n}\n\nadd(1, 2) // 3, runs as native WASM\n```\n\nWASM is compiled at transpile time and embedded as base64 in the output.\nNo separate `.wasm` files.\n\n---\n\n## Safe Eval\n\nThe killer feature for many use cases. Run untrusted code safely:\n\n```javascript\nimport { Eval, SafeFunction } from 'tjs-lang/eval'\n\n// One-shot evaluation\nconst { result } = await Eval({\n code: 'return items.filter(x => x > threshold)',\n context: { items: [1, 5, 10, 15], threshold: 7 },\n fuel: 1000,\n})\n// result: [10, 15]\n\n// Reusable safe function\nconst transform = await SafeFunction({\n body: 'return x * multiplier',\n params: ['x'],\n fuel: 500,\n})\n\nawait transform(21) // { result: 42, fuelUsed: 8 }\n```\n\n- Gas-limited (fuel runs out = execution stops, no infinite loops)\n- Capability-based (no I/O unless you grant it)\n- No `eval()`, no CSP violations, no containers\n\n---\n\n## What You Give Up\n\nTJS is opinionated. Here's what changes:\n\n| JavaScript | TJS | Why |\n| -------------------------- | ----------------------------- | ------------------------------- |\n| `==` (type coercion) | `==` (structural equality) | Coercion is a bug factory |\n| Exceptions for type bugs | Monadic error values | Exceptions escape, values don't |\n| `new ClassName()` | `ClassName()` preferred | Cleaner, more functional |\n| No runtime types | `__tjs` metadata on functions | Types should exist at runtime |\n| `typeof null === 'object'` | `typeOf(null) === 'null'` | JS got this wrong in 1995 |\n\nNothing is taken away. `new` still works. `===` still works. You can write\nplain JavaScript in a `.tjs` file and it works. The type-related additions\nuse explicit syntax (`:` annotations, `:` return types, `Type` declarations).\nBehavioral modes like structural equality, callable classes, and honest\n`typeof` are enabled by default in native TJS files. Use `TjsCompat` at the\ntop of a file to disable all modes for gradual migration or JS interop.\n\n---\n\n## Getting Started\n\n```bash\nnpm install tjs-lang\n```\n\n### Try It in the Browser\n\nThe playground runs the full TJS compiler in your browser -- no backend needed:\n\n**[tjs-platform.web.app](https://tjs-platform.web.app)**\n\n### CLI\n\n```bash\nbun src/cli/tjs.ts check file.tjs # Parse and type check\nbun src/cli/tjs.ts run file.tjs # Transpile and execute\nbun src/cli/tjs.ts emit file.tjs # Output transpiled JS\nbun src/cli/tjs.ts test file.tjs # Run inline tests\n```\n\n### From Code\n\n```javascript\nimport { tjs } from 'tjs-lang'\n\nconst output = tjs`\n function add(a: 0, b: 0): 0 {\n return a + b\n }\n`\n```\n\n---\n\n## Runtime Traceability\n\nEvery TJS function carries its source identity in `__tjs` metadata.\nWhen validation fails, the error tells you exactly which function and\nparameter failed, in which source file:\n\n```javascript\nfunction add(a: 0, b: 0): 0 {\n return a + b\n}\n\nadd.__tjs.source // \"mymodule.tjs:3\"\nadd('oops', 1) // MonadicError { path: \"mymodule.tjs:3:add.a\", expected: \"integer\", actual: \"string\" }\n```\n\nNo source maps. No build artifacts. The function _knows where it came from_.\n\n---\n\n## Learn More\n\n- [TJS Language Reference](DOCS-TJS.md) -- Full syntax and features\n- [AJS Agent Language](DOCS-AJS.md) -- The sandboxed agent VM\n- [TJS for TypeScript Programmers](TJS-FOR-TS.md) -- Coming from TypeScript?\n- [Playground](https://tjs-platform.web.app) -- Try it live\n"
|
|
1611
1611
|
},
|
|
1612
1612
|
{
|
|
1613
1613
|
"title": "TJS for TypeScript Programmers",
|
|
@@ -1617,6 +1617,6 @@
|
|
|
1617
1617
|
"group": "docs",
|
|
1618
1618
|
"order": 0,
|
|
1619
1619
|
"navTitle": "TJS for TS Devs",
|
|
1620
|
-
"text": "<!--{\"section\": \"tjs-for-ts\", \"group\": \"docs\", \"order\": 0, \"navTitle\": \"TJS for TS Devs\"}-->\n\n# TJS for TypeScript Programmers\n\n_What if your types didn't disappear at runtime?_\n\n---\n\nTypeScript is great. It catches bugs at compile time, makes refactoring safer,\nand gives you autocomplete. But it has a fundamental limitation: types are\nfiction. They exist only in your editor, and they vanish completely at runtime.\n\nTJS starts from a different premise: **types are example values that survive\nto runtime**. This gives you everything TypeScript gives you, plus runtime\nvalidation, reflection, documentation, inline tests of private methods, and traceability of errors back to source code -- from a single source of truth.\n\nThis guide is split into two paths:\n\n1. **[Using TJS from TypeScript](#part-1-using-tjs-from-typescript)** -- Keep your TS codebase, use TJS for safe eval and agent execution\n2. **[Migrating to TJS](#part-2-migrating-to-tjs)** -- Convert your codebase from TypeScript to TJS\n\n---\n\n# Part 1: Using TJS from TypeScript\n\nYou don't have to rewrite anything. TJS provides tools you can use directly\nfrom your TypeScript codebase.\n\n## Safe Eval\n\nThe most common reason to reach for TJS from TypeScript: running untrusted\ncode safely.\n\n```typescript\nimport { Eval, SafeFunction } from 'tjs-lang/eval'\n\n// Run user-provided code with a gas limit\nconst { result, fuelUsed } = await Eval({\n code: userCode,\n context: { items: data, threshold: 10 },\n fuel: 1000,\n capabilities: {\n fetch: sandboxedFetch, // your whitelist-wrapped fetch\n },\n})\n\n// Or create a reusable safe function\nconst transform = await SafeFunction({\n body: 'return items.filter(x => x.price < budget)',\n params: ['items', 'budget'],\n fuel: 500,\n})\n\nconst { result } = await transform(products, 100)\n```\n\nNo `eval()`. No CSP violations. No Docker containers. The code runs in a\nfuel-metered sandbox with only the capabilities you inject.\n\n## Agent VM\n\nBuild and execute JSON-serializable agents:\n\n```typescript\nimport { ajs, AgentVM } from 'tjs-lang'\n\n// Parse agent source to JSON AST\nconst agent = ajs`\n function analyze({ data, query }) {\n let filtered = data.filter(x => x.score > 0.5)\n let summary = llmPredict({\n prompt: 'Summarize findings for: ' + query,\n data: filtered\n })\n return { query, summary, count: filtered.length }\n }\n`\n\n// Execute with resource limits\nconst vm = new AgentVM()\nconst { result } = await vm.run(\n agent,\n { data, query },\n {\n fuel: 1000,\n timeoutMs: 5000,\n capabilities: { fetch: myFetch, llm: myLlm },\n }\n)\n```\n\nThe agent AST is JSON. You can store it in a database, send it over the\nnetwork, version it, diff it, audit it.\n\n## Type-Safe Builder\n\nConstruct agents programmatically with full TypeScript support:\n\n```typescript\nimport { Agent, AgentVM, s } from 'tjs-lang'\n\nconst pipeline = Agent.take(s.object({ url: s.string, maxResults: s.number }))\n .httpFetch({ url: { $kind: 'arg', path: 'url' } })\n .as('response')\n .varSet({\n key: 'results',\n value: {\n $expr: 'member',\n object: { $expr: 'ident', name: 'response' },\n property: 'items',\n },\n })\n .return(s.object({ results: s.array(s.any) }))\n\nconst vm = new AgentVM()\nconst { result } = await vm.run(\n pipeline.toJSON(),\n { url, maxResults: 10 },\n {\n fuel: 500,\n capabilities: { fetch },\n }\n)\n```\n\n## TypeScript Entry Points\n\nTJS is tree-shakeable. Import only what you need:\n\n```typescript\nimport { Agent, AgentVM, ajs, tjs } from 'tjs-lang' // Everything\nimport { Eval, SafeFunction } from 'tjs-lang/eval' // Safe eval only\nimport { tjs, transpile } from 'tjs-lang/lang' // Language tools\nimport { fromTS } from 'tjs-lang/lang/from-ts' // TS -> TJS converter\n```\n\n## When to Stay in TypeScript\n\nIf your codebase is TypeScript and you're happy with it, you probably only\nneed TJS for:\n\n- Running user-provided or LLM-generated code safely\n- Building agents that travel over the network\n- Adding runtime type validation at system boundaries\n- Eval without `eval()`\n\nYou don't need to migrate anything. The libraries work from TypeScript.\n\n---\n\n# Part 2: Migrating to TJS\n\nIf you want the full TJS experience -- runtime types, structural equality,\nmonadic errors, inline tests -- here's how to convert.\n\n## The Core Idea: Types as Examples\n\nTypeScript describes types abstractly. TJS describes them concretely:\n\n```typescript\n// TypeScript: what TYPE is this?\nfunction greet(name: string): string { ... }\n\n// TJS: what's an EXAMPLE of this?\nfunction greet(name: 'World'): '' { ... }\n```\n\n`'World'` tells TJS: this is a string, it's required, and here's a valid\nexample. The example doubles as documentation and test data.\n\n## Conversion Reference\n\n### Primitives\n\n```typescript\n// TypeScript // TJS\nname: string name: ''\ncount: number count: 0.0 // float (any number)\nindex: number index: 0 // integer\nage: number age: +0 // non-negative integer\nflag: boolean flag: true\nitems: string[] items: ['']\nnested: number[][] nested: [[0]]\n```\n\n**Important:** The example value determines the _type_, not a literal\nconstraint. `name: 'World'` means \"required string\" -- not \"must be the\nstring `'World'`.\" Any string passes validation. The example is there for\ndocumentation, testing, and type inference. Think of it as `string` with\na built-in `@example` tag.\n\n**Numeric precision:** TJS distinguishes three numeric types using valid\nJavaScript syntax that JS itself ignores:\n\n| You Write | TJS Type | Runtime Check |\n| --------- | ---------------------- | ------------------------------- |\n| `3.14` | `number` (float) | Any number |\n| `0.0` | `number` (float) | Any number |\n| `42` | `integer` | `Number.isInteger(x)` |\n| `0` | `integer` | `Number.isInteger(x)` |\n| `+20` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n| `+0` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n\nTypeScript's `number` is a single type. TJS gives you three levels of\nprecision -- all using expressions that are already legal JavaScript.\nThe automatic converter maps TypeScript `number` to `0.0` (float) to\npreserve the widest behavior; you can then narrow manually to `0` (integer)\nor `+0` (non-negative integer) where appropriate.\n\n### Optional Parameters\n\n```typescript\n// TypeScript // TJS\nfunction f(x?: string) {}\nfunction f(x = '') {}\nfunction f(x: string = 'hi') {}\nfunction f(x = 'hi') {}\n```\n\nIn TypeScript, `?` means optional with type `string | undefined`.\nIn TJS, `= value` means optional with that default. Same semantics, less syntax.\n\n### Object Shapes\n\n```typescript\n// TypeScript\nfunction createUser(opts: { name: string; age: number; email?: string }) {}\n\n// TJS\nfunction createUser(opts: { name: '', age: 0, email = '' }) {}\n```\n\nRequired properties use `:`, optional ones use `=`.\n\n### Return Types\n\n```typescript\n// TypeScript // TJS\nfunction add(a: number, b: number): number function add(a: 0, b: 0): 0\nfunction getUser(): { name: string } function getUser(): { name: '' }\nfunction fetchData(): Promise<string[]> function fetchData(): ['']\n```\n\nThe `fromTS` converter unwraps `Promise<T>` in return type annotations --\nyou annotate the resolved type, not the wrapper. This only applies when\nconverting from TypeScript; in native TJS you just write normal\n`async`/`await` and annotate what the function resolves to.\n\nThe return annotation also generates an automatic test: `add(0, 0)` must\nreturn a number. If it doesn't, you get an error at transpile time.\n\n### Interfaces and Type Aliases\n\n```typescript\n// TypeScript\ninterface User {\n name: string\n age: number\n email?: string\n}\n\ntype Status = 'active' | 'inactive' | 'banned'\n\n// TJS\nType User {\n description: 'a registered user'\n example: { name: '', age: 0, email = '' }\n}\n\nUnion Status 'account status' 'active' | 'inactive' | 'banned'\n```\n\n### Enums\n\n```typescript\n// TypeScript\nenum Color {\n Red = 'red',\n Green = 'green',\n Blue = 'blue',\n}\n\n// TJS\nEnum Color 'CSS color' {\n Red = 'red'\n Green = 'green'\n Blue = 'blue'\n}\n```\n\n### Classes\n\n```typescript\n// TypeScript\nclass Point {\n private x: number\n private y: number\n\n constructor(x: number, y: number) {\n this.x = x\n this.y = y\n }\n\n distanceTo(other: Point): number {\n return Math.sqrt((this.x - other.x) ** 2 + (this.y - other.y) ** 2)\n }\n}\n\nconst p = new Point(10, 20)\n\n// TJS\nclass Point {\n #x\n #y\n\n constructor(x: 0, y: 0) {\n this.#x = x\n this.#y = y\n }\n\n distanceTo(other: Point): 0 {\n return Math.sqrt((this.#x - other.#x) ** 2 + (this.#y - other.#y) ** 2)\n }\n}\n\nconst p = Point(10, 20) // no 'new' needed\n```\n\nKey differences:\n\n- `private` is stripped by default (TS `private` is compile-time only).\n With `TjsClass` (on by default in native TJS, add via `/* @tjs TjsClass */` for TS-originated code), `private` converts to `#` (true JS runtime privacy).\n- Type annotations become example values\n- With `TjsClass`, `new` is optional (linter warns against it)\n\n### Generics\n\nTJS takes a different approach to generics. TypeScript has function-level\ntype parameters (`<T>`) that vanish at runtime. TJS has `Generic` declarations\nthat produce runtime-checkable type constructors:\n\n```typescript\n// TypeScript -- compile-time only, gone at runtime\nfunction identity<T>(x: T): T { return x }\ninterface Box<T> { value: T }\n\n// TJS -- no function-level generics; use Generic for container types\nGeneric Box<T> {\n description: 'a boxed value'\n predicate(x, T) {\n return typeof x === 'object' && x !== null && 'value' in x && T(x.value)\n }\n}\n\n// Usage: Box(Number) is a runtime type checker\nconst isNumberBox = Box(Number)\nisNumberBox({ value: 42 }) // true\nisNumberBox({ value: 'nope' }) // false\n```\n\nFor simple generic functions like `identity` or `first`, you don't need\ngenerics at all -- just skip the type parameter. TJS validates the\nconcrete types at call sites, not the abstract relationship between them.\n\n```javascript\n// Simple -- no generics needed, the function just works\nfunction first(arr: [0]) { return arr[0] }\n\n// If you need runtime-checked containers, use Generic\nGeneric Pair<T, U> {\n description: 'a typed pair'\n predicate(x, T, U) { return T(x[0]) && U(x[1]) }\n}\n```\n\nWhen converting from TypeScript, the `fromTS` converter preserves generic\nmetadata but types become `any`. This is a place where manual review helps.\n\n### Nullability\n\n```typescript\n// TypeScript\nfunction find(id: number): User | null { ... }\n\n// TJS\nfunction find(id: 0): { name: '', age: 0 } || null { ... }\n```\n\nTJS distinguishes `null` from `undefined` -- they're different types, just\nas `typeOf(null)` returns `'null'` and `typeOf(undefined)` returns\n`'undefined'`. Writing `|| null` means the value can be the base type or\n`null`, but not `undefined`. Optional parameters (using `=`) accept\n`undefined` because that's what you get when the caller omits the argument.\n\n## What TypeScript Has That TJS Doesn't\n\nTJS intentionally skips TypeScript features that don't survive to runtime\nor add complexity without proportional value:\n\n| TypeScript Feature | TJS Equivalent |\n| --------------------------- | ------------------------------------------- |\n| `interface` | `Type` with example |\n| `type` aliases | `Type`, `Union`, or `Enum` |\n| Conditional types | Preserved in `.d.ts` via declaration blocks |\n| Mapped types | Preserved in `.d.ts` via declaration blocks |\n| `keyof`, `typeof` | Use runtime `Object.keys()`, `typeOf()` |\n| `Partial<T>`, `Pick<T>` | Define the shape you need directly |\n| Declaration files (`.d.ts`) | Generated from `__tjs` metadata (`--dts`) |\n| `as` type assertions | Not needed (values are checked) |\n| `any` escape hatch | `safety none` per-module or `!` per-fn |\n| Decorators | Not supported |\n| `namespace` | Use modules |\n\nThe philosophy: if a type feature doesn't do something at runtime, it's\ncomplexity without payoff.\n\n### But What About Narrowing?\n\nTypeScript's type system is Turing-complete. You can express astonishing\nconstraints -- `Pick<Omit<T, K>, Extract<keyof T, string>>` -- but the\nresulting types are often harder to understand than the code they describe.\nAnd they vanish at runtime, so they can't protect you from bad API data.\n\nTJS takes the opposite approach: `Type()` gives you a predicate function.\nIf you can write a boolean expression, you can define a type. No type-level\nprogramming language to learn.\n\n```javascript\n// TypeScript: branded types + manual validation\ntype Email = string & { __brand: 'email' }\nfunction isEmail(s: string): s is Email {\n return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(s)\n}\nfunction validateEmail(input: string): Email {\n if (!isEmail(input)) throw new Error('Invalid email')\n return input\n}\n\n// TJS: one line, works at runtime\nType Email {\n description: 'email address'\n example: 'user@example.com'\n predicate(v) { return typeof v === 'string' && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(v) }\n}\n```\n\nThe TJS `Type()` built-in handles everything from simple shapes to\nsophisticated domain constraints:\n\n```javascript\n// Simple -- infer from example\nType Name 'Alice' // string\n\n// Constrained -- predicate narrows beyond the base type\nType PositiveInt {\n description: 'a positive integer'\n example: 1\n predicate(v) { return typeof v === 'number' && Number.isInteger(v) && v > 0 }\n}\n\n// Domain-specific -- readable business rules\nType USZipCode {\n description: '5-digit US zip code'\n example: '90210'\n predicate(v) { return typeof v === 'string' && /^\\d{5}$/.test(v) }\n}\n\n// Combinators -- compose types\nType OptionalEmail Nullable(Email) // Email | null\n\n// Schema-based -- use tosijs-schema for structured validation\nType AgeRange {\n description: 'valid age'\n example: 25\n predicate(v) { return typeof v === 'number' && v >= 0 && v <= 150 }\n}\n```\n\nCompare the TypeScript equivalents:\n\n| What you want | TypeScript | TJS |\n| ------------------- | ----------------------------------------------------------- | -------------------------------------- |\n| String with format | Branded type + type guard + validation function | `Type Email { predicate(v) {...} }` |\n| Number in range | Branded type + manual check | `Type Age { predicate(v) {...} }` |\n| Non-empty string | Template literal type (compile-only) | `Type NonEmpty { predicate(v) {...} }` |\n| Nullable variant | `T \\| null` (compile-only) | `Nullable(MyType)` (runtime-checked) |\n| Union of literals | `'a' \\| 'b' \\| 'c'` (compile-only) | `Union Status 'a' \\| 'b' \\| 'c'` |\n| Discriminated union | `type Shape = { kind: 'circle' } \\| ...` + manual narrowing | `Union('kind', { circle: {...} })` |\n| Generic container | `interface Box<T>` (compile-only) | `Generic Box<T> { predicate(...) }` |\n\nEvery row in the TypeScript column is compile-time fiction that disappears\nwhen your code runs. Every row in the TJS column is a runtime check that\nactually catches bugs in production. And the TJS versions are shorter,\nbecause a predicate is just a function -- not a type-level program.\n\nTJS also ships common types out of the box: `TString`, `TNumber`,\n`TBoolean`, `TInteger`, `TPositiveInt`, `TNonEmptyString`, `TEmail`,\n`TUrl`, `TUuid`, `Timestamp`, `LegalDate`. No imports from a validation\nlibrary needed.\n\n### Tooling Comparison\n\n| Concern | TypeScript | TJS |\n| ------------------- | ---------------------------------- | ---------------------------------------------------------------------- |\n| **Type checking** | `tsc` (compile-time only) | Runtime validation (survives build) |\n| **Runtime schemas** | Zod / io-ts / Ajv (separate) | Built-in (types _are_ schemas) |\n| **Linting** | ESLint + plugins | Built-in linter (unused vars, unreachable code, no-explicit-new) |\n| **Testing** | Vitest / Jest (separate files) | Inline `test` blocks (transpile-time) |\n| **Equality** | Reference-based only | Honest `==` (no coercion), `Is`/`IsNot` (structural), `===` (identity) |\n| **Build toolchain** | tsc + bundler (webpack/Vite/etc) | Transpiles in-browser, no build step |\n| **Debugging** | Source maps (brittle, build bloat) | Functions carry source identity via `__tjs` metadata |\n| **Documentation** | JSDoc / TypeDoc (manual) | Generated from `__tjs` metadata |\n| **Editor support** | Mature (VSCode, etc) | Monaco/CodeMirror/Ace + VSCode/Cursor extensions |\n\n## What TJS Has That TypeScript Doesn't\n\n### Runtime Validation\n\nTypeScript:\n\n```typescript\n// Types are a promise. A lie, if the data comes from outside.\nfunction processOrder(order: Order) {\n // If order came from an API, nothing guarantees it matches Order.\n // You need Zod/io-ts/ajv AND the TypeScript type AND keep them in sync.\n}\n```\n\nTJS:\n\n```javascript\n// Types are checked at runtime. One source of truth.\nfunction processOrder(order: { items: [{ id: 0, qty: 0 }], total: 0 }): { status: '' } {\n // If order doesn't match, caller gets a MonadicError -- no crash.\n}\n```\n\n### Honest Equality\n\nTypeScript inherits JavaScript's broken equality. Native TJS fixes this by default. For TS-originated code, add the `TjsEquals` directive (or use `/* @tjs TjsEquals */` in the source `.ts` file):\n\n```javascript\nTjsEquals // needed for TS-originated code; native TJS has this on by default\n\n// == is honest: no coercion, unwraps boxed primitives\n0 == '' // false (JS: true!)\n[] == ![] // false (JS: true!)\nnew String('foo') == 'foo' // true (unwraps boxed)\nnull == undefined // true (useful pattern preserved)\ntypeof null // 'null' (JS: 'object')\n\n// == is fast: O(1) reference equality for objects/arrays\n{a: 1} == {a: 1} // false (different refs)\n[1, 2] == [1, 2] // false (different refs)\n\n// Is/IsNot for explicit deep structural comparison (O(n))\n{a: 1} Is {a: 1} // true\n[1, 2, 3] Is [1, 2, 3] // true\nnew Set([1,2]) Is new Set([2,1]) // true (Sets are order-independent)\n\n// === unchanged: identity check\nobj === obj // true (same reference)\n```\n\n`==` fixes coercion without the performance cost of deep comparison.\nUse `Is`/`IsNot` when you explicitly need structural comparison.\n\n### Monadic Errors\n\nTypeScript uses exceptions. TJS uses values:\n\n```javascript\n// TypeScript -- you have to remember to try/catch\nfunction divide(a: number, b: number): number {\n if (b === 0) throw new Error('Division by zero')\n return a / b\n}\n// Caller forgets try/catch? Crash.\n\n// TJS -- errors flow through the pipeline\nfunction divide(a: 0, b: 0): 0 {\n if (b === 0) return MonadicError('Division by zero')\n return a / b\n}\n// Caller gets an error value. No crash. Ever.\n```\n\n**How the caller handles it:**\n\n```javascript\n// Option 1: Check the result\nconst result = divide(10, 0)\nif (result instanceof Error) {\n console.log(result.message) // 'Division by zero'\n} else {\n useResult(result)\n}\n\n// Option 2: Just keep going -- errors propagate automatically\nconst a = divide(10, 0) // MonadicError\nconst b = double(a) // Receives error, returns it immediately (skips execution)\nconst c = format(b) // Same -- error flows through the whole chain\n// c is still the original MonadicError from divide()\n```\n\nIf you've used Rust's `Result<T, E>` or Haskell's `Either`, the pattern\nis familiar. The key difference from TypeScript: you never have to guess\nwhether a function might throw. Type errors and validation failures are\nalways values, never exceptions.\n\n### Inline Tests\n\n```javascript\nfunction fibonacci(n: 0): 0 {\n if (n <= 1) return n\n return fibonacci(n - 1) + fibonacci(n - 2)\n}\n\ntest 'fibonacci sequence' {\n expect(fibonacci(0)).toBe(0)\n expect(fibonacci(1)).toBe(1)\n expect(fibonacci(10)).toBe(55)\n}\n```\n\nTests run at transpile time. They're stripped from production output.\nNo separate test files, no test runner configuration.\n\n### Safety Controls\n\n```javascript\nsafety none // This module: skip all validation (performance)\nsafety inputs // This module: validate inputs only (default)\nsafety all // This module: validate everything (debug)\n\n// Per-function overrides\nfunction hot(! x: 0) {} // Skip validation even if module says 'inputs'\nfunction safe(? x: 0) {} // Force validation even if module says 'none'\n```\n\nUse `!` (skip validation) only in hot loops where every microsecond counts\nand the data source is already trusted. In all other cases, the ~1.5x\noverhead of `safety inputs` is negligible compared to the bugs it catches.\n\n#### Additional Safety Features\n\n```javascript\nTjsNoVar // var declarations are syntax errors\nconst! config = {} // Compile-time immutability (zero runtime cost)\n\n// Debug mode: make type errors visible\nimport { configure } from 'tjs-lang/lang'\nconfigure({ logTypeErrors: true }) // console.error on every type error\nconfigure({ throwTypeErrors: true }) // throw instead of returning MonadicError\n```\n\nTypeScript has no equivalent to most of these. You're either all-in on\ntypes or you use `as any` to escape.\n\n---\n\n## Automatic Conversion\n\nTJS includes a TypeScript-to-TJS converter that has been validated against\nthe tosijs production codebase (35 files, 523 tests passing):\n\n```bash\n# Convert a single file\nbun src/cli/tjs.ts convert input.ts --emit-tjs > output.tjs\n\n# Convert a directory (produces .js + .d.ts + .md per file)\nbun src/cli/tjs.ts convert src/ -o tjs-out/\n\n# Emit JavaScript directly\nbun src/cli/tjs.ts convert input.ts > output.js\n```\n\nFrom code:\n\n```typescript\nimport { fromTS } from 'tjs-lang/lang/from-ts'\n\nconst result = fromTS(tsSource, { emitTJS: true })\nconsole.log(result.code) // TJS source\n```\n\n### What `fromTS` Handles\n\n- Primitive annotations (`string`, `number`, `boolean`) → example values\n- Interfaces → `Type` declarations with declaration blocks for `.d.ts` round-tripping\n- Generic interfaces → `Generic` with predicates + declaration blocks\n- Conditional/mapped types → preserved verbatim in declaration blocks\n- Function type aliases → `FunctionPredicate` declarations (including generics)\n- String literal unions → `Union`\n- Enums → `Enum`\n- Rest parameters (`...args: T[]`) → preserved with `...` prefix\n- Nullable types (`T | null`) → proper null guards in runtime checks\n- Optional params, default values\n- `private` → stripped (or `#` with `TjsClass`)\n- Static getters/setters → `static` keyword preserved\n- `Promise<T>` → unwrapped return types\n- DOM types (130+) → `{}` (opaque object, keeps params annotated)\n- JSDoc comments → TDoc comments\n- Exported constants → type-inferred `.d.ts` entries\n\n### `@tjs` Annotations in TypeScript\n\nAnnotate your `.ts` files with `/* @tjs ... */` comments to enrich\nthe TJS output. The TS compiler ignores them.\n\n```typescript\n/* @tjs TjsClass TjsEquals */ // Enable TJS modes (off by default in TS-originated code)\n\n/* @tjs-skip */ // Skip this type declaration\nexport type Unboxed<T> = T extends { value: infer U } ? U : T\n\n/* @tjs predicate(x, T) { return typeof x === 'object' && T(x.value) } */\nexport interface Box<T> {\n value: T\n}\n\n/* @tjs example: { name: 'Alice', age: 30 } */\nexport interface User {\n name: string\n age: number\n}\n\n/* @tjs declaration { value: T; path: string } */\nexport interface BoxedProxy<T> {\n /* complex conditional type */\n}\n```\n\n### `.d.ts` Generation\n\nThe DTS emitter produces TypeScript declarations from TJS transpilation:\n\n```bash\nbun src/cli/tjs.ts emit input.tjs # emits .js, .d.ts, .md\n```\n\n- **Interfaces with declaration blocks** → `export interface Name<T> { ... }`\n- **Conditional/mapped types** → `export type Name<T> = ...` (verbatim TS body)\n- **Function types** → `export type Name = (...) => T`\n- **Simple type aliases** → `export type Name = original TS body`\n- **Constants** → `export declare const Name: type`\n- **Functions** → `export declare function Name(params): returnType`\n- **Classes** → callable function + class declaration\n\n### Constrained Generics\n\nWhen the converter encounters a constrained generic like\n`<T extends { id: number }>`, it uses the constraint shape as the\nexample value instead of falling back to `any`. This means:\n\n```typescript\n// TypeScript\nfunction first<T extends { id: number }>(items: T[]): T {\n return items[0]\n}\n\n// Converted TJS — uses constraint shape, not 'any'\nfunction first(items: [{ id: 0.0 }]):! { id: 0.0 } { ... }\n```\n\nGeneric defaults also work: `<T = string>` uses `string` as the example.\nUnconstrained generics (`<T>` with no `extends` or default) still degrade\nto `any` — there's genuinely no information about what T is.\n\n### What `fromTS` Can't Fully Express\n\nTJS types are example values, not abstract type algebra. Some TypeScript\npatterns have no direct TJS equivalent — but most now preserve their\noriginal TS body for `.d.ts` round-tripping:\n\n| TypeScript Pattern | What Happens |\n| ------------------------------------------- | ----------------------------------------------------- |\n| Conditional types (`T extends U ? X : Y`) | TS body preserved verbatim in `.d.ts` |\n| Mapped types (`{ [K in keyof T]: ... }`) | TS body preserved verbatim in `.d.ts` |\n| Intersection types (`A & B`) | TS body preserved verbatim in `.d.ts` |\n| `Partial<T>`, `Required<T>`, `Pick`, `Omit` | Emits warning, uses base shape |\n| `ReturnType<T>`, `Parameters<T>` | Drops to `any` |\n| Template literal types (`` `${A}-${B}` ``) | Becomes `string` |\n| Deeply nested generics (`Foo<Bar<U>>`) | Inner params become `any` |\n| `readonly`, `as const` | Stripped (use `const!` for compile-time immutability) |\n\nThe key improvement: complex types that can't be expressed as runtime\npredicates are still preserved in the `.d.ts` output via declaration blocks.\nThe runtime code works with `any`, but TypeScript consumers of your library\nget the full type information.\n\n## Migration Strategy\n\n### Incremental Adoption\n\nYou don't have to convert everything at once:\n\n1. **Start at boundaries.** Convert API handlers and validation layers\n first -- these benefit most from runtime types.\n2. **Convert hot modules.** Modules with frequent type-related bugs are\n good candidates.\n3. **Leave internals for last.** Pure computational code that's already\n well-tested benefits least from migration.\n\n### The Bun Plugin\n\nIf you use Bun, `.tjs` files work alongside `.ts` files with zero config:\n\n```javascript\n// bunfig.toml already preloads the TJS plugin\nimport { processOrder } from './orders.tjs' // just works\nimport { validateUser } from './users.ts' // also works\n```\n\n### What to Watch For\n\n**Example values matter.** `count: 0` means \"number, example is 0.\" If\nyour function breaks on 0 (division, array index), the automatic signature\ntest will catch it immediately. Choose examples that exercise the\nhappy path.\n\n**Return types generate tests.** `: 0` means TJS will call your function\nwith the parameter examples and check the result. If your function has\nside effects or requires setup, use `:! 0` to skip the signature test.\n\n**Structural equality changes behavior.** If your code relies on `==`\nfor type coercion (comparing numbers to strings, etc.), you'll need to\nupdate those comparisons. This is almost always a bug fix.\n\n---\n\n## Side-by-Side: A Complete Example\n\n### TypeScript\n\n```typescript\ninterface Product {\n id: string\n name: string\n price: number\n tags: string[]\n}\n\ninterface CartItem {\n product: Product\n quantity: number\n}\n\nfunction calculateTotal(items: CartItem[], taxRate: number = 0.1): number {\n const subtotal = items.reduce(\n (sum, item) => sum + item.product.price * item.quantity,\n 0\n )\n return Math.round(subtotal * (1 + taxRate) * 100) / 100\n}\n\nfunction applyDiscount(\n total: number,\n code: string | null\n): { final: number; discount: number } {\n const discounts: Record<string, number> = {\n SAVE10: 0.1,\n SAVE20: 0.2,\n }\n const rate = code ? discounts[code] ?? 0 : 0\n return {\n final: Math.round(total * (1 - rate) * 100) / 100,\n discount: rate,\n }\n}\n```\n\n### TJS\n\n```javascript\nType Product {\n description: 'a product in the catalog'\n example: { id: '', name: '', price: 0.0, tags: [''] }\n}\n\nType CartItem {\n description: 'a product with quantity'\n example: { product: { id: '', name: '', price: 0.0, tags: [''] }, quantity: +0 }\n}\n\nfunction calculateTotal(items: [CartItem], taxRate = 0.1): 0.0 {\n const subtotal = items.reduce(\n (sum, item) => sum + item.product.price * item.quantity,\n 0\n )\n return Math.round(subtotal * (1 + taxRate) * 100) / 100\n}\n\nfunction applyDiscount(total: 0.0, code: '' || null): { final: 0.0, discount: 0.0 } {\n const discounts = {\n SAVE10: 0.1,\n SAVE20: 0.2,\n }\n const rate = code ? discounts[code] ?? 0 : 0\n return {\n final: Math.round(total * (1 - rate) * 100) / 100,\n discount: rate,\n }\n}\n\ntest 'cart calculation' {\n const items = [\n { product: { id: '1', name: 'Widget', price: 10, tags: [] }, quantity: 3 }\n ]\n expect(calculateTotal(items, 0)).toBe(30)\n expect(calculateTotal(items, 0.1)).toBe(33)\n}\n\ntest 'discount codes' {\n expect(applyDiscount(100, 'SAVE10')).toEqual({ final: 90, discount: 0.1 })\n expect(applyDiscount(100, null)).toEqual({ final: 100, discount: 0 })\n expect(applyDiscount(100, 'INVALID')).toEqual({ final: 100, discount: 0 })\n}\n```\n\nThe TJS version is about the same length, but the types exist at runtime,\nthe tests live with the code, and invalid inputs return errors instead\nof crashing.\n\n---\n\n## Traceability: The Death of Source Maps\n\nTypeScript debugging relies on source maps -- external files that try to\nmap minified, transpiled JavaScript back to your original code. They're\nbrittle, often out of sync, and fail entirely in complex build pipelines.\n\nTJS eliminates source maps. Every function carries its source identity\nin `__tjs` metadata:\n\n```javascript\nfunction add(a: 0, b: 0): 0 { return a + b }\n\nadd.__tjs.source // \"mymodule.tjs:3\"\n```\n\n- **Zero-config debugging:** If a function fails validation, the error\n points to the exact line in your `.tjs` source, not a generated `.js` file.\n- **Transparent eval:** Even code run via `Eval()` or the `AgentVM`\n provides clear traces because AST and source metadata are preserved.\n- **No build bloat:** You don't ship `.map` files to production just to\n know why your app crashed.\n\n---\n\n## FAQ\n\n### How do I use TJS with existing NPM packages?\n\nTJS is a superset of JavaScript. Import any NPM package as usual:\n\n```javascript\nimport lodash from 'https://esm.sh/lodash@4.17.21'\nimport { z } from 'zod' // works, though you won't need it\n```\n\nWhen you import a vanilla JS library, its exports have no TJS metadata.\nYou can wrap them in a TJS boundary to get runtime safety:\n\n```javascript\nimport { rawGeocode } from 'legacy-geo-pkg'\n\n// Wrap to validate at your system boundary\nfunction geocode(addr: ''): { lat: 0.0, lon: 0.0 } {\n return rawGeocode(addr)\n}\n```\n\nThe untyped library code runs freely. Your TJS wrapper validates the\nresult before it enters your typed world.\n\n### Do Proxies, WeakMaps, and other advanced patterns work?\n\nYes. TJS is purely additive — it adds inline type checks and metadata\nproperties but does not wrap, intercept, or modify JavaScript runtime\nbehavior. Specifically:\n\n- **Proxies** work identically to plain JS. TJS attaches `.__tjs` as a\n plain property on function objects, which doesn't trigger Proxy traps.\n If your Proxy needs custom equality, use the `[tjsEquals]` symbol protocol.\n- **WeakMap/WeakSet** are unaffected. TJS doesn't inspect collection contents.\n- **Symbols** work normally. TJS reserves `Symbol.for('tjs.equals')` for\n custom equality but doesn't interfere with other symbols.\n- **`Object.defineProperty`**, getters/setters, non-enumerable properties\n — all work as expected. TJS validation checks value types, not property\n descriptors.\n- **Prototype chains** are preserved. `instanceof` works correctly with\n TJS-wrapped classes.\n\nIf you're building a Proxy-heavy library (reactive state, ORMs, etc.),\nTJS will not interfere. The transpiled output is plain JavaScript with\nsome `typeof` checks at function entry points.\n\n### Does structural equality (`==`) handle circular references?\n\nNo. Circular structures will cause infinite recursion. Use identity\ncomparison (`===`) for objects that might be circular, or define a\ncustom `.Equals` method on the class.\n\n### What happens to TypeScript's `strict` mode checks?\n\nTJS doesn't have `strictNullChecks` or `noImplicitAny` because the\nproblems they solve don't exist:\n\n- **Null safety:** `|| null` explicitly marks nullable parameters.\n Functions without it reject null at runtime.\n- **Implicit any:** Every TJS parameter has an example value that\n determines its type. There's nothing to be implicit about.\n- **Strict property access:** Runtime validation catches missing\n properties with a clear error message instead of `undefined`.\n\n---\n\n## Learn More\n\n- [TJS Language Reference](DOCS-TJS.md) -- Full syntax and features\n- [TJS for JavaScript Programmers](TJS-FOR-JS.md) -- Coming from vanilla JS?\n- [AJS Agent Language](DOCS-AJS.md) -- The sandboxed agent VM\n- [Playground](https://tjs-platform.web.app) -- Try it live\n"
|
|
1620
|
+
"text": "<!--{\"section\": \"tjs-for-ts\", \"group\": \"docs\", \"order\": 0, \"navTitle\": \"TJS for TS Devs\"}-->\n\n# TJS for TypeScript Programmers\n\n_What if your types didn't disappear at runtime?_\n\n---\n\nTypeScript is great. It catches bugs at compile time, makes refactoring safer,\nand gives you autocomplete. But it has a fundamental limitation: types are\nfiction. They exist only in your editor, and they vanish completely at runtime.\n\nTJS starts from a different premise: **types are example values that survive\nto runtime**. This gives you everything TypeScript gives you, plus runtime\nvalidation, reflection, documentation, inline tests of private methods, and traceability of errors back to source code -- from a single source of truth.\n\nThis guide is split into two paths:\n\n1. **[Using TJS from TypeScript](#part-1-using-tjs-from-typescript)** -- Keep your TS codebase, use TJS for safe eval and agent execution\n2. **[Migrating to TJS](#part-2-migrating-to-tjs)** -- Convert your codebase from TypeScript to TJS\n\n---\n\n# Part 1: Using TJS from TypeScript\n\nYou don't have to rewrite anything. TJS provides tools you can use directly\nfrom your TypeScript codebase.\n\n## Safe Eval\n\nThe most common reason to reach for TJS from TypeScript: running untrusted\ncode safely.\n\n```typescript\nimport { Eval, SafeFunction } from 'tjs-lang/eval'\n\n// Run user-provided code with a gas limit\nconst { result, fuelUsed } = await Eval({\n code: userCode,\n context: { items: data, threshold: 10 },\n fuel: 1000,\n capabilities: {\n fetch: sandboxedFetch, // your whitelist-wrapped fetch\n },\n})\n\n// Or create a reusable safe function\nconst transform = await SafeFunction({\n body: 'return items.filter(x => x.price < budget)',\n params: ['items', 'budget'],\n fuel: 500,\n})\n\nconst { result } = await transform(products, 100)\n```\n\nNo `eval()`. No CSP violations. No Docker containers. The code runs in a\nfuel-metered sandbox with only the capabilities you inject.\n\n## Agent VM\n\nBuild and execute JSON-serializable agents:\n\n```typescript\nimport { ajs, AgentVM } from 'tjs-lang'\n\n// Parse agent source to JSON AST\nconst agent = ajs`\n function analyze({ data, query }) {\n let filtered = data.filter(x => x.score > 0.5)\n let summary = llmPredict({\n prompt: 'Summarize findings for: ' + query,\n data: filtered\n })\n return { query, summary, count: filtered.length }\n }\n`\n\n// Execute with resource limits\nconst vm = new AgentVM()\nconst { result } = await vm.run(\n agent,\n { data, query },\n {\n fuel: 1000,\n timeoutMs: 5000,\n capabilities: { fetch: myFetch, llm: myLlm },\n }\n)\n```\n\nThe agent AST is JSON. You can store it in a database, send it over the\nnetwork, version it, diff it, audit it.\n\n## Type-Safe Builder\n\nConstruct agents programmatically with full TypeScript support:\n\n```typescript\nimport { Agent, AgentVM, s } from 'tjs-lang'\n\nconst pipeline = Agent.take(s.object({ url: s.string, maxResults: s.number }))\n .httpFetch({ url: { $kind: 'arg', path: 'url' } })\n .as('response')\n .varSet({\n key: 'results',\n value: {\n $expr: 'member',\n object: { $expr: 'ident', name: 'response' },\n property: 'items',\n },\n })\n .return(s.object({ results: s.array(s.any) }))\n\nconst vm = new AgentVM()\nconst { result } = await vm.run(\n pipeline.toJSON(),\n { url, maxResults: 10 },\n {\n fuel: 500,\n capabilities: { fetch },\n }\n)\n```\n\n## TypeScript Entry Points\n\nTJS is tree-shakeable. Import only what you need:\n\n```typescript\nimport { Agent, AgentVM, ajs, tjs } from 'tjs-lang' // Everything\nimport { Eval, SafeFunction } from 'tjs-lang/eval' // Safe eval only\nimport { tjs, transpile } from 'tjs-lang/lang' // Language tools\nimport { fromTS } from 'tjs-lang/lang/from-ts' // TS -> TJS converter\n```\n\n## When to Stay in TypeScript\n\nIf your codebase is TypeScript and you're happy with it, you probably only\nneed TJS for:\n\n- Running user-provided or LLM-generated code safely\n- Building agents that travel over the network\n- Adding runtime type validation at system boundaries\n- Eval without `eval()`\n\nYou don't need to migrate anything. The libraries work from TypeScript.\n\n---\n\n# Part 2: Migrating to TJS\n\nIf you want the full TJS experience -- runtime types, structural equality,\nmonadic errors, inline tests -- here's how to convert.\n\n## The Core Idea: Types as Examples\n\nTypeScript describes types abstractly. TJS describes them concretely:\n\n```typescript\n// TypeScript: what TYPE is this?\nfunction greet(name: string): string { ... }\n\n// TJS: what's an EXAMPLE of this?\nfunction greet(name: 'World'): '' { ... }\n```\n\n`'World'` tells TJS: this is a string, it's required, and here's a valid\nexample. The example doubles as documentation and test data.\n\n## Conversion Reference\n\n### Primitives\n\n```typescript\n// TypeScript // TJS\nname: string name: ''\ncount: number count: 0.0 // float (any number)\nindex: number index: 0 // integer\nage: number age: +0 // non-negative integer\nflag: boolean flag: true\nitems: string[] items: ['']\nnested: number[][] nested: [[0]]\n```\n\n**Important:** The example value determines the _type_, not a literal\nconstraint. `name: 'World'` means \"required string\" -- not \"must be the\nstring `'World'`.\" Any string passes validation. The example is there for\ndocumentation, testing, and type inference. Think of it as `string` with\na built-in `@example` tag.\n\n**Numeric precision:** TJS distinguishes three numeric types using valid\nJavaScript syntax that JS itself ignores:\n\n| You Write | TJS Type | Runtime Check |\n| --------- | ---------------------- | ------------------------------- |\n| `3.14` | `number` (float) | Any number |\n| `0.0` | `number` (float) | Any number |\n| `42` | `integer` | `Number.isInteger(x)` |\n| `0` | `integer` | `Number.isInteger(x)` |\n| `+20` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n| `+0` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n\nTypeScript's `number` is a single type. TJS gives you three levels of\nprecision -- all using expressions that are already legal JavaScript.\nThe automatic converter maps TypeScript `number` to `0.0` (float) to\npreserve the widest behavior; you can then narrow manually to `0` (integer)\nor `+0` (non-negative integer) where appropriate.\n\n### Optional Parameters\n\n```typescript\n// TypeScript // TJS\nfunction f(x?: string) {}\nfunction f(x = '') {}\nfunction f(x: string = 'hi') {}\nfunction f(x = 'hi') {}\n```\n\nIn TypeScript, `?` means optional with type `string | undefined`.\nIn TJS, `= value` means optional with that default. Same semantics, less syntax.\n\n### Object Shapes\n\n```typescript\n// TypeScript\nfunction createUser(opts: { name: string; age: number; email?: string }) {}\n\n// TJS\nfunction createUser(opts: { name: '', age: 0, email = '' }) {}\n```\n\nRequired properties use `:`, optional ones use `=`.\n\n### Return Types\n\n```typescript\n// TypeScript // TJS\nfunction add(a: number, b: number): number function add(a: 0, b: 0): 0\nfunction getUser(): { name: string } function getUser(): { name: '' }\nfunction fetchData(): Promise<string[]> function fetchData(): ['']\n```\n\nThe `fromTS` converter unwraps `Promise<T>` in return type annotations --\nyou annotate the resolved type, not the wrapper. This only applies when\nconverting from TypeScript; in native TJS you just write normal\n`async`/`await` and annotate what the function resolves to.\n\nThe return annotation also generates an automatic test: `add(0, 0)` must\nreturn a number. If it doesn't, you get an error at transpile time.\n\n### Interfaces and Type Aliases\n\n```typescript\n// TypeScript\ninterface User {\n name: string\n age: number\n email?: string\n}\n\ntype Status = 'active' | 'inactive' | 'banned'\n\n// TJS\nType User {\n description: 'a registered user'\n example: { name: '', age: 0, email = '' }\n}\n\nUnion Status 'account status' 'active' | 'inactive' | 'banned'\n```\n\n### Enums\n\n```typescript\n// TypeScript\nenum Color {\n Red = 'red',\n Green = 'green',\n Blue = 'blue',\n}\n\n// TJS\nEnum Color 'CSS color' {\n Red = 'red'\n Green = 'green'\n Blue = 'blue'\n}\n```\n\n### Classes\n\n```typescript\n// TypeScript\nclass Point {\n private x: number\n private y: number\n\n constructor(x: number, y: number) {\n this.x = x\n this.y = y\n }\n\n distanceTo(other: Point): number {\n return Math.sqrt((this.x - other.x) ** 2 + (this.y - other.y) ** 2)\n }\n}\n\nconst p = new Point(10, 20)\n\n// TJS\nclass Point {\n #x\n #y\n\n constructor(x: 0, y: 0) {\n this.#x = x\n this.#y = y\n }\n\n distanceTo(other: Point): 0 {\n return Math.sqrt((this.#x - other.#x) ** 2 + (this.#y - other.#y) ** 2)\n }\n}\n\nconst p = Point(10, 20) // no 'new' needed\n```\n\nKey differences:\n\n- `private` is stripped by default (TS `private` is compile-time only).\n With `TjsClass` (on by default in native TJS, add via `/* @tjs TjsClass */` for TS-originated code), `private` converts to `#` (true JS runtime privacy).\n- Type annotations become example values\n- With `TjsClass`, `new` is optional (linter warns against it)\n\n### Generics\n\nTJS takes a different approach to generics. TypeScript has function-level\ntype parameters (`<T>`) that vanish at runtime. TJS has `Generic` declarations\nthat produce runtime-checkable type constructors:\n\n```typescript\n// TypeScript -- compile-time only, gone at runtime\nfunction identity<T>(x: T): T { return x }\ninterface Box<T> { value: T }\n\n// TJS -- no function-level generics; use Generic for container types\nGeneric Box<T> {\n description: 'a boxed value'\n predicate(x, T) {\n return typeof x === 'object' && x !== null && 'value' in x && T(x.value)\n }\n}\n\n// Usage: Box(Number) is a runtime type checker\nconst isNumberBox = Box(Number)\nisNumberBox({ value: 42 }) // true\nisNumberBox({ value: 'nope' }) // false\n```\n\nFor simple generic functions like `identity` or `first`, you don't need\ngenerics at all -- just skip the type parameter. TJS validates the\nconcrete types at call sites, not the abstract relationship between them.\n\n```javascript\n// Simple -- no generics needed, the function just works\nfunction first(arr: [0]) { return arr[0] }\n\n// If you need runtime-checked containers, use Generic\nGeneric Pair<T, U> {\n description: 'a typed pair'\n predicate(x, T, U) { return T(x[0]) && U(x[1]) }\n}\n```\n\nWhen converting from TypeScript, the `fromTS` converter preserves generic\nmetadata but types become `any`. This is a place where manual review helps.\n\n### Nullability\n\n```typescript\n// TypeScript\nfunction find(id: number): User | null { ... }\n\n// TJS\nfunction find(id: 0): { name: '', age: 0 } || null { ... }\n```\n\nTJS distinguishes `null` from `undefined` -- they're different types, just\nas `typeOf(null)` returns `'null'` and `typeOf(undefined)` returns\n`'undefined'`. Writing `|| null` means the value can be the base type or\n`null`, but not `undefined`. Optional parameters (using `=`) accept\n`undefined` because that's what you get when the caller omits the argument.\n\n## What TypeScript Has That TJS Doesn't\n\nTJS intentionally skips TypeScript features that don't survive to runtime\nor add complexity without proportional value:\n\n| TypeScript Feature | TJS Equivalent |\n| --------------------------- | ------------------------------------------- |\n| `interface` | `Type` with example |\n| `type` aliases | `Type`, `Union`, or `Enum` |\n| Conditional types | Preserved in `.d.ts` via declaration blocks |\n| Mapped types | Preserved in `.d.ts` via declaration blocks |\n| `keyof`, `typeof` | Use runtime `Object.keys()`, `typeOf()` |\n| `Partial<T>`, `Pick<T>` | Define the shape you need directly |\n| Declaration files (`.d.ts`) | Generated from `__tjs` metadata (`--dts`) |\n| `as` type assertions | Not needed (values are checked) |\n| `any` escape hatch | `safety none` per-module or `!` per-fn |\n| Decorators | Not supported |\n| `namespace` | Use modules |\n\nThe philosophy: if a type feature doesn't do something at runtime, it's\ncomplexity without payoff.\n\n### But What About Narrowing?\n\nTypeScript's type system is Turing-complete. You can express astonishing\nconstraints -- `Pick<Omit<T, K>, Extract<keyof T, string>>` -- but the\nresulting types are often harder to understand than the code they describe.\nAnd they vanish at runtime, so they can't protect you from bad API data.\n\nTJS takes the opposite approach: `Type()` gives you a predicate function.\nIf you can write a boolean expression, you can define a type. No type-level\nprogramming language to learn.\n\n```javascript\n// TypeScript: branded types + manual validation\ntype Email = string & { __brand: 'email' }\nfunction isEmail(s: string): s is Email {\n return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(s)\n}\nfunction validateEmail(input: string): Email {\n if (!isEmail(input)) throw new Error('Invalid email')\n return input\n}\n\n// TJS: one line, works at runtime\nType Email {\n description: 'email address'\n example: 'user@example.com'\n predicate(v) { return typeof v === 'string' && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(v) }\n}\n```\n\nThe TJS `Type()` built-in handles everything from simple shapes to\nsophisticated domain constraints:\n\n```javascript\n// Simple -- infer from example\nType Name 'Alice' // string\n\n// Constrained -- predicate narrows beyond the base type\nType PositiveInt {\n description: 'a positive integer'\n example: 1\n predicate(v) { return typeof v === 'number' && Number.isInteger(v) && v > 0 }\n}\n\n// Domain-specific -- readable business rules\nType USZipCode {\n description: '5-digit US zip code'\n example: '90210'\n predicate(v) { return typeof v === 'string' && /^\\d{5}$/.test(v) }\n}\n\n// Combinators -- compose types\nType OptionalEmail Nullable(Email) // Email | null\n\n// Schema-based -- use tosijs-schema for structured validation\nType AgeRange {\n description: 'valid age'\n example: 25\n predicate(v) { return typeof v === 'number' && v >= 0 && v <= 150 }\n}\n```\n\nCompare the TypeScript equivalents:\n\n| What you want | TypeScript | TJS |\n| ------------------- | ----------------------------------------------------------- | -------------------------------------- |\n| String with format | Branded type + type guard + validation function | `Type Email { predicate(v) {...} }` |\n| Number in range | Branded type + manual check | `Type Age { predicate(v) {...} }` |\n| Non-empty string | Template literal type (compile-only) | `Type NonEmpty { predicate(v) {...} }` |\n| Nullable variant | `T \\| null` (compile-only) | `Nullable(MyType)` (runtime-checked) |\n| Union of literals | `'a' \\| 'b' \\| 'c'` (compile-only) | `Union Status 'a' \\| 'b' \\| 'c'` |\n| Discriminated union | `type Shape = { kind: 'circle' } \\| ...` + manual narrowing | `Union('kind', { circle: {...} })` |\n| Generic container | `interface Box<T>` (compile-only) | `Generic Box<T> { predicate(...) }` |\n\nEvery row in the TypeScript column is compile-time fiction that disappears\nwhen your code runs. Every row in the TJS column is a runtime check that\nactually catches bugs in production. And the TJS versions are shorter,\nbecause a predicate is just a function -- not a type-level program.\n\nTJS also ships common types out of the box: `TString`, `TNumber`,\n`TBoolean`, `TInteger`, `TPositiveInt`, `TNonEmptyString`, `TEmail`,\n`TUrl`, `TUuid`, `Timestamp`, `LegalDate`. No imports from a validation\nlibrary needed.\n\n### Tooling Comparison\n\n| Concern | TypeScript | TJS |\n| ------------------- | ---------------------------------- | ---------------------------------------------------------------------- |\n| **Type checking** | `tsc` (compile-time only) | Runtime validation (survives build) |\n| **Runtime schemas** | Zod / io-ts / Ajv (separate) | Built-in (types _are_ schemas) |\n| **Linting** | ESLint + plugins | Built-in linter (unused vars, unreachable code, no-explicit-new) |\n| **Testing** | Vitest / Jest (separate files) | Inline `test` blocks (transpile-time) |\n| **Equality** | Reference-based only | Honest `==` (no coercion), `Is`/`IsNot` (structural), `===` (identity) |\n| **Build toolchain** | tsc + bundler (webpack/Vite/etc) | Transpiles in-browser, no build step |\n| **Debugging** | Source maps (brittle, build bloat) | Functions carry source identity via `__tjs` metadata |\n| **Documentation** | JSDoc / TypeDoc (manual) | Generated from `__tjs` metadata |\n| **Editor support** | Mature (VSCode, etc) | Monaco/CodeMirror/Ace + VSCode/Cursor extensions |\n\n## What TJS Has That TypeScript Doesn't\n\n### Runtime Validation\n\nTypeScript:\n\n```typescript\n// Types are a promise. A lie, if the data comes from outside.\nfunction processOrder(order: Order) {\n // If order came from an API, nothing guarantees it matches Order.\n // You need Zod/io-ts/ajv AND the TypeScript type AND keep them in sync.\n}\n```\n\nTJS:\n\n```javascript\n// Types are checked at runtime. One source of truth.\nfunction processOrder(order: { items: [{ id: 0, qty: 0 }], total: 0 }): {\n status: '',\n} {\n // If order doesn't match, caller gets a MonadicError -- no crash.\n}\n```\n\n### Honest Equality\n\nTypeScript inherits JavaScript's broken equality. Native TJS fixes this by default. For TS-originated code, add the `TjsEquals` directive (or use `/* @tjs TjsEquals */` in the source `.ts` file):\n\n```javascript\nTjsEquals // needed for TS-originated code; native TJS has this on by default\n\n// == is honest: no coercion, unwraps boxed primitives\n0 == '' // false (JS: true!)\n[] == ![] // false (JS: true!)\nnew String('foo') == 'foo' // true (unwraps boxed)\nnull == undefined // true (useful pattern preserved)\ntypeof null // 'null' (JS: 'object')\n\n// == is fast: O(1) reference equality for objects/arrays\n{a: 1} == {a: 1} // false (different refs)\n[1, 2] == [1, 2] // false (different refs)\n\n// Is/IsNot for explicit deep structural comparison (O(n))\n{a: 1} Is {a: 1} // true\n[1, 2, 3] Is [1, 2, 3] // true\nnew Set([1,2]) Is new Set([2,1]) // true (Sets are order-independent)\n\n// === unchanged: identity check\nobj === obj // true (same reference)\n```\n\n`==` fixes coercion without the performance cost of deep comparison.\nUse `Is`/`IsNot` when you explicitly need structural comparison.\n\n### Monadic Errors\n\nTypeScript uses exceptions. TJS uses values:\n\n```javascript\n// TypeScript -- you have to remember to try/catch\nfunction divide(a: number, b: number): number {\n if (b === 0) throw new Error('Division by zero')\n return a / b\n}\n// Caller forgets try/catch? Crash.\n\n// TJS -- errors flow through the pipeline\nfunction divide(a: 0, b: 0): 0 {\n if (b === 0) return MonadicError('Division by zero')\n return a / b\n}\n// Caller gets an error value. No crash. Ever.\n```\n\n**How the caller handles it:**\n\n```javascript\n// Option 1: Check the result\nconst result = divide(10, 0)\nif (result instanceof Error) {\n console.log(result.message) // 'Division by zero'\n} else {\n useResult(result)\n}\n\n// Option 2: Just keep going -- errors propagate automatically\nconst a = divide(10, 0) // MonadicError\nconst b = double(a) // Receives error, returns it immediately (skips execution)\nconst c = format(b) // Same -- error flows through the whole chain\n// c is still the original MonadicError from divide()\n```\n\nIf you've used Rust's `Result<T, E>` or Haskell's `Either`, the pattern\nis familiar. The key difference from TypeScript: you never have to guess\nwhether a function might throw. Type errors and validation failures are\nalways values, never exceptions.\n\n### Inline Tests\n\n```javascript\nfunction fibonacci(n: 0): 0 {\n if (n <= 1) return n\n return fibonacci(n - 1) + fibonacci(n - 2)\n}\n\ntest 'fibonacci sequence' {\n expect(fibonacci(0)).toBe(0)\n expect(fibonacci(1)).toBe(1)\n expect(fibonacci(10)).toBe(55)\n}\n```\n\nTests run at transpile time. They're stripped from production output.\nNo separate test files, no test runner configuration.\n\n### Safety Controls\n\n```javascript\nsafety none // This module: skip all validation (performance)\nsafety inputs // This module: validate inputs only (default)\nsafety all // This module: validate everything (debug)\n\n// Per-function overrides\nfunction hot(! x: 0) {} // Skip validation even if module says 'inputs'\nfunction safe(? x: 0) {} // Force validation even if module says 'none'\n```\n\nUse `!` (skip validation) only in hot loops where every microsecond counts\nand the data source is already trusted. In all other cases, the ~1.5x\noverhead of `safety inputs` is negligible compared to the bugs it catches.\n\n#### Additional Safety Features\n\n```javascript\nTjsNoVar // var declarations are syntax errors\nconst! config = {} // Compile-time immutability (zero runtime cost)\n\n// Debug mode: make type errors visible\nimport { configure } from 'tjs-lang/lang'\nconfigure({ logTypeErrors: true }) // console.error on every type error\nconfigure({ throwTypeErrors: true }) // throw instead of returning MonadicError\n```\n\nTypeScript has no equivalent to most of these. You're either all-in on\ntypes or you use `as any` to escape.\n\n---\n\n## Automatic Conversion\n\nTJS includes a TypeScript-to-TJS converter that has been validated against\nthe tosijs production codebase (35 files, 523 tests passing):\n\n```bash\n# Convert a single file\nbun src/cli/tjs.ts convert input.ts --emit-tjs > output.tjs\n\n# Convert a directory (produces .js + .d.ts + .md per file)\nbun src/cli/tjs.ts convert src/ -o tjs-out/\n\n# Emit JavaScript directly\nbun src/cli/tjs.ts convert input.ts > output.js\n```\n\nFrom code:\n\n```typescript\nimport { fromTS } from 'tjs-lang/lang/from-ts'\n\nconst result = fromTS(tsSource, { emitTJS: true })\nconsole.log(result.code) // TJS source\n```\n\n### What `fromTS` Handles\n\n- Primitive annotations (`string`, `number`, `boolean`) → example values\n- Interfaces → `Type` declarations with declaration blocks for `.d.ts` round-tripping\n- Generic interfaces → `Generic` with predicates + declaration blocks\n- Conditional/mapped types → preserved verbatim in declaration blocks\n- Function type aliases → `FunctionPredicate` declarations (including generics)\n- String literal unions → `Union`\n- Enums → `Enum`\n- Rest parameters (`...args: T[]`) → preserved with `...` prefix\n- Nullable types (`T | null`) → proper null guards in runtime checks\n- Optional params, default values\n- `private` → stripped (or `#` with `TjsClass`)\n- Static getters/setters → `static` keyword preserved\n- `Promise<T>` → unwrapped return types\n- DOM types (130+) → `{}` (opaque object, keeps params annotated)\n- JSDoc comments → TDoc comments\n- Exported constants → type-inferred `.d.ts` entries\n\n### `@tjs` Annotations in TypeScript\n\nAnnotate your `.ts` files with `/* @tjs ... */` comments to enrich\nthe TJS output. The TS compiler ignores them.\n\n```typescript\n/* @tjs TjsClass TjsEquals */ // Enable TJS modes (off by default in TS-originated code)\n\n/* @tjs-skip */ // Skip this type declaration\nexport type Unboxed<T> = T extends { value: infer U } ? U : T\n\n/* @tjs predicate(x, T) { return typeof x === 'object' && T(x.value) } */\nexport interface Box<T> {\n value: T\n}\n\n/* @tjs example: { name: 'Alice', age: 30 } */\nexport interface User {\n name: string\n age: number\n}\n\n/* @tjs declaration { value: T; path: string } */\nexport interface BoxedProxy<T> {\n /* complex conditional type */\n}\n```\n\n### `.d.ts` Generation\n\nThe DTS emitter produces TypeScript declarations from TJS transpilation:\n\n```bash\nbun src/cli/tjs.ts emit input.tjs # emits .js, .d.ts, .md\n```\n\n- **Interfaces with declaration blocks** → `export interface Name<T> { ... }`\n- **Conditional/mapped types** → `export type Name<T> = ...` (verbatim TS body)\n- **Function types** → `export type Name = (...) => T`\n- **Simple type aliases** → `export type Name = original TS body`\n- **Constants** → `export declare const Name: type`\n- **Functions** → `export declare function Name(params): returnType`\n- **Classes** → callable function + class declaration\n\n### Constrained Generics\n\nWhen the converter encounters a constrained generic like\n`<T extends { id: number }>`, it uses the constraint shape as the\nexample value instead of falling back to `any`. This means:\n\n```typescript\n// TypeScript\nfunction first<T extends { id: number }>(items: T[]): T {\n return items[0]\n}\n\n// Converted TJS — uses constraint shape, not 'any'\nfunction first(items: [{ id: 0.0 }]):! { id: 0.0 } { ... }\n```\n\nGeneric defaults also work: `<T = string>` uses `string` as the example.\nUnconstrained generics (`<T>` with no `extends` or default) still degrade\nto `any` — there's genuinely no information about what T is.\n\n### What `fromTS` Can't Fully Express\n\nTJS types are example values, not abstract type algebra. Some TypeScript\npatterns have no direct TJS equivalent — but most now preserve their\noriginal TS body for `.d.ts` round-tripping:\n\n| TypeScript Pattern | What Happens |\n| ------------------------------------------- | ----------------------------------------------------- |\n| Conditional types (`T extends U ? X : Y`) | TS body preserved verbatim in `.d.ts` |\n| Mapped types (`{ [K in keyof T]: ... }`) | TS body preserved verbatim in `.d.ts` |\n| Intersection types (`A & B`) | TS body preserved verbatim in `.d.ts` |\n| `Partial<T>`, `Required<T>`, `Pick`, `Omit` | Emits warning, uses base shape |\n| `ReturnType<T>`, `Parameters<T>` | Drops to `any` |\n| Template literal types (`` `${A}-${B}` ``) | Becomes `string` |\n| Deeply nested generics (`Foo<Bar<U>>`) | Inner params become `any` |\n| `readonly`, `as const` | Stripped (use `const!` for compile-time immutability) |\n\nThe key improvement: complex types that can't be expressed as runtime\npredicates are still preserved in the `.d.ts` output via declaration blocks.\nThe runtime code works with `any`, but TypeScript consumers of your library\nget the full type information.\n\n## Migration Strategy\n\n### Incremental Adoption\n\nYou don't have to convert everything at once:\n\n1. **Start at boundaries.** Convert API handlers and validation layers\n first -- these benefit most from runtime types.\n2. **Convert hot modules.** Modules with frequent type-related bugs are\n good candidates.\n3. **Leave internals for last.** Pure computational code that's already\n well-tested benefits least from migration.\n\n### The Bun Plugin\n\nIf you use Bun, `.tjs` files work alongside `.ts` files with zero config:\n\n```javascript\n// bunfig.toml already preloads the TJS plugin\nimport { processOrder } from './orders.tjs' // just works\nimport { validateUser } from './users.ts' // also works\n```\n\n### What to Watch For\n\n**Example values matter.** `count: 0` means \"number, example is 0.\" If\nyour function breaks on 0 (division, array index), the automatic signature\ntest will catch it immediately. Choose examples that exercise the\nhappy path.\n\n**Return types generate tests.** `: 0` means TJS will call your function\nwith the parameter examples and check the result. If your function has\nside effects or requires setup, use `:! 0` to skip the signature test.\n\n**Structural equality changes behavior.** If your code relies on `==`\nfor type coercion (comparing numbers to strings, etc.), you'll need to\nupdate those comparisons. This is almost always a bug fix.\n\n---\n\n## Side-by-Side: A Complete Example\n\n### TypeScript\n\n```typescript\ninterface Product {\n id: string\n name: string\n price: number\n tags: string[]\n}\n\ninterface CartItem {\n product: Product\n quantity: number\n}\n\nfunction calculateTotal(items: CartItem[], taxRate: number = 0.1): number {\n const subtotal = items.reduce(\n (sum, item) => sum + item.product.price * item.quantity,\n 0\n )\n return Math.round(subtotal * (1 + taxRate) * 100) / 100\n}\n\nfunction applyDiscount(\n total: number,\n code: string | null\n): { final: number; discount: number } {\n const discounts: Record<string, number> = {\n SAVE10: 0.1,\n SAVE20: 0.2,\n }\n const rate = code ? discounts[code] ?? 0 : 0\n return {\n final: Math.round(total * (1 - rate) * 100) / 100,\n discount: rate,\n }\n}\n```\n\n### TJS\n\n```javascript\nType Product {\n description: 'a product in the catalog'\n example: { id: '', name: '', price: 0.0, tags: [''] }\n}\n\nType CartItem {\n description: 'a product with quantity'\n example: { product: { id: '', name: '', price: 0.0, tags: [''] }, quantity: +0 }\n}\n\nfunction calculateTotal(items: [CartItem], taxRate = 0.1): 0.0 {\n const subtotal = items.reduce(\n (sum, item) => sum + item.product.price * item.quantity,\n 0\n )\n return Math.round(subtotal * (1 + taxRate) * 100) / 100\n}\n\nfunction applyDiscount(total: 0.0, code: '' || null): { final: 0.0, discount: 0.0 } {\n const discounts = {\n SAVE10: 0.1,\n SAVE20: 0.2,\n }\n const rate = code ? discounts[code] ?? 0 : 0\n return {\n final: Math.round(total * (1 - rate) * 100) / 100,\n discount: rate,\n }\n}\n\ntest 'cart calculation' {\n const items = [\n { product: { id: '1', name: 'Widget', price: 10, tags: [] }, quantity: 3 }\n ]\n expect(calculateTotal(items, 0)).toBe(30)\n expect(calculateTotal(items, 0.1)).toBe(33)\n}\n\ntest 'discount codes' {\n expect(applyDiscount(100, 'SAVE10')).toEqual({ final: 90, discount: 0.1 })\n expect(applyDiscount(100, null)).toEqual({ final: 100, discount: 0 })\n expect(applyDiscount(100, 'INVALID')).toEqual({ final: 100, discount: 0 })\n}\n```\n\nThe TJS version is about the same length, but the types exist at runtime,\nthe tests live with the code, and invalid inputs return errors instead\nof crashing.\n\n---\n\n## Traceability: The Death of Source Maps\n\nTypeScript debugging relies on source maps -- external files that try to\nmap minified, transpiled JavaScript back to your original code. They're\nbrittle, often out of sync, and fail entirely in complex build pipelines.\n\nTJS eliminates source maps. Every function carries its source identity\nin `__tjs` metadata:\n\n```javascript\nfunction add(a: 0, b: 0): 0 {\n return a + b\n}\n\nadd.__tjs.source // \"mymodule.tjs:3\"\n```\n\n- **Zero-config debugging:** If a function fails validation, the error\n points to the exact line in your `.tjs` source, not a generated `.js` file.\n- **Transparent eval:** Even code run via `Eval()` or the `AgentVM`\n provides clear traces because AST and source metadata are preserved.\n- **No build bloat:** You don't ship `.map` files to production just to\n know why your app crashed.\n\n---\n\n## FAQ\n\n### How do I use TJS with existing NPM packages?\n\nTJS is a superset of JavaScript. Import any NPM package as usual:\n\n```javascript\nimport lodash from 'https://esm.sh/lodash@4.17.21'\nimport { z } from 'zod' // works, though you won't need it\n```\n\nWhen you import a vanilla JS library, its exports have no TJS metadata.\nYou can wrap them in a TJS boundary to get runtime safety:\n\n```javascript\nimport { rawGeocode } from 'legacy-geo-pkg'\n\n// Wrap to validate at your system boundary\nfunction geocode(addr: ''): { lat: 0.0, lon: 0.0 } {\n return rawGeocode(addr)\n}\n```\n\nThe untyped library code runs freely. Your TJS wrapper validates the\nresult before it enters your typed world.\n\n### Do Proxies, WeakMaps, and other advanced patterns work?\n\nYes. TJS is purely additive — it adds inline type checks and metadata\nproperties but does not wrap, intercept, or modify JavaScript runtime\nbehavior. Specifically:\n\n- **Proxies** work identically to plain JS. TJS attaches `.__tjs` as a\n plain property on function objects, which doesn't trigger Proxy traps.\n If your Proxy needs custom equality, use the `[tjsEquals]` symbol protocol.\n- **WeakMap/WeakSet** are unaffected. TJS doesn't inspect collection contents.\n- **Symbols** work normally. TJS reserves `Symbol.for('tjs.equals')` for\n custom equality but doesn't interfere with other symbols.\n- **`Object.defineProperty`**, getters/setters, non-enumerable properties\n — all work as expected. TJS validation checks value types, not property\n descriptors.\n- **Prototype chains** are preserved. `instanceof` works correctly with\n TJS-wrapped classes.\n\nIf you're building a Proxy-heavy library (reactive state, ORMs, etc.),\nTJS will not interfere. The transpiled output is plain JavaScript with\nsome `typeof` checks at function entry points.\n\n### Does structural equality (`==`) handle circular references?\n\nNo. Circular structures will cause infinite recursion. Use identity\ncomparison (`===`) for objects that might be circular, or define a\ncustom `.Equals` method on the class.\n\n### What happens to TypeScript's `strict` mode checks?\n\nTJS doesn't have `strictNullChecks` or `noImplicitAny` because the\nproblems they solve don't exist:\n\n- **Null safety:** `|| null` explicitly marks nullable parameters.\n Functions without it reject null at runtime.\n- **Implicit any:** Every TJS parameter has an example value that\n determines its type. There's nothing to be implicit about.\n- **Strict property access:** Runtime validation catches missing\n properties with a clear error message instead of `undefined`.\n\n---\n\n## Learn More\n\n- [TJS Language Reference](DOCS-TJS.md) -- Full syntax and features\n- [TJS for JavaScript Programmers](TJS-FOR-JS.md) -- Coming from vanilla JS?\n- [AJS Agent Language](DOCS-AJS.md) -- The sandboxed agent VM\n- [Playground](https://tjs-platform.web.app) -- Try it live\n"
|
|
1621
1621
|
}
|
|
1622
|
-
]
|
|
1622
|
+
]
|