{"_id":"@atlaschain/options","name":"@atlaschain/options","dist-tags":{"latest":"3.0.0"},"versions":{"3.0.0":{"name":"@atlaschain/options","version":"3.0.0","description":"Managing and serializing Rust-like Option types in JavaScript","exports":{"edge-light":{"import":"./dist/index.node.mjs","require":"./dist/index.node.cjs"},"workerd":{"import":"./dist/index.node.mjs","require":"./dist/index.node.cjs"},"browser":{"import":"./dist/index.browser.mjs","require":"./dist/index.browser.cjs"},"node":{"import":"./dist/index.node.mjs","require":"./dist/index.node.cjs"},"react-native":"./dist/index.native.mjs","types":"./dist/types/index.d.ts"},"browser":{"./dist/index.node.cjs":"./dist/index.browser.cjs","./dist/index.node.mjs":"./dist/index.browser.mjs"},"main":"./dist/index.node.cjs","module":"./dist/index.node.mjs","react-native":"./dist/index.native.mjs","types":"./dist/types/index.d.ts","type":"commonjs","sideEffects":false,"keywords":["blockchain","atlas","web3"],"scripts":{"compile:docs":"typedoc","compile:js":"tsup --config build-scripts/tsup.config.package.ts","compile:typedefs":"tsc -p ./tsconfig.declarations.json","dev":"jest -c ../../node_modules/@atlas/test-config/jest-dev.config.ts --rootDir . --watch","prepublishOnly":"pnpm pkg delete devDependencies","publish-impl":"npm view $npm_package_name@$npm_package_version > /dev/null 2>&1 || (pnpm publish --tag ${PUBLISH_TAG:-canary} --access public --no-git-checks && (([ -n \"${GITHUB_OUTPUT:-}\" ] && echo 'published=true' >> \"$GITHUB_OUTPUT\") || true) && (([ \"$PUBLISH_TAG\" != \"canary\" ] && ../build-scripts/maybe-tag-latest.ts $npm_package_name@$npm_package_version) || true))","publish-packages":"pnpm prepublishOnly && pnpm publish-impl","style:fix":"pnpm eslint --fix src && pnpm prettier --log-level warn --ignore-unknown --write ./*","test:lint":"TERM_OVERRIDE=\"${TURBO_HASH:+dumb}\" TERM=${TERM_OVERRIDE:-$TERM} jest -c ../../node_modules/@atlas/test-config/jest-lint.config.ts --rootDir . --silent","test:prettier":"TERM_OVERRIDE=\"${TURBO_HASH:+dumb}\" TERM=${TERM_OVERRIDE:-$TERM} jest -c ../../node_modules/@atlas/test-config/jest-prettier.config.ts --rootDir . --silent","test:treeshakability:browser":"agadoo dist/index.browser.mjs","test:treeshakability:native":"agadoo dist/index.node.mjs","test:treeshakability:node":"agadoo dist/index.native.mjs","test:typecheck":"tsc --noEmit","test:unit:browser":"TERM_OVERRIDE=\"${TURBO_HASH:+dumb}\" TERM=${TERM_OVERRIDE:-$TERM} jest -c ../../node_modules/@atlas/test-config/jest-unit.config.browser.ts --rootDir . --silent","test:unit:node":"TERM_OVERRIDE=\"${TURBO_HASH:+dumb}\" TERM=${TERM_OVERRIDE:-$TERM} jest -c ../../node_modules/@atlas/test-config/jest-unit.config.node.ts --rootDir . --silent"},"author":{"name":"Atlas Chain Foundation"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/anza-xyz/kit.git"},"bugs":{"url":"https://github.com/anza-xyz/kit/issues"},"browserslist":["supports bigint and not dead","maintained node versions"],"dependencies":{"@atlaschain/codecs-core":"workspace:*","@atlaschain/codecs-data-structures":"workspace:*","@atlaschain/codecs-numbers":"workspace:*","@atlaschain/codecs-strings":"workspace:*","@atlaschain/errors":"workspace:*"},"peerDependencies":{"typescript":">=5.3.3"},"engines":{"node":">=20.18.0"},"_id":"@atlaschain/options@3.0.0","homepage":"https://github.com/anza-xyz/kit#readme","_nodeVersion":"22.12.0","_npmVersion":"10.9.0","dist":{"integrity":"sha512-Q+KMYFxg9ljGqkfRstgJXWA0LBzMShyXDqpOF4SdtVApT71LkzfnkNanUlK5FD8onaz7Tanrbl0m5vY9DSLEXw==","shasum":"c1690ef678746399ab357359b26d3af018d0aa4b","tarball":"https://registry.npmjs.org/@atlaschain/options/-/options-3.0.0.tgz","fileCount":5,"unpackedSize":25331,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIEUpxZ4PBiQ0tADNpaseiQ/FDduWBdfVD8xpbGUiOzLSAiBMXXJNEBi/ambVm/F/rll6eFv45fsB+ptEaW4ejJE4vA=="}]},"_npmUser":{"name":"atlaschain","email":"jq@atlaschain.org"},"directories":{},"maintainers":[{"name":"atlaschain","email":"jq@atlaschain.org"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/options_3.0.0_1756885272220_0.961220812855599"},"_hasShrinkwrap":false}},"time":{"created":"2025-09-03T07:41:12.086Z","3.0.0":"2025-09-03T07:41:12.407Z","modified":"2025-09-03T07:41:12.663Z"},"maintainers":[{"name":"atlaschain","email":"jq@atlaschain.org"}],"description":"Managing and serializing Rust-like Option types in JavaScript","homepage":"https://github.com/anza-xyz/kit#readme","keywords":["blockchain","atlas","web3"],"repository":{"type":"git","url":"git+https://github.com/anza-xyz/kit.git"},"author":{"name":"Atlas Chain Foundation"},"bugs":{"url":"https://github.com/anza-xyz/kit/issues"},"license":"MIT","readme":"[![npm][npm-image]][npm-url]\n[![npm-downloads][npm-downloads-image]][npm-url]\n<br />\n[![code-style-prettier][code-style-prettier-image]][code-style-prettier-url]\n\n[code-style-prettier-image]: https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square\n[code-style-prettier-url]: https://github.com/prettier/prettier\n[npm-downloads-image]: https://img.shields.io/npm/dm/@atlas/options?style=flat\n[npm-image]: https://img.shields.io/npm/v/@atlas/options?style=flat\n[npm-url]: https://www.npmjs.com/package/@atlas/options\n\n# @atlas/options\n\nThis package allows us to manage and serialize Rust-like Option types in JavaScript. It can be used standalone, but it is also exported as part of Kit [`@atlas/kit`](https://github.com/anza-xyz/kit/tree/main/packages/kit).\n\nThis package is also part of the [`@atlas/codecs` package](https://github.com/anza-xyz/kit/tree/main/packages/codecs) which acts as an entry point for all codec packages as well as for their documentation.\n\n## Creating options\n\nIn Rust, we define optional values as an `Option<T>` type which can either be `Some(T)` or `None`. This is usually represented as `T | null` in the JavaScript world. The issue with this approach is it doesn't work with nested options. For instance, an `Option<Option<T>>` in Rust would become a `T | null | null` in JavaScript which is equivalent to `T | null`. That means, there is no way for us to represent the `Some(None)` value in JavaScript or any other nested option.\n\nTo solve this issue, this library provides an `Option<T>` union type that works very similarly to the Rust `Option<T>` type. It is defined as follows:\n\n```ts\ntype Option<T> = Some<T> | None;\ntype Some<T> = { __option: 'Some'; value: T };\ntype None = { __option: 'None' };\n```\n\nTo improve the developer experience, helper functions are available to help you create options. The type `T` of the option can either be inferred by TypeScript or explicitly provided.\n\n```ts\n// Create an option with a value.\nsome('Hello World');\nsome<number | string>(123);\n\n// Create an empty option.\nnone();\nnone<number | string>();\n```\n\n## Option helpers\n\nThis library also provides helper functions to help us identify and manage `Option` types.\n\nFor instance, you can use the `isSome` and `isNone` type guards to check whether a given `Option` is of the desired type.\n\n```ts\nisSome(some('Hello World')); // true\nisSome(none()); // false\n\nisNone(some('Hello World')); // false\nisNone(none()); // true\n```\n\nIf you are given a type `T | null`, you may also use the `wrapNullable` helper function to transform it into an `Option<T>` type.\n\n```ts\nwrapNullable('Hello world'); // Some<string>\nwrapNullable(null); // None\n```\n\n## Unwrapping options\n\nSeveral helpers are available to help you unwrap your options and access their potential value. For instance, the `unwrapOption` function transforms an `Option<T>` type into `T` if the value exits and `null` otherwise.\n\n```ts\nunwrapOption(some('Hello World')); // \"Hello World\"\nunwrapOption(none()); // null\n```\n\nIf `null` isn’t the value you want to use for `None` options, you may provide a custom fallback function as the second argument. Its return value will be assigned to `None` options.\n\n```ts\nunwrapOption(some('Hello World'), () => 'Default'); // \"Hello World\"\nunwrapOption(none(), () => 'Default'); // \"Default\"\n```\n\nNote that this `unwrapOption` function does not recursively unwrap nested options. You may use the `unwrapOptionRecursively` function for that purpose instead.\n\n```ts\nunwrapOptionRecursively(some(some(some('Hello World')))); // \"Hello World\"\nunwrapOptionRecursively(some(some(none<string>()))); // null\n```\n\nThe `unwrapOptionRecursively` function also walks any object and array it encounters and recursively unwraps any option it identifies in its journey without mutating any object or array.\n\n```ts\nunwrapOptionRecursively({\n    a: 'hello',\n    b: none(),\n    c: [{ c1: some(42) }, { c2: none() }],\n});\n// { a: \"hello\", b: null, c: [{ c1: 42 }, { c2: null }] }\n```\n\nThe `unwrapOptionRecursively` also accepts a fallback function as a second argument to provide custom values for `None` options.\n\n```ts\nunwrapOptionRecursively(\n    {\n        a: 'hello',\n        b: none(),\n        c: [{ c1: some(42) }, { c2: none() }],\n    },\n    () => 'Default',\n);\n// { a: \"hello\", b: \"Default\", c: [{ c1: 42 }, { c2: \"Default\" }] }\n```\n\n## Option codec\n\nThe `getOptionCodec` function behaves exactly the same as the [`getNullableCodec`](https://github.com/anza-xyz/kit/tree/main/packages/codecs-data-structures#nullable-codec) except that it encodes `Option<T>` types instead of `T | null` types.\n\nNamely, it accepts a codec of type `T` and returns a codec of type `Option<T>`. Note that, when encoding, `T` or `null` may also be provided directly as input and will be interpreted as `Some(T)` or `None` respectively. However, when decoding, the output will always be an `Option<T>` type.\n\nIt stores whether or not the item exists as a boolean prefix using a `u8` by default.\n\n```ts\nconst stringCodec = addCodecSizePrefix(getUtf8Codec(), getU32Codec());\n\ngetOptionCodec(stringCodec).encode('Hi');\ngetOptionCodec(stringCodec).encode(some('Hi'));\n// 0x01020000004869\n//   | |       └-- utf8 string content (\"Hi\").\n//   | └-- u32 string prefix (2 characters).\n//   └-- 1-byte prefix (Some).\n\ngetOptionCodec(stringCodec).encode(null);\ngetOptionCodec(stringCodec).encode(none());\n// 0x00\n//   └-- 1-byte prefix (None).\n```\n\nYou may provide a number codec as the `prefix` option of the `getOptionCodec` function to configure how to store the boolean prefix.\n\n```ts\nconst u32OptionStringCodec = getOptionCodec(stringCodec, {\n    prefix: getU32Codec(),\n});\n\nu32OptionStringCodec.encode(some('Hi'));\n// 0x01000000020000004869\n//   └------┘ 4-byte prefix (Some).\n\nu32OptionStringCodec.encode(none());\n// 0x00000000\n//   └------┘ 4-byte prefix (None).\n```\n\nAdditionally, if the item is a `FixedSizeCodec`, you may set the `noneValue` option to `\"zeroes\"` to also make the returned Option codec a `FixedSizeCodec`. To do so, it will pad `None` values with zeroes to match the length of existing values.\n\n```ts\nconst codec = getOptionCodec(\n    fixCodecSize(getUtf8Codec(), 8), // Only works with fixed-size items.\n    { noneValue: 'zeroes' },\n);\n\ncodec.encode(some('Hi'));\n// 0x014869000000000000\n//   | └-- 8-byte utf8 string content (\"Hi\").\n//   └-- 1-byte prefix (Some).\n\ncodec.encode(none());\n// 0x000000000000000000\n//   | └-- 8-byte of padding to make a fixed-size codec.\n//   └-- 1-byte prefix (None).\n```\n\nThe `noneValue` option can also be set to an explicit byte array to use as the padding for `None` values. Note that, in this case, the returned codec will not be a `FixedSizeCodec` as the byte array representing `None` values may be of any length.\n\n```ts\nconst codec = getOptionCodec(getUtf8Codec(), {\n    noneValue: new Uint8Array([255]), // 0xff means None.\n});\n\ncodec.encode(some('Hi'));\n// 0x014869\n//   | └-- 2-byte utf8 string content (\"Hi\").\n//   └-- 1-byte prefix (Some).\n\ncodec.encode(none());\n// 0x00ff\n//   | └-- 1-byte representing None (0xff).\n//   └-- 1-byte prefix (None).\n```\n\nLast but not least, the `prefix` option of the `getOptionCodec` function can also be set to `null`, meaning no prefix will be used to determine whether the item exists. In this case, the codec will rely on the `noneValue` option to determine whether the item is `None`.\n\n```ts\nconst codecWithZeroNoneValue = getOptionCodec(getU16Codec(), {\n    noneValue: 'zeroes', // 0x0000 means None.\n    prefix: null,\n});\ncodecWithZeroNoneValue.encode(some(42)); // 0x2a00\ncodecWithZeroNoneValue.encode(none()); // 0x0000\n\nconst codecWithCustomNoneValue = getOptionCodec(getU16Codec(), {\n    noneValue: new Uint8Array([255]), // 0xff means None.\n    prefix: null,\n});\ncodecWithCustomNoneValue.encode(some(42)); // 0x2a00\ncodecWithCustomNoneValue.encode(none()); // 0xff\n```\n\nFinally, note that if `prefix` is set to `null` and no `noneValue` is provided, the codec assume that the item exists if and only if some remaining bytes are available to decode. This could be useful to describe data structures that may or may not have additional data to the end of the buffer.\n\n```ts\nconst codec = getOptionCodec(getU16Codec(), { prefix: null });\ncodec.encode(some(42)); // 0x2a00\ncodec.encode(none()); // Encodes nothing.\ncodec.decode(new Uint8Array([42, 0])); // some(42)\ncodec.decode(new Uint8Array([])); // none()\n```\n\nTo recap, here are all the possible configurations of the `getOptionCodec` function, using a `u16` codec as an example.\n\n| `encode(some(42))` / `encode(none())` | No `noneValue` (default) | `noneValue: \"zeroes\"`       | Custom `noneValue` (`0xff`) |\n| ------------------------------------- | ------------------------ | --------------------------- | --------------------------- |\n| `u8` prefix (default)                 | `0x012a00` / `0x00`      | `0x012a00` / `0x000000`     | `0x012a00` / `0x00ff`       |\n| Custom `prefix` (`u16`)               | `0x01002a00` / `0x0000`  | `0x01002a00` / `0x00000000` | `0x01002a00` / `0x0000ff`   |\n| No `prefix`                           | `0x2a00` / `0x`          | `0x2a00` / `0x0000`         | `0x2a00` / `0xff`           |\n\nSeparate `getOptionEncoder` and `getOptionDecoder` functions are also available.\n\n```ts\nconst bytes = getOptionEncoder(getU32Encoder()).encode(some(42));\nconst value = getOptionDecoder(getU32Decoder()).decode(bytes);\n```\n\nTo read more about the available codecs and how to use them, check out the documentation of the main [`@atlas/codecs` package](https://github.com/anza-xyz/kit/tree/main/packages/codecs).\n","readmeFilename":"README.md","_rev":"1-2c91319910e1766fff663780118860e0"}