UNPKG

@flopflip/react-broadcast

Version:

A feature toggle wrapper to use LaunchDarkly with React

458 lines (435 loc) 14.4 kB
var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); var __objRest = (source, exclude) => { var target = {}; for (var prop in source) if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0) target[prop] = source[prop]; if (source != null && __getOwnPropSymbols) for (var prop of __getOwnPropSymbols(source)) { if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop)) target[prop] = source[prop]; } return target; }; // src/branch-on-feature-toggle.tsx import React from "react"; // src/use-feature-toggle.ts import { getIsFeatureEnabled, useAdapterContext } from "@flopflip/react"; import { useDebugValue } from "react"; // src/use-flags-context.ts import { useContext } from "react"; // src/flags-context.ts import { adapterIdentifiers as allAdapterIdentifiers } from "@flopflip/types"; import { createContext } from "react"; var createIntialFlagsContext = (adapterIdentifiers2, initialFlags) => Object.fromEntries( Object.values(adapterIdentifiers2).map((adapterInterfaceIdentifier) => [ adapterInterfaceIdentifier, initialFlags ]) ); var FlagsContext = createContext( createIntialFlagsContext(allAdapterIdentifiers, {}) ); // src/use-flags-context.ts var useFlagsContext = () => useContext(FlagsContext); // src/use-feature-toggle.ts function useFeatureToggle(flagName, flagVariation = true) { const adapterContext = useAdapterContext(); const flagsContext = useFlagsContext(); const isFeatureEnabled = getIsFeatureEnabled( flagsContext, adapterContext.adapterEffectIdentifiers, flagName, flagVariation ); useDebugValue({ flagName, flagVariation, isEnabled: isFeatureEnabled }); return isFeatureEnabled; } // src/branch-on-feature-toggle.tsx function branchOnFeatureToggle({ flag: flagName, variation: flagVariation }, UntoggledComponent) { return (ToggledComponent) => { function WrappedToggledComponent(ownProps) { const isFeatureEnabled = useFeatureToggle(flagName, flagVariation); if (isFeatureEnabled) { return /* @__PURE__ */ React.createElement(ToggledComponent, __spreadValues({}, ownProps)); } if (UntoggledComponent) { return /* @__PURE__ */ React.createElement(UntoggledComponent, __spreadValues({}, ownProps)); } return null; } return WrappedToggledComponent; }; } // src/configure.tsx import { ConfigureAdapter, useAdapterSubscription } from "@flopflip/react"; import { AdapterSubscriptionStatus } from "@flopflip/types"; import React2, { useCallback, useMemo } from "react"; import { useSyncExternalStore } from "use-sync-external-store/shim"; // src/store.ts function createStore(initialState) { let state = initialState; const getSnapshot = () => state; const listeners = /* @__PURE__ */ new Set(); function setState(fn) { state = fn(state); for (const listener of listeners) { listener(); } } function subscribe(listener) { listeners.add(listener); return () => { listeners.delete(listener); }; } return { getSnapshot, setState, subscribe }; } // src/configure.tsx var store = createStore({ status: {}, flags: {} }); var useFlagsState = ({ adapterIdentifiers: adapterIdentifiers2 }) => { const flags = useSyncExternalStore( store.subscribe, () => store.getSnapshot().flags, () => store.getSnapshot().flags ); const updateFlags = useCallback( (flagsChange) => { store.setState((prevState) => { let nextState; if (flagsChange.id) { nextState = __spreadProps(__spreadValues({}, prevState), { flags: __spreadProps(__spreadValues({}, prevState.flags), { [flagsChange.id]: __spreadValues(__spreadValues({}, prevState.flags[flagsChange.id]), flagsChange.flags) }) }); return nextState; } nextState = __spreadProps(__spreadValues({}, prevState), { flags: __spreadValues(__spreadValues({}, prevState.flags), Object.fromEntries( adapterIdentifiers2.map((adapterInterfaceIdentifier) => [ adapterInterfaceIdentifier, __spreadValues(__spreadValues({}, prevState.flags[adapterInterfaceIdentifier]), flagsChange.flags) ]) )) }); return nextState; }); }, [adapterIdentifiers2] ); return [flags, updateFlags]; }; var useStatusState = ({ adapterIdentifiers: adapterIdentifiers2 }) => { const status = useSyncExternalStore( store.subscribe, () => store.getSnapshot().status, () => store.getSnapshot().status ); const setStatus = useCallback( (statusChange) => { store.setState((prevState) => { let nextState; if (statusChange.id) { nextState = __spreadProps(__spreadValues({}, prevState), { status: __spreadProps(__spreadValues({}, prevState.status), { [statusChange.id]: __spreadValues(__spreadValues({}, prevState.status[statusChange.id]), statusChange.status) }) }); return nextState; } nextState = __spreadProps(__spreadValues({}, prevState), { status: __spreadValues(__spreadValues({}, prevState.status), Object.fromEntries( adapterIdentifiers2.map((adapterInterfaceIdentifier) => [ adapterInterfaceIdentifier, __spreadValues(__spreadValues({}, prevState.status[adapterInterfaceIdentifier]), statusChange.status) ]) )) }); return nextState; }); }, [adapterIdentifiers2] ); return [status, setStatus]; }; function Configure({ children, shouldDeferAdapterConfiguration = false, defaultFlags = {}, adapter, adapterArgs }) { const adapterIdentifiers2 = useMemo(() => [adapter.id], [adapter.id]); const [flags, updateFlags] = useFlagsState({ adapterIdentifiers: adapterIdentifiers2 }); const [status, updateStatus] = useStatusState({ adapterIdentifiers: adapterIdentifiers2 }); const getHasAdapterSubscriptionStatus = useAdapterSubscription(adapter); const handleUpdateFlags = useCallback( (flagsChange) => { if (getHasAdapterSubscriptionStatus(AdapterSubscriptionStatus.Unsubscribed)) { return; } updateFlags(flagsChange); }, [updateFlags, getHasAdapterSubscriptionStatus] ); const handleUpdateStatus = useCallback( (statusChange) => { if (getHasAdapterSubscriptionStatus(AdapterSubscriptionStatus.Unsubscribed)) { return; } updateStatus(statusChange); }, [updateStatus, getHasAdapterSubscriptionStatus] ); return /* @__PURE__ */ React2.createElement(FlagsContext.Provider, { value: flags }, /* @__PURE__ */ React2.createElement( ConfigureAdapter, { adapter, adapterArgs, adapterStatus: status, defaultFlags, shouldDeferAdapterConfiguration, onFlagsStateChange: handleUpdateFlags, onStatusStateChange: handleUpdateStatus }, children )); } Configure.displayName = "ConfigureFlopflip"; // src/inject-feature-toggle.tsx import { DEFAULT_FLAG_PROP_KEY, setDisplayName, wrapDisplayName } from "@flopflip/react"; import React3 from "react"; // src/use-flag-variations.ts import { getFlagVariation, useAdapterContext as useAdapterContext2 } from "@flopflip/react"; function useFlagVariations(flagNames) { const adapterContext = useAdapterContext2(); const flagsContext = useFlagsContext(); const flagVariations = flagNames.map( (requestedVariation) => getFlagVariation( flagsContext, adapterContext.adapterEffectIdentifiers, requestedVariation ) ); return flagVariations; } // src/inject-feature-toggle.tsx function injectFeatureToggle(flagName, propKey = DEFAULT_FLAG_PROP_KEY) { return (Component) => { function WrappedComponent(ownProps) { const [flagVariation] = useFlagVariations([flagName]); const props = __spreadProps(__spreadValues({}, ownProps), { [propKey]: flagVariation }); return /* @__PURE__ */ React3.createElement(Component, __spreadValues({}, props)); } setDisplayName(wrapDisplayName(WrappedComponent, "injectFeatureToggle")); return WrappedComponent; }; } // src/inject-feature-toggles.tsx import { DEFAULT_FLAGS_PROP_KEY, setDisplayName as setDisplayName2, wrapDisplayName as wrapDisplayName2 } from "@flopflip/react"; import React4 from "react"; function injectFeatureToggles(flagNames, propKey = DEFAULT_FLAGS_PROP_KEY) { return (Component) => { function WrappedComponent(ownProps) { const flagVariations = useFlagVariations(flagNames); const flags = Object.fromEntries( flagNames.map((flagName, indexOfFlagName) => [ flagName, flagVariations[indexOfFlagName] ]) ); const props = __spreadProps(__spreadValues({}, ownProps), { [propKey]: flags }); return /* @__PURE__ */ React4.createElement(Component, __spreadValues({}, props)); } setDisplayName2(wrapDisplayName2(WrappedComponent, "injectFeatureToggles")); return WrappedComponent; }; } // src/reconfigure.ts import { ReconfigureAdapter } from "@flopflip/react"; // src/test-provider.tsx import { AdapterContext, createAdapterContext } from "@flopflip/react"; import { AdapterConfigurationStatus, AdapterSubscriptionStatus as AdapterSubscriptionStatus2, adapterIdentifiers } from "@flopflip/types"; import React5 from "react"; var defaultProps = { adapterIdentifiers: ["test"], status: __spreadValues({}, Object.fromEntries( Object.values(adapterIdentifiers).map((adapterInterfaceIdentifier) => [ adapterInterfaceIdentifier, { subscriptionStatus: AdapterSubscriptionStatus2.Subscribed, configurationStatus: AdapterConfigurationStatus.Configured } ]) )) }; function TestProvider({ adapterIdentifiers: adapterIdentifiers2 = defaultProps.adapterIdentifiers, reconfigure, flags, children, status = defaultProps.status }) { const adapterContextValue = createAdapterContext( adapterIdentifiers2, reconfigure, status ); const flagsContextValue = createIntialFlagsContext( // @ts-expect-error Can not remember. Sorry to myself. adapterIdentifiers2, flags ); return /* @__PURE__ */ React5.createElement(AdapterContext.Provider, { value: adapterContextValue }, /* @__PURE__ */ React5.createElement(FlagsContext.Provider, { value: flagsContextValue }, children)); } TestProvider.displayName = "TestProviderFlopFlip"; // src/toggle-feature.tsx import { ToggleFeature as SharedToggleFeature } from "@flopflip/react"; import React6 from "react"; function ToggleFeature(_a) { var _b = _a, { flag, variation } = _b, remainingProps = __objRest(_b, [ "flag", "variation" ]); const isFeatureEnabled = useFeatureToggle(flag, variation); return /* @__PURE__ */ React6.createElement( SharedToggleFeature, __spreadProps(__spreadValues({ flag, variation }, remainingProps), { isFeatureEnabled }) ); } ToggleFeature.displayName = "ToggleFeature"; // src/use-adapter-reconfiguration.ts import { useAdapterReconfiguration } from "@flopflip/react"; // src/use-adapter-status.ts import { selectAdapterConfigurationStatus, useAdapterContext as useAdapterContext3 } from "@flopflip/react"; import { useDebugValue as useDebugValue2 } from "react"; function useAdapterStatus({ adapterIdentifiers: adapterIdentifiers2 } = {}) { const { status } = useAdapterContext3(); const adapterStatus = selectAdapterConfigurationStatus( status, adapterIdentifiers2 ); useDebugValue2({ adapterStatus }); return adapterStatus; } // src/use-all-feature-toggles.ts import { useAdapterContext as useAdapterContext4 } from "@flopflip/react"; function useAllFeatureToggles() { const adapterContext = useAdapterContext4(); const flagsContext = useFlagsContext(); const reversedAdapterEffectIdentifiers = [ ...adapterContext.adapterEffectIdentifiers ].reverse(); return reversedAdapterEffectIdentifiers.reduce( (_allFlags, adapterIdentifier) => __spreadValues(__spreadValues({}, _allFlags), flagsContext[adapterIdentifier]), {} ); } // src/use-feature-toggles.ts import { getIsFeatureEnabled as getIsFeatureEnabled2, useAdapterContext as useAdapterContext5 } from "@flopflip/react"; function useFeatureToggles(flags) { const adapterContext = useAdapterContext5(); const flagsContext = useFlagsContext(); const requestedFlags = Object.entries(flags).reduce( (previousFlags, [flagName, flagVariation]) => { const isFeatureEnabled = getIsFeatureEnabled2( flagsContext, adapterContext.adapterEffectIdentifiers, flagName, flagVariation ); previousFlags.push(isFeatureEnabled); return previousFlags; }, [] ); return requestedFlags; } // src/use-flag-variation.ts function useFlagVariation(flagName) { const [flagVariation] = useFlagVariations([flagName]); return flagVariation; } // src/index.ts var version = "__@FLOPFLIP/VERSION_OF_RELEASE__"; export { Configure as ConfigureFlopFlip, ReconfigureAdapter as ReconfigureFlopFlip, TestProvider as TestProviderFlopFlip, ToggleFeature, branchOnFeatureToggle, injectFeatureToggle, injectFeatureToggles, useAdapterReconfiguration, useAdapterStatus, useAllFeatureToggles, useFeatureToggle, useFeatureToggles, useFlagVariation, useFlagVariations, version }; //# sourceMappingURL=index.js.map