@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.
- package/README.md +153 -4
- 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
|
-
|
|
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
|
-
|
|
21
|
+
## Creating options
|
|
22
22
|
|
|
23
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
53
|
-
"@solana/codecs-numbers": "2.0.0-experimental.
|
|
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",
|