{"id":35664040,"url":"https://github.com/tr-yasuda/kitsunejs","last_synced_at":"2026-01-25T17:00:17.780Z","repository":{"id":325006517,"uuid":"1096524039","full_name":"tr-yasuda/kitsunejs","owner":"tr-yasuda","description":"Type-safe Result and Option utilities for TypeScript, inspired by Rust. Safer error handling with a lightweight functional style.","archived":false,"fork":false,"pushed_at":"2026-01-18T17:43:27.000Z","size":229,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-01-20T23:21:44.586Z","etag":null,"topics":["error-handling","functional-programming","monad","option","result","rust","type-safe","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tr-yasuda.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","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":"AGENTS.md","dco":null,"cla":null}},"created_at":"2025-11-14T14:53:13.000Z","updated_at":"2026-01-05T15:44:23.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/tr-yasuda/kitsunejs","commit_stats":null,"previous_names":["tr-yasuda/kitsunejs"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/tr-yasuda/kitsunejs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tr-yasuda%2Fkitsunejs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tr-yasuda%2Fkitsunejs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tr-yasuda%2Fkitsunejs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tr-yasuda%2Fkitsunejs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tr-yasuda","download_url":"https://codeload.github.com/tr-yasuda/kitsunejs/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tr-yasuda%2Fkitsunejs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28755561,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-25T16:32:25.380Z","status":"ssl_error","status_checked_at":"2026-01-25T16:32:09.189Z","response_time":113,"last_error":"SSL_read: 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":["error-handling","functional-programming","monad","option","result","rust","type-safe","typescript"],"created_at":"2026-01-05T17:16:05.754Z","updated_at":"2026-01-25T17:00:17.767Z","avatar_url":"https://github.com/tr-yasuda.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# kitsunejs\n\n[![npm version](https://img.shields.io/npm/v/kitsunejs.svg)](https://www.npmjs.com/package/kitsunejs)\n[![npm downloads](https://img.shields.io/npm/dm/kitsunejs.svg)](https://www.npmjs.com/package/kitsunejs)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)\n[![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue.svg)](https://www.typescriptlang.org/)\n\nRust-inspired `Result` and `Option` types for TypeScript, enabling type-safe error handling and null safety.\n\n## Features\n\n- 🦀 **Rust-like API**: Familiar `Result\u003cT, E\u003e` and `Option\u003cT\u003e` types with methods like `map`, `andThen`, `unwrap`, etc.\n- 🔒 **Type-safe**: Full TypeScript support with proper type inference and narrowing\n- 🌳 **Tree-shakeable**: Fully ESM-ready with optional CJS support\n- 📦 **Zero dependencies**: Lightweight and self-contained\n- ⚡ **Async-ready**: Built-in support for `Promise` with `Result.tryAsync`\n\n## Installation\n\n```bash\n# npm\nnpm install kitsunejs\n\n# pnpm\npnpm add kitsunejs\n\n# yarn\nyarn add kitsunejs\n```\n\n## Usage\n\n### Result Type\n\nThe `Result\u003cT, E\u003e` type represents either success (`Ok\u003cT\u003e`) or failure (`Err\u003cE\u003e`).\n\n#### Basic Usage\n\n```typescript\nimport { Result } from 'kitsunejs';\n\n// Creating Results\nconst success = Result.ok(42);\nconst failure = Result.err('Something went wrong');\n\n// Checking variants\nif (success.isOk()) {\n  console.log(success.unwrap()); // 42\n}\n\nif (failure.isErr()) {\n  console.log(failure.unwrapOr(0)); // 0 (default value)\n}\n```\n\n#### Additional Helpers\n\n```typescript\nimport { Result } from 'kitsunejs';\n\nconst result: Result\u003cnumber, string\u003e = Result.ok(42);\n\n// Type guard + predicate\nif (result.isOkAnd((v) =\u003e v \u003e 0)) {\n  console.log(result.unwrap()); // 42\n}\n\n// Map with defaults\nconst value = Result.err\u003cnumber, string\u003e('error').mapOrElse(\n  (e) =\u003e e.length,\n  (v) =\u003e v * 2,\n);\nconsole.log(value); // 5\n\n// Inspect without changing the Result\nResult.err\u003cnumber, string\u003e('error')\n  .inspectErr((e) =\u003e console.error(e))\n  .unwrapOr(0);\n\n// Extract Err as Option\nconst maybeError = Result.err\u003cnumber, string\u003e('error').err();\nconsole.log(maybeError.unwrap()); // 'error'\n\n// Extract Err value with custom message if Ok\nconst error = Result.err\u003cnumber, string\u003e('error').expectErr('Expected Err');\nconsole.log(error); // 'error'\n```\n\n#### Error Handling with try/tryAsync\n\n```typescript\nimport { Result } from 'kitsunejs';\n\n// Sync: Convert exceptions to Result\nconst result = Result.try(() =\u003e {\n  return JSON.parse('{\"name\": \"Alice\"}');\n});\n\n// Async: Handle Promise rejections\ntype User = {\n  id: number;\n  name: string;\n}\n\nasync function fetchUser(id: number): Promise\u003cResult\u003cUser, Error\u003e\u003e {\n  return Result.tryAsync(async () =\u003e {\n    const response = await fetch(`https://api.example.com/users/${id}`);\n    if (!response.ok) {\n      throw new Error(`HTTP ${response.status}`);\n    }\n    return (await response.json()) as User;\n  });\n}\n\nasync function main() {\n  const userResult = await fetchUser(1);\n\n  if (userResult.isOk()) {\n    console.log('User:', userResult.unwrap());\n  } else {\n    console.error('Failed to fetch user:', userResult.unwrapErr());\n    // Provide fallback value\n    const defaultUser = { id: 0, name: 'Unknown' };\n    console.log('Using default user:', defaultUser);\n  }\n}\n\nmain();\n```\n\n#### Chaining Operations\n\n```typescript\nimport { Result } from 'kitsunejs';\n\ntype User = {\n  name: string;\n  age: number;\n}\n\nfunction findUser(id: number): Result\u003cUser, string\u003e {\n  if (id === 1) {\n    return Result.ok({ name: 'Alice', age: 30 });\n  }\n  return Result.err('User not found');\n}\n\n// Transform success values with map\nconst userName = findUser(1)\n  .map((user) =\u003e user.name)\n  .unwrapOr('Unknown');\n\nconsole.log(userName); // 'Alice'\n\n// Chain multiple Result-returning operations\nfunction getAge(user: User): Result\u003cnumber, string\u003e {\n  if (user.age \u003c 0) {\n    return Result.err('Invalid age');\n  }\n  return Result.ok(user.age);\n}\n\nconst age = findUser(1)\n  .andThen((user) =\u003e getAge(user))\n  .unwrapOr(0);\n```\n\n#### Combining Multiple Results\n\n```typescript\nimport { Result } from 'kitsunejs';\n\nconst results = [\n  Result.ok(1),\n  Result.ok(2),\n  Result.ok(3),\n];\n\n// All must be Ok to get Ok\u003cT[]\u003e\nconst allOk = Result.all(results);\nconsole.log(allOk.unwrap()); // [1, 2, 3]\n\n// Get the first Ok, or Err\u003cE[]\u003e if all fail\nconst firstOk = Result.any([\n  Result.err('error1'),\n  Result.ok(42),\n  Result.err('error2'),\n]);\nconsole.log(firstOk.unwrap()); // 42\n\n// All Err case returns Err\u003cE[]\u003e\nconst allErr = Result.any([\n  Result.err('error1'),\n  Result.err('error2'),\n  Result.err('error3'),\n]);\nif (allErr.isErr()) {\n  console.log(allErr.unwrapErr()); // ['error1', 'error2', 'error3']\n}\n```\n\n### Option Type\n\nThe `Option\u003cT\u003e` type represents an optional value: either `Some\u003cT\u003e` or `None`.\n\n#### Basic Usage\n\n```typescript\nimport { Option } from 'kitsunejs';\n\n// Creating Options\nconst some = Option.some(42);\nconst none = Option.none();\n\n// Checking variants\nif (some.isSome()) {\n  console.log(some.unwrap()); // 42\n}\n\nif (none.isNone()) {\n  console.log('No value');\n}\n\n// Safe handling of null/undefined\nfunction getConfig(key: string): string | null {\n  // Simulated config lookup\n  return null;\n}\n\nconst config = Option.fromNullable(getConfig('api_key'))\n  .unwrapOr('default-api-key');\n\nconsole.log(config); // 'default-api-key'\n```\n\n#### Chaining Operations\n\n```typescript\nimport { Option } from 'kitsunejs';\n\n// Transform values with map\nconst doubled = Option.some(10)\n  .map((n) =\u003e n * 2)\n  .unwrapOr(0);\n\nconsole.log(doubled); // 20\n\n// Filter values based on predicates\nconst filtered = Option.some(10)\n  .filter((n) =\u003e n \u003e 15)\n  .unwrapOr(0);\n\nconsole.log(filtered); // 0 (filtered out)\n\n// Chain Option-returning operations\nfunction parseNumber(str: string): Option\u003cnumber\u003e {\n  const num = Number.parseFloat(str);\n  if (Number.isNaN(num)) {\n    return Option.none();\n  }\n  return Option.some(num);\n}\n\nconst result = Option.some('42.5')\n  .andThen((str) =\u003e parseNumber(str))\n  .map((num) =\u003e num * 2)\n  .unwrapOr(0);\n\nconsole.log(result); // 85\n```\n\n#### Converting Between Result and Option\n\n```typescript\nimport { Result, Option } from 'kitsunejs';\n\n// Option to Result\nconst option = Option.some(42);\nconst result = option.toResult('No value provided');\nconsole.log(result.unwrap()); // 42\n\n// Result to Option\nconst okResult = Result.ok(42);\nconst optionFromResult = okResult.toOption();\nconsole.log(optionFromResult.unwrap()); // 42\n\nconst errResult = Result.err('error');\nconst noneFromErr = errResult.toOption();\nconsole.log(noneFromErr.isNone()); // true\n```\n\n#### Combining Multiple Options\n\n```typescript\nimport { Option } from 'kitsunejs';\n\nconst options = [\n  Option.some(1),\n  Option.some(2),\n  Option.some(3),\n];\n\n// All must be Some to get Some\u003cT[]\u003e\nconst allSome = Option.all(options);\nconsole.log(allSome.unwrap()); // [1, 2, 3]\n\n// Get the first Some, or None if all are None\nconst firstSome = Option.any([\n  Option.none(),\n  Option.some(42),\n  Option.none(),\n]);\nconsole.log(firstSome.unwrap()); // 42\n```\n\n## Documentation\n\nFor more detailed information, please refer to the following documentation:\n\n- **[API Reference](./docs/api-reference.md)** - Complete list of all methods with detailed explanations and examples\n- **[Rust Comparison](./docs/rust-comparison.md)** - Comparison table between Rust's `Result`/`Option` and kitsune\n- **[Recipes](./docs/recipes.md)** - Practical usage patterns and best practices for common scenarios\n\n## Contributing\n\nWe welcome contributions! Please see our [Contributing Guide](./CONTRIBUTING.md) for details on:\n\n- How to set up your development environment\n- Our coding standards and [Style Guide](./STYLE_GUIDE.md)\n- How to submit pull requests\n- Our commit message conventions\n\n## Quick API Reference\n\nBelow is a quick reference of available methods. For detailed documentation with examples, see [API Reference](./docs/api-reference.md).\n\n### Result\u003cT, E\u003e Methods\n\n- `isOk()`, `isErr()` - Type guards\n- `unwrap()`, `expect(message)` - Extract values (throws on error)\n- `unwrapErr()` - Extract error value (throws on Ok)\n- `unwrapOr(defaultValue)`, `unwrapOrElse(fn)` - Safe extraction with fallback\n- `map(fn)`, `mapErr(fn)` - Transform values\n- `and(other)`, `or(other)` - Combine Results\n- `andThen(fn)`, `orElse(fn)` - Chain operations\n- `toOption()` - Convert to Option\n\n### Result Static Methods\n\n- `Result.ok(value)`, `Result.err(error)` - Constructors\n- `Result.fromNullable(value, error)` - Convert nullable to Result\n- `Result.try(fn)`, `Result.tryAsync(fn)` - Exception handling\n- `Result.all(results)` - All must be `Ok` to return `Ok\u003cT[]\u003e`, otherwise returns the first `Err`\n- `Result.any(results)` - Returns the first `Ok`, or `Err\u003cE[]\u003e` containing all errors if none succeed\n\n### Option\u003cT\u003e Methods\n\n- `isSome()`, `isNone()`, `isSomeAnd(predicate)`, `isNoneOr(predicate)` - Type guards / conditional checks\n- `unwrap()`, `expect(message)` - Extract values (throws on None)\n- `unwrapOr(defaultValue)`, `unwrapOrElse(fn)` - Safe extraction with fallback\n- `map(fn)`, `mapOr(defaultValue, fn)`, `mapOrElse(defaultFn, fn)` - Transform values\n- `and(other)`, `or(other)` - Combine Options\n- `andThen(fn)` - Chain operations\n- `filter(predicate)` - Filter values\n- `toResult(error)`, `toResultElse(fn)` - Convert to Result\n\n### Option Static Methods\n\n- `Option.some(value)`, `Option.none()` - Constructors\n- `Option.fromNullable(value)` - Convert nullable to Option\n- `Option.all(options)` - All must be `Some` to return `Some\u003cT[]\u003e`, otherwise returns `None`\n- `Option.any(options)` - Returns the first `Some`, or `None` if all are `None`\n\n## License\n\nMIT © @tr-yasuda\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftr-yasuda%2Fkitsunejs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftr-yasuda%2Fkitsunejs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftr-yasuda%2Fkitsunejs/lists"}