@solana/options 2.0.0-experimental.2ac1df6 → 2.0.0-experimental.2d1d49c

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +153 -4
  2. package/package.json +4 -4
package/README.md CHANGED
@@ -16,10 +16,159 @@
16
16
 
17
17
  This 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 the Solana JavaScript SDK [`@solana/web3.js@experimental`](https://github.com/solana-labs/solana-web3.js/tree/master/packages/library).
18
18
 
19
- ## Types
19
+ This package is also part of the [`@solana/codecs` package](https://github.com/solana-labs/solana-web3.js/tree/master/packages/codecs) which acts as an entry point for all codec packages as well as for their documentation.
20
20
 
21
- TODO
21
+ ## Creating options
22
22
 
23
- ## Functions
23
+ In 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.
24
24
 
25
- TODO
25
+ To 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:
26
+
27
+ ```ts
28
+ type Option<T> = Some<T> | None;
29
+ type Some<T> = { __option: 'Some'; value: T };
30
+ type None = { __option: 'None' };
31
+ ```
32
+
33
+ To 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.
34
+
35
+ ```ts
36
+ // Create an option with a value.
37
+ some('Hello World');
38
+ some<number | string>(123);
39
+
40
+ // Create an empty option.
41
+ none();
42
+ none<number | string>();
43
+ ```
44
+
45
+ ## Option helpers
46
+
47
+ This library also provides helper functions to help us identify and manage `Option` types.
48
+
49
+ For instance, you can use the `isSome` and `isNone` type guards to check whether a given `Option` is of the desired type.
50
+
51
+ ```ts
52
+ isSome(some('Hello World')); // true
53
+ isSome(none()); // false
54
+
55
+ isNone(some('Hello World')); // false
56
+ isNone(none()); // true
57
+ ```
58
+
59
+ If you are given a type `T | null`, you may also use the `wrapNullable` helper function to transform it into an `Option<T>` type.
60
+
61
+ ```ts
62
+ wrapNullable('Hello world'); // Some<string>
63
+ wrapNullable(null); // None
64
+ ```
65
+
66
+ ## Unwrapping options
67
+
68
+ Several 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.
69
+
70
+ ```ts
71
+ unwrapOption(some('Hello World')); // "Hello World"
72
+ unwrapOption(none()); // null
73
+ ```
74
+
75
+ If `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.
76
+
77
+ ```ts
78
+ unwrapOption(some('Hello World'), () => 'Default'); // "Hello World"
79
+ unwrapOption(none(), () => 'Default'); // "Default"
80
+ ```
81
+
82
+ Note that this `unwrapOption` function does not recursively unwrap nested options. You may use the `unwrapOptionRecursively` function for that purpose instead.
83
+
84
+ ```ts
85
+ unwrapOptionRecursively(some(some(some('Hello World')))); // "Hello World"
86
+ unwrapOptionRecursively(some(some(none<string>()))); // null
87
+ ```
88
+
89
+ The `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.
90
+
91
+ ```ts
92
+ unwrapOptionRecursively({
93
+ a: 'hello',
94
+ b: none(),
95
+ c: [{ c1: some(42) }, { c2: none() }],
96
+ });
97
+ // { a: "hello", b: null, c: [{ c1: 42 }, { c2: null }] }
98
+ ```
99
+
100
+ The `unwrapOptionRecursively` also accepts a fallback function as a second argument to provide custom values for `None` options.
101
+
102
+ ```ts
103
+ unwrapOptionRecursively(
104
+ {
105
+ a: 'hello',
106
+ b: none(),
107
+ c: [{ c1: some(42) }, { c2: none() }],
108
+ },
109
+ () => 'Default',
110
+ );
111
+ // { a: "hello", b: "Default", c: [{ c1: 42 }, { c2: "Default" }] }
112
+ ```
113
+
114
+ ## Option codec
115
+
116
+ The `getOptionCodec` function behaves exactly the same as the [`getNullableCodec`](https://github.com/solana-labs/solana-web3.js/tree/master/packages/codecs-data-structures#nullable-codec) except that it encodes `Option<T>` types instead of `T | null` types.
117
+
118
+ Namely, it accepts a codec of type `T` and returns a codec of type `Option<T>`. It stores whether or not the item exists as a boolean prefix using a `u8` by default.
119
+
120
+ ```ts
121
+ getOptionCodec(getStringCodec()).encode(some('Hi'));
122
+ // 0x01020000004869
123
+ // | | └-- utf8 string content ("Hi").
124
+ // | └-- u32 string prefix (2 characters).
125
+ // └-- 1-byte prefix (Some).
126
+
127
+ getOptionCodec(getStringCodec()).encode(none());
128
+ // 0x00
129
+ // └-- 1-byte prefix (None).
130
+ ```
131
+
132
+ You may provide a number codec as the `prefix` option of the `getOptionCodec` function to configure how to store the boolean prefix.
133
+
134
+ ```ts
135
+ const u32OptionStringCodec = getOptionCodec(getStringCodec(), {
136
+ prefix: getU32Codec(),
137
+ });
138
+
139
+ u32OptionStringCodec.encode(some('Hi'));
140
+ // 0x01000000020000004869
141
+ // └------┘ 4-byte prefix (Some).
142
+
143
+ u32OptionStringCodec.encode(none());
144
+ // 0x00000000
145
+ // └------┘ 4-byte prefix (None).
146
+ ```
147
+
148
+ Additionally, if the item is a `FixedSizeCodec`, you may set the `fixed` option to `true` to also make the returned option codec a `FixedSizeCodec`. To do so, it will pad `null` values with zeroes to match the length of existing values.
149
+
150
+ ```ts
151
+ const fixedOptionStringCodec = getOptionCodec(
152
+ getStringCodec({ size: 8 }), // Only works with fixed-size items.
153
+ { fixed: true },
154
+ );
155
+
156
+ fixedOptionStringCodec.encode(some('Hi'));
157
+ // 0x014869000000000000
158
+ // | └-- 8-byte utf8 string content ("Hi").
159
+ // └-- 1-byte prefix (Some).
160
+
161
+ fixedOptionStringCodec.encode(none());
162
+ // 0x000000000000000000
163
+ // | └-- 8-byte of padding to make a fixed-size codec.
164
+ // └-- 1-byte prefix (None).
165
+ ```
166
+
167
+ Separate `getOptionEncoder` and `getOptionDecoder` functions are also available.
168
+
169
+ ```ts
170
+ const bytes = getOptionEncoder(getStringEncoder()).encode(some('Hi'));
171
+ const value = getOptionDecoder(getStringDecoder()).decode(bytes);
172
+ ```
173
+
174
+ To read more about the available codecs and how to use them, check out the documentation of the main [`@solana/codecs` package](https://github.com/solana-labs/solana-web3.js/tree/master/packages/codecs).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solana/options",
3
- "version": "2.0.0-experimental.2ac1df6",
3
+ "version": "2.0.0-experimental.2d1d49c",
4
4
  "description": "Managing and serializing Rust-like Option types in JavaScript",
5
5
  "exports": {
6
6
  "browser": {
@@ -49,8 +49,8 @@
49
49
  "node": ">=17.4"
50
50
  },
51
51
  "dependencies": {
52
- "@solana/codecs-core": "2.0.0-experimental.2ac1df6",
53
- "@solana/codecs-numbers": "2.0.0-experimental.2ac1df6"
52
+ "@solana/codecs-core": "2.0.0-experimental.2d1d49c",
53
+ "@solana/codecs-numbers": "2.0.0-experimental.2d1d49c"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@solana/eslint-config-solana": "^1.0.2",
@@ -86,7 +86,7 @@
86
86
  "compile:js": "tsup --config build-scripts/tsup.config.package.ts",
87
87
  "compile:typedefs": "tsc -p ./tsconfig.declarations.json && node node_modules/build-scripts/add-js-extension-to-types.mjs",
88
88
  "dev": "jest -c node_modules/test-config/jest-dev.config.ts --rootDir . --watch",
89
- "publish-packages": "pnpm publish --tag experimental --access public --no-git-checks",
89
+ "publish-packages": "npm view $npm_package_name@$npm_package_version > /dev/null 2>&1 || pnpm publish --tag experimental --access public --no-git-checks",
90
90
  "style:fix": "pnpm eslint --fix src/* && pnpm prettier -w src/* package.json",
91
91
  "test:lint": "jest -c node_modules/test-config/jest-lint.config.ts --rootDir . --silent",
92
92
  "test:prettier": "jest -c node_modules/test-config/jest-prettier.config.ts --rootDir . --silent",