Skip to content

Commit d0fdc21

Browse files
committed
Add dynamic-codecs package
1 parent 62fcd21 commit d0fdc21

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+2023
-3
lines changed

.changeset/witty-wasps-look.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@codama/dynamic-codecs': minor
3+
---
4+
5+
Add new `dynamic-codecs` package to create `Codecs` from nodes on demand
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports = {
2+
extends: ['../../.eslintrc.js'],
3+
rules: {
4+
'sort-keys-fix/sort-keys-fix': 'off',
5+
},
6+
};

packages/dynamic-codecs/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
dist/
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
dist/
2+
e2e/
3+
test-ledger/
4+
target/
5+
CHANGELOG.md

packages/dynamic-codecs/LICENSE

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
MIT License
2+
3+
Copyright (c) 2024 Codama
4+
5+
Permission is hereby granted, free of charge, to any person obtaining
6+
a copy of this software and associated documentation files (the
7+
"Software"), to deal in the Software without restriction, including
8+
without limitation the rights to use, copy, modify, merge, publish,
9+
distribute, sublicense, and/or sell copies of the Software, and to
10+
permit persons to whom the Software is furnished to do so, subject to
11+
the following conditions:
12+
13+
The above copyright notice and this permission notice shall be
14+
included in all copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

packages/dynamic-codecs/README.md

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# Codama ➤ Dynamic Codecs
2+
3+
[![npm][npm-image]][npm-url]
4+
[![npm-downloads][npm-downloads-image]][npm-url]
5+
6+
[npm-downloads-image]: https://img.shields.io/npm/dm/@codama/dynamic-codecs.svg?style=flat
7+
[npm-image]: https://img.shields.io/npm/v/@codama/dynamic-codecs.svg?style=flat&label=%40codama%2Fdynamic-codecs
8+
[npm-url]: https://www.npmjs.com/package/@codama/dynamic-codecs
9+
10+
This package provides a set of helpers that provide `Codecs` for Codama nodes that describe data.
11+
12+
## Installation
13+
14+
```sh
15+
pnpm install @codama/dynamic-codecs
16+
```
17+
18+
> [!NOTE]
19+
> This package is **not** included in the main [`codama`](../library) package.
20+
21+
## Functions
22+
23+
### `getNodeCodec(path, options?)`
24+
25+
Given the full `NodePath` of a node inside a Codama IDL, returns a `Codec<unknown>` (as defined in `@solana/codecs`) that enables encoding and decoding data for that node.
26+
27+
```ts
28+
const codec = getNodeCodec([root, program, definedType]);
29+
const bytes = codec.encode(someData);
30+
const decodedData = codec.decode(bytes);
31+
```
32+
33+
Note that it is important to provide the full `NodePath` of the node in order to properly follow link nodes inside the Codama IDL. Here is a more complex example illustrating how link nodes are resolved:
34+
35+
```ts
36+
// Here we define a program with two types, one of which is a link to the other.
37+
const root = rootNode(
38+
programNode({
39+
definedTypes: [
40+
definedTypeNode({ name: 'slot', type: numberTypeNode('u64') }),
41+
definedTypeNode({ name: 'lastSlot', type: definedTypeLinkNode('slot') }),
42+
],
43+
name: 'myProgram',
44+
publicKey: '1111',
45+
}),
46+
);
47+
48+
// The codec for the linked `lastSlot` defined type is resolved using the `slot` defined type.
49+
const codec = getNodeCodec([root, root.program, root.program.definedTypes[1]]);
50+
expect(codec.encode(42)).toStrictEqual(hex('2a00000000000000'));
51+
expect(codec.decode(hex('2a00000000000000'))).toBe(42n);
52+
```
53+
54+
#### Options
55+
56+
The `getNodeCodec` function accepts the following options.
57+
58+
| Name | Type | Default | Description |
59+
| --------------- | --------------- | ---------- | -------------------------------------------------------- |
60+
| `bytesEncoding` | `BytesEncoding` | `"base64"` | The default encoding to use when formatting plain bytes. |
61+
62+
#### Decoded format
63+
64+
In the table below, we illustrate the format of each codec based on the node from which it was created.
65+
66+
Note that we purposefully avoid types such as `Uint8Array`, `Set` or `Map` in order to keep the format JSON compatible. For instance, plain bytes are not provided as `Uint8Array` but as a tuple of type `[BytesEncoding, string]` — e.g. `["base64", "HelloWorld++"]` — where the default bytes encoding is `base64` which is configurable via the `bytesEncoding` option.
67+
68+
| Node | Example | Notes |
69+
| --------------------------------------------------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
70+
| [`AccountLinkNode`](../nodes/docs/linkNodes/AccountLinkNode.md) | - | Same as `AccountNode` |
71+
| [`AccountNode`](../nodes/docs/AccountNode.md) | - | Same as `node.data` |
72+
| [`DefinedTypeLinkNode`](../nodes/docs/linkNodes/DefinedTypeLinkNode.md) | - | Same as `DefinedTypeNode` |
73+
| [`DefinedTypeNode`](../nodes/docs/DefinedTypeNode.md) | - | Same as `node.type` |
74+
| [`InstructionArgumentLinkNode`](../nodes/docs/linkNodes/InstructionArgumentLinkNode.md) | - | Same as `InstructionArgumentNode` |
75+
| [`InstructionArgumentNode`](../nodes/docs/InstructionArgumentNode.md) | - | Same as `node.type` |
76+
| [`InstructionLinkNode`](../nodes/docs/linkNodes/InstructionLinkNode.md) | - | Same as `InstructionNode` |
77+
| [`InstructionNode`](../nodes/docs/InstructionNode.md) | - | Same as a `StructTypeNode` containing all `node.arguments` |
78+
| [`AmountTypeNode`](../nodes/docs/typeNodes/AmountTypeNode.md) | `42` | Same as `NumberTypeNode` |
79+
| [`ArrayTypeNode`](../nodes/docs/typeNodes/ArrayTypeNode.md) | `[1, 2, 3]` | |
80+
| [`BooleanTypeNode`](../nodes/docs/typeNodes/BooleanTypeNode.md) | `true` or `false` | |
81+
| [`BytesTypeNode`](../nodes/docs/typeNodes/BytesTypeNode.md) | `["base16", "00ffaa"]` | Uses `bytesEncoding` option to decode |
82+
| [`DateTimeTypeNode`](../nodes/docs/typeNodes/DateTimeTypeNode.md) | `42` | Same as `NumberTypeNode` |
83+
| [`EnumTypeNode`](../nodes/docs/typeNodes/EnumTypeNode.md) | `2` or `{ __kind: "move", x: 12, y: 34 }` | Uses number indices for scalar enums. Uses discriminated unions otherwise. |
84+
| [`FixedSizeTypeNode`](../nodes/docs/typeNodes/FixedSizeTypeNode.md) | - | Same as `node.type` |
85+
| [`HiddenPrefixTypeNode`](../nodes/docs/typeNodes/HiddenPrefixTypeNode.md) | - | Same as `node.type` |
86+
| [`HiddenSuffixTypeNode`](../nodes/docs/typeNodes/HiddenSuffixTypeNode.md) | - | Same as `node.type` |
87+
| [`MapTypeNode`](../nodes/docs/typeNodes/MapTypeNode.md) | `{ key1: "value1", key2: "value2" }` | Represent `Maps` as `objects` |
88+
| [`NumberTypeNode`](../nodes/docs/typeNodes/NumberTypeNode.md) | `42` | This could be a `bigint` |
89+
| [`OptionTypeNode`](../nodes/docs/typeNodes/OptionTypeNode.md) | `{ __option: "Some", value: 42 }` or `{ __option: "None" }` | Uses value objects (instead of `T \| null`) to avoid loosing information on nested options. |
90+
| [`PostOffsetTypeNode`](../nodes/docs/typeNodes/PostOffsetTypeNode.md) | - | Same as `node.type` |
91+
| [`PreOffsetTypeNode`](../nodes/docs/typeNodes/PreOffsetTypeNode.md) | - | Same as `node.type` |
92+
| [`PublicKeyTypeNode`](../nodes/docs/typeNodes/PublicKeyTypeNode.md) | `"3QC7Pnv2KfwwdC44gPcmQWuZXmRSbUpmWMJnhenMC8CU"` | Uses base58 representations of public keys |
93+
| [`RemainderOptionTypeNode`](../nodes/docs/typeNodes/RemainderOptionTypeNode.md) | `{ __option: "Some", value: 42 }` or `{ __option: "None" }` | Same as `OptionTypeNode` |
94+
| [`SentinelTypeNode`](../nodes/docs/typeNodes/SentinelTypeNode.md) | - | Same as `node.type` |
95+
| [`SetTypeNode`](../nodes/docs/typeNodes/SetTypeNode.md) | `[1, 2, 3]` | Same as `ArrayTypeNode` |
96+
| [`SizePrefixTypeNode`](../nodes/docs/typeNodes/SizePrefixTypeNode.md) | - | Same as `node.type` |
97+
| [`SolAmountTypeNode`](../nodes/docs/typeNodes/SolAmountTypeNode.md) | `42` | Same as `NumberTypeNode` |
98+
| [`StringTypeNode`](../nodes/docs/typeNodes/StringTypeNode.md) | `"Hello World"` | Uses the encoding defined in the node — i.e. `node.encoding` |
99+
| [`StructTypeNode`](../nodes/docs/typeNodes/StructTypeNode.md) | `{ name: "John", age: 42 }` | |
100+
| [`TupleTypeNode`](../nodes/docs/typeNodes/TupleTypeNode.md) | `["John", 42]` | Uses arrays to create tuples |
101+
| [`ZeroableOptionTypeNode`](../nodes/docs/typeNodes/ZeroableOptionTypeNode.md) | `{ __option: "Some", value: 42 }` or `{ __option: "None" }` | Same as `OptionTypeNode` |
102+
103+
### `getNodeCodecVisitor(linkables, options?)`
104+
105+
This visitor is used by `getNodeCodec` under the hood. It returns a `Codec<unknown>` for the visited node.
106+
107+
```ts
108+
return visit(someTypeNode, getNodeCodecVisitor(linkables));
109+
```
110+
111+
### `getValueNodeVisitor(linkables, options?)`
112+
113+
This visitor is used by the `getValueNodeVisitor` under the hood. It returns an `unknown` value for the visited `ValueNode`.
114+
115+
```ts
116+
return visit(someValueNode, getValueNodeVisitor(linkables));
117+
```
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
{
2+
"name": "@codama/dynamic-codecs",
3+
"version": "1.0.0",
4+
"description": "Node specifications and helpers for the Codama standard",
5+
"exports": {
6+
"types": "./dist/types/index.d.ts",
7+
"react-native": "./dist/index.react-native.mjs",
8+
"browser": {
9+
"import": "./dist/index.browser.mjs",
10+
"require": "./dist/index.browser.cjs"
11+
},
12+
"node": {
13+
"import": "./dist/index.node.mjs",
14+
"require": "./dist/index.node.cjs"
15+
}
16+
},
17+
"browser": {
18+
"./dist/index.node.cjs": "./dist/index.browser.cjs",
19+
"./dist/index.node.mjs": "./dist/index.browser.mjs"
20+
},
21+
"main": "./dist/index.node.cjs",
22+
"module": "./dist/index.node.mjs",
23+
"react-native": "./dist/index.react-native.mjs",
24+
"types": "./dist/types/index.d.ts",
25+
"type": "commonjs",
26+
"files": [
27+
"./dist/types",
28+
"./dist/index.*"
29+
],
30+
"sideEffects": false,
31+
"keywords": [
32+
"solana",
33+
"framework",
34+
"standard",
35+
"specifications"
36+
],
37+
"scripts": {
38+
"build": "rimraf dist && pnpm build:src && pnpm build:types",
39+
"build:src": "zx ../../node_modules/@codama/internals/scripts/build-src.mjs package",
40+
"build:types": "zx ../../node_modules/@codama/internals/scripts/build-types.mjs",
41+
"dev": "zx ../../node_modules/@codama/internals/scripts/test-unit.mjs node --watch",
42+
"lint": "zx ../../node_modules/@codama/internals/scripts/lint.mjs",
43+
"lint:fix": "zx ../../node_modules/@codama/internals/scripts/lint.mjs --fix",
44+
"test": "pnpm test:types && pnpm test:treeshakability && pnpm test:browser && pnpm test:node && pnpm test:react-native",
45+
"test:browser": "zx ../../node_modules/@codama/internals/scripts/test-unit.mjs browser",
46+
"test:node": "zx ../../node_modules/@codama/internals/scripts/test-unit.mjs node",
47+
"test:react-native": "zx ../../node_modules/@codama/internals/scripts/test-unit.mjs react-native",
48+
"test:treeshakability": "zx ../../node_modules/@codama/internals/scripts/test-treeshakability.mjs",
49+
"test:types": "zx ../../node_modules/@codama/internals/scripts/test-types.mjs"
50+
},
51+
"dependencies": {
52+
"@codama/errors": "workspace:*",
53+
"@codama/nodes": "workspace:*",
54+
"@codama/visitors-core": "workspace:*",
55+
"@solana/addresses": "2.0.0-rc.3",
56+
"@solana/codecs": "2.0.0-rc.3"
57+
},
58+
"license": "MIT",
59+
"repository": {
60+
"type": "git",
61+
"url": "https://github.com/codama-idl/codama"
62+
},
63+
"bugs": {
64+
"url": "http://github.com/codama-idl/codama/issues"
65+
},
66+
"browserslist": [
67+
"supports bigint and not dead",
68+
"maintained node versions"
69+
]
70+
}

0 commit comments

Comments
 (0)