diff --git a/.changeset/blue-zebras-laugh.md b/.changeset/blue-zebras-laugh.md new file mode 100644 index 0000000..e1ca854 --- /dev/null +++ b/.changeset/blue-zebras-laugh.md @@ -0,0 +1,5 @@ +--- +'@codama/renderers-rust': patch +--- + +Add `syncCargoToml` and `dependencyVersions` options in order to create or update the Cargo.toml of the Rust crate when generating its code. \ No newline at end of file diff --git a/README.md b/README.md index 4c36ed2..6a3863b 100644 --- a/README.md +++ b/README.md @@ -36,17 +36,19 @@ An object can be passed as a second argument to further configure the renderer. The `renderVisitor` accepts the following options. -| Name | Type | Default | Description | -| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `deleteFolderBeforeRendering` | `boolean` | `true` | Whether the base directory should be cleaned before generating new files. | -| `formatCode` | `boolean` | `false` | Whether we should use `cargo fmt` to format the generated code. When set to `true`, the `crateFolder` option must be provided. | -| `toolchain` | `string` | `"+stable"` | The toolchain to use when formatting the generated code. | -| `crateFolder` | `string` | none | The path to the root folder of the Rust crate. This option is required when `formatCode` is set to `true`. | -| `linkOverrides` | `Record<'accounts' \| 'definedTypes' \| 'instructions' \| 'pdas' \| 'programs' \| 'resolvers', Record>` | `{}` | A object that overrides the import path of link nodes. For instance, `{ definedTypes: { counter: 'hooked' } }` uses the `hooked` folder to import any link node referring to the `counter` type. | -| `dependencyMap` | `Record` | `{}` | A mapping between import aliases and their actual crate name or path in Rust. | -| `renderParentInstructions` | `boolean` | `false` | When using nested instructions, whether the parent instructions should also be rendered. When set to `false` (default), only the instruction leaves are being rendered. | -| `traitOptions` | [`TraitOptions`](#trait-options) | `DEFAULT_TRAIT_OPTIONS` | A set of options that can be used to configure how traits are rendered for every Rust types. See [documentation below](#trait-options) for more information. | -| `anchorTraits` | `boolean` | `true` | Whether to generate Anchor traits `impl` for account types. | +| Name | Type | Default | Description | +| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `deleteFolderBeforeRendering` | `boolean` | `true` | Whether the base directory should be cleaned before generating new files. | +| `formatCode` | `boolean` | `false` | Whether we should use `cargo fmt` to format the generated code. When set to `true`, the `crateFolder` option must be provided. | +| `toolchain` | `string` | `"+stable"` | The toolchain to use when formatting the generated code. | +| `crateFolder` | `string` | none | The path to the root folder of the Rust crate. This option is required when `formatCode` is set to `true`. | +| `linkOverrides` | `Record<'accounts' \| 'definedTypes' \| 'instructions' \| 'pdas' \| 'programs' \| 'resolvers', Record>` | `{}` | A object that overrides the import path of link nodes. For instance, `{ definedTypes: { counter: 'hooked' } }` uses the `hooked` folder to import any link node referring to the `counter` type. | +| `dependencyMap` | `Record` | `{}` | A mapping between import aliases and their actual crate name or path in Rust. | +| `dependencyVersions` | `Record` | `{}` | A mapping between external crates — e.g. `solana-pubkey` — and the version range we should use for them — e.g. `^3.0` or the equivalent dependency object — e.g. `{ version: "^3.0" }`. The renderer offers default values for all external dependencies it relies on but this option may be used to override some of these values or add new ones. | +| `renderParentInstructions` | `boolean` | `false` | When using nested instructions, whether the parent instructions should also be rendered. When set to `false` (default), only the instruction leaves are being rendered. | +| `traitOptions` | [`TraitOptions`](#trait-options) | `DEFAULT_TRAIT_OPTIONS` | A set of options that can be used to configure how traits are rendered for every Rust types. See [documentation below](#trait-options) for more information. | +| `anchorTraits` | `boolean` | `true` | Whether to generate Anchor traits `impl` for account types. | +| `syncCargoToml` | `boolean` | `false` | Whether to update the dependencies of the existing `Cargo.toml` — or create a new `Cargo.toml` when missing — at the provided `crateFolder` option if provided. | ## Trait Options diff --git a/package.json b/package.json index 7b08f48..d5a36b6 100644 --- a/package.json +++ b/package.json @@ -41,21 +41,24 @@ "test:unit": "vitest run" }, "dependencies": { - "@codama/errors": "^1.4.4", - "@codama/nodes": "^1.4.4", - "@codama/renderers-core": "^1.3.3", - "@codama/visitors-core": "^1.4.4", - "@solana/codecs-strings": "^5.0.0", - "nunjucks": "^3.2.4" + "@codama/errors": "^1.5.0", + "@codama/nodes": "^1.5.0", + "@codama/renderers-core": "^1.3.5", + "@codama/visitors-core": "^1.5.0", + "@iarna/toml": "^2.2.5", + "@solana/codecs-strings": "^6.0.0", + "nunjucks": "^3.2.4", + "semver": "^7.7.3" }, "devDependencies": { - "@codama/nodes-from-anchor": "^1.3.6", "@changesets/changelog-github": "^0.5.1", "@changesets/cli": "^2.29.7", + "@codama/nodes-from-anchor": "^1.3.6", "@solana/eslint-config-solana": "^5.0.0", "@solana/prettier-config-solana": "0.0.5", "@types/node": "^25", "@types/nunjucks": "^3.2.6", + "@types/semver": "^7.7.1", "agadoo": "^3.0.0", "eslint": "^9.35.0", "happy-dom": "^20.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 87f10e1..0146059 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,45 +9,54 @@ importers: .: dependencies: '@codama/errors': - specifier: ^1.4.4 + specifier: ^1.5.0 version: 1.5.0 '@codama/nodes': - specifier: ^1.4.4 + specifier: ^1.5.0 version: 1.5.0 '@codama/renderers-core': - specifier: ^1.3.3 + specifier: ^1.3.5 version: 1.3.5 '@codama/visitors-core': - specifier: ^1.4.4 + specifier: ^1.5.0 version: 1.5.0 + '@iarna/toml': + specifier: ^2.2.5 + version: 2.2.5 '@solana/codecs-strings': - specifier: ^5.0.0 - version: 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + specifier: ^6.0.0 + version: 6.0.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) nunjucks: specifier: ^3.2.4 version: 3.2.4 + semver: + specifier: ^7.7.3 + version: 7.7.3 devDependencies: '@changesets/changelog-github': specifier: ^0.5.1 version: 0.5.2 '@changesets/cli': specifier: ^2.29.7 - version: 2.29.8(@types/node@25.0.1) + version: 2.29.8(@types/node@25.2.3) '@codama/nodes-from-anchor': specifier: ^1.3.6 - version: 1.3.7(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + version: 1.3.6(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/eslint-config-solana': specifier: ^5.0.0 - version: 5.0.0(@eslint/js@9.39.1)(@types/eslint__js@8.42.3)(eslint-plugin-jest@29.0.1(@typescript-eslint/eslint-plugin@8.43.0(@typescript-eslint/parser@8.43.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(jest@30.1.3(@types/node@25.0.1))(typescript@5.9.3))(eslint-plugin-react-hooks@5.2.0(eslint@9.39.1))(eslint-plugin-simple-import-sort@12.1.1(eslint@9.39.1))(eslint-plugin-sort-keys-fix@1.1.2)(eslint-plugin-typescript-sort-keys@3.3.0(@typescript-eslint/parser@8.43.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(globals@14.0.0)(jest@30.1.3(@types/node@25.0.1))(typescript-eslint@8.43.0(eslint@9.39.1)(typescript@5.9.3))(typescript@5.9.3) + version: 5.0.0(@eslint/js@9.39.1)(@types/eslint__js@8.42.3)(eslint-plugin-jest@29.0.1(@typescript-eslint/eslint-plugin@8.43.0(@typescript-eslint/parser@8.43.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(jest@30.1.3(@types/node@25.2.3))(typescript@5.9.3))(eslint-plugin-react-hooks@5.2.0(eslint@9.39.1))(eslint-plugin-simple-import-sort@12.1.1(eslint@9.39.1))(eslint-plugin-sort-keys-fix@1.1.2)(eslint-plugin-typescript-sort-keys@3.3.0(@typescript-eslint/parser@8.43.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(globals@14.0.0)(jest@30.1.3(@types/node@25.2.3))(typescript-eslint@8.43.0(eslint@9.39.1)(typescript@5.9.3))(typescript@5.9.3) '@solana/prettier-config-solana': specifier: 0.0.5 - version: 0.0.5(prettier@3.7.4) + version: 0.0.5(prettier@3.7.3) '@types/node': specifier: ^25 - version: 25.0.1 + version: 25.2.3 '@types/nunjucks': specifier: ^3.2.6 version: 3.2.6 + '@types/semver': + specifier: ^7.7.1 + version: 7.7.1 agadoo: specifier: ^3.0.0 version: 3.0.0 @@ -59,7 +68,7 @@ importers: version: 20.0.11 prettier: specifier: ^3.6.2 - version: 3.7.4 + version: 3.7.3 rimraf: specifier: 6.1.2 version: 6.1.2 @@ -71,7 +80,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.1 - version: 4.0.15(@types/node@25.0.1)(happy-dom@20.0.11) + version: 4.0.14(@types/node@25.2.3)(happy-dom@20.0.11) packages: @@ -305,15 +314,25 @@ packages: '@changesets/write@0.4.0': resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} + '@codama/errors@1.4.4': + resolution: {integrity: sha512-XC86H5X+zGTi0cSRKLc+wFkeXNsvnh+ttOgVnVHIljmXOJWbUt9wXhKding3UftipLWwlHPuoswERJ0vS0mO2A==} + hasBin: true + '@codama/errors@1.5.0': resolution: {integrity: sha512-i4cS+S7JaZXhofQHFY3cwzt8rqxUVPNaeJND5VOyKUbtcOi933YXJXk52gDG4mc+CpGqHJijsJjfSpr1lJGxzg==} hasBin: true + '@codama/node-types@1.4.4': + resolution: {integrity: sha512-uUeIz34Id/TTAMi4k5OVl9FByM/PawnlNIIVqgpooH9AS0UlniICZ+KJ/mdHZidJs/AGo6bSRoOPS1BLtMajyw==} + '@codama/node-types@1.5.0': resolution: {integrity: sha512-Ebz2vOUukmNaFXWdkni1ZihXkAIUnPYtqIMXYxKXOxjMP+TGz2q0lGtRo7sqw1pc2ksFBIkfBp5pZsl5p6gwXA==} - '@codama/nodes-from-anchor@1.3.7': - resolution: {integrity: sha512-MJbTVfyCAmRilJ6wDqk8dT2kMU3nvUnImaiLLOagAG9aKlKeBcjDvqNp0XM6qhb49jogXgfK7x1q9ERaQQnacQ==} + '@codama/nodes-from-anchor@1.3.6': + resolution: {integrity: sha512-614DZS9H5gW16Rkeu0ES8BHnDvbd8M9FLqPWnp9QUE0b+wCvWB36yZiRylJ4fw8gRDSc+FR7C2i3NXKycpvBEg==} + + '@codama/nodes@1.4.4': + resolution: {integrity: sha512-JzlY5qLk3rhsnu0nerC/Vkc9/2HjdsLtEpBtST0dxC1j9kpfHvIc2uyIj+5hlB1YIBRJIDNo+UOHGla8hidkaA==} '@codama/nodes@1.5.0': resolution: {integrity: sha512-yg+xmorWiMNjS3n19CGIt/FZ/ZCuDIu+HEY45bq6gHu1MN3RtJZY+Q3v0ErnBPA60D8mNWkvkKoeSZXfzcAvfw==} @@ -321,11 +340,14 @@ packages: '@codama/renderers-core@1.3.5': resolution: {integrity: sha512-MuZLU+3LZPQb1HuZffwZl+v5JHQDe5LYHGhA1wTMNlwRedYIysSxBjogHNciNIHsKP3JjmqyYmLO5LCEp3hjaQ==} + '@codama/visitors-core@1.4.4': + resolution: {integrity: sha512-vk/4tczViAUHa7c8PF7FxN+JWbuTcDB0pIdrDbbO6eBPKDPQGZCUCEp6rXIYBVxfO129jWrNf2+CuyYre/c/vA==} + '@codama/visitors-core@1.5.0': resolution: {integrity: sha512-3PIAlBX0a06hIxzyPtQMfQcqWGFBgfbwysSwcXBbvHUYbemwhD6xwlBKJuqTwm9DyFj3faStp5fpvcp03Rjxtw==} - '@codama/visitors@1.5.0': - resolution: {integrity: sha512-SwtQaleXxAaFz6uHygxki621q4nPUDQlnwEhsg+QKOjHpKWXjLYdJof+R8gUiTV/n7/IeNnjvxJTTNfUsvETPQ==} + '@codama/visitors@1.4.4': + resolution: {integrity: sha512-3w2aRNvGV6/rXTfRDynXR82zoAqX0P4tlfQ/zT4I4Bby4xTobKgDZLyAstodmA0D878eKW7sMg4Gb1m1R5dOig==} '@emnapi/core@1.7.1': resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==} @@ -702,6 +724,9 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} + '@iarna/toml@2.2.5': + resolution: {integrity: sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==} + '@inquirer/external-editor@1.0.3': resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} engines: {node: '>=18'} @@ -1106,6 +1131,15 @@ packages: peerDependencies: typescript: '>=5.3.3' + '@solana/codecs-core@6.0.1': + resolution: {integrity: sha512-OnUQk94qfvfE0nVveZ638aNUL3tyRJoorUFiAG0ICTGUo3c6fkYb8vH23o/5O2qmuSmYND1sn+UCaldNMVkFpg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + '@solana/codecs-data-structures@5.0.0': resolution: {integrity: sha512-y503Pqmv0LHcfcf0vQJGaxDvydQJbyCo8nK3nxn56EhFj5lBQ1NWb3WvTd83epigwuZurW2MhJARrpikfhQglQ==} engines: {node: '>=20.18.0'} @@ -1118,6 +1152,15 @@ packages: peerDependencies: typescript: '>=5.3.3' + '@solana/codecs-numbers@6.0.1': + resolution: {integrity: sha512-ZrI1NjUsf4I+Klue/2rlQbZLcGRom/G2E4VB/8x4IEHGOeFLQhXcxmnib8kdgomQRYOzF1BjVDmCYxvZr+6AWA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + '@solana/codecs-strings@5.0.0': resolution: {integrity: sha512-ALkRwpV8bGR6qjAYw0YXZwp2YI4wzvKOJGmx04Ut8gMdbaUx7qOcJkhEQKI6ZVC3lAWSIS1N1wGccUZDwvfKxw==} engines: {node: '>=20.18.0'} @@ -1125,6 +1168,18 @@ packages: fastestsmallesttextencoderdecoder: ^1.0.22 typescript: '>=5.3.3' + '@solana/codecs-strings@6.0.1': + resolution: {integrity: sha512-OmMIfMFbbJVIxveBeATKCj9DsmZ8l4vJPnOLHUop0hLWRiYHTQ1qokMqfk/X8PCmUjXmbXnlp63BikGtdKN3/g==} + engines: {node: '>=20.18.0'} + peerDependencies: + fastestsmallesttextencoderdecoder: ^1.0.22 + typescript: ^5.0.0 + peerDependenciesMeta: + fastestsmallesttextencoderdecoder: + optional: true + typescript: + optional: true + '@solana/codecs@5.0.0': resolution: {integrity: sha512-KOw0gFUSBxIMDWLJ3AkVFkEci91dw0Rpx3C6y83Our7fSW+SEP8vRZklCElieYR85LHVB1QIEhoeHR7rc+Ifkw==} engines: {node: '>=20.18.0'} @@ -1138,6 +1193,16 @@ packages: peerDependencies: typescript: '>=5.3.3' + '@solana/errors@6.0.1': + resolution: {integrity: sha512-sMe5GCsXto8F1KDeq9GbZR0+m841SqEYep3NAcYlC0lqF2RG4giaaPQHgrWI5DJR/L7yc8FzUIQfTxnaN7bwOQ==} + engines: {node: '>=20.18.0'} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + '@solana/eslint-config-solana@5.0.0': resolution: {integrity: sha512-DPsoloOpVf/4JD7m3j394BvJX8mCKTSQ1xJrP0tyyeLEZN7x09OL9uj2bo2Ma4UTYZsyV97p2eiuiHmJVA0Kfw==} peerDependencies: @@ -1213,14 +1278,11 @@ packages: '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - '@types/node@20.19.26': - resolution: {integrity: sha512-0l6cjgF0XnihUpndDhk+nyD3exio3iKaYROSgvh/qSevPXax3L8p5DBRFjbvalnwatGgHEQn2R88y2fA3g4irg==} - - '@types/node@25.0.1': - resolution: {integrity: sha512-czWPzKIAXucn9PtsttxmumiQ9N0ok9FrBwgRWrwmVLlp86BrMExzvXRLFYRJ+Ex3g6yqj+KuaxfX1JTgV2lpfg==} + '@types/node@20.19.25': + resolution: {integrity: sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ==} - '@types/node@25.0.1': - resolution: {integrity: sha512-czWPzKIAXucn9PtsttxmumiQ9N0ok9FrBwgRWrwmVLlp86BrMExzvXRLFYRJ+Ex3g6yqj+KuaxfX1JTgV2lpfg==} + '@types/node@25.2.3': + resolution: {integrity: sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==} '@types/nunjucks@3.2.6': resolution: {integrity: sha512-pHiGtf83na1nCzliuAdq8GowYiXvH5l931xZ0YEHaLMNFgynpEqx+IPStlu7UaDkehfvl01e4x/9Tpwhy7Ue3w==} @@ -1267,8 +1329,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.49.0': - resolution: {integrity: sha512-/wJN0/DKkmRUMXjZUXYZpD1NEQzQAAn9QWfGwo+Ai8gnzqH7tvqS7oNVdTjKqOcPyVIdZdyCMoqN66Ia789e7g==} + '@typescript-eslint/project-service@8.48.1': + resolution: {integrity: sha512-HQWSicah4s9z2/HifRPQ6b6R7G+SBx64JlFQpgSSHWPKdvCZX57XCbszg/bapbRsOEv42q5tayTYcEFpACcX1w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' @@ -1281,8 +1343,8 @@ packages: resolution: {integrity: sha512-daSWlQ87ZhsjrbMLvpuuMAt3y4ba57AuvadcR7f3nl8eS3BjRc8L9VLxFLk92RL5xdXOg6IQ+qKjjqNEimGuAg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/scope-manager@8.49.0': - resolution: {integrity: sha512-npgS3zi+/30KSOkXNs0LQXtsg9ekZ8OISAOLGWA/ZOEn0ZH74Ginfl7foziV8DT+D98WfQ5Kopwqb/PZOaIJGg==} + '@typescript-eslint/scope-manager@8.48.1': + resolution: {integrity: sha512-rj4vWQsytQbLxC5Bf4XwZ0/CKd362DkWMUkviT7DCS057SK64D5lH74sSGzhI6PDD2HCEq02xAP9cX68dYyg1w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/tsconfig-utils@8.43.0': @@ -1291,8 +1353,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/tsconfig-utils@8.49.0': - resolution: {integrity: sha512-8prixNi1/6nawsRYxet4YOhnbW+W9FK/bQPxsGB1D3ZrDzbJ5FXw5XmzxZv82X3B+ZccuSxo/X8q9nQ+mFecWA==} + '@typescript-eslint/tsconfig-utils@8.48.1': + resolution: {integrity: sha512-k0Jhs4CpEffIBm6wPaCXBAD7jxBtrHjrSgtfCjUvPp9AZ78lXKdTR8fxyZO5y4vWNlOvYXRtngSZNSn+H53Jkw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' @@ -1312,8 +1374,8 @@ packages: resolution: {integrity: sha512-vQ2FZaxJpydjSZJKiSW/LJsabFFvV7KgLC5DiLhkBcykhQj8iK9BOaDmQt74nnKdLvceM5xmhaTF+pLekrxEkw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.49.0': - resolution: {integrity: sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ==} + '@typescript-eslint/types@8.48.1': + resolution: {integrity: sha512-+fZ3LZNeiELGmimrujsDCT4CRIbq5oXdHe7chLiW8qzqyPMnn1puNstCrMNVAqwcl2FdIxkuJ4tOs/RFDBVc/Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/typescript-estree@5.62.0': @@ -1331,8 +1393,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/typescript-estree@8.49.0': - resolution: {integrity: sha512-jrLdRuAbPfPIdYNppHJ/D0wN+wwNfJ32YTAm10eJVsFmrVpXQnDWBn8niCSMlWjvml8jsce5E/O+86IQtTbJWA==} + '@typescript-eslint/typescript-estree@8.48.1': + resolution: {integrity: sha512-/9wQ4PqaefTK6POVTjJaYS0bynCgzh6ClJHGSBj06XEHjkfylzB+A3qvyaXnErEZSaxhIo4YdyBgq6j4RysxDg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' @@ -1350,8 +1412,8 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.49.0': - resolution: {integrity: sha512-N3W7rJw7Rw+z1tRsHZbK395TWSYvufBXumYtEGzypgMUthlg0/hmCImeA8hgO2d2G4pd7ftpxxul2J8OdtdaFA==} + '@typescript-eslint/utils@8.48.1': + resolution: {integrity: sha512-fAnhLrDjiVfey5wwFRwrweyRlCmdz5ZxXz2G/4cLn0YDLjTapmN4gcCsTBR1N2rWnZSDeWpYtgLDsJt+FpmcwA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -1365,8 +1427,8 @@ packages: resolution: {integrity: sha512-T+S1KqRD4sg/bHfLwrpF/K3gQLBM1n7Rp7OjjikjTEssI2YJzQpi5WXoynOaQ93ERIuq3O8RBTOUYDKszUCEHw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/visitor-keys@8.49.0': - resolution: {integrity: sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA==} + '@typescript-eslint/visitor-keys@8.48.1': + resolution: {integrity: sha512-BmxxndzEWhE4TIEEMBs8lP3MBWN3jFPs/p6gPm/wkv02o41hI6cq9AuSmGAaTTHPtA1FTi2jBre4A9rm5ZmX+Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@ungap/structured-clone@1.3.0': @@ -1467,11 +1529,11 @@ packages: cpu: [x64] os: [win32] - '@vitest/expect@4.0.15': - resolution: {integrity: sha512-Gfyva9/GxPAWXIWjyGDli9O+waHDC0Q0jaLdFP1qPAUUfo1FEXPXUfUkp3eZA0sSq340vPycSyOlYUeM15Ft1w==} + '@vitest/expect@4.0.14': + resolution: {integrity: sha512-RHk63V3zvRiYOWAV0rGEBRO820ce17hz7cI2kDmEdfQsBjT2luEKB5tCOc91u1oSQoUOZkSv3ZyzkdkSLD7lKw==} - '@vitest/mocker@4.0.15': - resolution: {integrity: sha512-CZ28GLfOEIFkvCFngN8Sfx5h+Se0zN+h4B7yOsPVCcgtiO7t5jt9xQh2E1UkFep+eb9fjyMfuC5gBypwb07fvQ==} + '@vitest/mocker@4.0.14': + resolution: {integrity: sha512-RzS5NujlCzeRPF1MK7MXLiEFpkIXeMdQ+rN3Kk3tDI9j0mtbr7Nmuq67tpkOJQpgyClbOltCXMjLZicJHsH5Cg==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0-0 @@ -1481,20 +1543,20 @@ packages: vite: optional: true - '@vitest/pretty-format@4.0.15': - resolution: {integrity: sha512-SWdqR8vEv83WtZcrfLNqlqeQXlQLh2iilO1Wk1gv4eiHKjEzvgHb2OVc3mIPyhZE6F+CtfYjNlDJwP5MN6Km7A==} + '@vitest/pretty-format@4.0.14': + resolution: {integrity: sha512-SOYPgujB6TITcJxgd3wmsLl+wZv+fy3av2PpiPpsWPZ6J1ySUYfScfpIt2Yv56ShJXR2MOA6q2KjKHN4EpdyRQ==} - '@vitest/runner@4.0.15': - resolution: {integrity: sha512-+A+yMY8dGixUhHmNdPUxOh0la6uVzun86vAbuMT3hIDxMrAOmn5ILBHm8ajrqHE0t8R9T1dGnde1A5DTnmi3qw==} + '@vitest/runner@4.0.14': + resolution: {integrity: sha512-BsAIk3FAqxICqREbX8SetIteT8PiaUL/tgJjmhxJhCsigmzzH8xeadtp7LRnTpCVzvf0ib9BgAfKJHuhNllKLw==} - '@vitest/snapshot@4.0.15': - resolution: {integrity: sha512-A7Ob8EdFZJIBjLjeO0DZF4lqR6U7Ydi5/5LIZ0xcI+23lYlsYJAfGn8PrIWTYdZQRNnSRlzhg0zyGu37mVdy5g==} + '@vitest/snapshot@4.0.14': + resolution: {integrity: sha512-aQVBfT1PMzDSA16Y3Fp45a0q8nKexx6N5Amw3MX55BeTeZpoC08fGqEZqVmPcqN0ueZsuUQ9rriPMhZ3Mu19Ag==} - '@vitest/spy@4.0.15': - resolution: {integrity: sha512-+EIjOJmnY6mIfdXtE/bnozKEvTC4Uczg19yeZ2vtCz5Yyb0QQ31QWVQ8hswJ3Ysx/K2EqaNsVanjr//2+P3FHw==} + '@vitest/spy@4.0.14': + resolution: {integrity: sha512-JmAZT1UtZooO0tpY3GRyiC/8W7dCs05UOq9rfsUUgEZEdq+DuHLmWhPsrTt0TiW7WYeL/hXpaE07AZ2RCk44hg==} - '@vitest/utils@4.0.15': - resolution: {integrity: sha512-HXjPW2w5dxhTD0dLwtYHDnelK3j8sR8cWIaLxr22evTyY6q8pRCjZSmhRWVjBaOVXChQd6AwMzi9pucorXCPZA==} + '@vitest/utils@4.0.14': + resolution: {integrity: sha512-hLqXZKAWNg8pI+SQXyXxWCTOpA3MvsqcbVeNgSi8x/CSN2wi26dSzn1wrOhmCmFjEvN9p8/kLFRHa6PI8jHazw==} a-sync-waterfall@1.0.1: resolution: {integrity: sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==} @@ -1601,8 +1663,8 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - baseline-browser-mapping@2.9.6: - resolution: {integrity: sha512-v9BVVpOTLB59C9E7aSnmIF8h7qRsFpx+A2nugVMTszEOMcfjlZMsXRm4LF23I3Z9AJxc8ANpIvzbzONoX9VJlg==} + baseline-browser-mapping@2.8.32: + resolution: {integrity: sha512-OPz5aBThlyLFgxyhdwf/s2+8ab3OvT7AdTNvKHBwpXomIYeXqpUUuT8LrdtxZSsWJ4R4CU1un4XGh5Ez3nlTpw==} hasBin: true better-path-resolve@1.0.0: @@ -1619,8 +1681,8 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.28.1: - resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + browserslist@4.28.0: + resolution: {integrity: sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -1664,8 +1726,8 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001760: - resolution: {integrity: sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==} + caniuse-lite@1.0.30001757: + resolution: {integrity: sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ==} chai@6.2.1: resolution: {integrity: sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==} @@ -1727,6 +1789,10 @@ packages: resolution: {integrity: sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==} engines: {node: '>=20'} + commander@14.0.3: + resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} + engines: {node: '>=20'} + commander@4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} @@ -1806,8 +1872,8 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - electron-to-chromium@1.5.267: - resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} + electron-to-chromium@1.5.263: + resolution: {integrity: sha512-DrqJ11Knd+lo+dv+lltvfMDLU27g14LMdH2b0O3Pio4uk0x+z7OR+JrmyacTPN2M8w3BrZ7/RTwG3R9B7irPlg==} emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} @@ -2106,7 +2172,7 @@ packages: glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} @@ -2737,8 +2803,8 @@ packages: engines: {node: '>=10.13.0'} hasBin: true - prettier@3.7.4: - resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==} + prettier@3.7.3: + resolution: {integrity: sha512-QgODejq9K3OzoBbuyobZlUhznP5SKwPqp+6Q6xw6o8gnhr4O85L2U915iM2IDcfF2NPXVaM9zlo9tdwipnYwzg==} engines: {node: '>=14'} hasBin: true @@ -2960,10 +3026,6 @@ packages: tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - tinyexec@1.0.2: - resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} - engines: {node: '>=18'} - tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} @@ -3066,8 +3128,8 @@ packages: unrs-resolver@1.11.1: resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} - update-browserslist-db@1.2.2: - resolution: {integrity: sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==} + update-browserslist-db@1.1.4: + resolution: {integrity: sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -3079,8 +3141,8 @@ packages: resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} engines: {node: '>=10.12.0'} - vite@7.2.6: - resolution: {integrity: sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ==} + vite@7.2.4: + resolution: {integrity: sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -3119,18 +3181,18 @@ packages: yaml: optional: true - vitest@4.0.15: - resolution: {integrity: sha512-n1RxDp8UJm6N0IbJLQo+yzLZ2sQCDyl1o0LeugbPWf8+8Fttp29GghsQBjYJVmWq3gBFfe9Hs1spR44vovn2wA==} + vitest@4.0.14: + resolution: {integrity: sha512-d9B2J9Cm9dN9+6nxMnnNJKJCtcyKfnHj15N6YNJfaFHRLua/d3sRKU9RuKmO9mB0XdFtUizlxfz/VPbd3OxGhw==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.0.15 - '@vitest/browser-preview': 4.0.15 - '@vitest/browser-webdriverio': 4.0.15 - '@vitest/ui': 4.0.15 + '@vitest/browser-playwright': 4.0.14 + '@vitest/browser-preview': 4.0.14 + '@vitest/browser-webdriverio': 4.0.14 + '@vitest/ui': 4.0.14 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -3256,7 +3318,7 @@ snapshots: dependencies: '@babel/compat-data': 7.28.5 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.28.1 + browserslist: 4.28.0 lru-cache: 5.1.1 semver: 6.3.1 @@ -3444,7 +3506,7 @@ snapshots: transitivePeerDependencies: - encoding - '@changesets/cli@2.29.8(@types/node@25.0.1)': + '@changesets/cli@2.29.8(@types/node@25.2.3)': dependencies: '@changesets/apply-release-plan': 7.0.14 '@changesets/assemble-release-plan': 6.0.9 @@ -3460,7 +3522,7 @@ snapshots: '@changesets/should-skip-package': 0.1.2 '@changesets/types': 6.1.0 '@changesets/write': 0.4.0 - '@inquirer/external-editor': 1.0.3(@types/node@25.0.1) + '@inquirer/external-editor': 1.0.3(@types/node@25.2.3) '@manypkg/get-packages': 1.1.3 ansi-colors: 4.1.3 ci-info: 3.9.0 @@ -3566,25 +3628,38 @@ snapshots: human-id: 4.1.3 prettier: 2.8.8 + '@codama/errors@1.4.4': + dependencies: + '@codama/node-types': 1.4.4 + commander: 14.0.2 + picocolors: 1.1.1 + '@codama/errors@1.5.0': dependencies: '@codama/node-types': 1.5.0 commander: 14.0.2 picocolors: 1.1.1 + '@codama/node-types@1.4.4': {} + '@codama/node-types@1.5.0': {} - '@codama/nodes-from-anchor@1.3.7(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': + '@codama/nodes-from-anchor@1.3.6(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: - '@codama/errors': 1.5.0 - '@codama/nodes': 1.5.0 - '@codama/visitors': 1.5.0 + '@codama/errors': 1.4.4 + '@codama/nodes': 1.4.4 + '@codama/visitors': 1.4.4 '@noble/hashes': 2.0.1 '@solana/codecs': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) transitivePeerDependencies: - fastestsmallesttextencoderdecoder - typescript + '@codama/nodes@1.4.4': + dependencies: + '@codama/errors': 1.4.4 + '@codama/node-types': 1.4.4 + '@codama/nodes@1.5.0': dependencies: '@codama/errors': 1.5.0 @@ -3596,17 +3671,23 @@ snapshots: '@codama/nodes': 1.5.0 '@codama/visitors-core': 1.5.0 + '@codama/visitors-core@1.4.4': + dependencies: + '@codama/errors': 1.4.4 + '@codama/nodes': 1.4.4 + json-stable-stringify: 1.3.0 + '@codama/visitors-core@1.5.0': dependencies: '@codama/errors': 1.5.0 '@codama/nodes': 1.5.0 json-stable-stringify: 1.3.0 - '@codama/visitors@1.5.0': + '@codama/visitors@1.4.4': dependencies: - '@codama/errors': 1.5.0 - '@codama/nodes': 1.5.0 - '@codama/visitors-core': 1.5.0 + '@codama/errors': 1.4.4 + '@codama/nodes': 1.4.4 + '@codama/visitors-core': 1.4.4 '@emnapi/core@1.7.1': dependencies: @@ -3837,12 +3918,14 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@inquirer/external-editor@1.0.3(@types/node@25.0.1)': + '@iarna/toml@2.2.5': {} + + '@inquirer/external-editor@1.0.3(@types/node@25.2.3)': dependencies: chardet: 2.1.1 iconv-lite: 0.7.0 optionalDependencies: - '@types/node': 25.0.1 + '@types/node': 25.2.3 '@isaacs/balanced-match@4.0.1': {} @@ -3872,7 +3955,7 @@ snapshots: '@jest/console@30.1.2': dependencies: '@jest/types': 30.0.5 - '@types/node': 25.0.1 + '@types/node': 25.2.3 chalk: 4.1.2 jest-message-util: 30.1.0 jest-util: 30.0.5 @@ -3886,14 +3969,14 @@ snapshots: '@jest/test-result': 30.1.3 '@jest/transform': 30.1.2 '@jest/types': 30.0.5 - '@types/node': 25.0.1 + '@types/node': 25.2.3 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 4.3.1 exit-x: 0.2.2 graceful-fs: 4.2.11 jest-changed-files: 30.0.5 - jest-config: 30.1.3(@types/node@25.0.1) + jest-config: 30.1.3(@types/node@25.2.3) jest-haste-map: 30.1.0 jest-message-util: 30.1.0 jest-regex-util: 30.0.1 @@ -3920,7 +4003,7 @@ snapshots: dependencies: '@jest/fake-timers': 30.1.2 '@jest/types': 30.0.5 - '@types/node': 25.0.1 + '@types/node': 25.2.3 jest-mock: 30.0.5 '@jest/expect-utils@30.1.2': @@ -3938,7 +4021,7 @@ snapshots: dependencies: '@jest/types': 30.0.5 '@sinonjs/fake-timers': 13.0.5 - '@types/node': 25.0.1 + '@types/node': 25.2.3 jest-message-util: 30.1.0 jest-mock: 30.0.5 jest-util: 30.0.5 @@ -3956,7 +4039,7 @@ snapshots: '@jest/pattern@30.0.1': dependencies: - '@types/node': 25.0.1 + '@types/node': 25.2.3 jest-regex-util: 30.0.1 '@jest/reporters@30.1.3': @@ -3967,7 +4050,7 @@ snapshots: '@jest/transform': 30.1.2 '@jest/types': 30.0.5 '@jridgewell/trace-mapping': 0.3.31 - '@types/node': 25.0.1 + '@types/node': 25.2.3 chalk: 4.1.2 collect-v8-coverage: 1.0.3 exit-x: 0.2.2 @@ -4044,7 +4127,7 @@ snapshots: '@jest/schemas': 30.0.5 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 25.0.1 + '@types/node': 25.2.3 '@types/yargs': 17.0.35 chalk: 4.1.2 @@ -4260,6 +4343,12 @@ snapshots: '@solana/errors': 5.0.0(typescript@5.9.3) typescript: 5.9.3 + '@solana/codecs-core@6.0.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 6.0.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + '@solana/codecs-data-structures@5.0.0(typescript@5.9.3)': dependencies: '@solana/codecs-core': 5.0.0(typescript@5.9.3) @@ -4273,6 +4362,13 @@ snapshots: '@solana/errors': 5.0.0(typescript@5.9.3) typescript: 5.9.3 + '@solana/codecs-numbers@6.0.1(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 6.0.1(typescript@5.9.3) + '@solana/errors': 6.0.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + '@solana/codecs-strings@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: '@solana/codecs-core': 5.0.0(typescript@5.9.3) @@ -4281,6 +4377,15 @@ snapshots: fastestsmallesttextencoderdecoder: 1.0.22 typescript: 5.9.3 + '@solana/codecs-strings@6.0.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 6.0.1(typescript@5.9.3) + '@solana/codecs-numbers': 6.0.1(typescript@5.9.3) + '@solana/errors': 6.0.1(typescript@5.9.3) + optionalDependencies: + fastestsmallesttextencoderdecoder: 1.0.22 + typescript: 5.9.3 + '@solana/codecs@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: '@solana/codecs-core': 5.0.0(typescript@5.9.3) @@ -4298,18 +4403,25 @@ snapshots: commander: 14.0.1 typescript: 5.9.3 - '@solana/eslint-config-solana@5.0.0(@eslint/js@9.39.1)(@types/eslint__js@8.42.3)(eslint-plugin-jest@29.0.1(@typescript-eslint/eslint-plugin@8.43.0(@typescript-eslint/parser@8.43.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(jest@30.1.3(@types/node@25.0.1))(typescript@5.9.3))(eslint-plugin-react-hooks@5.2.0(eslint@9.39.1))(eslint-plugin-simple-import-sort@12.1.1(eslint@9.39.1))(eslint-plugin-sort-keys-fix@1.1.2)(eslint-plugin-typescript-sort-keys@3.3.0(@typescript-eslint/parser@8.43.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(globals@14.0.0)(jest@30.1.3(@types/node@25.0.1))(typescript-eslint@8.43.0(eslint@9.39.1)(typescript@5.9.3))(typescript@5.9.3)': + '@solana/errors@6.0.1(typescript@5.9.3)': + dependencies: + chalk: 5.6.2 + commander: 14.0.3 + optionalDependencies: + typescript: 5.9.3 + + '@solana/eslint-config-solana@5.0.0(@eslint/js@9.39.1)(@types/eslint__js@8.42.3)(eslint-plugin-jest@29.0.1(@typescript-eslint/eslint-plugin@8.43.0(@typescript-eslint/parser@8.43.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(jest@30.1.3(@types/node@25.2.3))(typescript@5.9.3))(eslint-plugin-react-hooks@5.2.0(eslint@9.39.1))(eslint-plugin-simple-import-sort@12.1.1(eslint@9.39.1))(eslint-plugin-sort-keys-fix@1.1.2)(eslint-plugin-typescript-sort-keys@3.3.0(@typescript-eslint/parser@8.43.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(globals@14.0.0)(jest@30.1.3(@types/node@25.2.3))(typescript-eslint@8.43.0(eslint@9.39.1)(typescript@5.9.3))(typescript@5.9.3)': dependencies: '@eslint/js': 9.39.1 '@types/eslint__js': 8.42.3 eslint: 9.39.1 - eslint-plugin-jest: 29.0.1(@typescript-eslint/eslint-plugin@8.43.0(@typescript-eslint/parser@8.43.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(jest@30.1.3(@types/node@25.0.1))(typescript@5.9.3) + eslint-plugin-jest: 29.0.1(@typescript-eslint/eslint-plugin@8.43.0(@typescript-eslint/parser@8.43.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(jest@30.1.3(@types/node@25.2.3))(typescript@5.9.3) eslint-plugin-react-hooks: 5.2.0(eslint@9.39.1) eslint-plugin-simple-import-sort: 12.1.1(eslint@9.39.1) eslint-plugin-sort-keys-fix: 1.1.2 eslint-plugin-typescript-sort-keys: 3.3.0(@typescript-eslint/parser@8.43.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3) globals: 14.0.0 - jest: 30.1.3(@types/node@25.0.1) + jest: 30.1.3(@types/node@25.2.3) typescript: 5.9.3 typescript-eslint: 8.43.0(eslint@9.39.1)(typescript@5.9.3) @@ -4324,9 +4436,9 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/prettier-config-solana@0.0.5(prettier@3.7.4)': + '@solana/prettier-config-solana@0.0.5(prettier@3.7.3)': dependencies: - prettier: 3.7.4 + prettier: 3.7.3 '@standard-schema/spec@1.0.0': {} @@ -4388,15 +4500,11 @@ snapshots: '@types/node@12.20.55': {} - '@types/node@20.19.26': + '@types/node@20.19.25': dependencies: undici-types: 6.21.0 - '@types/node@25.0.1': - dependencies: - undici-types: 7.16.0 - - '@types/node@25.0.1': + '@types/node@25.2.3': dependencies: undici-types: 7.16.0 @@ -4453,17 +4561,17 @@ snapshots: '@typescript-eslint/project-service@8.43.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.43.0(typescript@5.9.3) - '@typescript-eslint/types': 8.43.0 + '@typescript-eslint/tsconfig-utils': 8.48.1(typescript@5.9.3) + '@typescript-eslint/types': 8.48.1 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.49.0(typescript@5.9.3)': + '@typescript-eslint/project-service@8.48.1(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.49.0(typescript@5.9.3) - '@typescript-eslint/types': 8.49.0 + '@typescript-eslint/tsconfig-utils': 8.48.1(typescript@5.9.3) + '@typescript-eslint/types': 8.48.1 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: @@ -4479,16 +4587,16 @@ snapshots: '@typescript-eslint/types': 8.43.0 '@typescript-eslint/visitor-keys': 8.43.0 - '@typescript-eslint/scope-manager@8.49.0': + '@typescript-eslint/scope-manager@8.48.1': dependencies: - '@typescript-eslint/types': 8.49.0 - '@typescript-eslint/visitor-keys': 8.49.0 + '@typescript-eslint/types': 8.48.1 + '@typescript-eslint/visitor-keys': 8.48.1 '@typescript-eslint/tsconfig-utils@8.43.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/tsconfig-utils@8.49.0(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.48.1(typescript@5.9.3)': dependencies: typescript: 5.9.3 @@ -4508,7 +4616,7 @@ snapshots: '@typescript-eslint/types@8.43.0': {} - '@typescript-eslint/types@8.49.0': {} + '@typescript-eslint/types@8.48.1': {} '@typescript-eslint/typescript-estree@5.62.0(typescript@5.9.3)': dependencies: @@ -4540,12 +4648,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.49.0(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.48.1(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.49.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.49.0(typescript@5.9.3) - '@typescript-eslint/types': 8.49.0 - '@typescript-eslint/visitor-keys': 8.49.0 + '@typescript-eslint/project-service': 8.48.1(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.48.1(typescript@5.9.3) + '@typescript-eslint/types': 8.48.1 + '@typescript-eslint/visitor-keys': 8.48.1 debug: 4.4.3 minimatch: 9.0.5 semver: 7.7.3 @@ -4581,12 +4689,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.49.0(eslint@9.39.1)(typescript@5.9.3)': + '@typescript-eslint/utils@8.48.1(eslint@9.39.1)(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) - '@typescript-eslint/scope-manager': 8.49.0 - '@typescript-eslint/types': 8.49.0 - '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.48.1 + '@typescript-eslint/types': 8.48.1 + '@typescript-eslint/typescript-estree': 8.48.1(typescript@5.9.3) eslint: 9.39.1 typescript: 5.9.3 transitivePeerDependencies: @@ -4602,9 +4710,9 @@ snapshots: '@typescript-eslint/types': 8.43.0 eslint-visitor-keys: 4.2.1 - '@typescript-eslint/visitor-keys@8.49.0': + '@typescript-eslint/visitor-keys@8.48.1': dependencies: - '@typescript-eslint/types': 8.49.0 + '@typescript-eslint/types': 8.48.1 eslint-visitor-keys: 4.2.1 '@ungap/structured-clone@1.3.0': {} @@ -4668,43 +4776,43 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true - '@vitest/expect@4.0.15': + '@vitest/expect@4.0.14': dependencies: '@standard-schema/spec': 1.0.0 '@types/chai': 5.2.3 - '@vitest/spy': 4.0.15 - '@vitest/utils': 4.0.15 + '@vitest/spy': 4.0.14 + '@vitest/utils': 4.0.14 chai: 6.2.1 tinyrainbow: 3.0.3 - '@vitest/mocker@4.0.15(vite@7.2.6(@types/node@25.0.1))': + '@vitest/mocker@4.0.14(vite@7.2.4(@types/node@25.2.3))': dependencies: - '@vitest/spy': 4.0.15 + '@vitest/spy': 4.0.14 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.2.6(@types/node@25.0.1) + vite: 7.2.4(@types/node@25.2.3) - '@vitest/pretty-format@4.0.15': + '@vitest/pretty-format@4.0.14': dependencies: tinyrainbow: 3.0.3 - '@vitest/runner@4.0.15': + '@vitest/runner@4.0.14': dependencies: - '@vitest/utils': 4.0.15 + '@vitest/utils': 4.0.14 pathe: 2.0.3 - '@vitest/snapshot@4.0.15': + '@vitest/snapshot@4.0.14': dependencies: - '@vitest/pretty-format': 4.0.15 + '@vitest/pretty-format': 4.0.14 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.0.15': {} + '@vitest/spy@4.0.14': {} - '@vitest/utils@4.0.15': + '@vitest/utils@4.0.14': dependencies: - '@vitest/pretty-format': 4.0.15 + '@vitest/pretty-format': 4.0.14 tinyrainbow: 3.0.3 a-sync-waterfall@1.0.1: {} @@ -4827,7 +4935,7 @@ snapshots: balanced-match@1.0.2: {} - baseline-browser-mapping@2.9.6: {} + baseline-browser-mapping@2.8.32: {} better-path-resolve@1.0.0: dependencies: @@ -4846,13 +4954,13 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.28.1: + browserslist@4.28.0: dependencies: - baseline-browser-mapping: 2.9.6 - caniuse-lite: 1.0.30001760 - electron-to-chromium: 1.5.267 + baseline-browser-mapping: 2.8.32 + caniuse-lite: 1.0.30001757 + electron-to-chromium: 1.5.263 node-releases: 2.0.27 - update-browserslist-db: 1.2.2(browserslist@4.28.1) + update-browserslist-db: 1.1.4(browserslist@4.28.0) bser@2.1.1: dependencies: @@ -4890,7 +4998,7 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001760: {} + caniuse-lite@1.0.30001757: {} chai@6.2.1: {} @@ -4935,6 +5043,8 @@ snapshots: commander@14.0.2: {} + commander@14.0.3: {} + commander@4.1.1: {} commander@5.1.0: {} @@ -4989,7 +5099,7 @@ snapshots: eastasianwidth@0.2.0: {} - electron-to-chromium@1.5.267: {} + electron-to-chromium@1.5.263: {} emittery@0.13.1: {} @@ -5080,13 +5190,13 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-plugin-jest@29.0.1(@typescript-eslint/eslint-plugin@8.43.0(@typescript-eslint/parser@8.43.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(jest@30.1.3(@types/node@25.0.1))(typescript@5.9.3): + eslint-plugin-jest@29.0.1(@typescript-eslint/eslint-plugin@8.43.0(@typescript-eslint/parser@8.43.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(jest@30.1.3(@types/node@25.2.3))(typescript@5.9.3): dependencies: - '@typescript-eslint/utils': 8.49.0(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/utils': 8.48.1(eslint@9.39.1)(typescript@5.9.3) eslint: 9.39.1 optionalDependencies: '@typescript-eslint/eslint-plugin': 8.43.0(@typescript-eslint/parser@8.43.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3) - jest: 30.1.3(@types/node@25.0.1) + jest: 30.1.3(@types/node@25.2.3) transitivePeerDependencies: - supports-color - typescript @@ -5391,7 +5501,7 @@ snapshots: happy-dom@20.0.11: dependencies: - '@types/node': 20.19.26 + '@types/node': 20.19.25 '@types/whatwg-mimetype': 3.0.2 whatwg-mimetype: 3.0.0 @@ -5515,7 +5625,7 @@ snapshots: '@jest/expect': 30.1.2 '@jest/test-result': 30.1.3 '@jest/types': 30.0.5 - '@types/node': 25.0.1 + '@types/node': 25.2.3 chalk: 4.1.2 co: 4.6.0 dedent: 1.7.0 @@ -5535,7 +5645,7 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@30.1.3(@types/node@25.0.1): + jest-cli@30.1.3(@types/node@25.2.3): dependencies: '@jest/core': 30.1.3 '@jest/test-result': 30.1.3 @@ -5543,7 +5653,7 @@ snapshots: chalk: 4.1.2 exit-x: 0.2.2 import-local: 3.2.0 - jest-config: 30.1.3(@types/node@25.0.1) + jest-config: 30.1.3(@types/node@25.2.3) jest-util: 30.0.5 jest-validate: 30.1.0 yargs: 17.7.2 @@ -5554,7 +5664,7 @@ snapshots: - supports-color - ts-node - jest-config@30.1.3(@types/node@25.0.1): + jest-config@30.1.3(@types/node@25.2.3): dependencies: '@babel/core': 7.28.5 '@jest/get-type': 30.1.0 @@ -5581,39 +5691,7 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 25.0.1 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - jest-config@30.1.3(@types/node@25.0.1): - dependencies: - '@babel/core': 7.28.5 - '@jest/get-type': 30.1.0 - '@jest/pattern': 30.0.1 - '@jest/test-sequencer': 30.1.3 - '@jest/types': 30.0.5 - babel-jest: 30.1.2(@babel/core@7.28.5) - chalk: 4.1.2 - ci-info: 4.3.1 - deepmerge: 4.3.1 - glob: 10.5.0 - graceful-fs: 4.2.11 - jest-circus: 30.1.3 - jest-docblock: 30.0.1 - jest-environment-node: 30.1.2 - jest-regex-util: 30.0.1 - jest-resolve: 30.1.3 - jest-runner: 30.1.3 - jest-util: 30.0.5 - jest-validate: 30.1.0 - micromatch: 4.0.8 - parse-json: 5.2.0 - pretty-format: 30.0.5 - slash: 3.0.0 - strip-json-comments: 3.1.1 - optionalDependencies: - '@types/node': 25.0.1 + '@types/node': 25.2.3 transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -5642,7 +5720,7 @@ snapshots: '@jest/environment': 30.1.2 '@jest/fake-timers': 30.1.2 '@jest/types': 30.0.5 - '@types/node': 25.0.1 + '@types/node': 25.2.3 jest-mock: 30.0.5 jest-util: 30.0.5 jest-validate: 30.1.0 @@ -5650,7 +5728,7 @@ snapshots: jest-haste-map@30.1.0: dependencies: '@jest/types': 30.0.5 - '@types/node': 25.0.1 + '@types/node': 25.2.3 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -5689,7 +5767,7 @@ snapshots: jest-mock@30.0.5: dependencies: '@jest/types': 30.0.5 - '@types/node': 25.0.1 + '@types/node': 25.2.3 jest-util: 30.0.5 jest-pnp-resolver@1.2.3(jest-resolve@30.1.3): @@ -5723,7 +5801,7 @@ snapshots: '@jest/test-result': 30.1.3 '@jest/transform': 30.1.2 '@jest/types': 30.0.5 - '@types/node': 25.0.1 + '@types/node': 25.2.3 chalk: 4.1.2 emittery: 0.13.1 exit-x: 0.2.2 @@ -5752,7 +5830,7 @@ snapshots: '@jest/test-result': 30.1.3 '@jest/transform': 30.1.2 '@jest/types': 30.0.5 - '@types/node': 25.0.1 + '@types/node': 25.2.3 chalk: 4.1.2 cjs-module-lexer: 2.1.1 collect-v8-coverage: 1.0.3 @@ -5799,7 +5877,7 @@ snapshots: jest-util@30.0.5: dependencies: '@jest/types': 30.0.5 - '@types/node': 25.0.1 + '@types/node': 25.2.3 chalk: 4.1.2 ci-info: 4.3.1 graceful-fs: 4.2.11 @@ -5818,7 +5896,7 @@ snapshots: dependencies: '@jest/test-result': 30.1.3 '@jest/types': 30.0.5 - '@types/node': 25.0.1 + '@types/node': 25.2.3 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -5827,18 +5905,18 @@ snapshots: jest-worker@30.1.0: dependencies: - '@types/node': 25.0.1 + '@types/node': 25.2.3 '@ungap/structured-clone': 1.3.0 jest-util: 30.0.5 merge-stream: 2.0.0 supports-color: 8.1.1 - jest@30.1.3(@types/node@25.0.1): + jest@30.1.3(@types/node@25.2.3): dependencies: '@jest/core': 30.1.3 '@jest/types': 30.0.5 import-local: 3.2.0 - jest-cli: 30.1.3(@types/node@25.0.1) + jest-cli: 30.1.3(@types/node@25.2.3) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -6134,7 +6212,7 @@ snapshots: prettier@2.8.8: {} - prettier@3.7.4: {} + prettier@3.7.3: {} pretty-format@30.0.5: dependencies: @@ -6374,8 +6452,6 @@ snapshots: tinyexec@0.3.2: {} - tinyexec@1.0.2: {} - tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.3) @@ -6490,9 +6566,9 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 - update-browserslist-db@1.2.2(browserslist@4.28.1): + update-browserslist-db@1.1.4(browserslist@4.28.0): dependencies: - browserslist: 4.28.1 + browserslist: 4.28.0 escalade: 3.2.0 picocolors: 1.1.1 @@ -6506,7 +6582,7 @@ snapshots: '@types/istanbul-lib-coverage': 2.0.6 convert-source-map: 2.0.0 - vite@7.2.6(@types/node@25.0.1): + vite@7.2.4(@types/node@25.2.3): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) @@ -6515,18 +6591,18 @@ snapshots: rollup: 4.53.3 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 25.0.1 + '@types/node': 25.2.3 fsevents: 2.3.3 - vitest@4.0.15(@types/node@25.0.1)(happy-dom@20.0.11): + vitest@4.0.14(@types/node@25.2.3)(happy-dom@20.0.11): dependencies: - '@vitest/expect': 4.0.15 - '@vitest/mocker': 4.0.15(vite@7.2.6(@types/node@25.0.1)) - '@vitest/pretty-format': 4.0.15 - '@vitest/runner': 4.0.15 - '@vitest/snapshot': 4.0.15 - '@vitest/spy': 4.0.15 - '@vitest/utils': 4.0.15 + '@vitest/expect': 4.0.14 + '@vitest/mocker': 4.0.14(vite@7.2.4(@types/node@25.2.3)) + '@vitest/pretty-format': 4.0.14 + '@vitest/runner': 4.0.14 + '@vitest/snapshot': 4.0.14 + '@vitest/spy': 4.0.14 + '@vitest/utils': 4.0.14 es-module-lexer: 1.7.0 expect-type: 1.2.2 magic-string: 0.30.21 @@ -6535,13 +6611,13 @@ snapshots: picomatch: 4.0.3 std-env: 3.10.0 tinybench: 2.9.0 - tinyexec: 1.0.2 + tinyexec: 0.3.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 7.2.6(@types/node@25.0.1) + vite: 7.2.4(@types/node@25.2.3) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 25.0.1 + '@types/node': 25.2.3 happy-dom: 20.0.11 transitivePeerDependencies: - jiti diff --git a/src/ImportMap.ts b/src/ImportMap.ts index 4600e0d..f4a59c6 100644 --- a/src/ImportMap.ts +++ b/src/ImportMap.ts @@ -1,4 +1,7 @@ -import { TypeManifest } from './getTypeManifestVisitor'; +import type { TypeManifest } from './getTypeManifestVisitor'; + +/** Rust keywords that should not be identified as crate names. */ +export const RUST_CORE_IMPORTS = new Set(['std', 'crate', 'super', 'self', 'core', 'alloc', 'clippy']); const DEFAULT_MODULE_MAP: Record = { generated: 'crate::generated', @@ -71,6 +74,11 @@ export class ImportMap { return newImportMap; } + getExternalDependencies(dependencyMap: Record): Set { + const resolvedMap = this.resolveDependencyMap(dependencyMap); + return new Set([...resolvedMap._imports].map(i => i.split('::')[0]).filter(i => !RUST_CORE_IMPORTS.has(i))); + } + toString(dependencies: Record): string { const resolvedMap = this.resolveDependencyMap(dependencies); const importStatements = [...resolvedMap.imports].map(i => { diff --git a/src/getRenderMapVisitor.ts b/src/getRenderMapVisitor.ts index 3f6832c..55e65a1 100644 --- a/src/getRenderMapVisitor.ts +++ b/src/getRenderMapVisitor.ts @@ -30,6 +30,8 @@ import { getTypeManifestVisitor } from './getTypeManifestVisitor'; import { ImportMap } from './ImportMap'; import { renderValueNode } from './renderValueNodeVisitor'; import { + CargoDependencies, + Fragment, getDiscriminatorConstants, getImportFromFactory, getTraitsFromNodeFactory, @@ -42,6 +44,7 @@ export type GetRenderMapOptions = { anchorTraits?: boolean; defaultTraitOverrides?: string[]; dependencyMap?: Record; + dependencyVersions?: CargoDependencies; linkOverrides?: LinkOverrides; renderParentInstructions?: boolean; traitOptions?: TraitOptions; @@ -64,7 +67,7 @@ export function getRenderMapVisitor(options: GetRenderMapOptions = {}) { const anchorTraits = options.anchorTraits ?? true; return pipe( - staticVisitor(() => createRenderMap(), { + staticVisitor(() => createRenderMap(), { keys: ['rootNode', 'programNode', 'instructionNode', 'accountNode', 'definedTypeNode'], }), v => @@ -107,11 +110,10 @@ export function getRenderMapVisitor(options: GetRenderMapOptions = {}) { .filter(isNodeFilter('constantPdaSeedNode')) .filter(seed => !isNode(seed.value, 'programIdValueNode')); - const { imports } = typeManifest; - - if (hasVariableSeeds) { - imports.mergeWith(seedsImports); - } + const imports = typeManifest.imports + .mergeWith(...(hasVariableSeeds ? [seedsImports] : [])) + .mergeWith(discriminatorConstants.imports) + .remove(`generatedAccounts::${pascalCase(node.name)}`); return createRenderMap(`accounts/${snakeCase(node.name)}.rs`, { content: render('accountsPage.njk', { @@ -120,28 +122,29 @@ export function getRenderMapVisitor(options: GetRenderMapOptions = {}) { constantSeeds, discriminatorConstants: discriminatorConstants.render, hasVariableSeeds, - imports: imports - .mergeWith(discriminatorConstants.imports) - .remove(`generatedAccounts::${pascalCase(node.name)}`) - .toString(dependencyMap), + imports: imports.toString(dependencyMap), pda, program, seeds, typeManifest, }), + imports, }); }, visitDefinedType(node) { const typeManifest = visit(node, typeManifestVisitor); - const imports = new ImportMap().mergeWithManifest(typeManifest); + const imports = new ImportMap() + .mergeWithManifest(typeManifest) + .remove(`generatedTypes::${pascalCase(node.name)}`); return createRenderMap(`types/${snakeCase(node.name)}.rs`, { content: render('definedTypesPage.njk', { definedType: node, - imports: imports.remove(`generatedTypes::${pascalCase(node.name)}`).toString(dependencyMap), + imports: imports.toString(dependencyMap), typeManifest, }), + imports, }); }, @@ -231,7 +234,10 @@ export function getRenderMapVisitor(options: GetRenderMapOptions = {}) { const typeManifest = visit(struct, structVisitor); const dataTraits = getTraitsFromNode(node); - imports.mergeWith(dataTraits.imports); + imports + .mergeWith(dataTraits.imports) + .mergeWith(discriminatorConstants.imports) + .remove(`generatedInstructions::${pascalCase(node.name)}`); return createRenderMap(`instructions/${snakeCase(node.name)}.rs`, { content: render('instructionsPage.njk', { @@ -239,15 +245,13 @@ export function getRenderMapVisitor(options: GetRenderMapOptions = {}) { discriminatorConstants: discriminatorConstants.render, hasArgs, hasOptional, - imports: imports - .mergeWith(discriminatorConstants.imports) - .remove(`generatedInstructions::${pascalCase(node.name)}`) - .toString(dependencyMap), + imports: imports.toString(dependencyMap), instruction: node, instructionArgs, program, typeManifest, }), + imports, }); }, @@ -269,6 +273,7 @@ export function getRenderMapVisitor(options: GetRenderMapOptions = {}) { imports: new ImportMap().toString(dependencyMap), program: node, }), + imports: new ImportMap(), }); } @@ -301,21 +306,29 @@ export function getRenderMapVisitor(options: GetRenderMapOptions = {}) { return mergeRenderMaps([ createRenderMap({ ['accounts/mod.rs']: - accountsToExport.length > 0 ? { content: render('accountsMod.njk', ctx) } : undefined, + accountsToExport.length > 0 + ? { content: render('accountsMod.njk', ctx), imports: new ImportMap() } + : undefined, ['errors/mod.rs']: - programsToExport.length > 0 ? { content: render('errorsMod.njk', ctx) } : undefined, + programsToExport.length > 0 + ? { content: render('errorsMod.njk', ctx), imports: new ImportMap() } + : undefined, ['instructions/mod.rs']: instructionsToExport.length > 0 - ? { content: render('instructionsMod.njk', ctx) } + ? { content: render('instructionsMod.njk', ctx), imports: new ImportMap() } : undefined, - ['mod.rs']: { content: render('rootMod.njk', ctx) }, + ['mod.rs']: { content: render('rootMod.njk', ctx), imports: new ImportMap() }, ['programs.rs']: - programsToExport.length > 0 ? { content: render('programsMod.njk', ctx) } : undefined, + programsToExport.length > 0 + ? { content: render('programsMod.njk', ctx), imports: new ImportMap() } + : undefined, ['shared.rs']: - accountsToExport.length > 0 ? { content: render('sharedPage.njk', ctx) } : undefined, + accountsToExport.length > 0 + ? { content: render('sharedPage.njk', ctx), imports: new ImportMap() } + : undefined, ['types/mod.rs']: definedTypesToExport.length > 0 - ? { content: render('definedTypesMod.njk', ctx) } + ? { content: render('definedTypesMod.njk', ctx), imports: new ImportMap() } : undefined, }), ...getAllPrograms(node).map(p => visit(p, self)), diff --git a/src/renderVisitor.ts b/src/renderVisitor.ts index d67911e..cfedb9e 100644 --- a/src/renderVisitor.ts +++ b/src/renderVisitor.ts @@ -1,14 +1,16 @@ import { logError, logWarn } from '@codama/errors'; -import { deleteDirectory, writeRenderMapVisitor } from '@codama/renderers-core'; +import { deleteDirectory, writeRenderMap } from '@codama/renderers-core'; import { rootNodeVisitor, visit } from '@codama/visitors-core'; import { spawnSync } from 'child_process'; import { GetRenderMapOptions, getRenderMapVisitor } from './getRenderMapVisitor'; +import { syncCargoToml } from './utils'; export type RenderOptions = GetRenderMapOptions & { crateFolder?: string; deleteFolderBeforeRendering?: boolean; formatCode?: boolean; + syncCargoToml?: boolean; toolchain?: string; }; @@ -20,7 +22,11 @@ export function renderVisitor(path: string, options: RenderOptions = {}) { } // Render the new files. - visit(root, writeRenderMapVisitor(getRenderMapVisitor(options), path)); + const renderMap = visit(root, getRenderMapVisitor(options)); + writeRenderMap(renderMap, path); + + // Sync Cargo.toml dependencies and versions, if requested. + syncCargoToml(renderMap, options); // format the code if (options.formatCode) { diff --git a/src/utils/cargoToml.ts b/src/utils/cargoToml.ts new file mode 100644 index 0000000..5ad55b9 --- /dev/null +++ b/src/utils/cargoToml.ts @@ -0,0 +1,379 @@ +import { CODAMA_ERROR__RENDERERS__MISSING_DEPENDENCY_VERSIONS, CodamaError, logWarn } from '@codama/errors'; +import { fileExists, joinPath, readFile, RenderMap, writeFile } from '@codama/renderers-core'; +import { parse, stringify } from '@iarna/toml'; +import { lt as ltVersion, minVersion, subset } from 'semver'; + +import { ImportMap, RUST_CORE_IMPORTS } from '../ImportMap'; +import type { RenderOptions } from '../renderVisitor'; +import type { Fragment } from './fragment'; + +type CargoToml = CargoDependencyGroups & { + description?: string; + edition?: string | { workspace: true }; + features?: Record; + license?: string | { workspace: true }; + name?: string; + repository?: string | { workspace: true }; + target?: Record; + version?: string; + workspace?: { dependencies?: CargoDependencies }; +}; +type CargoDependencyGroups = { + 'build-dependencies'?: CargoDependencies; + dependencies?: CargoDependencies; + 'dev-dependencies'?: CargoDependencies; +}; + +export type CargoDependencies = Record; +type CargoDependency = CargoDependencyObject | string; +type CargoDependencyObject = { + branch?: string; + 'default-features'?: boolean; + features?: string[]; + git?: string; + optional?: boolean; + package?: string; + path?: string; + tag?: string; + version?: string; + workspace?: boolean; +}; + +export const DEFAULT_DEPENDENCY_VERSIONS: CargoDependencies = { + 'anchor-lang': { optional: true, version: '~0.31' }, + borsh: '^0.10', + kaigan: { features: ['serde'], version: '^0.3' }, + 'num-derive': '^0.4', + 'num-traits': '^0.2', + serde: { features: ['derive'], optional: true, version: '^1.0' }, + 'serde-big-array': '^0.5', + serde_with: { optional: true, version: '^3.0' }, + 'solana-account': '~2.2', + 'solana-account-info': '~2.3', + 'solana-client': { optional: true, version: '^2.2' }, + 'solana-cpi': '~2.2', + 'solana-decode-error': '~2.3', + 'solana-instruction': '~2.3', + 'solana-program-error': '~2.2', + 'solana-pubkey': { features: ['curve25519', 'borsh'], version: '~2.3' }, + 'solana-sdk': { optional: true, version: '^2.3' }, + thiserror: '^1.0', +}; + +export function syncCargoToml( + renderMap: RenderMap, + options: Pick, +): void { + const shouldSyncCargoToml = options.syncCargoToml ?? false; + const crateFolder = options.crateFolder; + + // Without a `crateFolder`, we cannot sync the Cargo.toml. + if (!crateFolder) { + // If we should sync but have no folder, warn the user. + if (shouldSyncCargoToml) { + logWarn("Cannot sync Cargo.toml. Please provide the 'crateFolder' option."); + } + return; + } + + const cargoTomlPath = joinPath(crateFolder, 'Cargo.toml'); + const usedDependencies = getUsedDependencyVersions( + renderMap, + options.dependencyMap ?? {}, + options.dependencyVersions ?? {}, + ); + + // If we should not sync the Cargo.toml, exit early. + if (!shouldSyncCargoToml) { + // However, if the Cargo.toml exists, we can still check it and + // warn the user about out-of-date or missing dependencies. + if (fileExists(cargoTomlPath)) { + checkExistingCargoToml(readCargoToml(cargoTomlPath), usedDependencies); + } + return; + } + + if (fileExists(cargoTomlPath)) { + const cargoToml = updateExistingCargoToml(readCargoToml(cargoTomlPath), usedDependencies); + writeFile(cargoTomlPath, stringify(cargoToml) + '\n'); + } else { + const cargoToml = createNewCargoToml(usedDependencies); + writeFile(cargoTomlPath, stringify(cargoToml) + '\n'); + } +} + +export function createNewCargoToml(usedDependencies: CargoDependencies): CargoToml { + return updateExistingCargoToml( + { + name: 'rust-client', + // eslint-disable-next-line sort-keys-fix/sort-keys-fix + description: '', + version: '1.0.0', + // eslint-disable-next-line sort-keys-fix/sort-keys-fix + repository: { workspace: true }, + // eslint-disable-next-line sort-keys-fix/sort-keys-fix + edition: { workspace: true }, + license: { workspace: true }, + // eslint-disable-next-line sort-keys-fix/sort-keys-fix + features: { + anchor: ['dep:anchor-lang'], + 'anchor-idl-build': ['anchor', 'anchor-lang?/idl-build'], + fetch: ['dep:solana-client', 'dep:solana-sdk'], + serde: ['dep:serde', 'dep:serde_with'], + }, + // eslint-disable-next-line sort-keys-fix/sort-keys-fix + dependencies: {}, + }, + usedDependencies, + ); +} + +export function updateExistingCargoToml(cargoToml: CargoToml, usedDependencies: CargoDependencies): CargoToml { + const foundUsedDependencies = new Set(); + + const updatedCargoToml = updateCargoDependencies(cargoToml, dependencyGroup => { + return Object.fromEntries( + Object.entries(dependencyGroup).map(([dependencyKey, dependency]) => { + const foundUsedDependency = findCargoDependencyByImportName( + usedDependencies, + getCargoDependencyImportName(dependencyKey), + ); + if (!foundUsedDependency) { + return [dependencyKey, dependency]; + } + + const [usedDependencyKey, usedDependency] = foundUsedDependency; + foundUsedDependencies.add(usedDependencyKey); + + const usedDependencyCrateName = getCargoDependencyCrateName(usedDependencyKey, usedDependency); + if (!shouldUpdateDependency(usedDependencyCrateName, dependency, usedDependency)) { + return [dependencyKey, dependency]; + } + + const newVersion = getCargoDependencyVersion(usedDependency) as string; + return [ + dependencyKey, + typeof dependency === 'string' ? newVersion : { ...dependency, version: newVersion }, + ]; + }), + ); + }); + + const usedDependenciesToAdd = Object.entries(usedDependencies).filter( + ([usedDependencyKey]) => !foundUsedDependencies.has(usedDependencyKey), + ); + for (const [usedDependencyKey, usedDependency] of usedDependenciesToAdd) { + updatedCargoToml.dependencies = updatedCargoToml.dependencies ?? {}; + updatedCargoToml.dependencies[usedDependencyKey] = usedDependency; + } + + return updatedCargoToml; +} + +export function checkExistingCargoToml(cargoToml: CargoToml, usedDependencies: CargoDependencies): void { + const missingDependencies: string[] = []; + const dependenciesToUpdate: string[] = []; + const existingDependencies = { + ...cargoToml['build-dependencies'], + ...cargoToml['dev-dependencies'], + ...cargoToml.dependencies, + ...cargoToml.workspace?.dependencies, + ...Object.values(cargoToml.target ?? {}).reduce((acc, target) => { + return { + ...acc, + ...target['build-dependencies'], + ...target['dev-dependencies'], + ...target.dependencies, + }; + }, {} as CargoDependencies), + }; + + for (const [usedDependencyKey, usedDependency] of Object.entries(usedDependencies)) { + const foundExistingDependency = findCargoDependencyByImportName( + existingDependencies, + getCargoDependencyImportName(usedDependencyKey), + ); + if (!foundExistingDependency) { + missingDependencies.push(usedDependencyKey); + } else if (shouldUpdateDependency(foundExistingDependency[0], foundExistingDependency[1], usedDependency)) { + dependenciesToUpdate.push(usedDependencyKey); + } + } + + if (missingDependencies.length === 0 && dependenciesToUpdate.length === 0) return; + const missingList = missingDependencies + .map(d => `- ${d} missing: ${getCargoDependencyVersion(usedDependencies[d])}\n`) + .join(''); + const outdatedList = dependenciesToUpdate + .map( + d => + `- ${d} outdated: ${getCargoDependencyVersion(existingDependencies[d])} ` + + `-> ${getCargoDependencyVersion(usedDependencies[d])}\n`, + ) + .join(''); + logWarn( + `The following dependencies in your \`Cargo.toml\` are out-of-date or missing:\n` + + `${missingList}${outdatedList}`, + ); +} + +export function getUsedDependencyVersions( + renderMap: RenderMap, + dependencyMap: Record, + dependencyVersions: CargoDependencies, +): CargoDependencies { + const usedImportNames = getUsedImportNames(renderMap, dependencyMap); + const dependencyVersionsWithDefaults: CargoDependencies = { + ...DEFAULT_DEPENDENCY_VERSIONS, + ...dependencyVersions, + }; + + const [usedDependencyVersion, missingDependencies] = [...usedImportNames].reduce( + ([acc, missingDependencies], usedImportName) => { + const usedDependency = findCargoDependencyByImportName(dependencyVersionsWithDefaults, usedImportName); + if (usedDependency) { + acc[usedDependency[0]] = usedDependency[1]; + } else { + missingDependencies.add(usedImportName); + } + return [acc, missingDependencies]; + }, + [{} as CargoDependencies, new Set()], + ); + + if (missingDependencies.size > 0) { + throw new CodamaError(CODAMA_ERROR__RENDERERS__MISSING_DEPENDENCY_VERSIONS, { + dependencies: [...missingDependencies], + message: 'Please add these dependencies to the `dependencyVersions` option.', + }); + } + + return usedDependencyVersion; +} + +function getUsedImportNames(renderMap: RenderMap, dependencyMap: Record): Set { + const fragments = [...renderMap.values()]; + const fromImportMap = new ImportMap() + .mergeWith(...fragments.map(({ imports }) => imports)) + .getExternalDependencies(dependencyMap); + + // Match paths with at least 2 segments, optionally starting with "::" + // and capturing only the crate name (first segment). For instance, + // "some_crate::some_module::SomeType" or "::some_crate::SomeType". + const PATH_REGEX = /\b(?:::)?([a-z_][a-z0-9_]*)(?:::[a-zA-Z0-9_]+)+/g; + const fromContent = fragments.flatMap(({ content }) => { + return [...content.matchAll(PATH_REGEX)] + .map(match => match[1]) + .filter(crateName => !RUST_CORE_IMPORTS.has(crateName)); + }); + + return new Set([...fromImportMap, ...fromContent]); +} + +export function shouldUpdateDependency( + dependency: string, + currentDependency: CargoDependency, + requiredDependency: CargoDependency, +): boolean { + const currentRange = getCargoDependencyVersion(currentDependency); + const requiredRange = getCargoDependencyVersion(requiredDependency); + return !!currentRange && !!requiredRange && shouldUpdateRange(dependency, currentRange, requiredRange); +} + +export function shouldUpdateRange(dependency: string, currentRange: string, requiredRange: string): boolean { + currentRange = cargoToNpmSemver(currentRange); + requiredRange = cargoToNpmSemver(requiredRange); + + try { + // Check if currentRange is a subset of requiredRange + // If yes, required is looser or equal, no update needed + if (subset(currentRange, requiredRange)) { + return false; + } + + // Get the minimum versions from both ranges. + const minRequiredVersion = minVersion(requiredRange); + const minCurrentVersion = minVersion(currentRange); + if (!minCurrentVersion || !minRequiredVersion) { + throw new Error('Could not determine minimum versions.'); + } + + // Update if the minimum required version is greater than the current minimum version. + if (ltVersion(minCurrentVersion, minRequiredVersion)) { + return true; + } + + // Otherwise, do not update. + return false; + } catch (error) { + console.warn( + `Could not parse the following ranges for dependency "${dependency}":` + + ` [${currentRange}] and/or [${requiredRange}].` + + ` Caused by: ${(error as Error).message}`, + ); + return false; + } +} + +function updateCargoDependencies( + cargoToml: CargoToml, + updateFn: (deps: CargoDependencies) => CargoDependencies, +): CargoToml { + const updatedCargoToml = JSON.parse(JSON.stringify(cargoToml)) as CargoToml; + + // Standard dependency sections. + const standardSections = ['dependencies', 'dev-dependencies', 'build-dependencies'] as const; + for (const section of standardSections) { + if (updatedCargoToml[section]) { + updatedCargoToml[section] = updateFn(updatedCargoToml[section]); + } + } + + // Target-specific dependencies. + if (updatedCargoToml.target) { + for (const targetKey of Object.keys(updatedCargoToml.target)) { + for (const section of standardSections) { + if (updatedCargoToml.target[targetKey][section]) { + updatedCargoToml.target[targetKey][section] = updateFn(updatedCargoToml.target[targetKey][section]); + } + } + } + } + + // Workspace dependencies. + if (updatedCargoToml.workspace?.dependencies) { + updatedCargoToml.workspace.dependencies = updateFn(updatedCargoToml.workspace.dependencies); + } + + return updatedCargoToml; +} + +function cargoToNpmSemver(cargoVersion: string): string { + const version = cargoVersion.trim(); + return /^\d+(\.\d+)?(\.\d+)?/.test(version) ? `^${version}` : version; +} + +function getCargoDependencyVersion(dependency: CargoDependency): string | undefined { + return typeof dependency === 'string' ? dependency : dependency.version; +} + +function getCargoDependencyCrateName(key: string, dependency: CargoDependency): string { + return typeof dependency !== 'string' && dependency.package ? dependency.package : key; +} + +function getCargoDependencyImportName(key: string): string { + return key.replace(/-/g, '_'); +} + +function findCargoDependencyByImportName( + dependencies: CargoDependencies, + importName: string, +): [string, CargoDependency] | undefined { + return Object.entries(dependencies).find(([key]) => { + return getCargoDependencyImportName(key) === importName; + }); +} + +function readCargoToml(path: string): CargoToml { + return parse(readFile(path)) as CargoToml; +} diff --git a/src/utils/fragment.ts b/src/utils/fragment.ts new file mode 100644 index 0000000..f2d052d --- /dev/null +++ b/src/utils/fragment.ts @@ -0,0 +1,7 @@ +import { BaseFragment } from '@codama/renderers-core'; + +import { ImportMap } from '../ImportMap'; + +export type Fragment = BaseFragment & { + imports: ImportMap; +}; diff --git a/src/utils/index.ts b/src/utils/index.ts index 6a3f4aa..6e73eb3 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,5 +1,7 @@ +export * from './cargoToml'; export * from './codecs'; export * from './discriminatorConstant'; +export * from './fragment'; export * from './linkOverrides'; export * from './render'; export * from './traitOptions'; diff --git a/test/utils/cargoToml.test.ts b/test/utils/cargoToml.test.ts new file mode 100644 index 0000000..93a5dfe --- /dev/null +++ b/test/utils/cargoToml.test.ts @@ -0,0 +1,284 @@ +import { CODAMA_ERROR__RENDERERS__MISSING_DEPENDENCY_VERSIONS, CodamaError } from '@codama/errors'; +import { createRenderMap } from '@codama/renderers-core'; +import { describe, expect, test } from 'vitest'; + +import { ImportMap } from '../../src'; +import { + createNewCargoToml, + DEFAULT_DEPENDENCY_VERSIONS, + Fragment, + getUsedDependencyVersions, + shouldUpdateRange, + updateExistingCargoToml, +} from '../../src/utils'; + +function fragment(content: string): Fragment { + return { + content: content, + imports: new ImportMap(), + }; +} + +function use(dependency: string): Fragment { + const segments = dependency.split('::'); + return { + content: segments[segments.length - 1], + imports: new ImportMap().add(dependency), + }; +} + +describe('getUsedDependencyVersions', () => { + test('it returns the parsed dependencies of all used imports', () => { + const renderMap = createRenderMap({ + 'mint.rs': use('foo_crate::Foo'), + 'token.rs': use('bar_crate::Bar'), + }); + const dependencyVersions = { + 'bar-crate': '^1.0.0', + 'foo-crate': '^2.0.0', + 'unused-crate': '^3.0.0', + }; + + expect(getUsedDependencyVersions(renderMap, {}, dependencyVersions)).toEqual({ + 'bar-crate': '^1.0.0', + 'foo-crate': '^2.0.0', + }); + }); + + test('it also supports crate names with underscores', () => { + const renderMap = createRenderMap({ 'mint.rs': use('foo_crate::Foo') }); + const dependencyVersions = { foo_crate: '^2.0.0' }; + + expect(getUsedDependencyVersions(renderMap, {}, dependencyVersions)).toEqual({ + foo_crate: '^2.0.0', + }); + }); + + test('it automatically includes solana SDK dependencies', () => { + const renderMap = createRenderMap({ + 'mint.rs': use('solana_pubkey::Pubkey'), + 'token.rs': use('solana_instruction::AccountMeta'), + }); + + expect(getUsedDependencyVersions(renderMap, {}, {})).toEqual({ + 'solana-instruction': DEFAULT_DEPENDENCY_VERSIONS['solana-instruction'], + 'solana-pubkey': DEFAULT_DEPENDENCY_VERSIONS['solana-pubkey'], + }); + }); + + test('it throws if used dependency versions are not provided', () => { + const renderMap = createRenderMap({ 'mint.rs': use('foo_crate::Foo') }); + + expect(() => getUsedDependencyVersions(renderMap, {}, {})).toThrow( + new CodamaError(CODAMA_ERROR__RENDERERS__MISSING_DEPENDENCY_VERSIONS, { + dependencies: ['foo_crate'], + message: 'Please add these dependencies to the `dependencyVersions` option.', + }), + ); + }); + + test('it identifies used dependencies from the content itself', () => { + const renderMap = createRenderMap({ + 'mint.rs': fragment('pub Struct Mint(solana_pubkey::Pubkey);'), + 'token.rs': fragment('pub Struct Token(solana_instruction::AccountMeta);'), + }); + + expect(getUsedDependencyVersions(renderMap, {}, {})).toEqual({ + 'solana-instruction': DEFAULT_DEPENDENCY_VERSIONS['solana-instruction'], + 'solana-pubkey': DEFAULT_DEPENDENCY_VERSIONS['solana-pubkey'], + }); + }); +}); + +describe('createNewCargoToml', () => { + test('it returns a new Cargo.toml object with the given dependencies', () => { + const cargoToml = createNewCargoToml({ + 'bar-crate': '^1.0.0', + 'foo-crate': '^2.0.0', + }); + expect(cargoToml.dependencies).toEqual({ + 'bar-crate': '^1.0.0', + 'foo-crate': '^2.0.0', + }); + }); + test('it adds features to the new Cargo.toml object', () => { + const cargoToml = createNewCargoToml({}); + expect(cargoToml.features).toEqual({ + anchor: ['dep:anchor-lang'], + 'anchor-idl-build': ['anchor', 'anchor-lang?/idl-build'], + fetch: ['dep:solana-client', 'dep:solana-sdk'], + serde: ['dep:serde', 'dep:serde_with'], + }); + }); +}); + +describe('updateExistingCargoToml', () => { + test('it updates a Cargo.toml with the given dependencies in all standard dependency groups', () => { + const cargoToml = updateExistingCargoToml( + { + 'build-dependencies': { + 'crate-a': '^1.0.0', + 'crate-d': '^1.0.0', + }, + dependencies: { + 'crate-b': '^1.0.0', + 'crate-d': '^1.0.0', + }, + 'dev-dependencies': { + 'crate-c': '^1.0.0', + 'crate-d': '^1.0.0', + }, + }, + { + 'crate-a': '^2.0.0', + 'crate-b': '^2.0.0', + 'crate-c': '^2.0.0', + 'crate-d': '^2.0.0', + }, + ); + expect(cargoToml).toEqual({ + 'build-dependencies': { + 'crate-a': '^2.0.0', + 'crate-d': '^2.0.0', + }, + dependencies: { + 'crate-b': '^2.0.0', + 'crate-d': '^2.0.0', + }, + 'dev-dependencies': { + 'crate-c': '^2.0.0', + 'crate-d': '^2.0.0', + }, + }); + }); + + test('it updates a Cargo.toml with the given dependencies in any target-specific dependency groups', () => { + const cargoToml = updateExistingCargoToml( + { + target: { + 'cfg(unix)': { + 'build-dependencies': { 'my-crate': '^1.0.0' }, + dependencies: { 'my-crate': '^1.0.0' }, + 'dev-dependencies': { 'my-crate': '^1.0.0' }, + }, + 'cfg(windows)': { + 'build-dependencies': { 'my-crate': '^1.0.0' }, + dependencies: { 'my-crate': '^1.0.0' }, + 'dev-dependencies': { 'my-crate': '^1.0.0' }, + }, + }, + }, + { 'my-crate': '^2.0.0' }, + ); + expect(cargoToml).toEqual({ + target: { + 'cfg(unix)': { + 'build-dependencies': { 'my-crate': '^2.0.0' }, + dependencies: { 'my-crate': '^2.0.0' }, + 'dev-dependencies': { 'my-crate': '^2.0.0' }, + }, + 'cfg(windows)': { + 'build-dependencies': { 'my-crate': '^2.0.0' }, + dependencies: { 'my-crate': '^2.0.0' }, + 'dev-dependencies': { 'my-crate': '^2.0.0' }, + }, + }, + }); + }); + + test('it updates a Cargo.toml with the given dependencies in a workspace dependency group', () => { + const cargoToml = updateExistingCargoToml( + { workspace: { dependencies: { 'my-crate': '^1.0.0' } } }, + { 'my-crate': '^2.0.0' }, + ); + expect(cargoToml).toEqual({ + workspace: { dependencies: { 'my-crate': '^2.0.0' } }, + }); + }); + + test('it does not update non-dependency attributes', () => { + const cargoToml = updateExistingCargoToml( + { + dependencies: { 'my-crate': '^1.0.0' }, + name: 'my-crate', + version: '1.2.3', + }, + { 'my-crate': '^2.0.0' }, + ); + expect(cargoToml).toEqual({ + dependencies: { 'my-crate': '^2.0.0' }, + name: 'my-crate', + version: '1.2.3', + }); + }); + + test('it adds new dependencies to the main dependencies group by default', () => { + const cargoToml = updateExistingCargoToml({}, { 'new-crate': '^1.0.0' }); + expect(cargoToml).toEqual({ dependencies: { 'new-crate': '^1.0.0' } }); + }); + + test('it does not update nor add dependencies whose range is newer or stricter', () => { + const cargoToml = updateExistingCargoToml( + { + 'build-dependencies': { 'crate-a': '^2.0.0' }, + dependencies: { 'crate-b': '^2.5.0' }, + 'dev-dependencies': { 'crate-c': '^2.0.0' }, + }, + { + 'crate-a': '^1.0.0', + 'crate-b': '^2.0.0', + 'crate-c': '>=1 <5', + }, + ); + expect(cargoToml).toEqual({ + 'build-dependencies': { 'crate-a': '^2.0.0' }, + dependencies: { 'crate-b': '^2.5.0' }, + 'dev-dependencies': { 'crate-c': '^2.0.0' }, + }); + }); +}); + +describe('shouldUpdateRange', () => { + test('it returns true if the required version is stricter', () => { + expect(shouldUpdateRange('module', '^1.0.0', '^1.1.0')).toBe(true); + expect(shouldUpdateRange('module', '^0.1', '^0.1.5')).toBe(true); + expect(shouldUpdateRange('module', '>=1 <5', '^3.0')).toBe(true); + expect(shouldUpdateRange('module', '>=1 <5', '>=2 <4')).toBe(true); + }); + + test('it returns true if the required version is newer', () => { + expect(shouldUpdateRange('module', '^1.0', '^2.0')).toBe(true); + expect(shouldUpdateRange('module', '^1.0.0', '^2.0.0')).toBe(true); + expect(shouldUpdateRange('module', '^0.1', '^42.99.99')).toBe(true); + expect(shouldUpdateRange('module', '>=1 <5', '>=2 <6')).toBe(true); + }); + + test('it returns false if the required version is looser', () => { + expect(shouldUpdateRange('module', '^1.1.0', '^1.0.0')).toBe(false); + expect(shouldUpdateRange('module', '^0.1.5', '^0.1')).toBe(false); + expect(shouldUpdateRange('module', '^3.0', '>=1 <5')).toBe(false); + expect(shouldUpdateRange('module', '>=2 <4', '>=1 <5')).toBe(false); + }); + + test('it returns false if the required version is older', () => { + expect(shouldUpdateRange('module', '^2.0', '^1.0')).toBe(false); + expect(shouldUpdateRange('module', '^2.0.0', '^1.0.0')).toBe(false); + expect(shouldUpdateRange('module', '^42.99.99', '^0.1')).toBe(false); + expect(shouldUpdateRange('module', '>=2 <6', '>=1 <5')).toBe(false); + }); + + test('it returns false if either range cannot be parsed', () => { + expect(shouldUpdateRange('module', 'invalid', '^1.0.0')).toBe(false); + expect(shouldUpdateRange('module', '^1.0.0', 'invalid')).toBe(false); + }); + + test('it handles bare versions like caret versions', () => { + expect(shouldUpdateRange('module', '1.0.0', '1.1.0')).toBe(true); + expect(shouldUpdateRange('module', '1.1.0', '1.0.0')).toBe(false); + }); + + test('it handles equal versions like locked versions', () => { + expect(shouldUpdateRange('module', '=1.0.0', '=1.1.0')).toBe(true); + expect(shouldUpdateRange('module', '=1.1.0', '=1.0.0')).toBe(false); + }); +});