Security News
ESLint is Now Language-Agnostic: Linting JSON, Markdown, and Beyond
ESLint has added JSON and Markdown linting support with new officially-supported plugins, expanding its versatility beyond JavaScript.
ts-pattern
Advanced tools
ts-pattern is a pattern matching library for TypeScript that allows for expressive and type-safe pattern matching. It provides a way to handle complex data structures and control flow in a concise and readable manner.
Basic Pattern Matching
This feature allows you to match a value against multiple patterns and execute corresponding actions. The 'otherwise' method provides a default case.
const { match } = require('ts-pattern');
const value = 2;
const result = match(value)
.with(1, () => 'one')
.with(2, () => 'two')
.with(3, () => 'three')
.otherwise(() => 'unknown');
console.log(result); // Output: 'two'
Nested Pattern Matching
This feature allows you to match nested objects and structures, making it easier to handle complex data.
const { match } = require('ts-pattern');
const data = { type: 'user', user: { name: 'Alice', age: 30 } };
const result = match(data)
.with({ type: 'user', user: { name: 'Alice' } }, () => 'Hello Alice')
.with({ type: 'user', user: { age: 30 } }, () => 'User is 30 years old')
.otherwise(() => 'Unknown user');
console.log(result); // Output: 'Hello Alice'
Guard Functions
Guard functions allow you to use custom logic to determine if a pattern matches, providing greater flexibility.
const { match, when } = require('ts-pattern');
const value = 10;
const result = match(value)
.with(when(x => x > 5), () => 'greater than 5')
.with(when(x => x <= 5), () => '5 or less')
.otherwise(() => 'unknown');
console.log(result); // Output: 'greater than 5'
match-ts is another TypeScript pattern matching library. It offers similar functionality to ts-pattern but with a different API design. It focuses on providing a more functional programming style.
typescript-pattern-matching is a library that provides pattern matching capabilities for TypeScript. It is similar to ts-pattern but may have different performance characteristics and API design choices.
fp-ts is a library for functional programming in TypeScript. While it is not solely focused on pattern matching, it provides utilities for handling data structures and control flow in a functional style, which can be used to achieve similar outcomes.
The exhaustive Pattern Matching library for TypeScript with smart type inference.
import { match, select } from 'ts-pattern';
type Data =
| { type: 'text'; content: string }
| { type: 'img'; src: string };
type Result =
| { type: 'ok'; data: Data }
| { type: 'error'; error: Error };
const result: Result = ...;
return match(result)
.with({ type: 'error' }, (res) => `<p>Oups! An error occured</p>`)
.with({ type: 'ok', data: { type: 'text' } }, (res) => `<p>${res.data.content}</p>`)
.with({ type: 'ok', data: { type: 'img', src: select() } }, (src) => `<img src=${src} />`)
.exhaustive();
Write better and safer conditions. Pattern matching lets you express complex conditions in a single, compact expression. Your code becomes shorter and more readable. Exhaustiveness checking ensures you havenβt forgotten any possible case.
.exhaustive()
.__
.when(<predicate>)
and not(<pattern>)
patterns for complex cases.select(<name?>)
function.Pattern Matching is a technique coming from functional programming languages to declaratively write conditional code branches based on the structure of a value. This technique has proven itself to be much more powerful and much less verbose than imperative alternatives (if/else/switch statements) especially when branching on complex data structures or on several values.
Pattern Matching is implemented in Haskell, Rust, Swift, Elixir and many other languages. There is a tc39 proposal to add Pattern Matching to the EcmaScript specification, but it is still in stage 1 and isn't likely to land before several years (if ever). Luckily, pattern matching can be implemented in userland. ts-pattern
Provides a typesafe pattern matching implementation that you can start using today.
Read the introduction blog post: Bringing Pattern Matching to TypeScript π¨ Introducing TS-Pattern v3.0
Via npm
npm install ts-pattern
Via yarn
yarn add ts-pattern
ts-pattern | TypeScript v4.2+ | TypeScript v4.1+ | TypeScript v3.x- |
---|---|---|---|
v3.x | β | β οΈ | β |
v2.x | β | β | β |
v1.x | β | β | β |
β Full support
β οΈ Partial support, everything works except passing more than 2 patterns to .with()
β No support
when
Guard Demonot
Pattern Demoselect
Pattern DemoAs an example, we are going to create a state reducer for a frontend application fetching some data using an HTTP request.
Our application can be in four different states: idle
, loading
,
success
and error
. Depending on which state we are in, some events
can occur. Here are all the possible types of event our application
can respond to: fetch
, success
, error
and cancel
.
I use the word event
but you can replace it with action
if you are used
to Redux's terminology.
type State =
| { status: 'idle' }
| { status: 'loading'; startTime: number }
| { status: 'success'; data: string }
| { status: 'error'; error: Error };
type Event =
| { type: 'fetch' }
| { type: 'success'; data: string }
| { type: 'error'; error: Error }
| { type: 'cancel' };
Even though our application can handle 4 events, only a subset of these
events make sense for each given state. For instance we can only cancel
a request if we are currently in the loading
state.
To avoid unwanted state changes that could lead to bugs, we want to create
a reducer function that matches on both the state and the event
and return a new state.
This is a case where match
really shines. Instead of writing nested
switch statements, we can do that in a very expressive way:
import { match, __, not, select, when } from 'ts-pattern';
const reducer = (state: State, event: Event): State =>
match<[State, Event], State>([state, event])
.with([{ status: 'loading' }, { type: 'success' }], ([, event]) => ({
status: 'success',
data: event.data,
}))
.with(
[{ status: 'loading' }, { type: 'error', error: select() }],
(error) => ({
status: 'error',
error,
})
)
.with([{ status: not('loading') }, { type: 'fetch' }], () => ({
status: 'loading',
startTime: Date.now(),
}))
.with(
[
{ status: 'loading', startTime: when((t) => t + 2000 < Date.now()) },
{ type: 'cancel' },
],
() => ({
status: 'idle',
})
)
.with(__, () => state)
.exhaustive();
Let's go through this bit by bit:
match
takes a value and returns a builder on which you can add your pattern matching cases.
match<[State, Event], State>([state, event]);
Here we wrap the state and the event objects in an array and we explicitly
specify the type [State, Event]
to make sure it is interpreted as
a Tuple by TypeScript, so we
can match on each value separately.
Most of the time, you don't need to specify the type of input
and output with match<Input, Output>(...)
because match
is able to
infer both of these types.
Then we add a first with
clause:
.with([{ status: 'loading' }, { type: 'success' }], ([state, event]) => ({
// `state` is infered as { status: 'loading' }
// `event` is infered as { type: 'success', data: string }
status: 'success',
data: event.data,
}))
The first argument is the pattern: the shape of value you expect for this branch.
The second argument is the handler function: the code branch that will be called if the input value matches the pattern.
The handler function takes the input value as first parameter with its type narrowed down to what the pattern matches.
In the second with
clause, we use the select
function:
.with(
[{ status: 'loading' }, { type: 'error', error: select() }],
(error) => ({
status: 'error',
error,
})
)
select
let you extract a piece of your input value and inject it into your handler. It is pretty useful when pattern matching on deep data structures because it avoids the hassle of destructuring your input in your handler.
Since we didn't pass any name to select()
, It will inject the event.error
property as first argument to the handler function. Note that you can still access the full input value with its type narrowed by your pattern as second argument of the handler function:
.with(
[{ status: 'loading' }, { type: 'error', error: select() }],
(error, stateAndEvent) => {
// error: Error
// stateAndEvent: [{ status: 'loading' }, { type: 'error', error: Error }]
}
)
In a pattern, we can only have a single anonymous selection. If you need to select more properties on your input data structure, you will need to give them names:
.with(
[{ status: 'success', data: select('prevData') }, { type: 'error', error: select('err') }],
({ prevData, err }) => {
// Do something with (prevData: string) and (err: Error).
}
)
Each named selection will be injected inside a selections
object, passed as first argument to the handler function. Names can be any strings.
If you need to match on everything but a specific value, you can use a not(<pattern>)
pattern. it's a function taking a pattern and returning its opposite:
.with([{ status: not('loading') }, { type: 'fetch' }], () => ({
status: 'loading',
}))
when()
and guard functionsSometimes, we need to make sure our input value respects a condition that can't be expressed by a pattern. For example, imagine you need to check if a number is positive. In these cases, we can use guard functions: functions taking a value and returning a boolean
.
With ts-pattern
there are two options to use a guard function:
when(<guard function>)
inside your pattern.with(...)
.with(
[
{
status: 'loading',
startTime: when((t) => t + 2000 < Date.now()),
},
{ type: 'cancel' },
],
() => ({
status: 'idle',
})
)
.with(...)
.with
optionally accepts a guard function as second parameter, between
the pattern
and the handler
callback:
.with(
[{ status: 'loading' },{ type: 'cancel' }],
([state, event]) => state.startTime + 2000 < Date.now(),
() => ({
status: 'idle'
})
)
This pattern will only match if the guard function returns true
.
__
wildcard__
will match any value.
You can use it at the top level, or inside your pattern.
.with(__, () => state)
// You could also use it inside your pattern:
.with([__, __], () => state)
// at any level:
.with([__, { type: __ }], () => state)
.exhaustive();
.exhaustive()
executes the pattern matching expression, and returns the result. It also enables exhaustiveness checking, making sure we don't forget any possible case in our input value. This extra type safety is very nice because forgetting a case is an easy mistake to make, especially in an evolving code-base.
Note that exhaustive pattern matching is optional. It comes with the trade-off of having longer compilation times because the type checker has more work to do.
Alternatively you can use .otherwise()
, which takes an handler function returning a default value. .otherwise(handler)
is equivalent to .with(__, handler).exhaustive()
.
.otherwise(() => state);
If you don't want to use .exhaustive()
and also don't want to provide a default value with .otherwise()
, you can use .run()
instead:
.run();
It's just like .exhaustive()
, but it's unsafe and might throw runtime error if no branch matches your input value.
As you may know, switch
statements allow handling several cases with
the same code block:
switch (type) {
case 'text':
case 'span':
case 'p':
return 'text';
case 'btn':
case 'button':
return 'button';
}
Similarly, ts-pattern lets you pass several patterns to .with()
and if
one of these patterns matches your input, the handler function will be called:
const sanitize = (name: string) =>
match(name)
.with('text', 'span', 'p', () => 'text')
.with('btn', 'button', () => 'button')
.otherwise(() => name);
sanitize('span'); // 'text'
sanitize('p'); // 'text'
sanitize('button'); // 'button'
Obviously, it also works with more complex patterns than strings. Exhaustive matching also works as you would expect.
match
match(value);
Create a Match
object on which you can later call .with
, .when
, .otherwise
and .run
.
function match<TInput, TOutput>(input: TInput): Match<TInput, TOutput>;
input
.with
match(...)
.with(pattern, [...patterns], handler)
function with(
pattern: Pattern<TInput>,
handler: (value: TInput, selections: Selections<TInput>) => TOutput
): Match<TInput, TOutput>;
// Overload for multiple patterns
function with(
pattern1: Pattern<TInput>,
...patterns: Pattern<TInput>[],
// no selection object is provided when using multiple patterns
handler: (value: TInput) => TOutput
): Match<TInput, TOutput>;
// Overload for guard functions
function with(
pattern: Pattern<TInput>[],
when: (value: TInput) => unknown,
handler: (
[selection: Selection<TInput>, ]
value: TInput
) => TOutput
): Match<TInput, TOutput>;
pattern: Pattern<TInput>
handler
, the with
clause will match if one of the patterns matches.when: (value: TInput) => unknown
TInput
might be narrowed to a more precise type using the pattern
.handler: (value: TInput, selections: Selections<TInput>) => TOutput
match
case must return values of the same type, TOutput
.TInput
might be narrowed to a more precise type using the pattern
.selections
is an object of properties selected from the input with the select
function..when
match(...)
.when(predicate, handler)
function when(
predicate: (value: TInput) => unknown,
handler: (value: TInput) => TOutput
): Match<TInput, TOutput>;
predicate: (value: TInput) => unknown
handler: (value: TInput) => TOutput
match
case must return values of the same type, TOutput
..exhaustive
match(...)
.with(...)
.exhaustive()
Executes the match case, return its result, and enable exhaustive pattern matching, making sure at compile time that all possible cases are handled.
function exhaustive(): IOutput;
.otherwise
match(...)
.with(...)
.otherwise(defaultHandler)
Executes the match case and return its result.
function otherwise(defaultHandler: (value: TInput) => TOutput): TOutput;
defaultHandler: (value: TInput) => TOutput
default:
case of switch
statements.match
case must return values of the same type, TOutput
..run
match(...)
.with(...)
.run()
Executes the match case and return its result.
function run(): TOutput;
isMatching
With a single argument:
import { isMatching, __ } from 'ts-pattern';
const isBlogPost = isMatching({
title: __.string,
description: __.string,
});
if (isBlogPost(value)) {
// value: { title: string, description: string }
}
With two arguments:
const blogPostPattern = {
title: __.string,
description: __.string,
};
if (isMatching(blogPostPattern, value)) {
// value: { title: string, description: string }
}
Type guard function to check if a value is matching a pattern or not.
export function isMatching<p extends Pattern<any>>(
pattern: p
): (value: any) => value is InvertPattern<p>;
export function isMatching<p extends Pattern<any>>(
pattern: p,
value: any
): value is InvertPattern<p>;
pattern: Pattern<any>
value?: any
isMatching
will return a boolean telling us whether or not the value matches the pattern.isMatching
will return a type guard function taking a value and returning a boolean telling us whether or not the value matches the pattern.Patterns are values matching one of the possible shapes of your input. They can
be literal values, data structures, wildcards, or special functions like not
,
when
and select
.
If your input isn't typed, (if it's a any
or a unknown
), you have no constraints
on the shape of your pattern, you can put whatever you want. In your handler, your
value will take the type described by your pattern.
Literals are primitive JavaScript values, like number, string, boolean, bigint, null, undefined, and symbol.
import { match } from 'ts-pattern';
const input: unknown = 2;
const output = match(input)
.with(2, () => 'number: two')
.with(true, () => 'boolean: true')
.with('hello', () => 'string: hello')
.with(undefined, () => 'undefined')
.with(null, () => 'null')
.with(20n, () => 'bigint: 20n')
.otherwise(() => 'something else');
console.log(output);
// => 'two'
__
wildcardThe __
pattern will match any value.
import { match, __ } from 'ts-pattern';
const input = 'hello';
const output = match(input)
.with(__, () => 'It will always match')
.otherwise(() => 'This string will never be used');
console.log(output);
// => 'It will always match'
__.string
wildcardThe __.string
pattern will match any value of type string
.
import { match, __ } from 'ts-pattern';
const input = 'hello';
const output = match(input)
.with('bonjour', () => 'Wonβt match')
.with(__.string, () => 'it is a string!')
.run();
console.log(output);
// => 'it is a string!'
__.number
wildcardThe __.number
pattern will match any value of type number
.
import { match, __ } from 'ts-pattern';
const input = 2;
const output = match<number | string>(input)
.with(__.string, () => 'it is a string!')
.with(__.number, () => 'it is a number!')
.run();
console.log(output);
// => 'it is a number!'
__.boolean
wildcardThe __.boolean
pattern will match any value of type boolean
.
import { match, __ } from 'ts-pattern';
const input = true;
const output = match<number | string | boolean>(input)
.with(__.string, () => 'it is a string!')
.with(__.number, () => 'it is a number!')
.with(__.boolean, () => 'it is a boolean!')
.run();
console.log(output);
// => 'it is a boolean!'
__.nullish
wildcardThe __.nullish
pattern will match any value of type null
or undefined
.
You will not often need this wildcard as ordinarily null
and undefined
are their own wildcards.
However, sometimes null
and undefined
appear in a union together
(e.g. null | undefined | string
) and you may want to treat them as equivalent.
import { match, __ } from 'ts-pattern';
const input = null;
const output = match<number | string | boolean | null | undefined>(input)
.with(__.string, () => 'it is a string!')
.with(__.number, () => 'it is a number!')
.with(__.boolean, () => 'it is a boolean!')
.with(__.nullish, () => 'it is either null or undefined!')
.with(null, () => 'it is null!')
.with(undefined, () => 'it is undefined!')
.exhaustive();
console.log(output);
// => 'it is either null or undefined!'
A pattern can be an object with sub-pattern properties. In order to match, the input must be an object with all properties defined on the pattern object and each property must match its sub-pattern.
import { match } from 'ts-pattern';
type Input =
| { type: 'user'; name: string }
| { type: 'image'; src: string }
| { type: 'video'; seconds: number };
let input: Input = { type: 'user', name: 'Gabriel' };
const output = match(input)
.with({ type: 'image' }, () => 'image')
.with({ type: 'video', seconds: 10 }, () => 'video of 10 seconds.')
.with({ type: 'user' }, ({ name }) => `user of name: ${name}`)
.otherwise(() => 'something else');
console.log(output);
// => 'user of name: Gabriel'
To match on a list of values, your pattern can be an array with a single sub-pattern in it. This sub-pattern will be tested against all elements in your input array, and they must all match for your list pattern to match.
import { match, __ } from 'ts-pattern';
type Input = { title: string; content: string }[];
let input: Input = [
{ title: 'Hello world!', content: 'This is a very interesting content' },
{ title: 'Bonjour!', content: 'This is a very interesting content too' },
];
const output = match(input)
.with(
[{ title: __.string, content: __.string }],
(posts) => 'a list of posts!'
)
.otherwise(() => 'something else');
console.log(output);
// => 'a list of posts!'
In TypeScript, Tuples are arrays with a fixed number of elements which can be of different types. You can pattern match on tuples with a tuple pattern, matching your value in length and shape.
import { match, __ } from 'ts-pattern';
type Input =
| [number, '+', number]
| [number, '-', number]
| [number, '*', number]
| ['-', number];
const input: Input = [3, '*', 4];
const output = match<Input>(input)
.with([__, '+', __], ([x, , y]) => x + y)
.with([__, '-', __], ([x, , y]) => x - y)
.with([__, '*', __], ([x, , y]) => x * y)
.with(['-', __], ([, x]) => -x)
.otherwise(() => NaN);
console.log(output);
// => 12
Similarly to array patterns, set patterns have a different meaning if they contain a single sub-pattern or several of them:
import { match, __ } from 'ts-pattern';
type Input = Set<string | number>;
const input: Input = new Set([1, 2, 3]);
const output = match<Input>(input)
.with(new Set([1, 'hello']), (set) => `Set contains 1 and 'hello'`)
.with(new Set([1, 2]), (set) => `Set contains 1 and 2`)
.with(new Set([__.string]), (set) => `Set contains only strings`)
.with(new Set([__.number]), (set) => `Set contains only numbers`)
.otherwise(() => '');
console.log(output);
// => 'Set contains 1 and 2'
If a Set pattern contains one single wildcard pattern, it will match if each value in the input set match the wildcard.
If a Set pattern contains several values, it will match if the input Set contains each of these values.
Map patterns are similar to object patterns. They match if each keyed sub-pattern match the input value for the same key.
import { match, __ } from 'ts-pattern';
type Input = Map<string, string | number>;
const input: Input = new Map([
['a', 1],
['b', 2],
['c', 3],
]);
const output = match<Input>(input)
.with(new Map([['b', 2]]), (map) => `map.get('b') is 2`)
.with(new Map([['a', __.string]]), (map) => `map.get('a') is a string`)
.with(
new Map([
['a', __.number],
['c', __.number],
]),
(map) => `map.get('a') and map.get('c') are number`
)
.otherwise(() => '');
console.log(output);
// => 'map.get('b') is 2'
when
guardsthe when
function enables you to test the input with a custom guard function.
The pattern will match only if all when
functions return a truthy value.
Note that you can narrow down the type of your input by providing a Type Guard function to when.
import { match, when } from 'ts-pattern';
type Input = { score: number };
const output = match<Input>({ score: 10 })
.with(
{
score: when((score): score is 5 => score === 5),
},
(input) => 'π' // input is infered as { score: 5 }
)
.with({ score: when((score) => score < 5) }, () => 'π')
.with({ score: when((score) => score > 5) }, () => 'π')
.run();
console.log(output);
// => 'π'
not
patternsThe not
function enables you to match on everything but a specific value.
it's a function taking a pattern and returning its opposite:
import { match, not } from 'ts-pattern';
type Input = boolean | number;
const toNumber = (input: Input) =>
match(input)
.with(not(__.boolean), (n) => n) // n: number
.with(true, () => 1)
.with(false, () => 0)
.run();
console.log(toNumber(2));
// => 2
console.log(toNumber(true));
// => 1
select
patternsThe select
function enables us to pick a piece of our input data structure
and inject it in our handler function.
It's especially useful when pattern matching on deep data structure to avoid the hassle of destructuring it in the handler function.
Selections can be either named (with select('someName')
) or anonymous (with select()
).
You can have only one anonymous selection by pattern, and the selected value will be directly inject in your handler as first argument:
import { match, select } from 'ts-pattern';
type Input =
| { type: 'post'; user: { name: string } }
| { ... };
const input = { type: 'post', user: { name: 'Gabriel' } }
const output = match<Input>(input)
.with(
{ type: 'post', user: { name: select() } },
username => username // username: string
)
.otherwise(() => 'anonymous');
console.log(output);
// => 'Gabriel'
If you need to select several things inside your input data structure, you can name your selections by giving a string to select(<name>)
. Each selection will be passed as first argument to your handler in an object.
import { match, select } from 'ts-pattern';
type Input =
| { type: 'post'; user: { name: string }, content: string }
| { ... };
const input = { type: 'post', user: { name: 'Gabriel' }, content: 'Hello!' }
const output = match<Input>(input)
.with(
{ type: 'post', user: { name: select('name') }, content: select('body') },
({ name, body }) => `${name} wrote "${body}"`
)
.otherwise(() => '');
console.log(output);
// => 'Gabriel wrote "Hello!"'
instanceOf
patternsThe instanceOf
function lets you build a pattern to check if
a value is an instance of a class:
import { match, instanceOf } from 'ts-pattern';
class A {
a = 'a';
}
class B {
b = 'b';
}
type Input = { value: A | B };
const input = { value: new A() };
const output = match<Input>(input)
.with({ value: instanceOf(A) }, (a) => {
return 'instance of A!';
})
.with({ value: instanceOf(B) }, (b) => {
return 'instance of B!';
})
.exhaustive();
console.log(output);
// => 'instance of A!'
ts-pattern
heavily relies on TypeScript's type system to automatically infer the precise type of your input value based on your pattern. Here are a few examples showing how the input type would be narrowed using various patterns:
type Input = { type: string } | string;
match<Input, 'ok'>({ type: 'hello' })
.with(__, (value) => 'ok') // value: Input
.with(__.string, (value) => 'ok') // value: string
.with(
when((value) => true),
(value) => 'ok' // value: Input
)
.with(
when((value): value is string => true),
(value) => 'ok' // value: string
)
.with(not('hello'), (value) => 'ok') // value: Input
.with(not(__.string), (value) => 'ok') // value: { type: string }
.with(not({ type: __.string }), (value) => 'ok') // value: string
.with(not(when(() => true)), (value) => 'ok') // value: Input
.with({ type: __ }, (value) => 'ok') // value: { type: string }
.with({ type: __.string }, (value) => 'ok') // value: { type: string }
.with({ type: when(() => true) }, (value) => 'ok') // value: { type: string }
.with({ type: not('hello' as const) }, (value) => 'ok') // value: { type: string }
.with({ type: not(__.string) }, (value) => 'ok') // value: never
.with({ type: not(when(() => true)) }, (value) => 'ok') // value: { type: string }
.run();
This library has been heavily inspired by this great article by Wim Jongeneel: Pattern Matching in TypeScript with Record and Wildcard Patterns. It made me realize pattern matching could be implemented in userland and we didn't have to wait for it to be added to the language itself. I'm really grateful for that π
typescript-pattern-matching
Wim Jongeneel released his own npm package for pattern matching. ts-pattern
has a few
notable differences:
ts-patterns
's goal is to be a well unit-tested, well documented, production ready library.__
..exhaustive()
.select()
function.FAQs
The exhaustive Pattern Matching library for TypeScript.
The npm package ts-pattern receives a total of 989,302 weekly downloads. As such, ts-pattern popularity was classified as popular.
We found that ts-pattern demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.Β It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
ESLint has added JSON and Markdown linting support with new officially-supported plugins, expanding its versatility beyond JavaScript.
Security News
Members Hub is conducting large-scale campaigns to artificially boost Discord server metrics, undermining community trust and platform integrity.
Security News
NIST has failed to meet its self-imposed deadline of clearing the NVD's backlog by the end of the fiscal year. Meanwhile, CVE's awaiting analysis have increased by 33% since June.