{"_id":"@billdaddy/safekit","name":"@billdaddy/safekit","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@billdaddy/safekit","version":"0.1.0","description":"Zero-dependency Try monad for TypeScript. Capture exceptions as values — Try.of(() => risky()).map().recover().getOrElse(). Port of Java Vavr Try / Scala util.Try.","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"scripts":{"build":"tsup","typecheck":"tsc --noEmit","test":"node --experimental-vm-modules node_modules/.bin/jest --forceExit","prepublishOnly":"npm run typecheck && npm test && npm run build"},"keywords":["try","monad","vavr","scala","functional","error-handling","typescript","zero-dependencies"],"author":{"name":"trananhtung"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/trananhtung/safekit.git"},"devDependencies":{"@types/jest":"^30.0.0","jest":"^30.4.2","ts-jest":"^29.4.11","tsup":"^8.5.1","typescript":"^6.0.3"},"_id":"@billdaddy/safekit@0.1.0","gitHead":"f39357c704f21f0a41a27d8988b5d7fc54f82e96","bugs":{"url":"https://github.com/trananhtung/safekit/issues"},"homepage":"https://github.com/trananhtung/safekit#readme","_nodeVersion":"20.18.2","_npmVersion":"11.5.2","dist":{"integrity":"sha512-W9CYTBw0DWLVqwPbpLa8w3lYY5Qn/XO69HFwROtrFthm+iTuZdwKOmXAvfe8O6yIljyHNuegi6zwLpCxEbyL/A==","shasum":"18f88882509b0dfd70d48a8f344fd72c4e05dc8a","tarball":"https://registry.npmjs.org/@billdaddy/safekit/-/safekit-0.1.0.tgz","fileCount":9,"unpackedSize":51661,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDfuRIPQ2f2kaQdthQRk5/jtEgrKnEQ5F7ilsbzMmkaNAIhAKZSs8yZhwWEhXgRRDnkkfsgUSr5viCkNvpSj67kaMNo"}]},"_npmUser":{"name":"billdaddy","email":"tunganhtran94@gmail.com"},"directories":{},"maintainers":[{"name":"billdaddy","email":"tunganhtran94@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/safekit_0.1.0_1782286715266_0.05189822833683699"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-24T07:38:35.088Z","0.1.0":"2026-06-24T07:38:35.402Z","modified":"2026-06-24T07:38:35.558Z"},"maintainers":[{"name":"billdaddy","email":"tunganhtran94@gmail.com"}],"description":"Zero-dependency Try monad for TypeScript. Capture exceptions as values — Try.of(() => risky()).map().recover().getOrElse(). Port of Java Vavr Try / Scala util.Try.","homepage":"https://github.com/trananhtung/safekit#readme","keywords":["try","monad","vavr","scala","functional","error-handling","typescript","zero-dependencies"],"repository":{"type":"git","url":"git+https://github.com/trananhtung/safekit.git"},"author":{"name":"trananhtung"},"bugs":{"url":"https://github.com/trananhtung/safekit/issues"},"license":"MIT","readme":"# safekit\n\n[![All Contributors](https://img.shields.io/badge/all_contributors-1-orange.svg?style=flat-square)](#contributors-)\n\n> Zero-dependency Try monad for TypeScript.\n> Execute risky code and handle exceptions as values, not control flow.\n\n[![npm](https://img.shields.io/npm/v/@billdaddy/safekit)](https://www.npmjs.com/package/@billdaddy/safekit)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n\nPort of Java [Vavr Try](https://docs.vavr.io/#_try) / Scala [`scala.util.Try`](https://www.scala-lang.org/api/current/scala/util/Try.html). The existing `try-monad` npm package has been abandoned since 2017 (2 downloads/week).\n\n## Install\n\n```bash\nnpm install @billdaddy/safekit\n```\n\n## The problem Try solves\n\n`Result<T,E>` (neverthrow, resultkit) wraps **already-computed** values where you know the error type ahead of time. `Try<T>` **executes** a computation and captures **any thrown exception** automatically — no need to know what might throw:\n\n```typescript\n// Result: you write the error path manually\nconst result: Result<User, ApiError> = ok(user);\n\n// Try: exception is captured automatically\nconst t = Try.of(() => JSON.parse(rawJson));  // SyntaxError captured if thrown\n```\n\n## Quick start\n\n```typescript\nimport { Try } from \"@billdaddy/safekit\";\n\nconst result = Try.of(() => JSON.parse(rawInput))\n  .map(obj => obj.name as string)   // skipped if parse failed\n  .filter(name => name.length > 0)  // skipped if map failed\n  .recover(e => \"anonymous\")        // handles any prior failure\n  .get();                           // never throws — recover caught everything\n```\n\n## API\n\n### Try.of(fn)\n\nExecute a function and capture the result or exception:\n\n```typescript\nconst t1 = Try.of(() => parseInt(\"42\", 10));   // Success(42)\nconst t2 = Try.of(() => JSON.parse(\"bad\"));     // Failure(SyntaxError)\nconst t3 = Try.of(() => { throw \"string err\"; }); // Failure(\"string err\")\n```\n\n### Try.ofAsync(fn) — async computations\n\n```typescript\nconst t = await Try.ofAsync(async () => fetch(\"/api/users\").then(r => r.json()));\n// Never rejects — always resolves to Success or Failure\n\nif (t.isSuccess()) {\n  console.log(t.get()); // the parsed JSON\n} else {\n  console.error(t.getCause()); // the fetch/parse error\n}\n```\n\n### Transformations (fluent chaining)\n\n```typescript\nTry.of(() => \"  hello  \")\n  .map(s => s.trim())             // Success(\"hello\")\n  .map(s => s.toUpperCase())      // Success(\"HELLO\")\n  .filter(s => s.length > 3)      // Success(\"HELLO\") — passes\n  .flatMap(s => Try.of(() => s))  // Success(\"HELLO\")\n  .get()                          // \"HELLO\"\n```\n\nAll transformations on a `Failure` are no-ops — the original failure propagates:\n\n```typescript\nTry.of(() => { throw new Error(\"fail\"); })\n  .map(x => x)         // no-op\n  .filter(() => true)  // no-op\n  .getOrElse(\"default\") // \"default\"\n```\n\n### Recovery\n\n```typescript\n// recover — provide a fallback value\nconst t = Try.of(() => riskyParse())\n  .recover(e => fallbackValue);\n\n// recoverWith — provide a fallback Try computation\nconst t = Try.of(() => fetchPrimary())\n  .recoverWith(e => Try.of(() => fetchBackup()));\n```\n\n### Extracting values\n\n```typescript\nconst t = Try.of(() => compute());\n\nt.get()                              // value or rethrows\nt.getOrElse(defaultValue)            // value or default\nt.getOrElseGet(cause => handleErr()) // value or call fn(cause)\nt.getOrElseThrow(e => new MyErr(e))  // value or throw custom error\nt.toNullable()                       // value or null\nt.toArray()                          // [value] or []\nt.getCause()                         // cause (throws if Success)\n```\n\n### Fold\n\n```typescript\nconst message = Try.of(() => riskyOp()).fold(\n  value => `Success: ${value}`,\n  cause => `Error: ${(cause as Error).message}`,\n);\n```\n\n### Side effects with tap\n\n```typescript\nTry.of(() => loadConfig())\n  .tap(\n    config => logger.info(\"Loaded config\", config),\n    err => logger.error(\"Config load failed\", err),\n  )\n  .getOrElse(defaultConfig);\n```\n\n### Try.all — collect multiple results\n\n```typescript\nconst t = Try.all([\n  Try.of(() => parseA(rawA)),\n  Try.of(() => parseB(rawB)),\n  Try.of(() => parseC(rawC)),\n]);\n\nif (t.isSuccess()) {\n  const [a, b, c] = t.get();\n} else {\n  console.error(\"First failure:\", t.getCause());\n}\n```\n\n### instanceof narrowing\n\n```typescript\nimport { Try, Success, Failure } from \"@billdaddy/safekit\";\n\nconst t = Try.of(() => 42);\nif (t instanceof Success) {\n  t.get(); // TypeScript knows it's Success here\n} else {\n  t.getCause(); // TypeScript knows it's Failure here\n}\n```\n\n## Comparison with alternatives\n\n| Package | Lazy (captures exceptions) | TypeScript | Active | Zero deps |\n|---|---|---|---|---|\n| **safekit (Try)** | ✅ | ✅ | ✅ | ✅ |\n| neverthrow | ❌ (wraps already-computed) | ✅ | ✅ | ✅ |\n| resultkit | ❌ (wraps already-computed) | ✅ | ✅ | ✅ |\n| try-monad | ✅ | ❌ | ❌ (abandoned 2017) | ✅ |\n| fp-ts | ✅ (TaskEither) | ✅ | ✅ | ❌ (heavy) |\n| Java Vavr Try | ✅ | n/a | ✅ | n/a |\n| Scala Try | ✅ | n/a | ✅ | n/a |\n\n## Contributors ✨\n\nThis project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind are welcome — code, docs, bug reports, ideas, reviews! See the [emoji key](https://allcontributors.org/docs/en/emoji-key) for how each contribution is recognized, and open a PR or issue to get involved.\n\nThanks goes to these wonderful people:\n\n<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->\n<!-- prettier-ignore-start -->\n<!-- markdownlint-disable -->\n<table>\n  <tbody>\n    <tr>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/trananhtung\"><img src=\"https://avatars.githubusercontent.com/u/30992229?v=4?s=100\" width=\"100px;\" alt=\"Tung Tran\"/><br /><sub><b>Tung Tran</b></sub></a><br /><a href=\"https://github.com/trananhtung/safekit/commits?author=trananhtung\" title=\"Code\">💻</a> <a href=\"#maintenance-trananhtung\" title=\"Maintenance\">🚧</a></td>\n    </tr>\n  </tbody>\n</table>\n\n<!-- markdownlint-restore -->\n<!-- prettier-ignore-end -->\n\n<!-- ALL-CONTRIBUTORS-LIST:END -->\n\n## License\n\nMIT © [trananhtung](https://github.com/trananhtung)\n","readmeFilename":"README.md","_rev":"1-2e628ebdc45252b54bb3ac34862f62b1"}