UNPKG

@prismatic-io/embedded

Version:

Embed Prismatic's integration marketplace and workflow designer within your existing application.

1 lines 72.2 kB
{"version":3,"file":"index.mjs","names":["prismatic"],"sources":["../src/state.ts","../src/utils/assertInit.ts","../src/types/options.ts","../src/types/postMessage.ts","../src/utils/popover.ts","../src/utils/postMessage.ts","../src/utils/iframe.ts","../src/lib/configureInstance.ts","../src/lib/graphqlRequest.ts","../src/lib/authenticate.ts","../src/lib/createWorkflow.ts","../src/styles.ts","../src/lib/init.ts","../src/lib/dispose.ts","../src/lib/editInstanceConfiguration.ts","../src/lib/queryWorkflows.ts","../src/lib/setConfigVars.ts","../src/lib/showComponent.ts","../src/lib/showComponents.ts","../src/lib/showConnections.ts","../src/lib/showDashboard.ts","../src/utils/assertEmbeddedDesigner.ts","../src/lib/showDesigner.ts","../src/lib/showIntegrations.ts","../src/lib/showLogs.ts","../src/lib/showMarketplace.ts","../src/lib/showWorkflow.ts","../src/lib/showWorkflows.ts","../src/types/conditionalLogic.ts","../src/exports.ts","../src/index.ts"],"sourcesContent":["import type { Filters } from \"./types/filters\";\nimport type { FontConfiguration } from \"./types/fontConfiguration\";\nimport type { ScreenConfiguration } from \"./types/screenConfiguration\";\nimport type { Theme } from \"./types/theme\";\nimport type { Translation } from \"./types/translation\";\n\nexport interface State {\n filters: Filters;\n initComplete: boolean;\n jwt: string;\n embeddedDesignerEnabled: boolean;\n prismaticUrl: string;\n screenConfiguration?: ScreenConfiguration;\n skipPreload?: boolean;\n theme?: Theme;\n fontConfiguration?: FontConfiguration;\n translation?: Translation;\n}\n\nconst defaultState: State = {\n filters: {},\n initComplete: false,\n jwt: \"\",\n embeddedDesignerEnabled: false,\n prismaticUrl: \"https://app.prismatic.io\",\n screenConfiguration: undefined,\n theme: undefined,\n fontConfiguration: undefined,\n translation: undefined,\n};\n\nexport const ValidKeys: ReadonlySet<keyof State> = new Set(\n Object.keys(defaultState) as Array<keyof State>,\n);\n\nexport const isStateKey = (key: string): key is keyof State =>\n (ValidKeys as ReadonlySet<string>).has(key);\n\n// Assigning `state[key] = value` where `key: keyof State` trips TS's\n// correlated-types limitation — the index evaluates to an intersection\n// (`never`) instead of the union. Wrapping the assignment in a generic\n// function bound to a single `K` lets TS treat `State[K]` as a single\n// concrete type, which matches the `value` parameter cleanly.\nexport const setStateKey = <K extends keyof State>(\n state: State,\n key: K,\n value: State[K],\n): void => {\n state[key] = value;\n};\n\nclass StateService {\n private defaultState: State;\n private state: State | null = null;\n\n constructor(defaultState: State) {\n this.defaultState = defaultState;\n }\n\n getInitialState(): State {\n return JSON.parse(JSON.stringify(this.defaultState));\n }\n\n /**\n * A function that returns a copy of the current state. If a mutation is desired, you must call\n * `stateService.setState` afterwards to persist the updated copy.\n * @returns A deep copy of the state to prevent accidental mutations.\n */\n getStateCopy(): State {\n if (this.state) {\n return JSON.parse(JSON.stringify(this.state));\n }\n\n return this.getInitialState();\n }\n\n setState(state: State) {\n this.state = state;\n }\n}\n\nconst stateService = new StateService(defaultState);\n\nexport default stateService;\n","import stateService from \"../state\";\n\nexport const assertInit = (functionName: string) => {\n if (!stateService.getStateCopy().initComplete) {\n throw new Error(\n `Expected init to be called before calling ${functionName}`,\n );\n }\n\n if (!stateService.getStateCopy().jwt && functionName !== \"authenticate\") {\n throw new Error(\n `Expected authenticate to be called before calling ${functionName}`,\n );\n }\n};\n","import type { Filters } from \"./filters\";\nimport type { ScreenConfiguration } from \"./screenConfiguration\";\nimport type { Theme } from \"./theme\";\nimport type { Translation } from \"./translation\";\n\ninterface OptionsBase {\n autoFocusIframe?: boolean;\n filters?: Filters;\n screenConfiguration?: ScreenConfiguration;\n theme?: Theme;\n translation?: Translation;\n}\n\nexport interface SelectorOptions extends OptionsBase {\n selector: string;\n usePopover?: false;\n}\n\nexport interface PopoverOptions extends OptionsBase {\n usePopover: true;\n}\n\nexport type Options = PopoverOptions | SelectorOptions;\n\nexport const isPopover = (options: Options): options is PopoverOptions =>\n options.usePopover === true;\n","import type { ConfigVar } from \"./configVars\";\n\nexport interface WorkflowConfigurationData {\n customerId: string;\n customerName: string;\n instanceId: string;\n instanceName: string;\n workflowVersionId: string | null;\n}\n\nexport interface InstanceConfigurationData {\n customerId: string;\n customerName: string;\n instanceId: string;\n instanceName: string;\n integrationName: string;\n integrationVersionNumber: number;\n readOnly: boolean;\n}\n\nexport interface InstanceConfigurationLoadedData\n extends InstanceConfigurationData {\n configVars: Record<string, ConfigVar>;\n}\n\nexport interface InstanceConfigurationPageLoadedData\n extends InstanceConfigurationLoadedData {\n pageName: string;\n pageContent: Record<string, unknown>;\n}\n\nexport interface UserConfigurationData extends InstanceConfigurationData {\n userConfigId: string | undefined;\n userEmail: string;\n userId: string;\n userLevelConfigVariables: Record<string, ConfigVar>;\n userName: string;\n}\n\nexport interface UserConfigurationPageData extends UserConfigurationData {\n pageName: string;\n pageContent: Record<string, unknown>;\n}\n\nexport enum PrismaticMessageEvent {\n INSTANCE_CONFIGURATION_CANCELED = \"INSTANCE_CONFIGURATION_CANCELED\",\n INSTANCE_CONFIGURATION_CLOSED = \"INSTANCE_CONFIGURATION_CLOSED\",\n INSTANCE_CONFIGURATION_LOADED = \"INSTANCE_CONFIGURATION_LOADED\",\n INSTANCE_CONFIGURATION_PAGE_LOADED = \"INSTANCE_CONFIGURATION_PAGE_LOADED\",\n INSTANCE_CONFIGURATION_OPENED = \"INSTANCE_CONFIGURATION_OPENED\",\n INSTANCE_CREATED = \"INSTANCE_CREATED\",\n INSTANCE_DELETED = \"INSTANCE_DELETED\",\n INSTANCE_DEPLOYED = \"INSTANCE_DEPLOYED\",\n MARKETPLACE_CLOSED = \"MARKETPLACE_CLOSED\",\n POPOVER_CLOSE_REQUESTED = \"POPOVER_CLOSE_REQUESTED\",\n POPOVER_CLOSED = \"POPOVER_CLOSED\",\n SET_CONFIG_VAR = \"SET_CONFIG_VAR\",\n SET_FILTERS = \"SET_FILTERS\",\n SET_SCREEN_CONFIGURATION = \"SET_SCREEN_CONFIGURATION\",\n SET_TOKEN = \"SET_TOKEN\",\n SET_TRANSLATION = \"SET_TRANSLATION\",\n SET_VERSION = \"SET_VERSION\",\n USER_CONFIGURATION_CLOSED = \"USER_CONFIGURATION_CLOSED\",\n USER_CONFIGURATION_DELETED = \"USER_CONFIGURATION_DELETED\",\n USER_CONFIGURATION_DEPLOYED = \"USER_CONFIGURATION_DEPLOYED\",\n USER_CONFIGURATION_LOADED = \"USER_CONFIGURATION_LOADED\",\n USER_CONFIGURATION_PAGE_LOADED = \"USER_CONFIGURATION_PAGE_LOADED\",\n USER_CONFIGURATION_OPENED = \"USER_CONFIGURATION_OPENED\",\n WORKFLOW_ENABLED = \"WORKFLOW_ENABLED\",\n WORKFLOW_DELETED = \"WORKFLOW_DELETED\",\n WORKFLOW_DISABLED = \"WORKFLOW_DISABLED\",\n}\n\nexport type MessageData =\n | {\n data: InstanceConfigurationData;\n event: PrismaticMessageEvent.INSTANCE_CONFIGURATION_CANCELED;\n }\n | {\n data: InstanceConfigurationData;\n event: PrismaticMessageEvent.INSTANCE_CONFIGURATION_OPENED;\n }\n | {\n data: InstanceConfigurationLoadedData;\n event: PrismaticMessageEvent.INSTANCE_CONFIGURATION_LOADED;\n }\n | {\n data: InstanceConfigurationPageLoadedData;\n event: PrismaticMessageEvent.INSTANCE_CONFIGURATION_PAGE_LOADED;\n }\n | {\n data: InstanceConfigurationData;\n event: PrismaticMessageEvent.INSTANCE_CONFIGURATION_CLOSED;\n }\n | {\n data: InstanceConfigurationData;\n event: PrismaticMessageEvent.INSTANCE_CREATED;\n }\n | {\n data: InstanceConfigurationData;\n event: PrismaticMessageEvent.INSTANCE_DELETED;\n }\n | {\n data: InstanceConfigurationData;\n event: PrismaticMessageEvent.INSTANCE_DEPLOYED;\n }\n | {\n data: Record<string, never>;\n event: PrismaticMessageEvent.POPOVER_CLOSE_REQUESTED;\n }\n | {\n data: UserConfigurationData;\n event: PrismaticMessageEvent.USER_CONFIGURATION_DELETED;\n }\n | {\n data: UserConfigurationData;\n event: PrismaticMessageEvent.USER_CONFIGURATION_DEPLOYED;\n }\n | {\n data: UserConfigurationData;\n event: PrismaticMessageEvent.USER_CONFIGURATION_CLOSED;\n }\n | {\n data: UserConfigurationData;\n event: PrismaticMessageEvent.USER_CONFIGURATION_LOADED;\n }\n | {\n data: UserConfigurationPageData;\n event: PrismaticMessageEvent.USER_CONFIGURATION_PAGE_LOADED;\n }\n | {\n data: UserConfigurationData;\n event: PrismaticMessageEvent.USER_CONFIGURATION_OPENED;\n }\n | {\n data: WorkflowConfigurationData;\n event: PrismaticMessageEvent.WORKFLOW_ENABLED;\n }\n | {\n data: WorkflowConfigurationData;\n event: PrismaticMessageEvent.WORKFLOW_DELETED;\n }\n | {\n data: WorkflowConfigurationData;\n event: PrismaticMessageEvent.WORKFLOW_DISABLED;\n };\n","import { PrismaticMessageEvent } from \"../types/postMessage\";\nimport {\n EMBEDDED_IFRAME_CONTAINER_SELECTOR,\n EMBEDDED_OVERLAY_SELECTOR,\n EMBEDDED_OVERLAY_VISIBLE_CLASS,\n getIframeContainerElement,\n isIframe,\n} from \"./iframe\";\n\nexport const getPopover = () =>\n document.querySelector(EMBEDDED_OVERLAY_SELECTOR);\n\nexport const openPopover = () => {\n getPopover()?.classList.add(EMBEDDED_OVERLAY_VISIBLE_CLASS);\n};\n\n/**\n * Programmatically closes the currently open Prismatic popover. This notifies\n * the embedded iframe that the marketplace was closed and dispatches a\n * `POPOVER_CLOSED` event on the window.\n *\n * The popover also closes automatically when the user clicks the close button,\n * clicks the overlay backdrop, or presses the Escape key.\n *\n * @example\n * // Close the popover from your own UI\n * document.getElementById(\"my-close-btn\")\n * .addEventListener(\"click\", () => prismatic.closePopover());\n *\n * @example\n * // Listen for the popover closed event\n * window.addEventListener(\"message\", (event) => {\n * if (event.data.event === \"POPOVER_CLOSED\") {\n * console.log(\"Popover was closed\");\n * }\n * });\n *\n * @see {@link https://prismatic.io/docs/embed/marketplace/ | Embedding the Marketplace}\n */\nexport const closePopover = () => {\n const iframeElement = getIframeContainerElement(\n `${EMBEDDED_IFRAME_CONTAINER_SELECTOR} > iframe`,\n );\n\n if (!isIframe(iframeElement)) {\n return;\n }\n\n iframeElement.contentWindow?.postMessage(\n { event: PrismaticMessageEvent.MARKETPLACE_CLOSED },\n \"*\",\n );\n\n window.postMessage({ event: PrismaticMessageEvent.POPOVER_CLOSED }, \"*\");\n\n getPopover()?.classList.remove(EMBEDDED_OVERLAY_VISIBLE_CLASS);\n};\n","import { getIframeElement, isIframe } from \"./iframe\";\n\ninterface PostMessageBase {\n event: unknown;\n}\n\nexport interface SelectorPostMessage extends PostMessageBase {\n selector?: string;\n}\n\nexport interface ElementPostMessage extends PostMessageBase {\n iframe: Element;\n}\n\ntype PostMessageProps = SelectorPostMessage | ElementPostMessage;\n\n/**\n * Returns the iframe element that sent a given `MessageEvent`. This is typically\n * used inside a `window.addEventListener(\"message\", ...)` handler to identify\n * which Prismatic iframe dispatched an event, so you can pass it to\n * {@link setConfigVars} or perform other targeted operations.\n *\n * @param event - The `MessageEvent` received from `window.addEventListener(\"message\", ...)`.\n * @returns The matching `HTMLIFrameElement`, or `undefined` if no iframe matches.\n *\n * @example\n * window.addEventListener(\"message\", (event) => {\n * if (event.data.event === \"INSTANCE_CONFIGURATION_LOADED\") {\n * const iframe = prismatic.getMessageIframe(event);\n * if (iframe) {\n * prismatic.setConfigVars({\n * iframe,\n * configVars: { \"My Config Var\": { value: \"preset-value\" } },\n * });\n * }\n * }\n * });\n *\n * @see {@link https://prismatic.io/docs/embed/marketplace/ | Embedding the Marketplace}\n */\nexport const getMessageIframe = (event: MessageEvent) =>\n Array.from(document.getElementsByTagName(\"iframe\")).find(\n (iframe) => iframe.contentWindow === event.source,\n );\n\nconst isIframePostMessage = (\n props: PostMessageProps,\n): props is ElementPostMessage => \"iframe\" in props;\n\nexport const postMessage = (props: PostMessageProps) => {\n const iframeElement = isIframePostMessage(props)\n ? props.iframe\n : getIframeElement(props.selector);\n\n if (!isIframe(iframeElement)) {\n return;\n }\n\n iframeElement.contentWindow?.postMessage(props.event, \"*\");\n};\n","import merge from \"lodash.merge\";\nimport urlJoin from \"url-join\";\nimport stateService, { isStateKey, type State, setStateKey } from \"../state\";\nimport { isPopover, type Options } from \"../types/options\";\nimport { PrismaticMessageEvent } from \"../types/postMessage\";\nimport type { ScreenConfiguration } from \"../types/screenConfiguration\";\nimport { closePopover, openPopover } from \"./popover\";\nimport { postMessage } from \"./postMessage\";\n\nexport const EMBEDDED_ID = \"pio__embedded\";\nexport const EMBEDDED_IFRAME_ID = \"pio__iframe\";\nexport const EMBEDDED_IFRAME_PRELOAD_ID = \"pio__iframe-preload\";\nexport const EMBEDDED_STYLE_ID = \"pio__styles\";\nexport const EMBEDDED_IFRAME_CONTAINER_CLASS = \"pio__iframeContainer\";\nexport const EMBEDDED_IFRAME_CONTAINER_SELECTOR = `#${EMBEDDED_ID} .${EMBEDDED_IFRAME_CONTAINER_CLASS}`;\nexport const EMBEDDED_OVERLAY_CLASS = \"pio__overlay\";\nexport const EMBEDDED_OVERLAY_SELECTOR = `#${EMBEDDED_ID} > .${EMBEDDED_OVERLAY_CLASS}`;\nexport const EMBEDDED_OVERLAY_VISIBLE_CLASS = `${EMBEDDED_OVERLAY_CLASS}--is_visible`;\nexport const EMBEDDED_POPOVER_CLASS = \"pio__popover\";\nexport const EMBEDDED_POPOVER_CLOSE_CLASS = \"pio__closeBtn\";\n\nexport const getIframeContainerElement = (selector: string) =>\n document.querySelector(selector);\n\nexport const getIframeElement = (selector: string | undefined) =>\n document.querySelector(\n `${selector || EMBEDDED_IFRAME_CONTAINER_SELECTOR} > iframe`,\n );\n\nexport const isIframe = (\n element?: Element | null,\n): element is HTMLIFrameElement =>\n Boolean(element && element.tagName === \"IFRAME\");\n\nexport const setIframe = (\n route: string,\n options: Options,\n params?: Record<string, string>,\n) => {\n if (!isPopover(options) && !options.selector) {\n console.error(\"Could not find display selector.\");\n }\n\n const iframeContainerElement = getIframeContainerElement(\n isPopover(options) ? EMBEDDED_IFRAME_CONTAINER_SELECTOR : options.selector,\n );\n\n if (!iframeContainerElement) {\n return;\n }\n\n // we use a copy of state so that changes to this only impact this iframe and do not update the shared state\n const state = stateService.getStateCopy();\n\n if (options) {\n Object.entries(options).forEach(([key, value]) => {\n if (isStateKey(key)) {\n const existing = state[key];\n const next =\n existing instanceof Object ? merge(existing, value) : value;\n setStateKey(state, key, next as State[typeof key]);\n }\n });\n }\n\n const queryParams = new URLSearchParams({\n ...params,\n embed: \"true\",\n });\n\n if (state.theme) {\n queryParams.set(\"theme\", state.theme);\n }\n\n if (state.screenConfiguration?.initializing) {\n queryParams.set(\n \"initializing\",\n JSON.stringify(state.screenConfiguration.initializing),\n );\n }\n\n if (state.fontConfiguration) {\n queryParams.set(\n \"fontConfiguration\",\n JSON.stringify(state.fontConfiguration),\n );\n }\n\n const iframeSrc = `${urlJoin(state.prismaticUrl, route)}?${queryParams}`;\n\n iframeContainerElement.innerHTML = /* html */ `\n <iframe\n id=\"${EMBEDDED_IFRAME_ID}\"\n src=\"${iframeSrc}\"\n height=\"100%\"\n width=\"100%\"\n frameBorder=\"0\"\n allow=\"clipboard-read; clipboard-write\"\n ></iframe>\n `;\n\n const iframeElement = iframeContainerElement.querySelector(\"iframe\");\n\n if (iframeElement) {\n const abortController = new AbortController();\n observeForIframeRemoval(abortController);\n window.addEventListener(\n \"message\",\n (event: MessageEvent<{ event: string }>) => {\n if (event.data?.event === \"PRISMATIC_INITIALIZED\") {\n postMessage({\n iframe: iframeElement,\n event: {\n event: PrismaticMessageEvent.SET_TOKEN,\n data: state.jwt,\n },\n });\n\n postMessage({\n iframe: iframeElement,\n event: {\n event: PrismaticMessageEvent.SET_VERSION,\n data: EMBEDDED_VERSION,\n },\n });\n\n postMessage({\n iframe: iframeElement,\n event: {\n event: PrismaticMessageEvent.SET_TRANSLATION,\n data: state.translation,\n },\n });\n\n const screenConfigurationToPost: ScreenConfiguration | undefined =\n isPopover(options)\n ? {\n ...(state.screenConfiguration ?? {}),\n isInPopover: true,\n }\n : state.screenConfiguration;\n postMessage({\n iframe: iframeElement,\n event: {\n event: PrismaticMessageEvent.SET_SCREEN_CONFIGURATION,\n data: screenConfigurationToPost,\n },\n });\n\n postMessage({\n iframe: iframeElement,\n event: {\n event: PrismaticMessageEvent.SET_FILTERS,\n data: state.filters,\n },\n });\n } else if (\n event.data?.event === PrismaticMessageEvent.POPOVER_CLOSE_REQUESTED\n ) {\n closePopover();\n }\n },\n {\n signal: abortController.signal,\n },\n );\n\n if (options?.autoFocusIframe !== false) {\n iframeElement.addEventListener(\n \"mouseenter\",\n () => {\n iframeElement.focus();\n },\n {\n signal: abortController.signal,\n },\n );\n }\n }\n\n if (isPopover(options)) {\n openPopover();\n }\n};\n\nconst observeForIframeRemoval = (signal: AbortController) => {\n const config = { attributes: false, childList: true, subtree: true };\n\n // Create an observer instance that will self-terminate once the iframe that we care about has been removed\n const observer = new MutationObserver((mutationList, observer) => {\n for (const mutation of mutationList) {\n if (mutation.type === \"childList\") {\n const removedNodes = Array.from(mutation.removedNodes);\n for (const removedNode of removedNodes) {\n if (removedNode.nodeType === Node.ELEMENT_NODE) {\n if (\n (removedNode as HTMLElement).tagName === \"IFRAME\" &&\n (removedNode as HTMLIFrameElement).id === EMBEDDED_IFRAME_ID\n ) {\n signal.abort();\n observer.disconnect();\n } else {\n const iframe = (removedNode as HTMLElement).querySelector(\n `iframe#${EMBEDDED_IFRAME_ID}`,\n );\n if (iframe !== null) {\n signal.abort();\n observer.disconnect();\n }\n }\n }\n }\n }\n }\n });\n\n // Start observing the target node for configured mutations\n observer.observe(document.body, config);\n};\n","import type { Options } from \"../types/options\";\nimport { assertInit } from \"../utils/assertInit\";\nimport { setIframe } from \"../utils/iframe\";\n\ntype ConfigureInstancesBase = Options & {\n skipRedirectOnRemove: boolean;\n};\n\nexport type ConfigureInstanceWithIntegrationName = ConfigureInstancesBase & {\n integrationName: string;\n};\n\nexport type ConfigureInstanceWithIntegrationId = ConfigureInstancesBase & {\n integrationId: string;\n};\n\nexport type ConfigureInstanceWithInstanceId = ConfigureInstancesBase & {\n instanceId: string;\n};\n\nexport type ConfigureInstanceProps =\n | ConfigureInstanceWithIntegrationName\n | ConfigureInstanceWithIntegrationId\n | ConfigureInstanceWithInstanceId;\n\n/**\n * Opens the configuration wizard for an integration, allowing a customer user\n * to activate and configure an instance. You can identify the target by\n * `integrationName`, `integrationId`, or `instanceId`.\n *\n * - Use `integrationName` to let a customer activate a new instance of a\n * marketplace integration by its display name.\n * - Use `integrationId` to target a specific integration version.\n * - Use `instanceId` to reconfigure an already-deployed instance.\n *\n * @param props - Configuration options. Exactly one of `integrationName`, `integrationId`, or `instanceId` is required.\n * @param props.skipRedirectOnRemove - When `true`, prevents redirecting to the marketplace after the instance is removed.\n *\n * @example\n * // Configure by integration name (most common)\n * prismatic.configureInstance({\n * integrationName: \"Salesforce\",\n * usePopover: true,\n * skipRedirectOnRemove: false,\n * });\n *\n * @example\n * // Reconfigure an existing instance inline\n * prismatic.configureInstance({\n * instanceId: \"SW5zdGFuY2U6OGE2YjZi...\",\n * selector: \"#config-container\",\n * skipRedirectOnRemove: true,\n * });\n *\n * @see {@link https://prismatic.io/docs/embed/marketplace/ | Embedding the Marketplace}\n */\nexport const configureInstance = ({ ...props }: ConfigureInstanceProps) => {\n assertInit(\"configureInstance\");\n\n if (isConfigureInstanceWithInstanceId(props)) {\n const { instanceId, skipRedirectOnRemove, ...options } = props;\n\n return setIframe(\"/find-integration-marketplace/\", options, {\n instanceId,\n ...(skipRedirectOnRemove ? { skipRedirectOnRemove: \"true\" } : {}),\n });\n }\n\n if (isConfigureInstanceWithIntegrationId(props)) {\n const { integrationId, skipRedirectOnRemove, ...options } = props;\n\n return setIframe(\"/find-integration-marketplace/\", options, {\n integrationId,\n ...(skipRedirectOnRemove ? { skipRedirectOnRemove: \"true\" } : {}),\n });\n }\n\n if (isConfigureInstanceWithIntegrationName(props)) {\n const { integrationName, skipRedirectOnRemove, ...options } = props;\n\n setIframe(\"/find-integration-marketplace/\", options, {\n integrationName,\n ...(skipRedirectOnRemove ? { skipRedirectOnRemove: \"true\" } : {}),\n });\n }\n};\n\n/** Type guard that checks whether the props identify a target instance by `instanceId`. */\nexport const isConfigureInstanceWithInstanceId = (\n props: ConfigureInstanceProps,\n): props is ConfigureInstanceWithInstanceId => {\n return Boolean(\"instanceId\" in props && props.instanceId);\n};\n\n/** Type guard that checks whether the props identify a target integration by `integrationId`. */\nexport const isConfigureInstanceWithIntegrationId = (\n props: ConfigureInstanceProps,\n): props is ConfigureInstanceWithIntegrationId => {\n return Boolean(\"integrationId\" in props && props.integrationId);\n};\n\n/** Type guard that checks whether the props identify a target integration by `integrationName`. */\nexport const isConfigureInstanceWithIntegrationName = (\n props: ConfigureInstanceProps,\n): props is ConfigureInstanceWithIntegrationName => {\n return Boolean(\"integrationName\" in props && props.integrationName);\n};\n","import urlJoin from \"url-join\";\n\nimport stateService from \"../state\";\nimport { assertInit } from \"../utils/assertInit\";\n\nexport interface GraphqlRequestProps<TVariables = Record<string, unknown>> {\n query: string;\n variables?: TVariables;\n}\n\nexport interface GraphqlRequestResponse<TData = unknown> {\n data: TData;\n errors?: { message: string }[];\n}\n\n/**\n * Executes an authenticated GraphQL request against the Prismatic API.\n * The request is automatically authorized using the JWT from the most\n * recent {@link authenticate} call.\n *\n * This is useful for building custom UIs — for example, querying\n * `marketplaceIntegrations` to render a custom marketplace.\n *\n * @param props - The GraphQL query and optional variables.\n * @param props.query - The GraphQL query string.\n * @param props.variables - Optional variables to pass to the query.\n * @returns The parsed JSON response from the Prismatic API.\n *\n * @example\n * // Query available marketplace integrations\n * const result = await prismatic.graphqlRequest({\n * query: `{\n * marketplaceIntegrations(\n * filters: { category: \"ERP\" }\n * ) {\n * nodes {\n * id\n * name\n * description\n * category\n * }\n * }\n * }`,\n * });\n * console.log(result.data.marketplaceIntegrations.nodes);\n *\n * @example\n * // Query with variables\n * const result = await prismatic.graphqlRequest({\n * query: `query ($integrationId: ID!) {\n * integration(id: $integrationId) {\n * name\n * instances { nodes { id name } }\n * }\n * }`,\n * variables: { integrationId: \"SW50ZWdyYXRpb246...\" },\n * });\n *\n * @see {@link https://prismatic.io/docs/embed/embedded-api-requests/ | Embedded API Requests}\n */\nexport const graphqlRequest = async <\n TData = unknown,\n TVariables = Record<string, unknown>,\n>({\n query,\n variables,\n}: GraphqlRequestProps<TVariables>): Promise<GraphqlRequestResponse<TData>> => {\n assertInit(\"authenticate\");\n\n const { jwt, prismaticUrl } = stateService.getStateCopy();\n\n const response = await fetch(urlJoin(prismaticUrl, \"api\"), {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n Authorization: `Bearer ${jwt}`,\n },\n body: JSON.stringify({ query, variables }),\n });\n\n return await response.json();\n};\n","import urlJoin from \"url-join\";\nimport stateService from \"../state\";\nimport { PrismaticMessageEvent } from \"../types/postMessage\";\nimport { assertInit } from \"../utils/assertInit\";\nimport { EMBEDDED_IFRAME_ID } from \"../utils/iframe\";\nimport { postMessage } from \"../utils/postMessage\";\nimport { graphqlRequest } from \"./graphqlRequest\";\n\nconst ERROR_MESSAGE =\n \"The authenticate method expects an object containing a token and additional optional configuration.\";\nconst EXPECTED_KEYS = [\"token\"];\n\nexport interface AuthenticateProps {\n prismaticUrl?: string;\n token: string;\n}\n\n/**\n * Authenticates an embedded user with a signed JWT token. This must be called after\n * {@link init} and before rendering any embedded screens. The token should be a\n * short-lived RS256-signed JWT generated on your backend.\n *\n * After the initial call, call `authenticate` again before the token expires\n * (typically ~60 seconds before expiry) to keep the session alive. All active\n * iframes update automatically when a new token is provided.\n *\n * @param options - Authentication options.\n * @param options.token - A signed JWT containing `sub`, `organization`, `customer`, `iat`, and `exp` claims.\n * @param options.prismaticUrl - Override the Prismatic app URL for this authentication request.\n * @throws {Error} If the token is missing or the server rejects it.\n *\n * @example\n * // Authenticate with a token fetched from your backend\n * const response = await fetch(\"/api/prismatic-token\");\n * const { token } = await response.json();\n * await prismatic.authenticate({ token });\n *\n * @example\n * // Re-authenticate before token expiry\n * const TOKEN_LIFETIME_MS = 10 * 60 * 1000; // 10 minutes\n * const refreshToken = async () => {\n * const { token } = await fetchToken();\n * await prismatic.authenticate({ token });\n * setTimeout(refreshToken, TOKEN_LIFETIME_MS - 60_000);\n * };\n * refreshToken();\n *\n * @see {@link https://prismatic.io/docs/embed/authenticate-users/ | Authenticating Embedded Users}\n */\nexport const authenticate = async (options: AuthenticateProps) => {\n assertInit(\"authenticate\");\n\n if (!options) {\n throw new Error(ERROR_MESSAGE);\n }\n\n const givenProps = new Set(Object.keys(options));\n\n if (!EXPECTED_KEYS.every((key) => givenProps.has(key))) {\n throw new Error(ERROR_MESSAGE);\n }\n\n const { token } = options;\n\n const iframeElement = document.getElementById(\n EMBEDDED_IFRAME_ID,\n ) as HTMLIFrameElement;\n\n const state = stateService.getStateCopy();\n\n if (state.jwt !== token && iframeElement) {\n postMessage({\n iframe: iframeElement,\n event: {\n event: PrismaticMessageEvent.SET_TOKEN,\n data: token,\n },\n });\n }\n\n const prismaticUrl = options.prismaticUrl ?? state.prismaticUrl;\n\n const authResponse = await fetch(\n urlJoin(prismaticUrl, \"embedded\", \"authenticate\"),\n {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n method: \"post\",\n },\n );\n\n if (!authResponse.ok) {\n const responseText = await authResponse.text();\n\n if (responseText) {\n throw new Error(\n `Authentication failed. Server sent back: ${responseText}`,\n );\n } else {\n throw new Error(\n `Authentication failed. Please check that your customer and organization information are correct, and that the token you provided is not expired.`,\n );\n }\n }\n\n state.jwt = token;\n\n stateService.setState(state);\n\n const result = await graphqlRequest<{\n authenticatedUser: {\n customer: { allowEmbeddedDesigner: boolean };\n };\n }>({\n query: `{\n authenticatedUser {\n customer {\n allowEmbeddedDesigner\n }\n }\n }`,\n });\n\n state.embeddedDesignerEnabled =\n result.data.authenticatedUser.customer.allowEmbeddedDesigner;\n\n stateService.setState(state);\n};\n","import { assertInit } from \"../utils/assertInit\";\nimport { graphqlRequest } from \"./graphqlRequest\";\n\n// biome-ignore lint/suspicious/noEmptyInterface: Empty interface is intentional - consumers augment it via declaration merging.\nexport interface WorkflowContexts {}\n\nexport interface CreateWorkflowArgs<TContextData = unknown> {\n name: string;\n contextData: TContextData;\n externalId?: string;\n}\n\nexport interface CreateWorkflowData {\n importWorkflow: {\n workflow: {\n id: string;\n };\n errors: {\n field: string;\n messages: string[];\n }[];\n };\n}\n\ninterface CreateWorkflowVariables {\n name: string;\n contextStableKey: string;\n contextData: string;\n externalId?: string;\n}\n\nconst mutation = `\n mutation createWorkflow($name: String!, $contextStableKey: String, $contextData: String, $externalId: String) {\n importWorkflow(input: {\n name: $name\n contextStableKey: $contextStableKey\n contextData: $contextData\n externalId: $externalId\n }) {\n workflow {\n id\n }\n errors {\n field\n messages\n }\n }\n }\n`;\n\nexport async function createWorkflow<\n TKey extends keyof WorkflowContexts | (string & {}),\n>(\n contextStableKey: TKey,\n args: CreateWorkflowArgs<\n TKey extends keyof WorkflowContexts\n ? WorkflowContexts[TKey]\n : Record<string, unknown>\n >,\n) {\n assertInit(\"createWorkflow\");\n\n return graphqlRequest<CreateWorkflowData, CreateWorkflowVariables>({\n query: mutation,\n variables: {\n name: args.name,\n contextStableKey,\n contextData: JSON.stringify(args.contextData),\n externalId: args.externalId,\n },\n });\n}\n","import {\n EMBEDDED_IFRAME_CONTAINER_CLASS,\n EMBEDDED_OVERLAY_CLASS,\n EMBEDDED_OVERLAY_VISIBLE_CLASS,\n EMBEDDED_POPOVER_CLASS,\n EMBEDDED_POPOVER_CLOSE_CLASS,\n EMBEDDED_STYLE_ID,\n} from \"./utils/iframe\";\n\nexport const styles = /* html */ `<style id=\"${EMBEDDED_STYLE_ID}\">\n .${EMBEDDED_OVERLAY_CLASS} {\n backdrop-filter: blur(10px);\n background: rgb(119 119 119 / 29%);\n bottom: 0;\n display: block;\n left: 0;\n opacity: 0;\n position: fixed;\n right: 0;\n top: 0;\n transition: all 0.3s ease-in;\n visibility: hidden;\n z-index: 9999;\n }\n .${EMBEDDED_OVERLAY_CLASS}.${EMBEDDED_OVERLAY_VISIBLE_CLASS} {\n opacity: 1;\n visibility: visible;\n }\n .${EMBEDDED_POPOVER_CLASS} {\n background: #fff;\n border-radius: 4px;\n bottom: 0;\n height: 80vh;\n left: 0;\n margin: auto;\n overflow: hidden;\n position: absolute;\n right: 0;\n top: 0;\n width: 80vw;\n }\n .${EMBEDDED_POPOVER_CLOSE_CLASS} {\n all: unset;\n cursor: pointer;\n position: absolute;\n right: 1.5em;\n top: 1.3em;\n }\n .${EMBEDDED_IFRAME_CONTAINER_CLASS} {\n height: 100%\n }\n</style>`;\n","import merge from \"lodash.merge\";\nimport stateService, { isStateKey, type State, setStateKey } from \"../state\";\nimport { styles } from \"../styles\";\nimport {\n EMBEDDED_ID,\n EMBEDDED_IFRAME_CONTAINER_CLASS,\n EMBEDDED_IFRAME_PRELOAD_ID,\n EMBEDDED_OVERLAY_CLASS,\n EMBEDDED_OVERLAY_SELECTOR,\n EMBEDDED_OVERLAY_VISIBLE_CLASS,\n EMBEDDED_POPOVER_CLASS,\n EMBEDDED_POPOVER_CLOSE_CLASS,\n} from \"../utils/iframe\";\nimport { closePopover } from \"../utils/popover\";\n\nlet listenerController: AbortController | undefined;\n\nexport const releaseListeners = () => {\n listenerController?.abort();\n listenerController = undefined;\n};\n\nexport interface InitProps\n extends Pick<\n State,\n \"screenConfiguration\" | \"theme\" | \"fontConfiguration\" | \"translation\"\n >,\n Partial<Pick<State, \"filters\" | \"prismaticUrl\" | \"skipPreload\">> {}\n\nexport const EMBEDDED_DEFAULTS = {\n filters: {\n marketplace: {\n includeActiveIntegrations: true,\n },\n integrations: {},\n components: {},\n },\n screenConfiguration: {\n configurationWizard: {},\n instance: {},\n marketplace: {},\n initializing: {},\n },\n skipPreload: false,\n theme: \"LIGHT\",\n fontConfiguration: undefined,\n translation: {},\n};\n\n/**\n * Initializes the Prismatic embedded SDK. This must be called once before any other\n * SDK methods. It sets up the popover overlay, preloads Prismatic assets into the\n * browser cache, and applies global configuration (theme, filters, translations, etc.).\n *\n * Calling `init` again resets the in-memory SDK state with the new options,\n * but the popover DOM and document-level listeners are reused. Call\n * {@link dispose} first if you need a fully fresh teardown.\n *\n * @param optionsBase - Optional global configuration for the embedded SDK.\n * @param optionsBase.theme - The color theme to use (`\"LIGHT\"` or `\"DARK\"`). Defaults to `\"LIGHT\"`.\n * @param optionsBase.fontConfiguration - Google Font families to load for the embedded UI.\n * @param optionsBase.screenConfiguration - Per-screen display options (marketplace, config wizard, dashboard, etc.).\n * @param optionsBase.filters - Default filters for the marketplace, integrations, and components screens.\n * @param optionsBase.translation - Custom phrase overrides and debug mode for i18n.\n * @param optionsBase.prismaticUrl - Override the Prismatic app URL. Defaults to `\"https://app.prismatic.io\"`.\n * @param optionsBase.skipPreload - Skip preloading Prismatic assets. Defaults to `false`.\n *\n * @example\n * // Basic initialization with defaults\n * prismatic.init();\n *\n * @example\n * // Initialize with dark theme and custom screen configuration\n * prismatic.init({\n * theme: \"DARK\",\n * screenConfiguration: {\n * marketplace: { configuration: \"allow-details\" },\n * configurationWizard: { mode: \"streamlined\" },\n * },\n * filters: {\n * marketplace: { category: \"ERP\" },\n * },\n * });\n *\n * @see {@link https://prismatic.io/docs/embed/get-started/install-embedded-sdk/ | Installing the Embedded SDK}\n * @see {@link https://prismatic.io/docs/embed/theming/ | Theming}\n * @see {@link https://prismatic.io/docs/embed/translations-and-internationalization/ | Translations & i18n}\n */\nexport const init = (optionsBase?: InitProps) => {\n const options: InitProps = merge({}, EMBEDDED_DEFAULTS, optionsBase);\n\n // when we initialize, start from the fresh default state\n const state = stateService.getInitialState();\n\n if (options) {\n Object.entries(options).forEach(([key, value]) => {\n if (isStateKey(key)) {\n setStateKey(state, key, value as State[typeof key]);\n }\n });\n }\n\n state.initComplete = true;\n\n stateService.setState(state);\n\n const existingElement = document.getElementById(EMBEDDED_ID);\n\n if (existingElement) {\n return;\n }\n\n /**\n * This establishes a connection to the prismatic url and preloads\n * assets (css, js, fonts, etc.) into browser cache. Subsequent\n * calls, use existing connections and cached assets.\n */\n const existingIframePreload = document.getElementById(\n EMBEDDED_IFRAME_PRELOAD_ID,\n );\n\n if (!existingIframePreload && !options.skipPreload) {\n document.body.insertAdjacentHTML(\n \"beforeend\",\n `<iframe\n id=\"${EMBEDDED_IFRAME_PRELOAD_ID}\"\n src=\"${state.prismaticUrl}/embedded\"\n style=\"visibility: hidden; display: none;\"\n height=\"0\"\n width=\"0\"\n />`,\n );\n }\n\n document.head.insertAdjacentHTML(\"beforeend\", styles);\n\n const embeddedElement = document.createElement(\"div\");\n\n embeddedElement.id = EMBEDDED_ID;\n\n embeddedElement.innerHTML = /* html */ `\n <div class=\"${EMBEDDED_OVERLAY_CLASS}\">\n <div class=\"${EMBEDDED_POPOVER_CLASS}\">\n <button class=\"${EMBEDDED_POPOVER_CLOSE_CLASS}\" aria-label=\"close popover\" data-close>✕</button>\n <div class=\"${EMBEDDED_IFRAME_CONTAINER_CLASS}\"></div>\n </div>\n </div>\n `;\n\n document.body.appendChild(embeddedElement);\n\n const closeButtonElement = document.querySelector(\n `#${EMBEDDED_ID} .${EMBEDDED_POPOVER_CLOSE_CLASS}`,\n );\n\n const overlayElement = document.querySelector(EMBEDDED_OVERLAY_SELECTOR);\n\n listenerController = new AbortController();\n const { signal } = listenerController;\n\n overlayElement?.addEventListener(\n \"click\",\n (event) => {\n if (event.target !== event.currentTarget) {\n return;\n }\n\n closePopover();\n },\n { signal },\n );\n\n closeButtonElement?.addEventListener(\"click\", () => closePopover(), {\n signal,\n });\n\n document.addEventListener(\n \"keyup\",\n (e) => {\n if (\n e.key === \"Escape\" &&\n document.querySelector(`.${EMBEDDED_OVERLAY_VISIBLE_CLASS}`)\n ) {\n closePopover();\n }\n },\n { signal },\n );\n};\n","import stateService from \"../state\";\nimport {\n EMBEDDED_ID,\n EMBEDDED_IFRAME_PRELOAD_ID,\n EMBEDDED_STYLE_ID,\n} from \"../utils/iframe\";\nimport { releaseListeners } from \"./init\";\n\n/**\n * Removes every DOM node, listener, and piece of in-memory state created by\n * {@link init}. Use this when the embedded experience is being unmounted\n * (SPA route change, logout, conditional render) or to fully reset between\n * tests.\n *\n * After `dispose`, the next call to {@link init} starts from a clean slate:\n * the popover DOM, preload iframe, and stylesheet are recreated, and a fresh\n * Escape-key listener is attached.\n *\n * Calling `dispose` before `init`, or twice in a row, is a safe no-op.\n *\n * @example\n * // Fully reset the SDK\n * prismatic.dispose();\n *\n * @example\n * // Re-initialize with new options\n * prismatic.dispose();\n * prismatic.init({ theme: \"DARK\" });\n */\nexport const dispose = () => {\n document.getElementById(EMBEDDED_ID)?.remove();\n document.getElementById(EMBEDDED_IFRAME_PRELOAD_ID)?.remove();\n document.getElementById(EMBEDDED_STYLE_ID)?.remove();\n\n releaseListeners();\n\n stateService.setState(stateService.getInitialState());\n};\n","import { PrismaticMessageEvent } from \"../types/postMessage\";\nimport type { ConfigurationWizardConfiguration } from \"../types/screenConfiguration\";\nimport type { Theme } from \"../types/theme\";\nimport { assertInit } from \"../utils/assertInit\";\nimport { setIframe } from \"../utils/iframe\";\n\nexport type EditInstanceConfigurationProps = {\n instanceId: string;\n selector: string;\n theme?: Theme;\n screenConfiguration?: {\n configurationWizard?: Omit<ConfigurationWizardConfiguration, \"isInModal\">;\n };\n onCancel?: () => void;\n onSuccess?: () => void;\n onDelete?: () => void;\n};\n\n/**\n * Renders the configuration wizard for an existing instance directly into a\n * DOM element (no popover). This is useful when you want to embed instance\n * configuration inline within your own application's UI.\n *\n * Unlike {@link configureInstance}, this function opens the config wizard directly,\n * supports lifecycle callbacks (`onSuccess`, `onCancel`, `onDelete`), and returns a\n * cleanup function to remove event listeners.\n *\n * @param props - Configuration and display options.\n * @param props.instanceId - The ID of the instance to configure.\n * @param props.selector - A CSS selector for the DOM element to render into.\n * @param props.theme - Optional theme override (`\"LIGHT\"` or `\"DARK\"`).\n * @param props.screenConfiguration - Optional screen configuration for the configuration wizard.\n * @param props.onSuccess - Called when the instance is successfully deployed.\n * @param props.onCancel - Called when the user cancels the configuration.\n * @param props.onDelete - Called when the user deletes the instance.\n * @returns A cleanup function that removes the event listeners, or `undefined` if no callbacks were provided.\n *\n * @example\n * // Edit an instance's configuration with lifecycle callbacks\n * const cleanup = prismatic.editInstanceConfiguration({\n * instanceId: \"SW5zdGFuY2U6OGE2YjZi...\",\n * selector: \"#config-panel\",\n * onSuccess: () => console.log(\"Configuration saved!\"),\n * onCancel: () => console.log(\"Configuration canceled.\"),\n * onDelete: () => console.log(\"Instance deleted.\"),\n * });\n *\n * // Call cleanup() when you're done to remove event listeners\n * cleanup?.();\n *\n * @see {@link https://prismatic.io/docs/embed/marketplace/ | Embedding the Marketplace}\n */\nexport const editInstanceConfiguration = ({\n instanceId,\n selector,\n theme,\n screenConfiguration,\n onCancel,\n onSuccess,\n onDelete,\n}: EditInstanceConfigurationProps) => {\n assertInit(\"editInstanceConfiguration\");\n\n setIframe(\n `/configure-instance/${instanceId}/`,\n {\n selector,\n ...(theme ? { theme } : {}),\n screenConfiguration: {\n ...screenConfiguration,\n configurationWizard: {\n ...screenConfiguration?.configurationWizard,\n isInModal: true,\n },\n },\n },\n { reconfigure: \"true\" },\n );\n\n if (!onCancel && !onSuccess && !onDelete) {\n return;\n }\n\n const abortController = new AbortController();\n\n window.addEventListener(\n \"message\",\n (event: MessageEvent<{ event: string }>) => {\n switch (event.data?.event) {\n case PrismaticMessageEvent.INSTANCE_DEPLOYED:\n onSuccess?.();\n abortController.abort();\n break;\n case PrismaticMessageEvent.INSTANCE_DELETED:\n onDelete?.();\n abortController.abort();\n break;\n case PrismaticMessageEvent.INSTANCE_CONFIGURATION_CANCELED:\n onCancel?.();\n abortController.abort();\n break;\n }\n },\n { signal: abortController.signal },\n );\n\n return () => abortController.abort();\n};\n","import { assertInit } from \"../utils/assertInit\";\nimport { graphqlRequest } from \"./graphqlRequest\";\n\nexport interface Workflow {\n id: string;\n name: string;\n versionNumber: number;\n description: string;\n externalId: string | null;\n updatedAt: string;\n lastExecutedAt: string | null;\n createdAt: string;\n category: string | null;\n labels: string[];\n customer: {\n id: string;\n name: string;\n };\n instance: {\n enabled: boolean;\n } | null;\n deployedVersion: {\n id: string;\n } | null;\n}\n\nexport interface QueryWorkflowsData {\n workflows: {\n nodes: Workflow[];\n pageInfo: {\n hasNextPage: boolean;\n endCursor: string | null;\n };\n totalCount: number;\n };\n}\n\nexport interface QueryWorkflowsProps extends Record<string, unknown> {\n searchTerm?: string;\n descriptionSearch?: string;\n categorySearch?: string;\n labelSearch?: string;\n contextStableKey?: string;\n externalId?: string;\n sortBy?: string[];\n first?: number;\n cursor?: string;\n}\n\nconst query = `\n query queryWorkflows(\n $cursor: String\n $searchTerm: String\n $descriptionSearch: String\n $categorySearch: String\n $labelSearch: String\n $contextStableKey: String\n $externalId: String\n $first: Int\n $sortBy: [IntegrationVariantOrder]\n ) {\n workflows: integrationVariants(\n after: $cursor\n name_Icontains: $searchTerm\n description_Icontains: $descriptionSearch\n category: $categorySearch\n labels_Icontains: $labelSearch\n contextStableKey: $contextStableKey\n externalId: $externalId\n exclude: [INTEGRATION]\n first: $first\n sortBy: $sortBy\n ) {\n nodes {\n ... on Workflow {\n id\n name\n versionNumber\n description\n externalId\n updatedAt\n lastExecutedAt\n createdAt\n category\n labels\n customer {\n id\n name\n }\n instance {\n enabled\n }\n deployedVersion {\n id\n }\n }\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n totalCount\n }\n }\n`;\n\nexport const queryWorkflows = async (variables?: QueryWorkflowsProps) => {\n assertInit(\"queryWorkflows\");\n\n return graphqlRequest<QueryWorkflowsData, QueryWorkflowsProps>({\n query,\n variables,\n });\n};\n","import type { ConfigVars } from \"../types/configVars\";\nimport { PrismaticMessageEvent } from \"../types/postMessage\";\nimport { postMessage } from \"../utils/postMessage\";\n\ninterface SetConfigVarsPropsBase {\n configVars: ConfigVars;\n}\n\ninterface SelectorSetConfigVarProps extends SetConfigVarsPropsBase {\n selector?: string;\n}\n\ninterface IFrameSetConfigVarProps extends SetConfigVarsPropsBase {\n iframe: Element;\n}\n\nexport type SetConfigVarsProps =\n | IFrameSetConfigVarProps\n | SelectorSetConfigVarProps;\n\n/**\n * Programmatically sets configuration variable values on an active configuration\n * wizard. This is useful for pre-filling config values based on context from\n * your application (e.g., setting an API endpoint or customer-specific settings).\n *\n * You can target the iframe either by passing the iframe element directly\n * (obtained from {@link getMessageIframe}) or by passing a `selector` string.\n *\n * @param props - The config vars and target iframe.\n * @param props.configVars - A map of config variable keys to their values.\n * @param props.iframe - The iframe element to send config vars to (use with `getMessageIframe`).\n * @param props.selector - A CSS selector for the iframe to send config vars to.\n *\n * @example\n * // Set config vars using an iframe reference from a message event\n * window.addEventListener(\"message\", (event) => {\n * if (event.data.event === \"INSTANCE_CONFIGURATION_LOADED\") {\n * const iframe = prismatic.getMessageIframe(event);\n * prismatic.setConfigVars({\n * iframe,\n * configVars: {\n * \"API Endpoint\": { value: \"https://api.example.com\" },\n * \"Sync Frequency\": { value: \"daily\" },\n * },\n * });\n * }\n * });\n *\n * @example\n * // Set config vars using a CSS selector\n * prismatic.setConfigVars({\n * selector: \"#config-container\",\n * configVars: {\n * \"API Key\": { value: \"my-api-key\" },\n * },\n * });\n *\n * @see {@link https://prismatic.io/docs/embed/marketplace/ | Embedding the Marketplace}\n */\nexport const setConfigVars = ({\n configVars,\n ...options\n}: SetConfigVarsProps) => {\n postMessage({\n ...options,\n event: { event: PrismaticMessageEvent.SET_CONFIG_VAR, data: configVars },\n });\n};\n","import type { Options } from \"../types/options\";\nimport { assertInit } from \"../utils/assertInit\";\nimport { setIframe } from \"../utils/iframe\";\n\nexport type ShowComponentProps = Options & {\n componentId: string;\n};\n\n/**\n * Renders the detail page for a specific component, showing its available\n * actions, triggers, and data sources.\n *\n * @param props - Display options including the component to show.\n * @param props.componentId - The ID of the component to display.\n *\n * @example\n * // Show a specific component in a popover\n * prismatic.showComponent({\n * componentId: \"Q29tcG9uZW50OmFiY2Rl...\",\n * usePopover: true,\n * });\n *\n * @example\n * // Show a specific component inline\n * prismatic.showComponent({\n * componentId: \"Q29tcG9uZW50OmFiY2Rl...\",\n * selector: \"#component-detail\",\n * });\n *\n * @see {@link https:/