Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade to TypeScript v5.0 #3651

Closed
1 task
MajorLift opened this issue Dec 12, 2023 · 0 comments · Fixed by #4548
Closed
1 task

Upgrade to TypeScript v5.0 #3651

MajorLift opened this issue Dec 12, 2023 · 0 comments · Fixed by #4548
Assignees

Comments

@MajorLift
Copy link
Contributor

MajorLift commented Dec 12, 2023

Motivation

As part of Shared Libraries Q2 2024 OKRs (O3/KR4), we will be upgrading all core packages to use TypeScript v5.0.

There are some significant blockers associated with this upgrade due to breaking changes in TypeScript's handling of modules. Once unblocked, we will be able to proceed to further version upgrades, gaining access to the latest features and improvements in TypeScript, and reaching parity with the extension.

References

Features

  • const Type Parameters:
    • Causes as const inference by default.
    • 74 as const instances in core.
type HasNames = { names: readonly string[] };
function getNamesExactly<const T extends HasNames>(arg: T): T["names"] {
//                       ^^^^^
    return arg.names;
}
// Inferred type without `const`: string[]
// Inferred type with `const`: readonly ["Alice", "Bob", "Eve"]
// Note: Didn't need to write 'as const' here
const names = getNamesExactly({ names: ["Alice", "Bob", "Eve"] });

Blockers

node16/nodenext

  • TypeScript v4.7 introduced the node16/nodenext options for the --module and --moduleResolution compiler options.

  • We are currently at v4.8 using the node option for moduleResolution, which is renamed to node10 starting from TypeScript v5.0 (the node alias is maintained for compatibility).

    • node10 does not support correct module resolution for ESM packages.
    • node10 does not support the exports field in package.json

bundler

  • v5.0 introduces the bundler option for better compatibility, but this isn't intended for usage with npm libraries that don't use a bundler to build and deploy.

On the other hand, if you’re writing a library that’s meant to be published on npm, using the bundler option can hide compatibility issues that may arise for your users who aren’t using a bundler. So in these cases, using the node16 or nodenext resolution options is likely to be a better path.

https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-0.html#--moduleresolution-bundler

  • bundler requires --module to be es2020+ and looks for type declarations under dist/esm/.
    src/AccountsController.test.ts:6:28 - error TS7016: Could not find a declaration file for module '@metamask/snaps-utils'. '/home/runner/work/core/core/node_modules/@metamask/snaps-utils/dist/esm/index.js' implicitly has an 'any' type.
      Try `npm i --save-dev @types/metamask__snaps-utils` if it exists or add a new declaration (.d.ts) file containing `declare module '@metamask/snaps-utils';`

    6 import { SnapStatus } from '@metamask/snaps-utils';
                                 ~~~~~~~~~~~~~~~~~~~~~~~
  • TODO: Investigate feasibility of using bundler option with tsup

Explanation

We need to update --module and --moduleResolution to node16 at minimum and preferably nodenext before we upgrade to TypeScript v5.0, but this entails a number of breaking changes:

Add file extensions to relative import

For example, in an ECMAScript module in Node.js, any relative import needs to include a file extension.

// entry.mjs
import * as utils from "./utils";     // ❌ wrong - we need to include the file extension.
import * as utils from "./utils.mjs"; // ✅ works

https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-0.html#--moduleresolution-bundler

Also investigate allowImportingTsExtensions

Convert to dynamic import

it’s worth noting that the only way to import ESM files from a CJS module is using dynamic import() calls. This can present challenges, but is the behavior in Node.js today.

https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-7.html
https://nodejs.org/api/esm.html#esm_interoperability_with_commonjs

Add "type: module" to package.json

That’s why TypeScript 4.7 introduces a new option called moduleDetection. moduleDetection can take on 3 values: "auto" (the default), "legacy" (the same behavior as 4.6 and prior), and "force".

Under the mode "auto", TypeScript will not only look for import and export statements, but it will also check whether

  • the "type" field in package.json is set to "module" when running under --module nodenext/--module node16, and
  • check whether the current file is a JSX file when running under --jsx react-jsx

In cases where you want every file to be treated as a module, the "force" setting ensures that every non-declaration file is treated as a module. This will be true regardless of how module, moduleResolution, and jsx are configured.

https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-7.html#control-over-module-detection

Move type declarations in dependencies to dist/cjs/

    src/AccountsController.test.ts:6:28 - error TS7016: Could not find a declaration file for module '@metamask/snaps-utils'. '/home/runner/work/core/core/node_modules/@metamask/snaps-utils/dist/cjs/index.js' implicitly has an 'any' type.
      Try `npm i --save-dev @types/metamask__snaps-utils` if it exists or add a new declaration (.d.ts) file containing `declare module '@metamask/snaps-utils';`

    6 import { SnapStatus } from '@metamask/snaps-utils';
@MajorLift MajorLift changed the title Set TypeScript compiler options --module, --moduleResolution to node16/nodenext Prepare for TypeScript v5 upgrade by setting compiler options --module, --moduleResolution to node16/nodenext Feb 8, 2024
@MajorLift MajorLift changed the title Prepare for TypeScript v5 upgrade by setting compiler options --module, --moduleResolution to node16/nodenext Upgrade to TypeScript v5.0 and set --module, --moduleResolution to node16/nodenext Mar 19, 2024
@MajorLift MajorLift changed the title Upgrade to TypeScript v5.0 and set --module, --moduleResolution to node16/nodenext Upgrade to TypeScript v5.0 Mar 20, 2024
MajorLift added a commit to MetaMask/superstruct that referenced this issue May 29, 2024
)

## Explanation

As part of our OKR for upgrading TypeScript to v5.0+ (Q2 2024 O3KR4), we
are updating our core dependencies to generate builds and type
declarations for both CJS and ESM.

## References

- Same update made to `@metamask/utils`:
MetaMask/utils#182
- Required to update core repo's `moduleResolution` setting to
`NodeNext`:
  - Blocks: MetaMask/core#3651
 
## Changelog

### Added

- Newly exports the following types: `AnyStruct`, `EnumSchema`,
`InferStructTuple`, `IsExactMatch`, `IsMatch`, `IsRecord`, `IsTuple`,
`ObjectSchema`, `OmitBy`, `Optionalize`, `PickBy`, `Simplify`,
`UnionToIntersection`.
MajorLift added a commit to MetaMask/utils that referenced this issue Jun 4, 2024
## Explanation

As part of our OKR for upgrading TypeScript to v5.0+ (Q2 2024 O3KR4), we
are updating our core dependencies to generate builds and type
declarations for both CJS and ESM.

- Replaces all references to `superstruct` with ESM-compatible
`@metamask/superstruct` fork.
- Set TypeScript `module`, `moduleResolution` options to `NodeNext`.

## References

- Closes: #186
- Blocks: MetaMask/core#3651
- Blocked by: MetaMask/superstruct#20

## Changelog

```md
### Changed

- **BREAKING:** Replace dependency `superstruct` `^1.0.3` with ESM-compatible `@metamask/superstruct` `^3.0.0` ([#185](#185)).
- Bump devDependency `typescript` from `~4.8.4` to `~5.0.4` ([#185](#185)).
```

---------

Co-authored-by: Elliot Winkler <elliot.winkler@gmail.com>
MajorLift added a commit that referenced this issue Jul 22, 2024
…ode16` (#3645)

## Explanation

### TypeScript v5.0

As part of the Wallet Framework Team's OKRs (Q2 2024 O3KR4, Q3 2024 O2KR4),
we are upgrading TypeScript to v5.0+ for all packages in the core
monorepo.

These upgrades will give us access to new features, aid us in writing
more type-safe and modern code, and also allow us to reach parity with
Extension and other MetaMask projects.

### `Node16`

In order to maximize the benefits of this upgrade, we are also enabling
the `Node16` setting for the `module` and `moduleResolution` tsconfig
options.

## Motivation

### Interop: CJS modules referencing ESM modules

The core monorepo is a collection of CJS packages, which use CJS module
resolution rules internally, and are treated as CJS modules by Node.js.
This is true despite that fact that these packages are authored in
TypeScript using ESM syntax (`import`/`export` statements) and are set
up to output dual builds for both CJS and ESM.

With `Node16` or `NodeNext` enabled, CJS modules are unable to reference
ESM modules via static/synchronous `import` statements, as TypeScript
assumes them to be compiled down to CJS-only `require` statements.

There are three solutions for this issue, of which we are utilizing the
first two:
1. Update the ESM-only dependency so that it outputs a CJS build and
type declaration as well.
2. Replace the static `import` statements with dynamic import syntax
(which, based on CJS emit rules, are not transformed to `require`
statements).
3. Migrate our module to ESM (by setting `"type": "module"` in
package.json and renaming all of our scripts to *.cjs)

> See
https://www.typescriptlang.org/docs/handbook/modules/reference.html#interoperability-rules

With dependencies that we control or use extensively, we pursue the
first option, as we're doing with `superstruct` and `@metamask/utils`
(and all of the many core dependencies that are downstream of either or
both).

With dependencies that see more limited usage, we are opting for either
the second option (e.g. `multiformats`) or, if available, using a
CJS-compatible alternative (e.g. replacing `lodash-es` with `lodash`).

The third solution of migrating to ESM is the most fundamental,
long-term measure, but we are refraining from it at this stage until we
can make a concerted effort to migrate our codebase as a whole. This is
because any individual ESM migration can cause a cascading effect
through the dependency tree where other packages are now required to
migrate as well.

### Reasons for moving away from `node`/`node10`

The following outlines the motivation for switching to `Node16`, backed
by relevant entries from the official TypeScript documentation.
- Starting with TypeScript v5.0, the previously used `moduleResolution`
setting `node` is renamed to `node10`, and strongly discouraged from
usage.
- "It [reflects the CommonJS module resolution algorithm as it existed
in Node.js versions earlier than
v12](https://www.typescriptlang.org/docs/handbook/modules/reference.html#node10-formerly-known-as-node).
**It should no longer be used.**"
- "Because `node16` and `nodenext` are the only module options that
reflect the complexities of Node.js’s dual module system, they are the
[**only correct module options** for all apps and libraries that are
intended to run in Node.js v12 or
later](https://www.typescriptlang.org/docs/handbook/modules/reference.html#node16-nodenext),
whether they use ES modules or not."
- The `node10` setting is unable to guarantee correct module resolution
for ESM-only dependencies.
- "[Because Node.js v12 introduced different module resolution rules for
ES
modules,](https://www.typescriptlang.org/docs/handbook/modules/theory.html#module-resolution)
though, it’s a very bad model of modern versions of Node.js. It should
not be used for new projects."
- "`node16` and `nodenext` describe the full range of behavior for
Node.js’s dual-format module system, and **emit files in either CommonJS
or ESM format**. This is different from every other `module` option,
which are runtime-agnostic and force all output files into a single
format, [leaving it to the user to ensure the output is valid for their
runtime.](https://www.typescriptlang.org/docs/handbook/modules/reference.html#node16-nodenext)"
- The `node10` setting does not support the package.json `"exports"`
field, which is used in our libraries to expose dual builds and type
declarations.
-
https://www.typescriptlang.org/docs/handbook/modules/reference.html#packagejson-exports
- The `Node16` and `NodeNext` settings maximize downstream
compatibility.
- "When compiling a library, you don’t know where the output code will
run, but you’d like it to run in as many places as possible. Using
"module": "nodenext" (along with the implied "moduleResolution":
"nodenext") is the best bet for maximizing the compatibility of the
output JavaScript’s module specifiers, since [it will force you to
comply with Node.js’s stricter rules for import module
resolution.](https://www.typescriptlang.org/docs/handbook/modules/theory.html#module-resolution-for-libraries)"
- "`"moduleResolution": "nodenext"` is only checking that the output
works in Node.js, but in most cases, [module code that works in Node.js
will work in other runtimes and in
bundlers](https://www.typescriptlang.org/docs/handbook/modules/theory.html#module-resolution-for-libraries)"
  
#### `Node16` vs. `NodeNext`

The two settings are currently identical, and `Node16` is intended to
work with all current node versions v16 or higher. If additional
capabilities are added to `NodeNext` or `Node18`/`Node20` that we want
to apply to our codebases, we will be able to introduce them after
checking for disruptive regressions or breaking changes.

#### Relationship with other tsconfig options

- `--module` `nodenext` or `node16` implies and enforces the
`moduleResolution` with the same name (and vice versa).
- `--module` `node16` implies (up to) `--target` `es2022`.
  - `--module` `nodenext` implies (up to) `--target` `esnext`.
>
https://www.typescriptlang.org/docs/handbook/modules/reference.html#implied-and-enforced-options

## Description

- [x] Replace `superstruct` dependency with `@metamask/superstruct`
`^3.0.0`.
  - [x] `^3.1.0`
- [x] Replace all `superstruct` import statements with
`@metamask/superstruct`
- [x] Bump `@metamask/utils` to `^8.5.0`.
  - [x] `^9.0.0`
    - [x] remove yarn resolution to `@metamask/superstruct@npm:3.1.0`
- [x] Bump `typescript` to `~5.0.4`
  - [x] Set `module` and `moduleResolution` tsconfig options to `Node16`
  - [ ] ~#4507
  
Further context on why the `superstruct` and `utils` changes are
necessary:
- MetaMask/utils#144
- MetaMask/superstruct#1
- MetaMask/superstruct#18
- MetaMask/utils#182
- MetaMask/metamask-module-template#247
-
https://www.typescriptlang.org/docs/handbook/modules/guides/choosing-compiler-options.html#considerations-for-bundling-libraries

## Release order roadmap

Due to interdependencies between the packages involved in this PR, we
will need to update and release them in a specific order:

- [x] #4516
- [x] Release `{base,permission}-controller`
- [x] (wait for releases: `snaps-sdk`, `snaps-utils`,
`snaps-controllers`, `keyring-api`)
  - [x] MetaMask/keyring-api#356
  - [x] MetaMask/snaps#2445
  - [x] MetaMask/snaps#2584
  - [x] MetaMask/snaps#2589
- [x] Remove yarn resolutions for `snaps-sdk`, `snaps-utils`,
~`keyring-api`~
- [ ] Merge this PR: #3645
- [x] Set yarn resolution for `@metamask/providers` via
`@metamask/snaps-sdk` to `17.1.1`
- [x] Leave messages in Changelog for affected packages
(`{accounts,chain,profile-sync}-controller`) to hold off on new releases
    ```md
    ### Uncategorized

- Please hold off on new releases of this package until the yarn
resolution for `@metamask/providers` is removed.
- This is blocked by a `@metamask/snaps-sdk` release with
`@metamask/providers` bumped to `>=17.1.1`.
- See: [Fix regressions introduced by
@metamask/providers@17.1.1](MetaMask/snaps#2579)
- Build error fixed by yarn resolution:
[MetaMask/core/actions/runs/10011688901/job/27675682526?pr=3645](https://github.com/MetaMask/core/actions/runs/10011688901/job/27675682526?pr=3645)
    ```
- [ ] Release all core pkgs (especially deps of `snaps-controllers` and
consumers of `utils`)
- Exclude core pkgs that have `snaps-controllers` as dependency, and are
affected by `@metamask/providers` yarn resolution
    - `{accounts,chain,profile-sync}-controller`
- [ ] Release `snaps-controllers`
- [ ] Release `{accounts,chain,profile-sync}-controller`,
`eth-snap-keyring`
(MetaMask/eth-snap-keyring#311)
- [ ] (release remaining `snaps` packages while releasing
`@metamask/utils@9.0.0` version bumps for all dependencies and nested
dependencies)

## References

- Contributes to #3651
- Blocked by:
  - `superstruct`
    - [x] MetaMask/superstruct#18
    - [x] MetaMask/superstruct#24
    - [x] MetaMask/superstruct#25
    - [x] MetaMask/superstruct#28
  - `utils`
    - [x] MetaMask/utils#185
    - [x] MetaMask/utils#191
    - [x] MetaMask/utils#194
    - [x] MetaMask/utils#196
- Blocked by downstream consumers of `superstruct`, `utils`:
  - `abi-utils`: 
    - [x] MetaMask/abi-utils#73
    - [x] MetaMask/abi-utils#78
    - [x] MetaMask/abi-utils#80
    - [x] MetaMask/abi-utils#81
  - `chain-api`: 
    - [x] MetaMask/accounts-chain-api#5
- [x] https://github.com/MetaMask/accounts-chain-api/releases/tag/v0.1.0
  - `eth-simple-keyring`: 
    - [x] MetaMask/eth-simple-keyring#177
    - [x] MetaMask/eth-simple-keyring#178
  - `providers`:
    - [x] MetaMask/providers#336
    - [x] MetaMask/providers#337
- Blocked by MetaMask/providers#340,
ts-bridge/ts-bridge#22
- Causes CI failure:
https://github.com/MetaMask/snaps/actions/runs/9783767567/job/27013136688?pr=2445
    - [x] MetaMask/providers#345
    - [x] MetaMask/providers#347
  - `rpc-errors`: 
    - [x] MetaMask/rpc-errors#147
    - [x] MetaMask/rpc-errors#148    
  - `snaps-registry`:
    - [x] MetaMask/snaps-registry#613
    - [x] MetaMask/snaps-registry#670
    - [x] MetaMask/snaps-registry#693
    - [x] MetaMask/snaps-registry#694
- Blocked by `snaps` monorepo releases
  - `snaps-sdk`, `snaps-utils`
    - `keyring-api`
      - [x] MetaMask/keyring-api#328
  - `snaps-controllers` 
    - `eth-snap-keyring`
      - [x] MetaMask/eth-snap-keyring#311

## Changelog

### `@metamask/accounts-controller`

```md
### Changed

- Bump `@metamask/keyring-api` from `^8.0.0` to `^8.0.1` ([#3645](#3645))
- Bump `@metamask/snaps-sdk` from `^4.2.0` to `^6.1.0` ([#3645](#3645))
- Bump `@metamask/snaps-utils` from `^7.4.0` to `^7.8.0` ([#3645](#3645))
- Bump peer dependency `@metamask/snaps-controllers` from `^8.1.1` to `^9.3.0` ([#3645](#3645))
```

### `@metamask/assets-controllers` (major)

```md
### Changed

- **BREAKING:** `getIpfsCIDv1AndPath`, `getFormattedIpfsUrl` are now async functions ([#3645](#3645))
- Add `immer` `^9.0.6` as a new dependenc. ([#3645](#3645))
- Bump `@metamask/abi-utils` from `^2.0.2` to `^2.0.3` ([#3645](#3645))
- Bump `multiformats` from `^9.5.2` to `^13.1.0` ([#3645](#3645))
```

### `@metamask/chain-controller`

```md
### Changed

- Bump `@metamask/chain-api` from `^0.0.1` to `^0.1.0` ([#3645](#3645))
- Bump `@metamask/keyring-api` from `^8.0.0` to `^8.0.1` ([#3645](#3645))
- Bump `@metamask/snaps-controllers` from `^8.1.1` to `^9.3.0` ([#3645](#3645))
- Bump `@metamask/snaps-sdk` from `^4.2.0` to `^6.1.0` ([#3645](#3645))
- Bump `@metamask/snaps-utils` from `^7.4.0` to `^7.8.0` ([#3645](#3645))
```

### `@metamask/keyring-controller`

```md
### Changed

- Bump `@metamask/eth-simple-keyring` from `^6.0.1` to `^6.0.2` ([#3645](#3645))
- Bump `@metamask/keyring-api` from `^8.0.0` to `^8.0.1` ([#3645](#3645))
- Set yarn resolution for `@metamask/snaps-sdk/@metamask/providers` to `17.1.1` ([#3645](#3645))
  - Remove once `@metamask/snaps-sdk` bumps its `@metamask/providers` version to `>=17.1.1`.
```
  
### `@metamask/network-controller` (minor)

```md
### Added

- Newly exports the following types: `AutoManagedNetworkClient`, `InfuraNetworkClientConfiguration`, `CustomNetworkClientConfiguration` ([#3645](#3645))
```

### `@metamask/profile-sync-controller`

```md
### Changed

- Bump dependency and peer dependency `@metamask/snaps-controllers` from `^8.1.1` to `^9.3.0` ([#3645](#3645))
- Bump `@metamask/snaps-sdk` from `^4.2.0` to `^6.1.0` ([#3645](#3645))
- Bump `@metamask/snaps-utils` from `^7.4.0` to `^7.8.0` ([#3645](#3645))
```

### `@metamask/transaction-controller`

```md
### Changed

- Bump `@metamask/keyring-api` from `^8.0.0` to `^8.0.1` ([#3645](#3645))
```

### `@metamask/user-operation-controller`

```md
### Fixed

- Replace `superstruct` with ESM-compatible `@metamask/superstruct` `^3.1.0` ([#3645](#3645))
  - This fixes the issue of this package being unusable by any TypeScript project that uses `Node16` or `NodeNext` as its `moduleResolution` option.
```

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] I've updated documentation (JSDoc, Markdown, etc.) for new or
updated code as appropriate
- [x] I've highlighted breaking changes using the "BREAKING" category
above as appropriate

---------

Co-authored-by: Elliot Winkler <elliot.winkler@gmail.com>
MajorLift added a commit that referenced this issue Jul 23, 2024
…tion for `@metamask/providers` (#4547)

## References

- Contributes to #3651
- Follows from #3645

## Changelog

### `@metamask/accounts-controller`

```md
### Changed

- Bump `@metamask/snaps-sdk` from `^6.1.0` to `^6.1.1` ([#4547](#4547))
- Bump `@metamask/snaps-utils` from `^7.8.0` to `^7.8.1` ([#4547](#4547))
```

### `@metamask/chain-controller`

```md
### Changed

- Bump `@metamask/snaps-controllers` from `^9.3.0` to `^9.3.1` ([#4547](#4547))
- Bump `@metamask/snaps-sdk` from `^6.1.0` to `^6.1.1` ([#4547](#4547))
- Bump `@metamask/snaps-utils` from `^7.8.0` to `^7.8.1` ([#4547](#4547))
```

### `@metamask/profile-sync-controller`

```md
### Changed

- Bump `@metamask/snaps-controllers` from `^9.3.0` to `^9.3.1` ([#4547](#4547))
- Bump `@metamask/snaps-sdk` from `^6.1.0` to `^6.1.1` ([#4547](#4547))
- Bump `@metamask/snaps-utils` from `^7.8.0` to `^7.8.1` ([#4547](#4547))
```

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] I've updated documentation (JSDoc, Markdown, etc.) for new or
updated code as appropriate
- [x] I've highlighted breaking changes using the "BREAKING" category
above as appropriate
@MajorLift MajorLift linked a pull request Jul 23, 2024 that will close this issue
3 tasks
@MajorLift MajorLift mentioned this issue Jul 23, 2024
3 tasks
AugmentedMode pushed a commit that referenced this issue Jul 30, 2024
This is the release candidate for `v180.0.0`:
- `@metamask/accounts-controller@18.0.0` (major)
- `@metamask/assets-controllers@37.0.0` (major)
- `@metamask/chain-controllers@0.1.1` (patch)
- `@metamask/ens-controller@13.0.1` (patch)
- `@metamask/eth-json-rpc-provider@4.1.2` (patch)
- `@metamask/gas-fee-controller@19.0.1` (patch)
- `@metamask/keyring-controller@17.1.2` (patch)
- `@metamask/message-manager@10.0.2` (patch)
- `@metamask/network-controller@20.1.0` (minor)
- `@metamask/notification-services-controller@0.2.0` (minor)
- `@metamask/polling-controller@9.0.1` (patch)
- `@metamask/preferences-controller@13.0.1` (patch)
- `@metamask/polling-controller@9.0.1` (patch)
- `@metamask/profile-sync-controller@0.2.0` (minor)
- `@metamask/queued-request-controller@4.0.0` (major)
- `@metamask/selected-network-controller@17.0.0` (major)
- `@metamask/signature-controller@18.0.1` (patch)
- `@metamask/transaction-controller@35.0.1` (patch)
- `@metamask/user-operation-controller@14.0.1` (patch)

- Closes #3651
- Unblocks new releases in core.

- [x] I've updated the test suite for new or updated code as appropriate
- [x] I've updated documentation (JSDoc, Markdown, etc.) for new or
updated code as appropriate
- [x] I've highlighted breaking changes using the "BREAKING" category
above as appropriate

---------

Co-authored-by: Elliot Winkler <elliot.winkler@gmail.com>
Co-authored-by: Mark Stacey <markjstacey@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant