@1fe/shell
Version: 
Frontend shell and runtime for 1FE micro-frontend applications
402 lines (388 loc) • 13.9 kB
text/typescript
import { DeepReadonly } from 'deep-freeze';
import { UnsubscribeFunction } from 'emittery';
type CSPPerEnvironment = {
    scriptSrc?: string[];
    connectSrc?: string[];
    imgSrc?: string[];
    objectSrc?: string[];
    frameSrc?: string[];
    styleSrc?: string[];
    frameAncestors?: string[];
    mediaSrc?: string[];
    workerSrc?: string[];
    formAction?: string[];
    fontSrc?: string[];
};
type CSPTypes = keyof CSPPerEnvironment;
type CSPDirectives = {
    [key: string]: CSPPerEnvironment;
};
type AuthenticationType = 'required' | 'lazy';
type WidgetType = 'pinned' | 'system';
type AuthConfig = {
    authenticationType?: AuthenticationType;
};
type UserExperienceConfig = {
    useShellLoadingScreen?: boolean;
};
type MetaTags = Record<string, string>[];
type RuntimePluginConfig = {
    metaTags?: MetaTags;
    baselineUrl?: string;
};
type HeadersConfig = {
    csp?: {
        enforced?: CSPPerEnvironment;
        reportOnly?: CSPPerEnvironment;
    };
};
type PinnedWidget = {
    widgetId: string;
    version: string;
};
type PreloadKey = 'apiGet' | 'html' | 'widget';
type PreloadType = {
    [k in PreloadKey]?: string;
};
type RuntimeConfig = {
    dependsOn?: {
        pinnedWidgets: PinnedWidget[];
    };
    preload?: PreloadType[];
    plugin?: RuntimePluginConfig;
    headers?: HeadersConfig;
};
/**
 * The raw data as stored on optimizely
 */
type PluginConfigRaw = {
    route: string;
    enabled: boolean;
    auth?: AuthConfig;
    baselineUrl?: string;
};
/**
 * The raw data as stored on optimizely
 */
type WidgetConfigRaw = {
    widgetId: string;
    type?: WidgetType;
    version: {
        current: string;
        next: string;
        nextReleaseStart: number;
        nextReleaseEnd: number;
        _url?: string;
    };
    plugin?: PluginConfigRaw;
};
/**
 * The plugin config after the raw data is parsed and stored in memory
 */
type PluginConfig = {
    enabled: boolean;
    route: string;
    widgetId: string;
    auth?: AuthConfig;
    baselineUrl?: string;
};
/**
 * The widget config after the raw data is parsed and stored in memory
 */
type WidgetConfig = {
    widgetId: string;
    version: string;
    runtime: RuntimeConfig;
    type?: WidgetType;
    plugin?: PluginConfigRaw;
};
type EnvConfig = {
    environment: string;
    isProduction: boolean;
};
type SystemWidgetConfig = {
    widgetId: string;
    version: string;
    _url?: string;
    type: 'system';
};
type WidgetConfigs<T extends PluginConfig | WidgetConfig = WidgetConfig> = ReadonlyMap<string, T>;
type OneFERoutes = {
    defaultRoute: `/${string}`;
};
type OneFELogObject = {
    message: string;
    [key: string]: any;
};
type OneFEShellLogger = {
    log: (logObject: OneFELogObject) => void;
    error: (logObject: OneFELogObject) => void;
    logPlatformUtilUsage?: boolean;
    redactSensitiveData?: boolean;
};
type OneFEAuth = {
    isAuthedCallback: (widgetId: string) => boolean;
    unauthedCallback: (widgetId: string) => void;
};
type OneFEUtilsFactories = {
    [key: string]: (widgetId: string) => Record<string, object>;
};
type OneFEErrorComponentProps = {
    type?: 'error' | 'notFound';
    plugin?: PluginConfig;
    message?: string | undefined;
};
type OneFEComponents = {
    getLoader?: () => JSX.Element;
    getError?: (props: OneFEErrorComponentProps | undefined) => JSX.Element;
};
type OneFEShellOptions = {
    utils?: OneFEUtilsFactories;
    auth?: OneFEAuth;
    shellLogger?: OneFEShellLogger;
    routes?: OneFERoutes;
    components?: OneFEComponents;
    /**
     * These hooks are used by 1fe shell to execute any configured custom operations
     * during various internal processes in 1fe shell.
     */
    hooks?: {
        /**
         * Callback to be executed before react's render method is called on the document root.
         * Use this to perform any necessary setup before the shell is rendered, for example: registering a service worker,
         * initializing global state, or setting up global event listeners.
         */
        onBeforeRenderShell?: () => void;
    };
};
declare const renderOneFEShell: (options: OneFEShellOptions) => void;
type WidgetTreeNode = {
    id: string;
    data: WidgetConfig;
    children?: WidgetTreeNode[];
};
type PublishToEventBusArgs<EventMap, K extends keyof EventMap> = {
    targetWidgetId: string;
    eventName: K;
    data: EventMap[K];
};
type SubscriptionToEventBusArgs<EventMap, K extends keyof EventMap> = {
    /**
     * The name of the event to subscribe to
     */
    eventName: K;
    /**
     * The function to call when the event is emitted
     * @param data - Event payload
     */
    listener: (data: EventPublishPayload<EventMap[K]>) => void;
};
type EventBusPlatformUtils = {
    /**
     * This function is used by widgets to publish events to other widgets.
     * The published event targets a specific widgetId, and will only be received by that widget.
     *
     * Be sure to understand the structure of the data payload the target widget is expecting for it to work correctly.
     *
     * @example publish({ eventName: 'EVENT_WIDGET_48_IS_SUBSCRIBED_TO', targetWidgetId: '@docusign/widget-48', data: { foo: 'bar' } })
     * @param targetWidgetId string - the widgetId of the widget that is subscribing to the event
     * @param eventName string - the name of the event to publish
     * @param data any - the data to send with the event
     * @returns void
     */
    publish<EventMap, K extends keyof EventMap>(args: PublishToEventBusArgs<EventMap, K>): void;
    /**
     * A function that can be used to subscribe to an event, that is automatically namespaced to your widgetId.
     * Calling the returned function will unsubscribe from the event.
     *
     * CAUTION: Calling this function multiple times will result in multiple subscriptions. Do not use this in a React Component that will re-render multiple times. Please use the useSubscribe hook instead.
     *
     * @example const unsubscribeFn = subscribe({ eventName: 'EVENT_WIDGET_48_IS_SUBSCRIBED_TO', listener: (data) => { console.log('event emitted with data', data) } })
     * @param args - An eventName and listener pair
     * @returns unsubscribeFn - function that can be used to unsubscribe from the event
     */
    subscribe<EventMap, K extends keyof EventMap>(args: SubscriptionToEventBusArgs<EventMap, K>): UnsubscribeFunction;
    /**
     * A hook wrapper around subscribe that handles React lifecycle
     * This hook is used by the platform to subscribe to events
     * And will auto unsubscribe when the component is unmounted
     *
     * @param args - An eventName and listener pair
     * @example useSubscribe({ eventName: 'event-name', listener: (data) => {} })
     * @returns void
     */
    useSubscribe<EventMap, K extends keyof EventMap>(args: SubscriptionToEventBusArgs<EventMap, K>): void;
};
type EventPublishPayload<T> = {
    eventInfo: {
        sender: {
            id: string;
        };
        timestamp: number;
    };
    data: T;
};
type AllowedTypes = string | boolean | number;
type SessionStoragePlatformUtils = {
    set: (key: string, value: AllowedTypes) => void;
    get: <T extends AllowedTypes>(key: string) => T;
    getAll: <T extends AllowedTypes>() => Record<string, T>;
    clear: () => void;
    remove: (key: string) => void;
    size: () => number | undefined;
};
type LocalStoragePlatformUtils = {
    set: (key: string, value: AllowedTypes) => void;
    get: <T extends AllowedTypes>(key: string) => T;
    getAll: <T extends AllowedTypes>() => Record<string, T>;
    clear: () => void;
    remove: (key: string) => void;
    size: () => number | undefined;
    keys: () => string[];
};
declare const initExperience: (widgetId: string) => {
    title: {
        set: (title: string) => void;
        get: () => string | undefined;
    };
};
type WidgetAppLoadTimeUtils = {
    /**
     * Used for starting a performance mark within a widget. In most cases you should use MarkStart and MarkEnd instead
     */
    mark: (markName: string, measureOptions?: PerformanceMeasureOptions) => void;
    /**
     * Used to end the primary performance mark started by the 1FE-Shell. Should only be called once
     */
    end: () => void;
    /**
     * Used for measuring the duration of 1 or more marks within a widget
     */
    measure: (measureName: string, measureOptions: PerformanceMeasureOptions) => PerformanceMeasure | undefined;
    /**
     * Used to get a list of all performance entries
     */
    getEntries: () => PerformanceEntryList;
    /**
     * Starts a custom performance mark
     */
    markStart: (markerName: string, markOptions?: PerformanceMarkOptions) => void;
    /**
     * Ends and measures a custom performance mark
     */
    markEnd: (markerName: string, markOptions?: PerformanceMarkOptions) => PerformanceMeasure | undefined;
};
type ExperienceUtils = ReturnType<typeof initExperience>;
interface KnownWidgets {
}
interface KnownVariants {
}
type UnknownWidgetContract = Record<string, unknown>;
type WidgetProps = KnownWidgets & {
    [unknownId: string]: UnknownWidgetContract;
};
type VariantProps = KnownVariants & {
    [widgetId: string]: {
        [variantId: string]: UnknownWidgetContract;
    };
};
type WidgetNavigation = {
    get: <TWidgetId extends string, TVariantId extends string | undefined = undefined, TWidgetProps = TVariantId extends string ? VariantProps[TWidgetId][TVariantId] : WidgetProps[TWidgetId]>(requestedWidgetId: TWidgetId, options?: {
        variantId?: TVariantId;
        Loader?: React.ReactNode;
    }) => React.FC<TWidgetProps>;
};
type PlatformUtils<CustomUtils = {}> = CustomUtils & {
    /**
     * Utilities for loading other widgets
     * Use the playground widget browser to find the widgetId you want to load
     *
     * Ensure they're available and deployed on the highest environment (Production) before consuming
     * @link https://github.docusignhq.com/pages/Core/1fe-docs/widgets/utils/widgets/
     */
    widgets: WidgetNavigation;
    /**
     * Utilities for measuring the time it takes for the widget to load
     * @link https://github.docusignhq.com/pages/Core/1fe-docs/widgets/utils/apploadtime/
     */
    appLoadTime: WidgetAppLoadTimeUtils;
    /**
     * Utilities for handling events and publishing/subscribing to the eventBus on the Shell
     * @link https://github.docusignhq.com/pages/Core/1fe-docs/widgets/utils/eventbus/
     */
    eventBus: EventBusPlatformUtils;
    /**
     * Utilities for handling session storage
     * Provides an interface to the browser's session storage via set, get, getAll, remove, size, and clear
     * @link https://github.docusignhq.com/pages/Core/1fe-docs/widgets/utils/sessionstorage/
     */
    sessionStorage: SessionStoragePlatformUtils;
    /**
     * Utilities for handling local storage
     * Provides an interface to the browser's local storage via set, get, getAll, remove, size, keys, and clear
     * @link https://github.docusignhq.com/pages/Core/1fe-docs/widgets/utils/localstorage/
     */
    localStorage: LocalStoragePlatformUtils;
    /**
     * Utilities for handling the 1FE shell experience
     *
     * @link https://github.docusignhq.com/pages/Core/1fe-docs/widgets/utils/experience/
     */
    experience: ExperienceUtils;
};
/**
 * Options parameter for the get method in the widgets utility
 */
type WidgetOptions<TVariantId extends string = string> = {
    /**
     * The variantId to be used for the widget
     * defaults to the value `'default'`
     */
    variantId: TVariantId;
    /**
     * The loading animation to be used while loading the requested widget
     * defaults to the value Ink's ProgressCircle Spinner Component
     */
    Loader: React.ReactNode;
};
type PlatformContextType = {
    /**
     * Who am I?
     */
    self: {
        widgetId: string;
        version: string;
        variantId?: string;
    };
    /**
     * *CAUTION: Writing logic specific to which parent widget loads you is **HIGHLY DISCOURAGED!***
     *
     * This utility function returns the immediate host of the calling widget
     * Useful for eventBus util implementation/targeting
     * @returns {WidgetTreeNode | null} - the parent widget
     */
    getHost: () => WidgetTreeNode | null;
};
type PlatformPropsType<CustomUtils = {}> = DeepReadonly<{
    /**
     * The environment the 1FE SHELL is running in.
     *
     * @link https://github.docusignhq.com/pages/Core/1fe-docs/ecosystem/deployment/environments/
     */
    environment: string;
    /**
     * The context of the widget's execution.
     * Some helpful information is provided here.
     */
    context: PlatformContextType;
    /**
     * Free utilities provided by the 1FE SHELL
     * All utilities are namespaced and unique to a widget loading inside the 1FE SHELL
     *
     * For a list of available utilities, see:
     * @link https://github.docusignhq.com/pages/Core/1fe-docs/widgets/utils/
     */
    utils: PlatformUtils<CustomUtils>;
}>;
declare const platformProps: PlatformPropsType;
export { type AuthenticationType, type CSPDirectives, type CSPPerEnvironment, type CSPTypes, type EnvConfig, type ExperienceUtils, type KnownVariants, type KnownWidgets, type OneFEAuth, type OneFEComponents, type OneFEErrorComponentProps, type OneFELogObject, type OneFERoutes, type OneFEShellLogger, type OneFEShellOptions, type OneFEUtilsFactories, type PinnedWidget, type PlatformContextType, type PlatformPropsType, type PlatformUtils, type PluginConfig, type PluginConfigRaw, type PreloadKey, type PreloadType, type RuntimeConfig, type SystemWidgetConfig, type UnknownWidgetContract, type UserExperienceConfig, type VariantProps, type WidgetConfig, type WidgetConfigRaw, type WidgetConfigs, type WidgetNavigation, type WidgetOptions, type WidgetProps, type WidgetType, renderOneFEShell as default, platformProps };