TypeScript throws `TS7056` because the schema object becomes too large
for the compiler to fully serialize when using deep literal inference.
Splitting the components object and explicitly reconstructing the type
prevents the error while preserving correct type inference.
Adds a test to ensure that the `getAll` method of the flag resolver
doesn't return the disabled variant if a flag is defined as a boolean in
the settings.
We have some places in the UI where we check `if
(uiConfig.flags.<flagname>) {...}`. If one of these flags were suddenly
returned as the disabled variant instead of `false`, then it'd be
impossible to turn it off.
As such, to maintain backwards compatibility and adhere to the principle
of least surprise, I'd like to add this test to ensure this doesn't
change going forward.
Fixes a bug / uncovered edge case in the flag resolver in Unleash:
If a local experiment was defined as false (the typical default value),
then that flag could only ever be returned as a boolean from the
`ui-config` endpoint. In other words, even if the external resolver has
a variant for that flag, the UI would never get the variant.
The fix is to not just check `isEnabled` for false flags, but instead:
- use `getVariant`
- then check `variant.enabled` (in which case we have a variant and can
return it)
- else check `variant.feature_enabled`, falling back to `isEnabled` only
if `feature_enabled` is null/undefined.
Updates the maintenance mode banner to accept string variants, allowing
for custom maintenance mode messages.
Because the banner is almost the same as the existing banner component
we have, we can simplify the impl and just reuse the existing banner
instead. The one difference is that the maintenance mode banner used to
be taller. However, after talking to UX, we agreed that the banner
should be the same size, anyway.
<img width="1552" height="120" alt="image"
src="https://github.com/user-attachments/assets/fc9dc8ad-26ba-411a-846e-a79e1b855f37"
/>
Configure the `maintenanceMode` flag type to be `boolean | Variant` and
update the env parsing to allow passing strings from the env.
The [first
impl](3bbfc9e681)
required you to set a full, variant -- stringified as json -- in the
env, but this is both error-prone and not very user friendly.
Additionally, the name of the variant isn't really important, and if
you're passing a string, you probably want it to be true.
As such, the [second
impl](c38357baa4)
updates the env parsing to read the full string value into a
pre-formatted variant if it's not parseable as a boolean.
As such, to set a custom message, you can now do:
```sh
UNLEASH_EXPERIMENTAL_MAINTENANCE_MODE='Custom message from plain env var string' yarn dev
```
With the [updates to the
UI](https://github.com/Unleash/unleash/pull/10961), it'll look a little
something like this:
<img width="388" height="64" alt="image"
src="https://github.com/user-attachments/assets/6b8a174b-d75f-4748-8f1a-1ad4ebce2073"
/>
## Rationale
This allows locking down Unleash instances with a custom message.
Previously, you'd have to use both maintenance mode and a custom banner
for this, but that requires more work to set properly and it shows two
banners, when you really only want the one.
Updates the flag resolver and other references to the unleash client's
deprecated `getDefaultVariant` to instead point to the `defaultVariant`
property instead, as described by the deprecation notice:
46bf068d26/src/variant.ts (L55-L60)
## Why
- @apidevtools/swagger-parser 12.1.0 switched its schema validator to
Ajv’s compile‑and‑reuse model, so validating our large OpenAPI document
no longer instantiates fresh Ajv/Z‑Schema instances per run. That lowers
the resident set size during spec validation.
- The bundled @apidevtools/json-schema-ref-parser dependency is now
14.x, which avoids mutating the input schema and uses a leaner
dereference cache, further trimming retained objects while we build the
OpenAPI spec.
In the previous 10.1.x line, lib/validators/schema.js was instantiating
Ajv (and even Z‑Schema in older builds) per invocation and mutating
large schema objects in place, which meant every validation spun up
fresh parser state plus lots of temporary objects.
This PR cleans up the lifecycleGraphs flag. These changes were
automatically generated by AI and should be reviewed carefully.
Fixes#10941
## 🧹 AI Flag Cleanup Summary
This change removes the `lifecycleGraphs` feature flag and makes the
associated
feature permanently available. The lifecycle graphs on the insights page
are now
enabled for all Enterprise users.
### 🚮 Removed
- **Configuration**
- `lifecycleGraphs` flag definition from `IFlagKey` and `flags` object
in
`src/lib/types/experimental.ts`.
- `lifecycleGraphs` flag from `UiFlags` in
`frontend/src/interfaces/uiConfig.ts`.
- `lifecycleGraphs: true` from `src/server-dev.ts` development config.
- **UI**
- The `useUiFlag('lifecycleGraphs')` hook call and associated
conditional
rendering logic in `PerformanceInsights.tsx`.
### 🛠 Kept
- **UI**
- The "New flags in production" and "Flags archived vs flags created"
widgets
are now always shown for Enterprise instances on the Performance
Insights page.
### 📝 Why
The `lifecycleGraphs` feature flag has been fully rolled out and is now
considered a permanent part of the application. This cleanup removes the
obsolete flag and its related conditional logic to simplify the
codebase.
---------
Co-authored-by: unleash-bot <194219037+unleash-bot[bot]@users.noreply.github.com>
Co-authored-by: Thomas Heartman <thomas@getunleash.io>
Fixes bug that would only occur if the first thing you do (when there's
no url query params) on the page is to try to change the sort order or
change the number of results per page.
In those cases, the table state would be replaced with only the new
state from the sorting/page limit (and probably page).
In more specific terms: if you're on the page with no query params, then
that means that you're seeing your open change requests.
But if you tried to change the sorting, say, then the "state" and
"createdBy" filters would be cleared, and you would end up showing "all
change requests ever".
The fix is to spread the implicit table state into the new state before
updating the actual state, such that implicit filters become explicit
when that happens.
## Implicit filters?
So why do we have implicit filters? Partly aesthetic, partly because
that's how it works on github (github.com/pulls), and partly because
that makes it easier to share with coworkers. You just need to go to the
change requests page and copy the url. With no query params, they'll see
their own results instead of yours.
Makes all columns in the change requests table unsortable. The API
doesn't support sorting yet and it's not entirely clear that we want it
at the moment. As such, make it so that the column headers aren't
interactable to make it less misleading.
## About the changes
This helps to specify how long to show new in Unleash so we don't forget
to remove it when doing a release. This doesn't mean we should keep this
list forever, but this helps us to keep it clean in the UI at least.
### Tests
We did unit test the logic as follows (manually because it's not an easy
piece of code to test in the UI):
```typescript
test('ui-test', () => {
const showUntil = '7.3.0';
expect(lt('6.9.3', showUntil)).toBe(true);
expect(lt('7.2.3', showUntil)).toBe(true);
expect(lt('7.3.0', showUntil)).toBe(false);
expect(lt('7.3.1', showUntil)).toBe(false);
expect(lt('7.4.0', showUntil)).toBe(false);
expect(lt('8.0.0', showUntil)).toBe(false);
});
```
Fixes a few errors appearing on the "assign user/group" on the project
settings pages. Namely:
- spreading "key" into props in two places (option list and selected
chips)
- incorrect HTML nesting ([`li`'s only permitted parents are `ul`, `ol`,
and
`menu`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/li#technical_summary))
- missing key in the list (caused by nesting the `li`)
The renderOption type update is based on a [fix that was added in a more
recent version of
mui](https://github.com/mui/material-ui/pull/42689/files).
For some reason, the default tag rendering spreads the key in somehow,
so I've had to add a manual `renderTags` prop, and as such extracted the
`getOptionLabel` function. If you know a better way of sorting out the
fact that the `key` prop is spread into the default MUI chips, I'd be
very happy to implement that instead.