UNPKG

@flopflip/react-redux

Version:

A feature toggle wrapper to use LaunchDarkly with React Redux

315 lines (314 loc) 12.7 kB
import React, { useCallback, useDebugValue } from "react"; import { ConfigureAdapter, DEFAULT_FLAGS_PROP_KEY, DEFAULT_FLAG_PROP_KEY, ReconfigureAdapter, ToggleFeature as ToggleFeature$1, getFlagVariation, getIsFeatureEnabled, isNil, selectAdapterConfigurationStatus, setDisplayName, useAdapterContext, useAdapterReconfiguration, useAdapterSubscription, wrapDisplayName } from "@flopflip/react"; import { useDispatch, useSelector } from "react-redux"; import { combineReducers, createAction, createSlice } from "@reduxjs/toolkit"; import { adapterIdentifiers } from "@flopflip/types"; //#region src/constants.ts const STATE_SLICE = "@flopflip"; //#endregion //#region \0@oxc-project+runtime@0.134.0/helpers/esm/typeof.js function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) { return typeof o; } : function(o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } //#endregion //#region \0@oxc-project+runtime@0.134.0/helpers/esm/toPrimitive.js function toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } //#endregion //#region \0@oxc-project+runtime@0.134.0/helpers/esm/toPropertyKey.js function toPropertyKey(t) { var i = toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } //#endregion //#region \0@oxc-project+runtime@0.134.0/helpers/esm/defineProperty.js function _defineProperty(e, r, t) { return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } //#endregion //#region \0@oxc-project+runtime@0.134.0/helpers/esm/objectSpread2.js function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function(r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread2(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function(r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function(r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } //#endregion //#region src/ducks/flags.ts const initialState = { memory: {} }; const flagsSlice = createSlice({ name: "flags", initialState, reducers: { updateFlags: { reducer(state, action) { if (action.payload.id) { state[action.payload.id] = _objectSpread2(_objectSpread2({}, state[action.payload.id]), action.payload.flags); return; } for (const adapterId of action.payload.adapterIdentifiers) state[adapterId] = _objectSpread2(_objectSpread2({}, state[adapterId]), action.payload.flags); }, prepare(flagsChange, adapterIdentifiers) { return { payload: _objectSpread2(_objectSpread2({}, flagsChange), {}, { adapterIdentifiers }) }; } } } }); const { updateFlags } = flagsSlice.actions; const reducer$1 = flagsSlice.reducer; const createReducer = (preloadedState = initialState) => { return (state = preloadedState, action) => reducer$1(state, action); }; const selectFlags = () => (state) => { var _state$STATE_SLICE$fl; return (_state$STATE_SLICE$fl = state["@flopflip"].flags) !== null && _state$STATE_SLICE$fl !== void 0 ? _state$STATE_SLICE$fl : {}; }; const selectFlag = (flagName, adapterIdentifiers) => (state) => { const allFlags = selectFlags()(state); let foundFlagVariation = false; for (const adapterId of adapterIdentifiers) { var _allFlags$adapterId; const flagValue = (_allFlags$adapterId = allFlags[adapterId]) === null || _allFlags$adapterId === void 0 ? void 0 : _allFlags$adapterId[flagName]; if (!isNil(flagValue)) foundFlagVariation = flagValue; } return foundFlagVariation; }; //#endregion //#region src/use-feature-toggle.ts function useFeatureToggle(flagName, flagVariation = true) { const adapterContext = useAdapterContext(); const isFeatureEnabled = getIsFeatureEnabled(useSelector(selectFlags()), adapterContext.adapterEffectIdentifiers, flagName, flagVariation); useDebugValue({ flagName, flagVariation, isEnabled: isFeatureEnabled }); return isFeatureEnabled; } //#endregion //#region src/branch-on-feature-toggle.tsx function branchOnFeatureToggle({ flag: flagName, variation: flagVariation }, UntoggledComponent) { return (ToggledComponent) => { function WrappedToggledComponent(ownProps) { if (useFeatureToggle(flagName, flagVariation)) return /* @__PURE__ */ React.createElement(ToggledComponent, ownProps); if (UntoggledComponent) return /* @__PURE__ */ React.createElement(UntoggledComponent, ownProps); return null; } return WrappedToggledComponent; }; } //#endregion //#region src/use-update-flags.ts const useUpdateFlags = ({ adapterIdentifiers }) => { const dispatch = useDispatch(); return useCallback((flagsChange) => dispatch(updateFlags(flagsChange, adapterIdentifiers)), [dispatch, adapterIdentifiers]); }; //#endregion //#region src/ducks/status.ts const statusSlice = createSlice({ name: "status", initialState: {}, reducers: { updateStatus: { reducer(state, action) { if (action.payload.id) { state[action.payload.id] = _objectSpread2(_objectSpread2({}, state[action.payload.id]), action.payload.status); return; } for (const adapterInterfaceIdentifier of action.payload.adapterIdentifiers) state[adapterInterfaceIdentifier] = _objectSpread2(_objectSpread2({}, state[adapterInterfaceIdentifier]), action.payload.status); }, prepare(statusChange, adapterIdentifiers) { return { payload: _objectSpread2(_objectSpread2({}, statusChange), {}, { adapterIdentifiers }) }; } } } }); const { updateStatus } = statusSlice.actions; const reducer = statusSlice.reducer; const selectStatus = ({ adapterIdentifiers } = {}) => (state) => { const { status } = state[STATE_SLICE]; return selectAdapterConfigurationStatus(status, adapterIdentifiers); }; //#endregion //#region src/use-update-status.ts const useUpdateStatus = () => { const dispatch = useDispatch(); return useCallback((statusChange) => dispatch(updateStatus(statusChange, Object.keys(adapterIdentifiers))), [dispatch]); }; //#endregion //#region src/configure.tsx function Configure({ adapter, adapterArgs, children, defaultFlags = {}, shouldDeferAdapterConfiguration = false }) { const handleUpdateFlags = useUpdateFlags({ adapterIdentifiers: [adapter.id] }); const handleUpdateStatus = useUpdateStatus(); useAdapterSubscription(adapter); return /* @__PURE__ */ React.createElement(ConfigureAdapter, { adapter, adapterArgs, defaultFlags, shouldDeferAdapterConfiguration, onFlagsStateChange: handleUpdateFlags, onStatusStateChange: handleUpdateStatus }, children); } Configure.displayName = "ConfigureFlopflip"; //#endregion //#region src/ducks/index.ts const flopflipReducer = combineReducers({ flags: reducer$1, status: reducer }); const createFlopflipReducer = (preloadedState = { memory: {} }) => combineReducers({ flags: createReducer(preloadedState), status: reducer }); //#endregion //#region src/enhancer.ts const configureAdapter = createAction("flopflip/configureAdapter"); function createFlopFlipEnhancer(adapter, adapterArgs) { return (next) => (...args) => { const store = next(...args); store.dispatch(configureAdapter({ adapter, adapterArgs })); adapter.configure(adapterArgs, { onFlagsStateChange: (flagsChange) => { store.dispatch(updateFlags(flagsChange, [adapter.id])); }, onStatusStateChange: (statusChange) => { store.dispatch(updateStatus(statusChange, Object.keys(adapterIdentifiers))); } }); return store; }; } //#endregion //#region src/use-flag-variations.ts function useFlagVariations(flagNames) { const adapterContext = useAdapterContext(); const allFlags = useSelector(selectFlags()); return flagNames.map((requestedVariation) => getFlagVariation(allFlags, adapterContext.adapterEffectIdentifiers, requestedVariation)); } //#endregion //#region src/inject-feature-toggle.tsx const injectFeatureToggle = (flagName, propKey = DEFAULT_FLAG_PROP_KEY) => (Component) => { function WrappedComponent(ownProps) { const [flagVariation] = useFlagVariations([flagName]); const props = _objectSpread2(_objectSpread2({}, ownProps), {}, { [propKey]: flagVariation }); return /* @__PURE__ */ React.createElement(Component, props); } setDisplayName(wrapDisplayName(WrappedComponent, "injectFeatureToggle")); return WrappedComponent; }; //#endregion //#region src/inject-feature-toggles.tsx const injectFeatureToggles = (flagNames, propKey = DEFAULT_FLAGS_PROP_KEY) => (Component) => { function WrappedComponent(ownProps) { const flagVariations = useFlagVariations(flagNames); const flags = Object.fromEntries(flagNames.map((flagName, indexOfFlagName) => [flagName, flagVariations[indexOfFlagName]])); const props = _objectSpread2(_objectSpread2({}, ownProps), {}, { [propKey]: flags }); return /* @__PURE__ */ React.createElement(Component, props); } setDisplayName(wrapDisplayName(WrappedComponent, "injectFeatureToggles")); return WrappedComponent; }; //#endregion //#region \0@oxc-project+runtime@0.134.0/helpers/esm/objectWithoutPropertiesLoose.js function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (e.includes(n)) continue; t[n] = r[n]; } return t; } //#endregion //#region \0@oxc-project+runtime@0.134.0/helpers/esm/objectWithoutProperties.js function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var s = Object.getOwnPropertySymbols(e); for (r = 0; r < s.length; r++) o = s[r], t.includes(o) || {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; } //#endregion //#region src/toggle-feature.tsx const _excluded = ["flag", "variation"]; function ToggleFeature(_ref) { let { flag, variation } = _ref, remainingProps = _objectWithoutProperties(_ref, _excluded); const isFeatureEnabled = useFeatureToggle(flag, variation); return /* @__PURE__ */ React.createElement(ToggleFeature$1, _objectSpread2(_objectSpread2({ flag, variation }, remainingProps), {}, { isFeatureEnabled })); } ToggleFeature.displayName = "ToggleFeature"; //#endregion //#region src/use-adapter-status.ts function useAdapterStatus({ adapterIdentifiers } = {}) { const adapterStatus = useSelector(selectStatus({ adapterIdentifiers })); useDebugValue({ adapterStatus }); return adapterStatus; } //#endregion //#region src/use-all-feature-toggles.ts function useAllFeatureToggles() { const adapterContext = useAdapterContext(); const allFlags = useSelector(selectFlags()); return [...adapterContext.adapterEffectIdentifiers].toReversed().reduce((_allFlags, adapterIdentifier) => _objectSpread2(_objectSpread2({}, _allFlags), allFlags[adapterIdentifier]), {}); } //#endregion //#region src/use-feature-toggles.ts function useFeatureToggles(flags) { const allFlags = useSelector(selectFlags()); const adapterContext = useAdapterContext(); return Object.entries(flags).reduce((previousFlags, [flagName, flagVariation]) => { const isFeatureEnabled = getIsFeatureEnabled(allFlags, adapterContext.adapterEffectIdentifiers, flagName, flagVariation); previousFlags.push(isFeatureEnabled); return previousFlags; }, []); } //#endregion //#region src/use-flag-variation.ts function useFlagVariation(flagName) { const [flagVariation] = useFlagVariations([flagName]); return flagVariation; } //#endregion //#region src/index.ts const version = "__@FLOPFLIP/VERSION_OF_RELEASE__"; //#endregion export { Configure as ConfigureFlopFlip, STATE_SLICE as FLOPFLIP_STATE_SLICE, ReconfigureAdapter as ReconfigureFlopFlip, ToggleFeature, branchOnFeatureToggle, createFlopFlipEnhancer, createFlopflipReducer, flopflipReducer, injectFeatureToggle, injectFeatureToggles, selectFlag as selectFeatureFlag, selectFlags as selectFeatureFlags, useAdapterReconfiguration, useAdapterStatus, useAllFeatureToggles, useFeatureToggle, useFeatureToggles, useFlagVariation, useFlagVariations, version }; //# sourceMappingURL=index.js.map