Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .changeset/lazy-regions-turn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
'@codama/spec': patch
---

Carry resolver docs and default value strategies in the official plugins. The `codama.resolver` payload gains an optional `docs` field describing the resolver, and the `codama.extraArgument` payload gains an optional `defaultValueStrategy`, as for struct fields. A struct field resolved by a `codama.resolver` plugin may now carry a `defaultValueStrategy`, which applies to the resolved value as it would to a `defaultValue`, e.g. `omitted` keeps the field out of generated inputs.

```ts
structFieldTypeNode({
identifier: 'tags',
type: integerTypeNode('u8'),
defaultValueStrategy: 'omitted',
plugins: [pluginNode('codama.resolver', { payload: { name: 'resolveTags', docs: 'Derives tags from the name.' } })],
});
```
4 changes: 2 additions & 2 deletions docs/PluginNode.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ Every node can carry plugins via the `plugins` base attribute.

The `codama.*` namespace is reserved for official plugins, defined by this specification for information that is renderer-specific by nature:

- `codama.resolver` — on a node whose value renderers resolve with custom code rather than from the IDL, e.g. an instruction account, a struct field, remaining accounts or a byte delta. Its payload is `{ name, dependsOn? }`: the `name` of the resolver function, and the inputs it depends on as `accounts.<identifier>` or `data.<path>` strings, e.g. `["accounts.mint", "data.config.fee"]`, where `data.*` covers both the instruction data and its extra arguments. `dependsOn` is interpreted relative to the enclosing instruction, so it must be omitted on nodes outside an instruction.
- `codama.extraArgument` — on an instruction node, one per client input that is not serialised in the instruction data, e.g. to feed a resolver. Its payload is `{ identifier, type, defaultValue?, docs? }`, where `type` and `defaultValue` are the JSON of a type node and an instruction input value node — as for instruction account defaults. Its `identifier` must not collide with a top-level field of the instruction data.
- `codama.resolver` — on a node whose value renderers resolve with custom code rather than from the IDL, e.g. an instruction account, a struct field, remaining accounts or a byte delta. Its payload is `{ name, dependsOn?, docs? }`: the `name` of the resolver function, the inputs it depends on as `accounts.<identifier>` or `data.<path>` strings, e.g. `["accounts.mint", "data.config.fee"]`, where `data.*` covers both the instruction data and its extra arguments. `dependsOn` is interpreted relative to the enclosing instruction, so it must be omitted on nodes outside an instruction, and optional `docs` describing the resolver. On a struct field, the `defaultValueStrategy` of the field applies to the resolved value as it would to a `defaultValue`.
- `codama.extraArgument` — on an instruction node, one per client input that is not serialised in the instruction data, e.g. to feed a resolver. Its payload is `{ identifier, type, defaultValue?, defaultValueStrategy?, docs? }`, where `type` and `defaultValue` are the JSON of a type node and an instruction input value node — as for instruction account defaults — and `defaultValueStrategy` is a `defaultValueStrategy` value, as for struct fields. Its `identifier` must not collide with a top-level field of the instruction data.

Like any payload, these are inert: renaming the nodes they reference does not update them.

Expand Down
16 changes: 8 additions & 8 deletions docs/typeNodes/StructFieldTypeNode.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ A named field within a struct type.

### Children

| Attribute | Type | Description |
| ---------------------- | ---------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `defaultValueStrategy` | [`DefaultValueStrategy`](../sharedNodes/DefaultValueStrategy.md) _(optional)_ | How a configured default value is exposed in generated APIs. Only relevant when `defaultValue` is set — a strategy without a default value is meaningless. When absent, `optional` is assumed. |
| `docs` | `string` \| [`TextNode`](../TextNode.md) _(optional)_ | Markdown documentation for the field. |
| `type` | [`TypeNode`](./TypeNode.md) | The type of the field. |
| `defaultValue` | [`ValueNode`](../valueNodes/ValueNode.md) _(optional)_ | A default value used when the field is omitted by callers. |
| `display` | [`StructFieldDisplayNode`](../displayNodes/StructFieldDisplayNode.md) _(optional)_ | Display metadata describing how the field is presented. |
| `plugins` | [`PluginNode`](../PluginNode.md)[] _(optional)_ | Namespaced plugins with custom structured data. |
| Attribute | Type | Description |
| ---------------------- | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `defaultValueStrategy` | [`DefaultValueStrategy`](../sharedNodes/DefaultValueStrategy.md) _(optional)_ | How a configured default value is exposed in generated APIs. Only relevant when `defaultValue` is set or a `codama.resolver` plugin resolves the field — a strategy without a default value is meaningless. When absent, `optional` is assumed. |
| `docs` | `string` \| [`TextNode`](../TextNode.md) _(optional)_ | Markdown documentation for the field. |
| `type` | [`TypeNode`](./TypeNode.md) | The type of the field. |
| `defaultValue` | [`ValueNode`](../valueNodes/ValueNode.md) _(optional)_ | A default value used when the field is omitted by callers. |
| `display` | [`StructFieldDisplayNode`](../displayNodes/StructFieldDisplayNode.md) _(optional)_ | Display metadata describing how the field is presented. |
| `plugins` | [`PluginNode`](../PluginNode.md)[] _(optional)_ | Namespaced plugins with custom structured data. |

## Examples

Expand Down
6 changes: 3 additions & 3 deletions spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -1252,7 +1252,7 @@
"optional": true,
"docs": [
"How a configured default value is exposed in generated APIs.",
"Only relevant when `defaultValue` is set — a strategy without a default value is meaningless. When absent, `optional` is assumed."
"Only relevant when `defaultValue` is set or a `codama.resolver` plugin resolves the field — a strategy without a default value is meaningless. When absent, `optional` is assumed."
]
},
{
Expand Down Expand Up @@ -7177,8 +7177,8 @@
"",
"The `codama.*` namespace is reserved for official plugins, defined by this specification for information that is renderer-specific by nature:",
"",
"- `codama.resolver` — on a node whose value renderers resolve with custom code rather than from the IDL, e.g. an instruction account, a struct field, remaining accounts or a byte delta. Its payload is `{ name, dependsOn? }`: the `name` of the resolver function, and the inputs it depends on as `accounts.<identifier>` or `data.<path>` strings, e.g. `[\"accounts.mint\", \"data.config.fee\"]`, where `data.*` covers both the instruction data and its extra arguments. `dependsOn` is interpreted relative to the enclosing instruction, so it must be omitted on nodes outside an instruction.",
"- `codama.extraArgument` — on an instruction node, one per client input that is not serialised in the instruction data, e.g. to feed a resolver. Its payload is `{ identifier, type, defaultValue?, docs? }`, where `type` and `defaultValue` are the JSON of a type node and an instruction input value node — as for instruction account defaults. Its `identifier` must not collide with a top-level field of the instruction data.",
"- `codama.resolver` — on a node whose value renderers resolve with custom code rather than from the IDL, e.g. an instruction account, a struct field, remaining accounts or a byte delta. Its payload is `{ name, dependsOn?, docs? }`: the `name` of the resolver function, the inputs it depends on as `accounts.<identifier>` or `data.<path>` strings, e.g. `[\"accounts.mint\", \"data.config.fee\"]`, where `data.*` covers both the instruction data and its extra arguments. `dependsOn` is interpreted relative to the enclosing instruction, so it must be omitted on nodes outside an instruction, and optional `docs` describing the resolver. On a struct field, the `defaultValueStrategy` of the field applies to the resolved value as it would to a `defaultValue`.",
"- `codama.extraArgument` — on an instruction node, one per client input that is not serialised in the instruction data, e.g. to feed a resolver. Its payload is `{ identifier, type, defaultValue?, defaultValueStrategy?, docs? }`, where `type` and `defaultValue` are the JSON of a type node and an instruction input value node — as for instruction account defaults — and `defaultValueStrategy` is a `defaultValueStrategy` value, as for struct fields. Its `identifier` must not collide with a top-level field of the instruction data.",
"",
"Like any payload, these are inert: renaming the nodes they reference does not update them."
],
Expand Down
4 changes: 2 additions & 2 deletions src/spec/nodes/PluginNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ export const pluginNode = defineNode('pluginNode', {
'',
'The `codama.*` namespace is reserved for official plugins, defined by this specification for information that is renderer-specific by nature:',
'',
'- `codama.resolver` — on a node whose value renderers resolve with custom code rather than from the IDL, e.g. an instruction account, a struct field, remaining accounts or a byte delta. Its payload is `{ name, dependsOn? }`: the `name` of the resolver function, and the inputs it depends on as `accounts.<identifier>` or `data.<path>` strings, e.g. `["accounts.mint", "data.config.fee"]`, where `data.*` covers both the instruction data and its extra arguments. `dependsOn` is interpreted relative to the enclosing instruction, so it must be omitted on nodes outside an instruction.',
'- `codama.extraArgument` — on an instruction node, one per client input that is not serialised in the instruction data, e.g. to feed a resolver. Its payload is `{ identifier, type, defaultValue?, docs? }`, where `type` and `defaultValue` are the JSON of a type node and an instruction input value node — as for instruction account defaults. Its `identifier` must not collide with a top-level field of the instruction data.',
'- `codama.resolver` — on a node whose value renderers resolve with custom code rather than from the IDL, e.g. an instruction account, a struct field, remaining accounts or a byte delta. Its payload is `{ name, dependsOn?, docs? }`: the `name` of the resolver function, the inputs it depends on as `accounts.<identifier>` or `data.<path>` strings, e.g. `["accounts.mint", "data.config.fee"]`, where `data.*` covers both the instruction data and its extra arguments. `dependsOn` is interpreted relative to the enclosing instruction, so it must be omitted on nodes outside an instruction, and optional `docs` describing the resolver. On a struct field, the `defaultValueStrategy` of the field applies to the resolved value as it would to a `defaultValue`.',
'- `codama.extraArgument` — on an instruction node, one per client input that is not serialised in the instruction data, e.g. to feed a resolver. Its payload is `{ identifier, type, defaultValue?, defaultValueStrategy?, docs? }`, where `type` and `defaultValue` are the JSON of a type node and an instruction input value node — as for instruction account defaults — and `defaultValueStrategy` is a `defaultValueStrategy` value, as for struct fields. Its `identifier` must not collide with a top-level field of the instruction data.',
'',
'Like any payload, these are inert: renaming the nodes they reference does not update them.',
],
Expand Down
2 changes: 1 addition & 1 deletion src/spec/nodes/typeNodes/StructFieldTypeNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const structFieldTypeNode = defineNode('structFieldTypeNode', {
optionalAttribute('defaultValueStrategy', enumeration('defaultValueStrategy'), {
docs: [
'How a configured default value is exposed in generated APIs.',
'Only relevant when `defaultValue` is set — a strategy without a default value is meaningless. When absent, `optional` is assumed.',
'Only relevant when `defaultValue` is set or a `codama.resolver` plugin resolves the field — a strategy without a default value is meaningless. When absent, `optional` is assumed.',
],
}),
optionalAttribute('docs', docs(), {
Expand Down
Loading