starling-devex 0.1.2

Starling: a local dev orchestrator with a central daemon, shared named-URL proxy, and a k9s-style TUI (a Rust port of Tilt + portless)
// This Features wrapper behaves differently than the one in Go.
// Checking for features that don't exist is *not* an error here.
// This is important because when the React app starts,
// it starts with an empty state and there won't be _any_ feature flags
// until the first engine state comes in over the Websocket.
import { createContext, PropsWithChildren, useContext, useMemo } from "react"
import type { UIFeatureFlag } from "./core"

type featureFlags = { [featureFlag in Flag]?: boolean }

// Flag names are defined in internal/feature/flags.go
export enum Flag {
  Events = "events",
  Snapshots = "snapshots",
  Labels = "labels",
}

export default class Features {
  private readonly flags: featureFlags

  constructor(flags: object | null | undefined) {
    if (flags) {
      this.flags = flags as featureFlags
    } else {
      this.flags = {}
    }
  }

  public isEnabled(flag: Flag): boolean {
    if (this.flags.hasOwnProperty(flag)) {
      return this.flags[flag] as boolean
    }
    return false
  }
}

export const FeaturesContext = createContext<Features>(new Features({}))
FeaturesContext.displayName = "Features"

export function useFeatures(): Features {
  return useContext(FeaturesContext)
}

// Server-side flags are formatted as a list.
// Many tests uses the {key: value} format.
export function FeaturesProvider(
  props: PropsWithChildren<{
    featureFlags: UIFeatureFlag[] | null
  }>
) {
  let flagList = props.featureFlags || []
  let features = useMemo(() => {
    let featureFlags = {} as { [key: string]: boolean }
    flagList.forEach((flag) => {
      featureFlags[flag.name || ""] = !!flag.value
    })
    return new Features(featureFlags)
  }, [flagList])

  return (
    <FeaturesContext.Provider value={features}>
      {props.children}
    </FeaturesContext.Provider>
  )
}

export let FeaturesTestProvider = FeaturesContext.Provider