{"id":48293136,"url":"https://github.com/boyeln/skyr","last_synced_at":"2026-04-04T23:21:32.760Z","repository":{"id":318576791,"uuid":"1071878516","full_name":"boyeln/skyr","owner":"boyeln","description":"Type-safe error handling for TypeScript, inspired by Rust's Result type. Method chaining, functional composition with pipe(), automatic async propagation, and optional dependency injection.","archived":false,"fork":false,"pushed_at":"2026-03-19T14:41:35.000Z","size":114,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-20T07:10:00.328Z","etag":null,"topics":["dependency-injection","error-handling","error-tracking","monad","railway-programming","result","typescript"],"latest_commit_sha":null,"homepage":"https://jsr.io/@thefridge/skyr","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/boyeln.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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":"AGENTS.md","dco":null,"cla":null}},"created_at":"2025-10-08T00:14:06.000Z","updated_at":"2026-03-09T12:44:02.000Z","dependencies_parsed_at":"2025-10-08T02:35:33.588Z","dependency_job_id":"3ae4d7b1-dd29-4eaf-950d-0aa8f0733594","html_url":"https://github.com/boyeln/skyr","commit_stats":null,"previous_names":["boyeln/skyr"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/boyeln/skyr","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boyeln%2Fskyr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boyeln%2Fskyr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boyeln%2Fskyr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boyeln%2Fskyr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/boyeln","download_url":"https://codeload.github.com/boyeln/skyr/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boyeln%2Fskyr/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31418291,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T20:09:54.854Z","status":"ssl_error","status_checked_at":"2026-04-04T20:09:44.350Z","response_time":60,"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":["dependency-injection","error-handling","error-tracking","monad","railway-programming","result","typescript"],"created_at":"2026-04-04T23:21:32.258Z","updated_at":"2026-04-04T23:21:32.751Z","avatar_url":"https://github.com/boyeln.png","language":"TypeScript","readme":"# skyr\n\nType-safe error handling for TypeScript, inspired by Rust's `Result` type.\nMethod chaining, functional composition with `pipe()`, automatic async\npropagation, and optional dependency injection.\n\n## Installation\n\n```bash\n# Deno\ndeno add jsr:@thefridge/skyr\n# Bun\nbunx jsr add @thefridge/skyr\n# pnpm\npnpm add jsr:@thefridge/skyr\n# npm\nnpx jsr add @thefridge/skyr\n# Yarn\nyarn add jsr:@thefridge/skyr\n```\n\n## Core Concepts\n\n### Results Instead of Exceptions\n\nA `Result\u003cT, E\u003e` is either **ok** (containing a value of type `T`) or an **err**\n(containing a structured error with a typed `code`, a `message`, and an optional\n`cause`). This replaces `try/catch` with values you can inspect, transform, and\ncompose.\n\n```typescript\nimport * as R from \"@thefridge/skyr\";\n\nfunction validateEmail(email: string) {\n\tif (!email.includes(\"@\")) {\n\t\treturn R.err(\"INVALID_EMAIL\", \"Email must contain @\");\n\t}\n\treturn R.ok(email);\n}\n\nconst result = validateEmail(\"user@example.com\");\n\nif (result.isOk()) {\n\tconsole.log(result.value); // \"user@example.com\"\n} else {\n\tconsole.log(result.code); // \"INVALID_EMAIL\"\n\tconsole.log(result.message); // \"Email must contain @\"\n}\n```\n\nError codes are string literals tracked by the type system. TypeScript knows\nexactly which errors a function can produce and autocompletes them for you.\n\nResults are plain objects with a `_tag` discriminant:\n\n- `ok(value)` creates `{ _tag: \"Ok\", value, ... }`\n- `err(code, message, cause?)` creates\n  `{ _tag: \"Err\", code, message, cause, ... }`\n\nThe `cause` field is `undefined` when not provided.\n\nEvery Result comes with methods for transforming and inspecting it. Type `.` in\nyour IDE and see what's available.\n\n### Letting TypeScript Infer Result Types\n\nThere's a subtlety with the example above. Without an explicit return type\nannotation, TypeScript infers the return type as\n`Result\u003cstring, never\u003e | Result\u003cnever, \"INVALID_EMAIL\"\u003e`, a union of two\nseparate Result types rather than a single unified\n`Result\u003cstring, \"INVALID_EMAIL\"\u003e`.\n\nYou could fix this by adding a return type annotation, but it's generally safer\nto let TypeScript infer return types whenever possible. Annotations can drift\nout of sync with the implementation and mask bugs.\n\nInstead, wrap the function with `fn()`:\n\n```typescript\nimport * as R from \"@thefridge/skyr\";\n\nconst validateEmail = R.fn((email: string) =\u003e {\n\tif (!email.includes(\"@\")) {\n\t\treturn R.err(\"INVALID_EMAIL\", \"Email must contain @\");\n\t}\n\treturn R.ok(email);\n});\n// (email: string) =\u003e Result\u003cstring, \"INVALID_EMAIL\"\u003e\n```\n\n`fn()` collapses all the Result branches into a single, clean `Result\u003cT, E\u003e`\ntype. No annotation needed; the ok value type and all possible error codes are\ninferred automatically.\n\nThis is the simplest use of `fn()`. It also supports\n[generator functions for railway-style programming and dependency injection](#dependency-injection-with-fn),\ncovered later.\n\n### Type Guards\n\nResults have `.isOk()` and `.isErr()` methods that act as type guards:\n\n```typescript\nif (result.isOk()) {\n\tresult.value; // T\n} else {\n\tresult.code; // E\n\tresult.message; // string\n\tresult.cause; // unknown | undefined\n}\n```\n\nStandalone functions `isOk()`, `isErr()`, and `isResult()` are also available:\n\n```typescript\nR.isOk(result); // narrows to Ok\u003cT\u003e\nR.isErr(result); // narrows to Err\u003cE\u003e\nR.isResult(value); // checks if any unknown value is a Result\n```\n\n`isResult(value)` checks whether any unknown value is a Result. A value is\nconsidered a Result if it's a non-null object with `_tag` equal to `\"Ok\"` or\n`\"Err\"`.\n\n### Method Chaining\n\nEvery Result has methods for transformation, error handling, and value\nextraction. Chain them directly, no imports or special syntax needed:\n\n```typescript\nconst message = validateEmail(\"User@Example.com\")\n\t.map((email) =\u003e email.toLowerCase().trim())\n\t.map((email) =\u003e `Welcome, ${email}!`)\n\t.match({\n\t\tok: (greeting) =\u003e greeting,\n\t\terr: (e) =\u003e `Error: ${e.message}`,\n\t});\n\nconsole.log(message); // \"Welcome, user@example.com!\"\n```\n\nMethods skip over errors automatically. If `validateEmail` returns an error, the\n`.map()` calls are never executed and the error flows straight to `.match()`.\n\n### Async Propagation\n\nWhen any step returns a `Promise`, the result becomes an `AsyncResult`, a\nwrapper around `Promise\u003cResult\u003e` with the same methods. This is called **async\npoison**: once async, always async (until you `await`).\n\n```typescript\nconst result = validateEmail(\"user@example.com\") // Result\u003cstring, ...\u003e\n\t.map((email) =\u003e fetchUser(email)) // returns Promise → AsyncResult\n\t.map((user) =\u003e user.name); // still async, still chainable\n// Type: AsyncResult\u003cstring, ...\u003e\n\nconst finalResult = await result;\n// Type: Result\u003cstring, ...\u003e (back to sync)\n```\n\n`AsyncResult` is `PromiseLike`, so you can `await` it to get back a sync\n`Result` with all its methods.\n\n### Functional Composition with `pipe()`\n\nFor those who prefer a functional style, `pipe()` threads a value through a\nsequence of functions left-to-right. All methods are also available as\nstandalone operators:\n\n```typescript\nimport * as R from \"@thefridge/skyr\";\n\nconst message = R.pipe(\n\tvalidateEmail(\"User@Example.com\"),\n\tR.map((email) =\u003e email.toLowerCase().trim()),\n\tR.map((email) =\u003e `Welcome, ${email}!`),\n\tR.match({\n\t\tok: (greeting) =\u003e greeting,\n\t\terr: (e) =\u003e `Error: ${e.message}`,\n\t}),\n);\n```\n\nStandalone operators accept both `Result` and `Promise\u003cResult\u003e` as input and\npropagate async automatically and work interchangeably with both styles.\n\n### Panics: Unexpected Throws\n\nIf your code calls something that might throw or reject, or you're unsure\nwhether it could, wrap it with `fromThrowable()` to convert it into a Result\nsafely. This is the recommended approach for any code you don't fully control.\n\nIf a callback passed to `map`, `mapErr`, or `match` throws synchronously without\nbeing wrapped, a `Panic` is thrown, halting execution immediately unless caught:\n\n```typescript\nR.ok(42).map(() =\u003e {\n\tthrow new Error(\"oops\");\n});\n// Throws: Panic(\"map() callback threw — use fromThrowable() for unsafe code\")\n//   cause: Error(\"oops\")\n```\n\n`Panic` extends `Error`, so you get a full stack trace. Catch it at the top\nlevel with `instanceof R.Panic` if needed.\n\n**Async functions** (Promises) returned from callbacks are automatically wrapped\nwith `fromThrowable` implicitly, so rejections become `UNKNOWN_ERR` results\ninstead of Panics. However, if you use multiple async functions, all their\nerrors will share the same `UNKNOWN_ERR` code, making it impossible to\ndistinguish between them. Wrapping each one with `fromThrowable` and a dedicated\nerror code is the recommended approach.\n\n## Methods\n\nEvery `Result` has the following methods. `AsyncResult` has the same methods,\nbut they always return `AsyncResult` (or `Promise` for terminal operations).\n\n### `.map(fn)`\n\nTransforms the ok value. Skips if the result is an error.\n\n```typescript\nR.ok(5)\n\t.map((n) =\u003e n * 2)\n\t.map((n) =\u003e `Value: ${n}`);\n// Result\u003cstring, never\u003e → Ok(\"Value: 10\")\n```\n\nIf `fn` returns a `Result`, it's automatically flattened (no nested Results). If\nit returns a `Promise`, the result becomes an `AsyncResult`. The Promise is\nautomatically handled like `fromThrowable`: resolved values become ok, rejected\nPromises become `UNKNOWN_ERR`.\n\n### `.mapErr(fn | handlers)`\n\nTransforms or recovers from errors. Has two forms:\n\n**Function form** - transform all errors:\n\n```typescript\nR.err(\"NOT_FOUND\", \"User not found\")\n\t.mapErr((e) =\u003e R.err(\"DEFAULT_ERROR\", e.message));\n// Result\u003cnever, \"DEFAULT_ERROR\"\u003e\n```\n\n**Handler object** - handle specific error codes with autocomplete:\n\n```typescript\ntype AppError = \"NOT_FOUND\" | \"TIMEOUT\" | \"AUTH_FAILED\";\n\ndeclare function fetchUser(id: string): R.Result\u003cUser, AppError\u003e;\n\nconst result = fetchUser(\"123\").mapErr({\n\tNOT_FOUND: () =\u003e R.ok(guestUser), // recover with ok()\n\tTIMEOUT: () =\u003e defaultUser, // recover with plain value (same as ok())\n\t// AUTH_FAILED not listed → passes through unchanged\n});\n// Result\u003cUser, \"AUTH_FAILED\"\u003e\n```\n\nHandlers get autocomplete for the available error codes. Each handler receives\nthe narrowed `Err\u003c\"CODE\"\u003e` and can:\n\n- Return `ok(value)` or a **plain value** to recover (both treated as success)\n- Return `err(code, message)` to transform the error\n\nUnhandled codes pass through unchanged.\n\n### `.match({ ok, err })`\n\nPattern match both cases and leave the Result world:\n\n```typescript\nconst label = R.ok(42).match({\n\tok: (n) =\u003e `Got ${n}`,\n\terr: (e) =\u003e `Error: ${e.code}`,\n});\n// \"Got 42\"\n```\n\nIf either handler returns a `Result`, the output is a `Result`. Otherwise it's a\nplain value. On `AsyncResult`, `.match()` returns a `Promise`.\n\n### `.inspect(fn)` / `.inspectErr(fn)`\n\nRun side effects (logging, metrics) without changing the Result:\n\n```typescript\nvalidateEmail(\"user@example.com\")\n\t.inspect((email) =\u003e console.log(\"Valid:\", email))\n\t.inspectErr((e) =\u003e console.error(\"Failed:\", e.code))\n\t.map((email) =\u003e email.toLowerCase());\n```\n\nThe callback's return value is ignored; the original Result is always returned\nunchanged. If the callback throws or the returned Promise rejects, the error is\nsilently swallowed and the original Result passes through. Side effects should\nnever break the pipeline.\n\n### `.unwrap()` / `.unwrapOr(default)`\n\nExtract values from Results:\n\n```typescript\n// unwrap() extracts the ok value, or returns undefined on error\nconst value = R.ok(42).map((n) =\u003e n * 2).unwrap();\n// number | undefined → 84\n\nconst missing = R.err(\"NOT_FOUND\", \"gone\").unwrap();\n// undefined\n\n// Works great with optional chaining\nfetchUser(\"123\").unwrap()?.name;\n\n// Or non-null assertion when you know it's Ok\nR.ok(42).unwrap()!;\n\n// unwrapOr() extracts the ok value, or returns the default on error\nconst fallback = R.err(\"ERROR\", \"Something went wrong\").unwrapOr(0);\n// 0\n```\n\nOn `AsyncResult`, `.unwrap()` returns `Promise\u003cT | undefined\u003e` and\n`.unwrapOr(default)` returns `Promise\u003cT | D\u003e`.\n\n## Converting Throwing Code\n\n### `fromThrowable(fn | promise, mapper?)`\n\nConvert code that throws (or Promises that reject) into Results:\n\n```typescript\n// Wrap a function call\nconst result = R.fromThrowable(\n\t() =\u003e JSON.parse('{\"name\": \"Alice\"}'),\n\t(err) =\u003e R.err(\"PARSE_ERROR\", \"Invalid JSON\", err),\n);\n// Result\u003cany, \"PARSE_ERROR\"\u003e\n\n// Wrap a Promise\nconst response = await R.fromThrowable(\n\tfetch(\"https://api.example.com\"),\n\t(err) =\u003e R.err(\"FETCH_ERROR\", \"Request failed\", err),\n);\n// Result\u003cResponse, \"FETCH_ERROR\"\u003e\n```\n\nWithout a mapper, errors become `\"UNKNOWN_ERR\"`.\n\nThe function overload calls the function synchronously and catches any thrown\nerror. If you have a Promise, use the Promise overload directly.\n\n### `wrapThrowable(fn, mapper?)`\n\nLike `fromThrowable`, but returns a reusable wrapper function:\n\n```typescript\nconst safeParse = R.wrapThrowable(\n\t(str: string) =\u003e JSON.parse(str),\n\t(err) =\u003e R.err(\"PARSE_ERROR\", \"Invalid JSON\", err),\n);\n\nsafeParse('{\"valid\": true}'); // Ok({valid: true})\nsafeParse(\"nope\"); // Err(\"PARSE_ERROR\")\n```\n\n## Dependency Injection with `fn()`\n\n`fn()` is a function builder. You pass it a generator and get back a function\nyou can call just like any other. The difference is that inside the generator\nyou get two superpowers:\n\n1. **Dependency requests** - `yield* R.use(Dep)` gives you an implementation of\n   a dependency without worrying about how to acquire it. Think of it like a\n   function parameter, except you don't have to pass it in at the call site.\n   When one `fn()` function calls another, unmet dependencies propagate up\n   automatically, no prop drilling required. You can also choose to supply some\n   dependencies but not all, and the rest keep propagating.\n\n2. **Result unwrapping** - `yield* someResult` extracts the success value from\n   any `Result` or `AsyncResult`. If it's an error, the function short-circuits\n   (early return) and the error propagates to the caller, just like the\n   non-generator version (`fn(() =\u003e ...)`).\n\nThe return type of an `fn()` function is `Fn\u003cArgs, Success, Errors, Deps\u003e`:\n\n- **Args** - the parameter list of your generator (e.g. `(email: string)` →\n  `[string]`)\n- **Success** - the unwrapped success type\n- **Errors** - a union of all error codes, accumulated from every `yield*` call\n- **Deps** - a union of all unmet dependencies, accumulated from every\n  `yield* R.use()` call\n\nThe `Fn` you get back is only callable once all dependencies are injected (via\n`.inject()`), at which point the `Deps` part of the type becomes `never`.\n\n### Declaring Dependencies\n\n```typescript\nconst Database = R.dependency\u003c{\n\tfindUser: (email: string) =\u003e Promise\u003cUser | null\u003e;\n}\u003e()(\"database\");\n\nconst Logger = R.dependency\u003c{\n\tinfo: (msg: string) =\u003e void;\n}\u003e()(\"logger\");\n```\n\n\u003e **Convention:** Dependencies and functions that still need injection use\n\u003e PascalCase. After injection, use camelCase to signal \"ready to call.\"\n\n### Creating Functions\n\n```typescript\nconst GetUser = R.fn(function* (email: string) {\n\tconst db = yield* R.use(Database);\n\tconst logger = yield* R.use(Logger);\n\n\tlogger.info(`Looking up ${email}`);\n\n\tconst validEmail = yield* validateEmail(email);\n\tconst user = yield* R.fromThrowable(db.findUser(validEmail));\n\n\tif (!user) return R.err(\"NOT_FOUND\", \"User not found\");\n\n\treturn R.ok(user);\n});\n// Type: Fn\u003c[string], User, \"INVALID_EMAIL\" | \"NOT_FOUND\" | \"UNKNOWN_ERR\", Database | Logger\u003e\n```\n\nKey points:\n\n- `yield* R.use(Database)` - acquires a dependency from the DI context\n- `yield* validateEmail(email)` - unwraps a Result; short-circuits on failure\n- `yield* R.fromThrowable(...)` - catches throws/rejections, converting them to\n  a Result, then unwraps it\n- Error types accumulate automatically across all `yield*` calls\n- Dependency types accumulate automatically across all `yield* R.use()` calls\n\n### Injecting Dependencies\n\nUse `.inject()` to provide implementations:\n\n```typescript\nconst getUser = GetUser.inject(\n\tDatabase.impl({ findUser: async (email) =\u003e db.query(email) }),\n\tLogger.impl({ info: console.log }),\n);\n\n// All dependencies satisfied, now callable\nconst result = await getUser(\"user@example.com\");\n```\n\nIf you try to call a function before all dependencies are injected, TypeScript\nshows an error:\n\n```\nERROR - Missing dependencies: database, logger. Use inject() first.\n```\n\nAt runtime, calling with missing dependencies throws an `Error` with a message\nlike `Missing dependency: \"database\". Use inject() to provide this dependency.`\n\nInjection can be done incrementally:\n\n```typescript\nconst withDb = GetUser.inject(Database.impl({/* ... */}));\n// Still needs Logger\n\nconst getUser = withDb.inject(Logger.impl({/* ... */}));\n// Fully callable\n```\n\nThe standalone `inject()` operator works the same way inside `pipe()`:\n\n```typescript\nconst getUser = R.pipe(\n\tGetUser,\n\tR.inject(\n\t\tDatabase.impl({ findUser: async (email) =\u003e db.query(email) }),\n\t\tLogger.impl({ info: console.log }),\n\t),\n);\n```\n\n### Nested Functions\n\nWhen one `fn()` uses another via `yield* R.use(ChildFn)`, the child's\ndependencies are inherited by the parent:\n\n```typescript\nconst CheckPermissions = R.fn(function* (userId: string) {\n\tconst db = yield* R.use(Database);\n\t// ...\n\treturn R.ok(canAccess);\n});\n\nconst LoginUser = R.fn(function* (email: string, password: string) {\n\tconst logger = yield* R.use(Logger);\n\n\tconst user = yield* getUser(email);\n\n\tconst checkPerms = yield* R.use(CheckPermissions);\n\tconst canAccess = yield* checkPerms(user.id);\n\n\treturn R.ok(user);\n});\n// Dependencies: Logger | Database (Database inherited from CheckPermissions)\n```\n\nThe two-step pattern, `yield* R.use(Fn)` then `yield* callable(args)`, separates\ndependency resolution from execution, keeping the control flow explicit.\n\n## API Reference\n\n### Constructors\n\n| Function                     | Description                       |\n| ---------------------------- | --------------------------------- |\n| `ok(value)`                  | Create an Ok result with methods  |\n| `err(code, message, cause?)` | Create an Err result with methods |\n\n### Type Guards\n\n| Function / Method | Description                                    |\n| ----------------- | ---------------------------------------------- |\n| `.isOk()`         | Narrow to `Ok\u003cT\u003e` (method)                     |\n| `.isErr()`        | Narrow to `Err\u003cE\u003e` (method)                    |\n| `isOk(result)`    | Narrow to `Ok\u003cT\u003e` (standalone)                 |\n| `isErr(result)`   | Narrow to `Err\u003cE\u003e` (standalone)                |\n| `isResult(value)` | Check if value has `_tag` of `\"Ok\"` or `\"Err\"` |\n\n### Methods on Result\n\n| Method                       | Description                                       |\n| ---------------------------- | ------------------------------------------------- |\n| `.map(fn)`                   | Transform ok value; Panics on sync throw          |\n| `.mapErr(fn)`                | Transform error; plain values treated as recovery |\n| `.mapErr({ CODE: handler })` | Handle specific error codes; Panics on sync throw |\n| `.match({ ok, err })`        | Pattern match both cases; Panics on sync throw    |\n| `.inspect(fn)`               | Side effect on ok; errors silently swallowed      |\n| `.inspectErr(fn)`            | Side effect on error; errors silently swallowed   |\n| `.unwrap()`                  | Extract ok value or return `undefined`            |\n| `.unwrapOr(default)`         | Extract ok value or return default                |\n\n### AsyncResult\n\n`AsyncResult\u003cT, E\u003e` wraps a `Promise\u003cResult\u003cT, E\u003e\u003e` and exposes the same methods\nas `Result`. All methods return `AsyncResult` (async poison), except terminal\noperations (`.match()`, `.unwrap()`, `.unwrapOr()`) which return `Promise`.\n`AsyncResult` is `PromiseLike`; `await` it to get a sync `Result`.\n\n### Standalone Operators (for `pipe()`)\n\n| Operator                    | Description                                       |\n| --------------------------- | ------------------------------------------------- |\n| `map(fn)`                   | Transform ok value; Panics on sync throw          |\n| `mapErr(fn)`                | Transform error; plain values treated as recovery |\n| `mapErr({ CODE: handler })` | Handle specific error codes; Panics on sync throw |\n| `match({ ok, err })`        | Pattern match both cases; Panics on sync throw    |\n| `inspect(fn)`               | Side effect on ok; errors silently swallowed      |\n| `inspectErr(fn)`            | Side effect on error; errors silently swallowed   |\n| `unwrap`                    | Extract ok value or return `undefined`            |\n| `unwrapOr(default)`         | Extract ok value or return default                |\n\n### Converters\n\n| Function                          | Description                         |\n| --------------------------------- | ----------------------------------- |\n| `fromThrowable(fn, mapper?)`      | Convert throwing function to Result |\n| `fromThrowable(promise, mapper?)` | Convert Promise to async Result     |\n| `wrapThrowable(fn, mapper?)`      | Wrap function to return Results     |\n\n### Errors\n\n| Type    | Description                                                 |\n| ------- | ----------------------------------------------------------- |\n| `Panic` | Thrown on sync throw in operator callbacks; extends `Error` |\n\n### Dependency Injection\n\n| Function               | Description                                   |\n| ---------------------- | --------------------------------------------- |\n| `dependency\u003cT\u003e()(key)` | Declare a dependency type                     |\n| `fn(generator)`        | Create function with DI and Result unwrapping |\n| `fn(func)`             | Unify Result return type                      |\n| `use(dep)`             | Acquire dependency inside a generator         |\n| `use(Fn)`              | Get contextualized callable for nested Fn     |\n| `.inject(...impls)`    | Provide dependency implementations (method)   |\n| `inject(...impls)`     | Provide dependency implementations (operator) |\n\n## License\n\nMIT\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fboyeln%2Fskyr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fboyeln%2Fskyr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fboyeln%2Fskyr/lists"}