UNPKG

@flopflip/react-broadcast

Version:

A feature toggle wrapper to use LaunchDarkly with React

458 lines (376 loc) 14.1 kB
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }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 var _react = require('react'); var _react2 = _interopRequireDefault(_react); // src/use-feature-toggle.ts var _react3 = require('@flopflip/react'); // src/use-flags-context.ts // src/flags-context.ts var _types = require('@flopflip/types'); var createIntialFlagsContext = (adapterIdentifiers2, initialFlags) => Object.fromEntries( Object.values(adapterIdentifiers2).map((adapterInterfaceIdentifier) => [ adapterInterfaceIdentifier, initialFlags ]) ); var FlagsContext = _react.createContext.call(void 0, createIntialFlagsContext(_types.adapterIdentifiers, {}) ); // src/use-flags-context.ts var useFlagsContext = () => _react.useContext.call(void 0, FlagsContext); // src/use-feature-toggle.ts function useFeatureToggle(flagName, flagVariation = true) { const adapterContext = _react3.useAdapterContext.call(void 0, ); const flagsContext = useFlagsContext(); const isFeatureEnabled = _react3.getIsFeatureEnabled.call(void 0, flagsContext, adapterContext.adapterEffectIdentifiers, flagName, flagVariation ); _react.useDebugValue.call(void 0, { 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__ */ _react2.default.createElement(ToggledComponent, __spreadValues({}, ownProps)); } if (UntoggledComponent) { return /* @__PURE__ */ _react2.default.createElement(UntoggledComponent, __spreadValues({}, ownProps)); } return null; } return WrappedToggledComponent; }; } // src/configure.tsx var _shim = require('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 = _shim.useSyncExternalStore.call(void 0, store.subscribe, () => store.getSnapshot().flags, () => store.getSnapshot().flags ); const updateFlags = _react.useCallback.call(void 0, (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 = _shim.useSyncExternalStore.call(void 0, store.subscribe, () => store.getSnapshot().status, () => store.getSnapshot().status ); const setStatus = _react.useCallback.call(void 0, (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 = _react.useMemo.call(void 0, () => [adapter.id], [adapter.id]); const [flags, updateFlags] = useFlagsState({ adapterIdentifiers: adapterIdentifiers2 }); const [status, updateStatus] = useStatusState({ adapterIdentifiers: adapterIdentifiers2 }); const getHasAdapterSubscriptionStatus = _react3.useAdapterSubscription.call(void 0, adapter); const handleUpdateFlags = _react.useCallback.call(void 0, (flagsChange) => { if (getHasAdapterSubscriptionStatus(_types.AdapterSubscriptionStatus.Unsubscribed)) { return; } updateFlags(flagsChange); }, [updateFlags, getHasAdapterSubscriptionStatus] ); const handleUpdateStatus = _react.useCallback.call(void 0, (statusChange) => { if (getHasAdapterSubscriptionStatus(_types.AdapterSubscriptionStatus.Unsubscribed)) { return; } updateStatus(statusChange); }, [updateStatus, getHasAdapterSubscriptionStatus] ); return /* @__PURE__ */ _react2.default.createElement(FlagsContext.Provider, { value: flags }, /* @__PURE__ */ _react2.default.createElement( _react3.ConfigureAdapter, { adapter, adapterArgs, adapterStatus: status, defaultFlags, shouldDeferAdapterConfiguration, onFlagsStateChange: handleUpdateFlags, onStatusStateChange: handleUpdateStatus }, children )); } Configure.displayName = "ConfigureFlopflip"; // src/inject-feature-toggle.tsx // src/use-flag-variations.ts function useFlagVariations(flagNames) { const adapterContext = _react3.useAdapterContext.call(void 0, ); const flagsContext = useFlagsContext(); const flagVariations = flagNames.map( (requestedVariation) => _react3.getFlagVariation.call(void 0, flagsContext, adapterContext.adapterEffectIdentifiers, requestedVariation ) ); return flagVariations; } // src/inject-feature-toggle.tsx function injectFeatureToggle(flagName, propKey = _react3.DEFAULT_FLAG_PROP_KEY) { return (Component) => { function WrappedComponent(ownProps) { const [flagVariation] = useFlagVariations([flagName]); const props = __spreadProps(__spreadValues({}, ownProps), { [propKey]: flagVariation }); return /* @__PURE__ */ _react2.default.createElement(Component, __spreadValues({}, props)); } _react3.setDisplayName.call(void 0, _react3.wrapDisplayName.call(void 0, WrappedComponent, "injectFeatureToggle")); return WrappedComponent; }; } // src/inject-feature-toggles.tsx function injectFeatureToggles(flagNames, propKey = _react3.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__ */ _react2.default.createElement(Component, __spreadValues({}, props)); } _react3.setDisplayName.call(void 0, _react3.wrapDisplayName.call(void 0, WrappedComponent, "injectFeatureToggles")); return WrappedComponent; }; } // src/reconfigure.ts // src/test-provider.tsx var defaultProps = { adapterIdentifiers: ["test"], status: __spreadValues({}, Object.fromEntries( Object.values(_types.adapterIdentifiers).map((adapterInterfaceIdentifier) => [ adapterInterfaceIdentifier, { subscriptionStatus: _types.AdapterSubscriptionStatus.Subscribed, configurationStatus: _types.AdapterConfigurationStatus.Configured } ]) )) }; function TestProvider({ adapterIdentifiers: adapterIdentifiers2 = defaultProps.adapterIdentifiers, reconfigure, flags, children, status = defaultProps.status }) { const adapterContextValue = _react3.createAdapterContext.call(void 0, adapterIdentifiers2, reconfigure, status ); const flagsContextValue = createIntialFlagsContext( // @ts-expect-error Can not remember. Sorry to myself. adapterIdentifiers2, flags ); return /* @__PURE__ */ _react2.default.createElement(_react3.AdapterContext.Provider, { value: adapterContextValue }, /* @__PURE__ */ _react2.default.createElement(FlagsContext.Provider, { value: flagsContextValue }, children)); } TestProvider.displayName = "TestProviderFlopFlip"; // src/toggle-feature.tsx function ToggleFeature(_a) { var _b = _a, { flag, variation } = _b, remainingProps = __objRest(_b, [ "flag", "variation" ]); const isFeatureEnabled = useFeatureToggle(flag, variation); return /* @__PURE__ */ _react2.default.createElement( _react3.ToggleFeature, __spreadProps(__spreadValues({ flag, variation }, remainingProps), { isFeatureEnabled }) ); } ToggleFeature.displayName = "ToggleFeature"; // src/use-adapter-reconfiguration.ts // src/use-adapter-status.ts function useAdapterStatus({ adapterIdentifiers: adapterIdentifiers2 } = {}) { const { status } = _react3.useAdapterContext.call(void 0, ); const adapterStatus = _react3.selectAdapterConfigurationStatus.call(void 0, status, adapterIdentifiers2 ); _react.useDebugValue.call(void 0, { adapterStatus }); return adapterStatus; } // src/use-all-feature-toggles.ts function useAllFeatureToggles() { const adapterContext = _react3.useAdapterContext.call(void 0, ); const flagsContext = useFlagsContext(); const reversedAdapterEffectIdentifiers = [ ...adapterContext.adapterEffectIdentifiers ].reverse(); return reversedAdapterEffectIdentifiers.reduce( (_allFlags, adapterIdentifier) => __spreadValues(__spreadValues({}, _allFlags), flagsContext[adapterIdentifier]), {} ); } // src/use-feature-toggles.ts function useFeatureToggles(flags) { const adapterContext = _react3.useAdapterContext.call(void 0, ); const flagsContext = useFlagsContext(); const requestedFlags = Object.entries(flags).reduce( (previousFlags, [flagName, flagVariation]) => { const isFeatureEnabled = _react3.getIsFeatureEnabled.call(void 0, 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__"; exports.ConfigureFlopFlip = Configure; exports.ReconfigureFlopFlip = _react3.ReconfigureAdapter; exports.TestProviderFlopFlip = TestProvider; exports.ToggleFeature = ToggleFeature; exports.branchOnFeatureToggle = branchOnFeatureToggle; exports.injectFeatureToggle = injectFeatureToggle; exports.injectFeatureToggles = injectFeatureToggles; exports.useAdapterReconfiguration = _react3.useAdapterReconfiguration; exports.useAdapterStatus = useAdapterStatus; exports.useAllFeatureToggles = useAllFeatureToggles; exports.useFeatureToggle = useFeatureToggle; exports.useFeatureToggles = useFeatureToggles; exports.useFlagVariation = useFlagVariation; exports.useFlagVariations = useFlagVariations; exports.version = version; //# sourceMappingURL=index.cjs.map