index.ts70 lines · main
| 1 | import type { components } from 'api-types' |
| 2 | |
| 3 | import enabledFeaturesRaw from './enabled-features.json' with { type: 'json' } |
| 4 | |
| 5 | const enabledFeaturesStaticObj = enabledFeaturesRaw as Omit<typeof enabledFeaturesRaw, '$schema'> |
| 6 | |
| 7 | type Profile = components['schemas']['ProfileResponse'] |
| 8 | |
| 9 | export type Feature = Profile['disabled_features'][number] | keyof typeof enabledFeaturesStaticObj |
| 10 | |
| 11 | const disabledFeaturesStaticArray = Object.entries(enabledFeaturesStaticObj) |
| 12 | .filter(([_, value]) => !value) |
| 13 | .map(([key]) => key as Feature) |
| 14 | |
| 15 | function checkFeature(feature: Feature, features: Set<Feature>) { |
| 16 | return !features.has(feature) |
| 17 | } |
| 18 | |
| 19 | type SnakeToCamelCase<S extends string> = S extends `${infer First}_${infer Rest}` |
| 20 | ? `${First}${SnakeToCamelCase<Capitalize<Rest>>}` |
| 21 | : S |
| 22 | |
| 23 | type FeatureToCamelCase<S extends Feature> = S extends `${infer P}:${infer R}` |
| 24 | ? `${SnakeToCamelCase<P>}${Capitalize<SnakeToCamelCase<R>>}` |
| 25 | : SnakeToCamelCase<S> |
| 26 | |
| 27 | function featureToCamelCase(feature: Feature) { |
| 28 | return feature |
| 29 | .replace(/:/g, '_') |
| 30 | .split('_') |
| 31 | .map((word, index) => (index === 0 ? word : word[0].toUpperCase() + word.slice(1))) |
| 32 | .join('') as FeatureToCamelCase<typeof feature> |
| 33 | } |
| 34 | |
| 35 | function isFeatureEnabled<T extends Feature[]>( |
| 36 | features: T, |
| 37 | runtimeDisabledFeatures?: Feature[] |
| 38 | ): { [key in FeatureToCamelCase<T[number]>]: boolean } |
| 39 | function isFeatureEnabled(features: Feature, runtimeDisabledFeatures?: Feature[]): boolean |
| 40 | function isFeatureEnabled<T extends Feature | Feature[]>( |
| 41 | features: T, |
| 42 | runtimeDisabledFeatures?: Feature[] |
| 43 | ) { |
| 44 | // Override is used to produce a filtered version of the docs search index |
| 45 | // using the same sync setup as our normal search index |
| 46 | if (process.env.ENABLED_FEATURES_OVERRIDE_DISABLE_ALL === 'true') { |
| 47 | if (Array.isArray(features)) { |
| 48 | return Object.fromEntries(features.map((feature) => [featureToCamelCase(feature), false])) |
| 49 | } |
| 50 | return false |
| 51 | } |
| 52 | |
| 53 | const disabledFeatures = new Set([ |
| 54 | ...(runtimeDisabledFeatures ?? []), |
| 55 | ...disabledFeaturesStaticArray, |
| 56 | ]) |
| 57 | |
| 58 | if (Array.isArray(features)) { |
| 59 | return Object.fromEntries( |
| 60 | features.map((feature) => [ |
| 61 | featureToCamelCase(feature), |
| 62 | checkFeature(feature, disabledFeatures), |
| 63 | ]) |
| 64 | ) |
| 65 | } |
| 66 | |
| 67 | return checkFeature(features, disabledFeatures) |
| 68 | } |
| 69 | |
| 70 | export { isFeatureEnabled } |