{"id":48441918,"url":"https://github.com/igorjs/pure-ts","last_synced_at":"2026-04-14T12:02:12.487Z","repository":{"id":344393597,"uuid":"1181584442","full_name":"igorjs/pure-ts","owner":"igorjs","description":"Functional application framework for TypeScript. Zero dependencies.","archived":false,"fork":false,"pushed_at":"2026-04-14T06:52:41.000Z","size":441,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-14T07:33:08.895Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/igorjs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-14T10:46:02.000Z","updated_at":"2026-04-14T06:52:42.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/igorjs/pure-ts","commit_stats":null,"previous_names":["igorjs/pure-ts"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/igorjs/pure-ts","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igorjs%2Fpure-ts","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igorjs%2Fpure-ts/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igorjs%2Fpure-ts/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igorjs%2Fpure-ts/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/igorjs","download_url":"https://codeload.github.com/igorjs/pure-ts/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igorjs%2Fpure-ts/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31795334,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-14T11:13:53.975Z","status":"ssl_error","status_checked_at":"2026-04-14T11:13:53.299Z","response_time":153,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-06T16:01:24.061Z","updated_at":"2026-04-14T12:02:12.481Z","avatar_url":"https://github.com/igorjs.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Pure TS\n\n[![npm](https://img.shields.io/npm/v/@igorjs/pure-ts?color=blue)](https://www.npmjs.com/package/@igorjs/pure-ts)\n[![JSR](https://jsr.io/badges/@igorjs/pure-ts)](https://jsr.io/@igorjs/pure-ts)\n[![JSR Score](https://jsr.io/badges/@igorjs/pure-ts/score)](https://jsr.io/@igorjs/pure-ts)\n[![License](https://img.shields.io/npm/l/@igorjs/pure-ts)](https://github.com/igorjs/pure-ts/blob/main/LICENSE)\n[![Tests](https://img.shields.io/badge/tests-1016_passing-brightgreen)]()\n[![Zero Dependencies](https://img.shields.io/badge/dependencies-0-brightgreen)]()\n\nFunctional application framework for TypeScript. Zero dependencies.\n\nErrors are values, not exceptions. Data is immutable, enforced at runtime. Async is lazy and composable. The type system carries everything.\n\n![Node.js](https://img.shields.io/badge/Node.js_22+-339933?logo=nodedotjs\u0026logoColor=white)\n![Deno](https://img.shields.io/badge/Deno_2+-000000?logo=deno\u0026logoColor=white)\n![Bun](https://img.shields.io/badge/Bun-000000?logo=bun\u0026logoColor=white)\n![Cloudflare Workers](https://img.shields.io/badge/CF_Workers-F38020?logo=cloudflare\u0026logoColor=white)\n![Browser](https://img.shields.io/badge/Chromium-4285F4?logo=googlechrome\u0026logoColor=white)\n![TypeScript](https://img.shields.io/badge/TypeScript_5.5+-3178C6?logo=typescript\u0026logoColor=white)\n\n```ts\nimport {\n  // Core\n  Result, Option, pipe, flow, Match, Eq, Ord, State,\n  Lens, LensOptional, Prism, Traversal,\n  // Data\n  Record, List, NonEmptyList, Schema, Codec,\n  // Types\n  ErrType, Duration, Cron,\n  // Async\n  Task, Stream, Lazy, Env, Timer, Retry, CircuitBreaker,\n  Semaphore, Mutex, RateLimiter, Cache, Channel,\n  // IO\n  Json, File, Crypto, Url, Encoding, Clone, Compression,\n  Client, WebSocket, Command, Dns, Net,\n  // Runtime\n  Server, Program, Logger, Config,\n  Path, Eol, Platform, Os, Process,\n} from '@igorjs/pure-ts'\n```\n\n## Install\n\n```bash\nnpm install @igorjs/pure-ts\n```\n\nAlso available on [JSR](https://jsr.io/@igorjs/pure-ts):\n\n```bash\nnpx jsr add @igorjs/pure-ts\n```\n\nRequires Node \u003e= 22 (LTS). Compatible with TypeScript \u003e= 5.5 and TypeScript 7 (`tsgo`).\n\n## What's in the box\n\n| Layer | Primitives | Docs |\n|-------|------------|------|\n| **Core** | `Result`, `Option`, `pipe`, `flow`, `Match`, `Eq`, `Ord`, `State`, `Lens`, `Iso` | [core](docs/core.md) |\n| **Data** | `Record`, `List`, `NonEmptyList`, `Schema`, `Codec`, `ADT` | [data](docs/data.md) |\n| **Types** | `Type` (nominal branding), `ErrType`, `Duration`, `Cron` | [types](docs/types.md) |\n| **Async** | `Task`, `Stream`, `Lazy`, `Env`, `Timer`, `Retry`, `CircuitBreaker`, `Semaphore`, `Mutex`, `RateLimiter`, `Cache`, `Channel`, `StateMachine`, `EventEmitter`, `Pool`, `Queue`, `CronRunner` | [async](docs/async.md) |\n| **IO** | `Json`, `File`, `Crypto`, `Url`, `Encoding`, `Clone`, `Compression`, `Client`, `WebSocket`, `Command`, `Dns`, `Net` | [io](docs/io.md) |\n| **Runtime** | `Server`, `Program`, `Logger`, `Config`, `Path`, `Eol`, `Platform`, `Os`, `Process`, adapters for Node, Deno, Bun, Lambda | [runtime](docs/runtime.md) |\n\nSee the [full documentation](docs/index.md) for API details and examples.\n\n## Why\n\n- **Runtime enforcement**: mutations on Records and Lists throw `TypeError`\n- **Type-level enforcement**: `readonly` all the way down, `tsc --strict` clean\n- **Errors as values**: `Result\u003cT, E\u003e` replaces try/catch, making failure explicit\n- **Lazy async**: `Task` and `Stream` describe work without executing it\n- **Composable optics**: `Lens`, `Prism`, `Traversal` for immutable nested updates\n- **Boundary validation**: `Schema` parses unknown input into typed values\n- **Resilience**: `Retry`, `CircuitBreaker`, `RateLimiter`, `Semaphore` for production systems\n- **Safe IO**: `Json.parse`, `File.read`, `Client.get` return `Result`/`Task`, never throw\n- **Production-ready server**: trie-based routing, typed middleware, graceful shutdown\n- **Zero dependencies**: ships as ESM\n\n## Quick start\n\n### Immutable data\n\n```ts\nconst user = Record({ name: 'Alice', address: { city: 'Sydney' } })\nuser.name                                    // 'Alice'\nuser.address.city                            // 'Sydney' (also a Record)\nuser.name = 'X'                              // TypeError\n\nuser.set(u =\u003e u.address.city, 'Melbourne')   // new Record\nuser.produce(d =\u003e { d.name = 'Bob' })        // batch mutations via draft\n\nconst nums = List([3, 1, 4])\nnums.sortByOrd(Ord.number)                   // List [1, 3, 4]\nnums.uniqBy(Eq.number)                       // deduplicated\nnums.groupBy(n =\u003e n \u003e 2 ? 'big' : 'small')  // { big: List[3,4], small: List[1] }\n\nconst nel = NonEmptyList.of(1, 2, 3)\nnel.first()                                  // 1 (not Option, guaranteed)\nnel.reduce1((a, b) =\u003e a + b)                 // 6 (no init needed)\n```\n\n### Error handling\n\n```ts\nconst parseAge = (input: unknown): Result\u003cnumber, string\u003e =\u003e\n  typeof input === 'number' \u0026\u0026 input \u003e 0 ? Ok(input) : Err('invalid age')\n\nparseAge(25).map(n =\u003e n + 1).unwrapOr(0)     // 26\nparseAge('x').map(n =\u003e n + 1).unwrapOr(0)    // 0\n\n// traverse: map + collect in one pass\nResult.traverse(['1', '2'], s =\u003e parseAge(Number(s)))  // Ok([1, 2])\n\n// Structured errors\nconst NotFound = ErrType('NotFound')          // code: 'NOT_FOUND' (auto-derived)\nNotFound('User not found', { id: 'u_123' })\n  .toResult()                                 // Result\u003cnever, ErrType\u003c'NotFound'\u003e\u003e\n\n// Exhaustive pattern matching\nMatch(result)\n  .with({ tag: 'Ok' }, r =\u003e r.value)\n  .with({ tag: 'Err' }, r =\u003e r.error)\n  .exhaustive()\n```\n\n### Validation\n\n```ts\nconst UserSchema = Schema.object({\n  name: Schema.string,\n  age: Schema.number.refine(n =\u003e n \u003e 0, 'positive'),\n  role: Schema.discriminatedUnion('type', {\n    admin: Schema.object({ type: Schema.literal('admin'), level: Schema.number }),\n    user: Schema.object({ type: Schema.literal('user') }),\n  }),\n})\n\nUserSchema.parse(untrustedInput)              // Result\u003cUser, SchemaError\u003e\n\n// Bidirectional: decode + encode\nconst DateCodec = Codec.from(\n  (input: unknown) =\u003e typeof input === 'string'\n    ? Ok(new Date(input)) : Err({ path: [], expected: 'ISO string', received: typeof input }),\n  (date: Date) =\u003e date.toISOString(),\n)\n```\n\n### Async pipelines\n\n```ts\nconst fetchUser = Task.fromPromise(\n  () =\u003e fetch('/api/user').then(r =\u003e r.json()),\n  e =\u003e String(e),\n)\n\n// Lazy: nothing executes until .run()\nconst pipeline = fetchUser\n  .map(user =\u003e user.name)\n  .timeout(Duration.seconds(5), () =\u003e 'timeout')\n\nawait pipeline.run()                          // Result\u003cstring, string\u003e\n\n// Retry with exponential backoff\nconst policy = Retry.policy()\n  .maxAttempts(3)\n  .exponentialBackoff(Duration.seconds(1))\n  .jitter()\n  .build()\n\nRetry.apply(policy, fetchUser)\n\n// Circuit breaker for cascading failure protection\nconst breaker = CircuitBreaker.create({\n  failureThreshold: 5,\n  successThreshold: 2,\n  timeout: Duration.seconds(30),\n})\nconst protected = breaker.protect(fetchUser)\n```\n\n### Streams\n\n```ts\n// Lazy pull-based async sequences\nconst numbers = Stream.of(1, 2, 3, 4, 5)\nconst result = await numbers\n  .filter(n =\u003e n % 2 === 0)\n  .map(n =\u003e n * 10)\n  .collect()\n  .run()                                      // Ok([20, 40])\n\n// Sliding window, groupBy, scan\nStream.interval(Duration.seconds(1))\n  .take(10)\n  .window(3)                                  // overlapping windows of 3\n  .scan((sum, w) =\u003e sum + w.length, 0)        // running total\n```\n\n### Optics\n\n```ts\ntype User = { name: string; address: { city: string } }\n\nconst city = Lens.prop\u003cUser\u003e()('address')\n  .compose(Lens.prop\u003c{ city: string }\u003e()('city'))\n\ncity.get(user)                                // 'Sydney'\npipe(user, city.set('Melbourne'))             // new object, city changed\npipe(user, city.modify(s =\u003e s.toUpperCase())) // new object, city uppercased\n\n// Array index access (partial: returns Option)\nconst second = LensOptional.index\u003cnumber\u003e(1)\nsecond.getOption([10, 20, 30])                // Some(20)\nsecond.getOption([10])                        // None\n```\n\n### HTTP Server\n\n```ts\nimport { bunAdapter } from '@igorjs/pure-ts/runtime/adapter/bun'\n\nconst app = Server('api')\n  .derive(req =\u003e Task.of({ requestId: crypto.randomUUID() }))\n  .get('/health', () =\u003e json({ ok: true }))\n  .get('/users/:id', ctx =\u003e json({ id: ctx.params.id, rid: ctx.requestId }))\n  .post('/users', ctx =\u003e Task(async () =\u003e {\n    const body = await ctx.req.json()\n    return Ok(json(body, { status: 201 }))\n  }))\n  .listen({ port: 3000 }, bunAdapter)\n\nawait app.run()\n// Handles SIGINT/SIGTERM, graceful shutdown, structured logging\n```\n\nRuntime adapters:\n\n```ts\nimport { nodeAdapter } from '@igorjs/pure-ts/runtime/adapter/node'\nimport { denoAdapter } from '@igorjs/pure-ts/runtime/adapter/deno'\nimport { bunAdapter } from '@igorjs/pure-ts/runtime/adapter/bun'\nimport { lambdaAdapter } from '@igorjs/pure-ts/runtime/adapter/lambda'\n```\n\n### Typed time\n\n```ts\nconst timeout = Duration.seconds(30)\nconst backoff = Duration.multiply(timeout, 2)\nDuration.format(backoff)                      // '1m'\nDuration.toMilliseconds(backoff)              // 60000\nDuration.ord.compare(timeout, backoff)        // -1\n\nconst schedule = Cron.parse('0 9 * * 1-5')   // Result\u003cCronExpression, SchemaError\u003e\nif (schedule.isOk) {\n  Cron.next(schedule.value)                   // Option\u003cDate\u003e (next 9am weekday)\n  Cron.matches(schedule.value, new Date())    // boolean\n}\n```\n\n### Safe IO\n\n```ts\n// JSON: returns Result instead of throwing\nJson.parse('{\"name\":\"Alice\"}')              // Ok({ name: 'Alice' })\nJson.parse('not json')                      // Err(JsonError('...'))\nJson.stringify(data)                        // Ok('{\"name\":\"Alice\"}')\n\n// File: multi-runtime (Deno + Node + Bun), returns Task\nconst content = await File.read('./config.json').run()\nawait File.write('./out.json', data).run()\nawait File.stat('./file.txt').run()         // Ok({ isFile, isDirectory, size })\nFile.copy('./a.txt', './b.txt')             // Task\u003cvoid, FileError\u003e\n\n// Crypto: web standard (all runtimes including browsers)\nCrypto.uuid()                               // 'a1b2c3d4-...'\nawait Crypto.hash('SHA-256', 'hello').run() // Ok(Uint8Array)\nCrypto.timingSafeEqual(a, b)                // boolean\n\n// URL: returns Result instead of throwing\nUrl.parse('https://example.com?q=1')        // Ok(URL)\nUrl.parse('not a url')                      // Err(UrlError)\n\n// Encoding: base64, hex, utf8\nEncoding.base64.encode(bytes)               // 'SGVsbG8='\nEncoding.base64.decode('SGVsbG8=')          // Ok(Uint8Array)\nEncoding.hex.encode(bytes)                  // 'deadbeef'\n\n// Compression: web standard streams\nawait Compression.gzip(data).run()          // Ok(Uint8Array)\nawait Compression.gunzip(compressed).run()  // roundtrip\n\n// Clone: safe structuredClone\nClone.deep({ nested: [1, 2] })              // Ok(deepCopy)\nClone.deep({ fn: () =\u003e {} })                // Err(CloneError)\n```\n\n### HTTP Client\n\n```ts\nconst api = Client.create({\n  baseUrl: 'https://api.example.com',\n  headers: { Authorization: 'Bearer token' },\n})\n\nconst result = await api.get('/users').run()\n// Ok(ClientResponse) or Err(NetworkError | HttpError)\n\nif (result.isOk) {\n  const users = await result.value.json()   // Result\u003cT, ParseError\u003e\n}\n```\n\n### Subprocess\n\n```ts\nconst result = await Command.exec('git', ['status']).run()\n// Ok({ exitCode: 0, stdout: '...', stderr: '' })\n// Works across Node, Deno, and Bun\n```\n\n### Timers\n\n```ts\nawait Timer.sleep(Duration.seconds(1)).run()    // Task\u003cvoid, never\u003e\nTimer.interval(Duration.seconds(1))             // Stream\u003cnumber, never\u003e\nawait Timer.deadline(Duration.seconds(5), task) // Err(TimeoutError) if slow\n```\n\n### DNS and TCP\n\n```ts\nawait Dns.lookup('example.com').run()           // Ok({ address, family })\nawait Dns.resolve('example.com', 'MX').run()    // Ok(['mx1.example.com', ...])\n\nconst conn = await Net.connect({ host: '127.0.0.1', port: 8080 }).run()\nif (conn.isOk) {\n  await conn.value.send('hello').run()\n  const data = await conn.value.receive().run() // Ok(Uint8Array)\n  conn.value.close()\n}\n```\n\n### Dependency injection\n\n```ts\ntype Deps = { db: Database; logger: Logger }\n\nconst getUser = (id: string) =\u003e\n  Env.access\u003cDeps\u003e().flatMap(({ db }) =\u003e\n    Env.fromSync(() =\u003e db.query(id))\n  )\n\n// Provide dependencies at the edge\nawait getUser('u_123').run({ db: realDb, logger: realLogger })\n```\n\n### Resilience\n\n```ts\n// Semaphore: limit concurrent access\nconst sem = Semaphore.create(3)\nconst limited = sem.wrap(expensiveTask)\n\n// Rate limiter: token bucket\nconst limiter = RateLimiter.create({\n  capacity: 100,\n  refillRate: 10,\n  refillInterval: Duration.seconds(1),\n})\nlimiter.wrap(apiCall)                       // Err(RateLimited) when empty\n\n// Cache: TTL + LRU\nconst cache = Cache.create({ ttl: Duration.minutes(5), maxSize: 1000 })\ncache.set('key', value)\ncache.get('key')                            // Option\u003cV\u003e\ncache.getOrElse('key', fetchFromDb)         // cache-aside pattern\n```\n\n### Channels\n\n```ts\nconst ch = Channel.bounded(10)\n\n// Producer\nawait ch.send('hello')\nch.close()\n\n// Consumer (as async iterable)\nfor await (const msg of ch.receive()) {\n  console.log(msg)\n}\n\n// Bridge to Stream\nconst stream = Stream.from(ch.receive())\n```\n\n### Logger and Config\n\n```ts\n// Structured logging\nconst log = Logger.create({ name: 'api', level: 'info' })\nlog.info('request', { method: 'GET', path: '/users' })\nconst child = log.child({ requestId: '123' })\n\n// Type-safe config from env\nconst AppConfig = Config.from({\n  PORT: Schema.string.transform(Number).refine(n =\u003e n \u003e 0, 'port'),\n  DATABASE_URL: Schema.string,\n  LOG_LEVEL: Schema.literal('debug').optional(),\n})\nconst config = AppConfig.loadFrom(process.env)  // Result\u003cConfig, SchemaError\u003e\n```\n\n### Cross-platform paths\n\n```ts\nPath.join('src', 'core', 'result.ts')       // native separator\nPath.basename('/home/user/file.ts')         // 'file.ts'\nPath.toPosix('src\\\\core\\\\file.ts')          // 'src/core/file.ts'\n\nEol.normalize('line1\\r\\nline2')             // 'line1\\nline2'\nEol.split('line1\\nline2')                   // ['line1', 'line2']\n```\n\n### Concurrent servers\n\n```ts\nconst publicApi = Server('public').get('/health', () =\u003e json({ ok: true }))\nconst adminApi = Server('admin').get('/metrics', () =\u003e json({ uptime: 0 }))\n\nProgram('platform', signal =\u003e Task.all([\n  publicApi.serve({ port: 3000, signal }),\n  adminApi.serve({ port: 3001, signal }),\n])).run()\n// Ctrl+C gracefully shuts down both\n```\n\n## API reference\n\n### Record\n\n| Method | Description |\n|---|---|\n| `Record(obj)` | Create immutable record. Freezes in-place |\n| `Record.clone(obj)` | Deep clone then freeze |\n| `.set(accessor, val)` | Replace nested value |\n| `.update(accessor, fn)` | Transform nested value |\n| `.produce(draft =\u003e ...)` | Batch mutations via draft |\n| `.merge({ ... })` | Shallow merge |\n| `.at(accessor)` | Safe deep access -\u003e `Option` |\n| `.equals(other)` | Structural deep equality |\n\n### List\n\n| Method | Description |\n|---|---|\n| `List(arr)` | Create immutable list |\n| `.append` / `.prepend` / `.setAt` / `.updateAt` / `.removeAt` | Mutations -\u003e new List |\n| `.map` / `.filter` / `.reduce` / `.flatMap` | Transformations |\n| `.find` / `.findIndex` / `.at` / `.first` / `.last` | Queries -\u003e `Option` |\n| `.sortBy(cmp)` / `.sortByOrd(ord)` | Sorting |\n| `.uniqBy(eq)` / `.groupBy(fn)` | Dedup and grouping |\n| `.equals(other)` | Structural deep equality |\n\n### NonEmptyList\n\nExtends List. `first()`, `last()`, `head` return `T` directly (not `Option`).\n`reduce1(fn)` folds without initial value. `sortByOrd(ord)` and `uniqBy(eq)` preserve non-emptiness.\n\n### Result\\\u003cT, E\\\u003e\n\n| Method | Description |\n|---|---|\n| `Ok(value)` / `Err(error)` | Construct |\n| `Result.tryCatch(fn, onError)` | Wrap throwing code |\n| `Result.collect` / `.sequence` | All-or-nothing collection |\n| `Result.traverse(items, fn)` | Map + collect in one pass |\n| `.map` / `.mapErr` / `.flatMap` | Transform |\n| `.tap` / `.tapErr` | Side effects |\n| `.match({ Ok, Err })` | Exhaustive pattern match |\n| `.unwrap` / `.unwrapOr` / `.unwrapOrElse` | Extract |\n| `.zip(other)` / `.ap(fnResult)` | Combine |\n| `.toOption()` / `.toJSON()` | Convert |\n\n### Option\\\u003cT\\\u003e\n\n| Method | Description |\n|---|---|\n| `Some(value)` / `None` | Construct |\n| `Option.fromNullable(v)` | Null-safe wrapping |\n| `Option.traverse(items, fn)` | Map + collect |\n| `.map` / `.flatMap` / `.filter` | Transform |\n| `.match({ Some, None })` | Exhaustive pattern match |\n| `.unwrap` / `.unwrapOr` / `.unwrapOrElse` | Extract |\n| `.zip` / `.or` / `.ap` | Combine |\n| `.toResult(error)` | Convert |\n\n### Schema\n\n| Method | Description |\n|---|---|\n| `.string` / `.number` / `.boolean` | Primitives |\n| `.object({ ... })` / `.array(el)` / `.tuple(...)` | Composite |\n| `.literal(v)` / `.union(...)` / `.discriminatedUnion(key, map)` | Sum types |\n| `.record(val)` / `.intersection(a, b)` / `.lazy(fn)` | Advanced |\n| `.parse(unknown)` -\u003e `Result` | Validate |\n| `.refine(pred, label)` / `.transform(fn)` / `.optional()` / `.default(v)` | Compose |\n\n### Codec\n\n| Method | Description |\n|---|---|\n| `Codec.from(decode, encode)` | Custom bidirectional codec |\n| `Codec.fromSchema(schema, encode)` | Bridge from Schema |\n| `Codec.string` / `.number` / `.boolean` | Primitives |\n| `Codec.object({ ... })` / `.array(el)` / `.nullable(codec)` | Composite |\n| `.decode(input)` -\u003e `Result` / `.encode(value)` | Transform |\n| `.pipe(other)` | Chain codecs |\n\n### Task\\\u003cT, E\\\u003e\n\n| Method | Description |\n|---|---|\n| `Task(async () =\u003e ...)` | Create lazy async computation |\n| `Task.of` / `.fromResult` / `.fromPromise` | Constructors |\n| `Task.all` / `.race` / `.allSettled` | Parallel execution |\n| `Task.traverse(items, fn)` / `.sequence(tasks)` | Collection |\n| `Task.ap(fnTask, argTask)` | Applicative |\n| `.map` / `.mapErr` / `.flatMap` / `.tap` / `.tapErr` | Transform |\n| `.timeout(ms, onTimeout)` / `.retry(n, delay?)` | Resilience |\n| `.memoize()` | Cache result |\n| `.run()` | Execute |\n\n### Stream\\\u003cT, E\\\u003e\n\n| Method | Description |\n|---|---|\n| `Stream.of(...)` / `.from(iterable)` / `.fromArray(arr)` | Create |\n| `Stream.unfold(seed, fn)` / `.interval(duration)` | Generate |\n| `.map` / `.flatMap` / `.filter` / `.take` / `.drop` / `.takeWhile` | Transform |\n| `.chunk(size)` / `.window(size)` / `.scan(fn, init)` | Batch |\n| `.mapErr` / `.tap` / `.concat` / `.zip` | Combine |\n| `.collect()` / `.forEach(fn)` / `.reduce(fn, init)` / `.first()` / `.groupBy(fn)` | Collect -\u003e Task |\n\n### Optics\n\n| Export | Description |\n|---|---|\n| `Lens.prop\u003cS\u003e()(key)` | Total lens for a property |\n| `Lens.from(get, set)` | Custom lens |\n| `LensOptional.index\u003cT\u003e(i)` | Array index (partial) |\n| `LensOptional.fromNullable\u003cS\u003e()(key)` | Nullable field |\n| `Prism.from(getOption, reverseGet)` | Sum type variant |\n| `Traversal.fromArray\u003cT\u003e()` | All array elements |\n| `.compose(other)` | Compose optics |\n| `.get` / `.set(v)(s)` / `.modify(fn)(s)` | Access and update |\n\n### Resilience\n\n| Export | Description |\n|---|---|\n| `Retry.policy()` | Builder: `.maxAttempts`, `.exponentialBackoff`, `.jitter`, `.maxDelay`, `.shouldRetry` |\n| `Retry.apply(policy, task)` | Apply policy to a Task |\n| `CircuitBreaker.create(policy)` | Create breaker: `failureThreshold`, `successThreshold`, `timeout` |\n| `breaker.protect(task)` | Wrap Task with circuit protection |\n| `breaker.state()` | `'closed'` / `'open'` / `'half-open'` |\n| `Semaphore.create(n)` | Counting semaphore: `.acquire()`, `.wrap(task)`, `.available()` |\n| `Mutex.create()` | Mutual exclusion (semaphore with 1 permit): `.wrap(task)`, `.isLocked()` |\n| `RateLimiter.create(policy)` | Token bucket: `capacity`, `refillRate`, `refillInterval` |\n| `limiter.wrap(task)` | Returns `Err(RateLimited)` when bucket is empty |\n| `Cache.create(options)` | TTL + LRU: `ttl`, `maxSize` |\n| `cache.get(key)` / `.set(key, value)` | Returns `Option`, auto-expires |\n| `cache.getOrElse(key, task)` | Cache-aside: returns cached or runs task |\n| `Channel.bounded(n)` / `.unbounded()` | Async producer/consumer with backpressure |\n| `ch.send(value)` / `ch.receive()` | Send blocks when full, receive is AsyncIterable |\n\n### Env\\\u003cR, T, E\\\u003e\n\n| Export | Description |\n|---|---|\n| `Env.of(value)` | Wrap value, ignore environment |\n| `Env.access()` | Read the full environment |\n| `Env.from(fn)` / `.fromSync(fn)` | Create from async/sync function |\n| `.map` / `.mapErr` / `.flatMap` / `.tap` | Transform |\n| `.provide(fn)` | Narrow the environment type |\n| `.provideAll(env)` | Provide all dependencies, get Task-like |\n\n### State\\\u003cS, A\\\u003e\n\n| Export | Description |\n|---|---|\n| `State.of(value)` | Pure value, state unchanged |\n| `State.get()` | Read state as value |\n| `State.set(s)` / `.modify(fn)` | Replace or transform state |\n| `.map` / `.flatMap` / `.tap` | Compose |\n| `.run(init)` | Execute: returns `[value, finalState]` |\n| `.eval(init)` / `.exec(init)` | Extract value or state only |\n\n### IO\n\n| Export | Description |\n|---|---|\n| `Json.parse(str)` | `Result\u003cT, JsonError\u003e`, never throws |\n| `Json.stringify(value)` | `Result\u003cstring, JsonError\u003e`, handles circular refs |\n| `File.read` / `.write` / `.exists` / `.stat` / `.copy` / `.rename` | Multi-runtime file ops as Task |\n| `File.makeDir` / `.remove` / `.list` / `.tempDir` | Directory operations as Task |\n| `Crypto.uuid()` | Random UUID v4 (web standard) |\n| `Crypto.randomBytes(n)` / `.hash(algo, data)` | Crypto ops returning Result/Task |\n| `Crypto.timingSafeEqual(a, b)` | Constant-time byte comparison |\n| `Url.parse(input)` | `Result\u003cURL, UrlError\u003e`, wraps `new URL()` |\n| `Url.searchParams(obj)` / `.parseSearchParams(str)` | Query string utilities |\n| `Encoding.base64` / `.hex` / `.utf8` | `.encode()` / `.decode()` with Result |\n| `Compression.gzip` / `.gunzip` / `.deflate` / `.inflate` | Web standard CompressionStream as Task |\n| `Clone.deep(value)` | `Result\u003cT, CloneError\u003e`, wraps `structuredClone` |\n| `Client.create(options?)` | HTTP client: `baseUrl`, `headers`, custom `fetch` |\n| `client.get` / `.post` / `.put` / `.patch` / `.delete` | Returns `Task\u003cClientResponse, ClientError\u003e` |\n| `WebSocket.router()` | WebSocket route builder: `.route(path, handler)`, `.match(path)` |\n| `Command.exec(cmd, args?)` | Multi-runtime subprocess: `Task\u003cCommandResult, CommandError\u003e` |\n| `Dns.lookup(host)` / `.resolve(host, type?)` | DNS resolution as Task |\n| `Net.connect({ host, port })` | TCP client: `Task\u003cTcpConnection, NetError\u003e` |\n| `Stream.fromReadable(stream)` | Bridge web ReadableStream to `Stream\u003cUint8Array\u003e` |\n\n### Timer\n\n| Export | Description |\n|---|---|\n| `Timer.sleep(duration)` | `Task\u003cvoid, never\u003e` |\n| `Timer.interval(period)` | `Stream\u003cnumber, never\u003e` |\n| `Timer.delay(duration, task)` | Run task after delay |\n| `Timer.deadline(duration, task)` | Race task against timeout |\n| `Timer.now()` | `performance.now()` wrapper |\n\n### Time\n\n| Export | Description |\n|---|---|\n| `Duration.seconds(n)` / `.minutes` / `.hours` / `.days` / `.milliseconds` | Create |\n| `Duration.add` / `.subtract` / `.multiply` | Arithmetic |\n| `Duration.toMilliseconds` / `.toSeconds` / `.toMinutes` / `.toHours` | Convert |\n| `Duration.format(d)` | Human-readable: `'2h 30m 15s'` |\n| `Duration.eq` / `.ord` | Typeclass instances |\n| `Cron.parse(expr)` -\u003e `Result` | Validate 5-field cron |\n| `Cron.next(expr, after?)` -\u003e `Option\u003cDate\u003e` | Next occurrence |\n| `Cron.matches(expr, date)` | Check match |\n\n### Typeclasses\n\n| Export | Description |\n|---|---|\n| `Eq(fn)` / `Eq.string` / `.number` / `.boolean` / `.date` | Equality |\n| `Eq.struct({ ... })` / `Eq.contramap(eq, fn)` | Compose |\n| `Ord(fn)` / `Ord.string` / `.number` / `.date` | Ordering |\n| `Ord.reverse` / `.contramap` / `.min` / `.max` / `.clamp` / `.between` | Compose |\n\n### Server\n\n| Method | Description |\n|---|---|\n| `Server(name)` | Create builder |\n| `.get` / `.post` / `.put` / `.patch` / `.delete` / `.head` / `.options` / `.all` | Routes |\n| `.use(...middlewares)` | Untyped middleware |\n| `.middleware(typedMw)` | Typed context-extending middleware |\n| `.derive(resolver)` | Sequential context derivation |\n| `.onError(handler)` | Custom error handler |\n| `.fetch(request)` | WHATWG fetch handler |\n| `.serve({ port, signal })` | Returns Task for composable concurrency |\n| `.listen(options, adapter?)` | Start with Program lifecycle |\n| `json` / `text` / `html` / `redirect` | Response helpers |\n\n### Program\n\n| Method | Description |\n|---|---|\n| `Program(name, (signal) =\u003e task)` | Create named program |\n| `.run()` | Execute: logging, signals, exit codes |\n| `.execute(signal?)` | Execute for testing: raw Result |\n\n### Logger\n\n| Export | Description |\n|---|---|\n| `Logger.create({ name, level?, sink? })` | Create structured logger |\n| `Logger.json` / `.pretty` / `.silent` | Built-in sinks |\n| `log.debug` / `.info` / `.warn` / `.error` | Log at level with optional context |\n| `log.child(context)` | Inherit context, add fields |\n| `log.named(name)` | Change logger name |\n\n### Config\n\n| Export | Description |\n|---|---|\n| `Config.from(shape)` | Define config schema from env |\n| `.load()` | Read from `process.env`, returns `Result` |\n| `.loadFrom(env)` | Read from custom record |\n\n### Platform\n\n| Export | Description |\n|---|---|\n| `Path.join` / `.normalize` / `.basename` / `.dirname` / `.extname` | OS-aware path operations |\n| `Path.resolve` / `.relative` / `.isAbsolute` / `.parse` | Advanced path operations |\n| `Path.toPosix(path)` | Convert to forward slashes |\n| `Eol.normalize(text)` | Replace `\\r\\n` with `\\n` |\n| `Eol.split(text)` | Split on `\\r?\\n` |\n| `Platform.os` / `.isWindows` | Runtime detection |\n| `Os.hostname` / `.arch` / `.platform` / `.cpuCount` | System info as `Option` |\n| `Os.totalMemory` / `.freeMemory` / `.tmpDir` / `.homeDir` | Resource info |\n| `Process.cwd()` | `Result\u003cstring, ProcessError\u003e` |\n| `Process.pid` / `.uptime` / `.memoryUsage` | Process info as `Option` |\n| `Process.argv()` / `.parseArgs(schema)` | Argument parsing with Schema validation |\n\n### Utilities\n\n| Export | Description |\n|---|---|\n| `pipe(val, f1, f2, ...)` | Left-to-right transformation (1-9 stages) |\n| `flow(f1, f2, ...)` | Point-free composition (1-6 stages) |\n| `Match(value).with(...).exhaustive()` | Exhaustive pattern matching |\n| `match(result, { Ok, Err })` | Two-arm match for Result/Option |\n| `ErrType(tag)` | Structured error constructor |\n| `Type\u003c'Name', Base\u003e` | Nominal typing (zero runtime) |\n| `Lazy(() =\u003e expr)` | Deferred cached computation |\n| `isImmutable(val)` | Type guard for Records and Lists |\n| `DeepReadonly\u003cT\u003e` | Recursive readonly type utility |\n\n## Runtime compatibility\n\nCI-tested on: \u0026nbsp; ![Node.js 22](https://img.shields.io/badge/Node.js_22-339933?logo=nodedotjs\u0026logoColor=white) \u0026nbsp; ![Node.js 24](https://img.shields.io/badge/Node.js_24-339933?logo=nodedotjs\u0026logoColor=white) \u0026nbsp; ![Deno](https://img.shields.io/badge/Deno_2+-000000?logo=deno\u0026logoColor=white) \u0026nbsp; ![Bun](https://img.shields.io/badge/Bun-000000?logo=bun\u0026logoColor=white) \u0026nbsp; ![CF Workers](https://img.shields.io/badge/CF_Workers-F38020?logo=cloudflare\u0026logoColor=white) \u0026nbsp; ![Browser](https://img.shields.io/badge/Chromium-4285F4?logo=googlechrome\u0026logoColor=white)\n\nEvery module is classified by its runtime requirements. Pure modules use no runtime APIs. Web modules use standard APIs (TextEncoder, fetch, etc.). Multi-runtime modules detect Deno/Bun/Node via `globalThis` and dispatch to the appropriate API, returning `Err`/`None` in unsupported runtimes.\n\n| Module | API | \u003cimg src=\"https://img.shields.io/badge/-339933?logo=nodedotjs\u0026logoColor=white\" height=\"14\"\u003e | \u003cimg src=\"https://img.shields.io/badge/-000000?logo=deno\u0026logoColor=white\" height=\"14\"\u003e | \u003cimg src=\"https://img.shields.io/badge/-000000?logo=bun\u0026logoColor=white\" height=\"14\"\u003e | \u003cimg src=\"https://img.shields.io/badge/-F38020?logo=cloudflare\u0026logoColor=white\" height=\"14\"\u003e | \u003cimg src=\"https://img.shields.io/badge/-4285F4?logo=googlechrome\u0026logoColor=white\" height=\"14\"\u003e |\n|--------|-----|:---:|:---:|:---:|:---:|:---:|\n| **Result / Option** | pure | ✅ | ✅ | ✅ | ✅ | ✅ |\n| **pipe / flow / Match** | pure | ✅ | ✅ | ✅ | ✅ | ✅ |\n| **Eq / Ord / State** | pure | ✅ | ✅ | ✅ | ✅ | ✅ |\n| **Lens / Prism / Traversal** | pure | ✅ | ✅ | ✅ | ✅ | ✅ |\n| **Record / List / NonEmptyList** | pure | ✅ | ✅ | ✅ | ✅ | ✅ |\n| **Schema / Codec** | pure | ✅ | ✅ | ✅ | ✅ | ✅ |\n| **ErrType / Type / Duration / Cron** | pure | ✅ | ✅ | ✅ | ✅ | ✅ |\n| **Task / Stream / Lazy** | pure | ✅ | ✅ | ✅ | ✅ | ✅ |\n| **Env / Channel / Cache** | pure | ✅ | ✅ | ✅ | ✅ | ✅ |\n| **Semaphore / Mutex / RateLimiter** | pure | ✅ | ✅ | ✅ | ✅ | ✅ |\n| **Retry / CircuitBreaker** | pure | ✅ | ✅ | ✅ | ✅ | ✅ |\n| **Json / Clone / Url / Timer** | web | ✅ | ✅ | ✅ | ✅ | ✅ |\n| **Crypto / Encoding / Compression** | web | ✅ | ✅ | ✅ | ✅ | ✅ |\n| **Client** | web (fetch) | ✅ | ✅ | ✅ | ✅ | ✅ |\n| **Path / Eol / Platform** | web | ✅ | ✅ | ✅ | ✅ | ✅ |\n| **Stream.fromReadable / WebSocket** | web | ✅ | ✅ | ✅ | ✅ | ✅ |\n| **File / Command** | multi-runtime | ✅ | ✅ | ✅ | ⬜ | ⬜ |\n| **Os / Process / Config** | multi-runtime | ✅ | ✅ | ✅ | ⬜ | ⬜ |\n| **Logger** | multi-runtime | ✅ | ✅ | ✅ | ✅ | ✅ |\n| **Server / Program** | multi-runtime | ✅ | ✅ | ✅ | ⬜ | ⬜ |\n| **Dns / Net** | multi-runtime | ✅ | ✅ | ✅ | ⬜ | ⬜ |\n\n**Legend:**\n- **pure**: No runtime APIs. Pure TypeScript logic.\n- **web**: Uses web standard APIs (`crypto.subtle`, `URL`, `TextEncoder`, `fetch`, etc.).\n- **multi-runtime**: Detects Deno/Bun/Node via `globalThis` and dispatches to the appropriate API.\n- ⬜ = loads safely, returns `Err`/`None` (no filesystem/subprocess in this environment).\n\n## Performance\n\nClass-per-shape Records eliminate Proxy from the read path:\n\n| Operation | ops/s | vs plain JS |\n|---|---|---|\n| Shallow read (`user.name`) | 207M | ~0.86x (near native) |\n| Deep read (`user.address.geo.lat`) | 19M | ~0.09x |\n| Construction | 192K | - |\n| `set()` shallow | 328K | - |\n| `produce()` 3 mutations | 193K | - |\n| `Ok(42)` | 48M | - |\n| `Ok().map().map().map()` | 35M | - |\n\nMemory per Record instance: ~410 bytes (vs ~376 bytes plain object = 1.09x overhead).\n\n## Building\n\n```bash\nnpm run check          # Type check (TS7)\nnpm run build          # Build\nnpm test               # Test runtime\nnpm run test:types     # Test types (compile-time safety suite)\nnpm run prepublishOnly # Full prepublish pipeline\n```\n\n## License\n\nApache-2.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Figorjs%2Fpure-ts","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Figorjs%2Fpure-ts","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Figorjs%2Fpure-ts/lists"}