@runtypelabs/persona 3.12.0 → 3.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +79 -0
- package/dist/index.cjs +26 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.global.js +39 -39
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +28 -28
- package/dist/index.js.map +1 -1
- package/dist/install.global.js +1 -1
- package/dist/install.global.js.map +1 -1
- package/dist/theme-editor.cjs +29 -6
- package/dist/theme-editor.js +29 -6
- package/package.json +1 -1
- package/src/client.test.ts +64 -0
- package/src/client.ts +37 -8
- package/src/install.ts +10 -2
- package/src/runtime/init.test.ts +132 -0
- package/src/utils/code-generators.test.ts +57 -0
- package/src/utils/code-generators.ts +21 -6
package/dist/install.global.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var SiteAgentInstaller=(()=>{var m=Object.defineProperty;var j=Object.getOwnPropertyDescriptor;var W=Object.getOwnPropertyNames;var T=Object.prototype.hasOwnProperty;var
|
|
1
|
+
"use strict";var SiteAgentInstaller=(()=>{var m=Object.defineProperty;var j=Object.getOwnPropertyDescriptor;var W=Object.getOwnPropertyNames;var T=Object.prototype.hasOwnProperty;var E=(a,r,u,t)=>{if(r&&typeof r=="object"||typeof r=="function")for(let l of W(r))!T.call(a,l)&&l!==u&&m(a,l,{get:()=>r[l],enumerable:!(t=j(r,l))||t.enumerable});return a};var _=a=>E(m({},"__esModule",{value:!0}),a);var F={};(function(){"use strict";if(window.__siteAgentInstallerLoaded)return;window.__siteAgentInstallerLoaded=!0;let r=(()=>{let n=document.currentScript;if(!n)return{};let e={},o=n.getAttribute("data-config");if(o)try{let c=o.replace(/[\r\n]+\s*/g,""),g=JSON.parse(c);if(g.config){let{__proto__:M,constructor:$,prototype:J,...P}=g;Object.assign(e,P)}else e.config=g}catch(c){console.error("Failed to parse data-config JSON:",c)}let s=n.getAttribute("data-runtype-token");s&&(e.clientToken=s);let d=n.getAttribute("data-flow-id");d&&(e.flowId=d);let i=n.getAttribute("data-api-url");i&&(e.apiUrl=i);let p=n.getAttribute("data-preview-param");return p&&(e.previewQueryParam=p),e})(),t={...window.siteAgentConfig||{},...r};if(!(()=>{if(!t.previewQueryParam)return!0;let e=new URLSearchParams(window.location.search).get(t.previewQueryParam);return e!==null&&e!==""&&e.toLowerCase()!=="false"&&e!=="0"})())return;let y=t.version||"latest",A=t.cdn||"jsdelivr",h=t.autoInit!==!1,C=()=>{if(t.cssUrl&&t.jsUrl)return{cssUrl:t.cssUrl,jsUrl:t.jsUrl};let e=`/npm/@runtypelabs/persona@${y}/dist`;return A==="unpkg"?{cssUrl:`https://unpkg.com${e}/widget.css`,jsUrl:`https://unpkg.com${e}/index.global.js`}:{cssUrl:`https://cdn.jsdelivr.net${e}/widget.css`,jsUrl:`https://cdn.jsdelivr.net${e}/index.global.js`}},{cssUrl:f,jsUrl:w}=C(),k=()=>!!document.head.querySelector("link[data-persona]")||!!document.head.querySelector('link[href*="widget.css"]'),S=()=>!!window.AgentWidget,U=n=>{let e=!1,o=()=>{e||(e=!0,n())},s=()=>{typeof requestIdleCallback!="undefined"?requestIdleCallback(()=>{requestAnimationFrame(()=>{requestAnimationFrame(o)})},{timeout:3e3}):setTimeout(o,300)};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",s):s()},b=()=>new Promise((n,e)=>{if(k()){n();return}let o=document.createElement("link");o.rel="stylesheet",o.href=f,o.setAttribute("data-persona","true"),o.onload=()=>n(),o.onerror=()=>e(new Error(`Failed to load CSS from ${f}`)),document.head.appendChild(o)}),v=()=>new Promise((n,e)=>{if(S()){n();return}let o=document.createElement("script");o.src=w,o.async=!0,o.onload=()=>n(),o.onerror=()=>e(new Error(`Failed to load JS from ${w}`)),document.head.appendChild(o)}),I=()=>{var s,d;if(!window.AgentWidget||!window.AgentWidget.initAgentWidget){console.warn("AgentWidget not available. Make sure the script loaded successfully.");return}let n=t.target||"body",e={...t.config};if(t.apiUrl&&!e.apiUrl&&(e.apiUrl=t.apiUrl),t.clientToken&&!e.clientToken&&(e.clientToken=t.clientToken),t.flowId&&!e.flowId&&(e.flowId=t.flowId),!(!(e.apiUrl||e.clientToken)&&Object.keys(e).length===0)){!e.postprocessMessage&&window.AgentWidget.markdownPostprocessor&&(e.postprocessMessage=({text:i})=>window.AgentWidget.markdownPostprocessor(i));try{let i=window.AgentWidget.initAgentWidget({target:n,config:e,useShadowDom:(s=t.useShadowDom)!=null?s:!1,windowKey:t.windowKey});(d=t.onReady)==null||d.call(t,i),window.dispatchEvent(new CustomEvent("persona:ready",{detail:i}))}catch(i){console.error("Failed to initialize AgentWidget:",i)}}};U(async()=>{try{await b(),await v(),h&&(t.config||t.apiUrl||t.clientToken)&&setTimeout(I,0)}catch(n){console.error("Failed to install AgentWidget:",n)}})})();return _(F);})();
|
|
2
2
|
//# sourceMappingURL=install.global.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/install.ts"],"sourcesContent":["/**\n * Standalone installer script for easy script tag installation\n * This script automatically loads CSS and JS, then initializes the widget\n * if configuration is provided via window.siteAgentConfig\n */\n\nexport {};\n\ninterface SiteAgentInstallConfig {\n version?: string;\n cdn?: \"unpkg\" | \"jsdelivr\";\n cssUrl?: string;\n jsUrl?: string;\n target?: string | HTMLElement;\n config?: any;\n autoInit?: boolean;\n // Client token mode options (can also be set via data attributes)\n clientToken?: string;\n flowId?: string;\n apiUrl?: string;\n // Optional query param key that gates widget installation in preview mode\n previewQueryParam?: string;\n // Shadow DOM option (defaults to false for better CSS compatibility)\n useShadowDom?: boolean;\n}\n\ndeclare global {\n interface Window {\n siteAgentConfig?: SiteAgentInstallConfig;\n AgentWidget?: any;\n }\n}\n\n(function() {\n \"use strict\";\n\n // Prevent double installation\n if ((window as any).__siteAgentInstallerLoaded) {\n return;\n }\n (window as any).__siteAgentInstallerLoaded = true;\n\n /**\n * Read configuration from data attributes on the current script tag.\n * Supports: data-config (JSON), data-runtype-token, data-flow-id, data-api-url\n */\n const getConfigFromScript = (): Partial<SiteAgentInstallConfig> => {\n // Try to get the current script element\n const script = document.currentScript as HTMLScriptElement | null;\n if (!script) return {};\n\n const scriptConfig: Partial<SiteAgentInstallConfig> = {};\n\n // Full config from data-config attribute (JSON string)\n const configJson = script.getAttribute('data-config');\n if (configJson) {\n try {\n // HTML attributes preserve literal newlines/tabs which are invalid\n // control characters inside JSON string literals — strip them.\n const normalizedJson = configJson.replace(/[\\r\\n]+\\s*/g, '');\n const parsedConfig = JSON.parse(normalizedJson);\n // If it has nested 'config' property, use it; otherwise treat as widget config\n if (parsedConfig.config) {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { __proto__: _a, constructor: _b, prototype: _c, ...safeConfig } = parsedConfig;\n Object.assign(scriptConfig, safeConfig);\n } else {\n // Treat the entire object as widget config\n scriptConfig.config = parsedConfig;\n }\n } catch (e) {\n console.error(\"Failed to parse data-config JSON:\", e);\n }\n }\n\n // Client token from data attribute (primary method for client token mode)\n const token = script.getAttribute('data-runtype-token');\n if (token) {\n scriptConfig.clientToken = token;\n }\n\n // Optional flow ID\n const flowId = script.getAttribute('data-flow-id');\n if (flowId) {\n scriptConfig.flowId = flowId;\n }\n\n // Optional API URL override\n const apiUrl = script.getAttribute('data-api-url');\n if (apiUrl) {\n scriptConfig.apiUrl = apiUrl;\n }\n\n // Optional preview query param gate\n const previewQueryParam = script.getAttribute('data-preview-param');\n if (previewQueryParam) {\n scriptConfig.previewQueryParam = previewQueryParam;\n }\n\n return scriptConfig;\n };\n\n // Get config from script attributes (must be called synchronously during script execution)\n const scriptConfig = getConfigFromScript();\n\n // Merge script attributes with window config (script attributes take precedence)\n const windowConfig: SiteAgentInstallConfig = window.siteAgentConfig || {};\n const config: SiteAgentInstallConfig = { ...windowConfig, ...scriptConfig };\n\n const isPreviewModeEnabled = (): boolean => {\n if (!config.previewQueryParam) {\n return true;\n }\n\n const params = new URLSearchParams(window.location.search);\n const value = params.get(config.previewQueryParam);\n return value !== null && value !== \"\" && value.toLowerCase() !== \"false\" && value !== \"0\";\n };\n\n if (!isPreviewModeEnabled()) {\n return;\n }\n \n const version = config.version || \"latest\";\n const cdn = config.cdn || \"jsdelivr\";\n const autoInit = config.autoInit !== false; // Default to true\n\n // Determine CDN base URL\n const getCdnBase = () => {\n if (config.cssUrl && config.jsUrl) {\n return { cssUrl: config.cssUrl, jsUrl: config.jsUrl };\n }\n \n const packageName = \"@runtypelabs/persona\";\n const basePath = `/npm/${packageName}@${version}/dist`;\n \n if (cdn === \"unpkg\") {\n return {\n cssUrl: `https://unpkg.com${basePath}/widget.css`,\n jsUrl: `https://unpkg.com${basePath}/index.global.js`\n };\n } else {\n return {\n cssUrl: `https://cdn.jsdelivr.net${basePath}/widget.css`,\n jsUrl: `https://cdn.jsdelivr.net${basePath}/index.global.js`\n };\n }\n };\n\n const { cssUrl, jsUrl } = getCdnBase();\n\n // Check if CSS is already loaded\n const isCssLoaded = () => {\n return !!document.head.querySelector('link[data-persona]') ||\n !!document.head.querySelector(`link[href*=\"widget.css\"]`);\n };\n\n // Check if JS is already loaded\n const isJsLoaded = () => {\n return !!(window as any).AgentWidget;\n };\n\n /**\n * Wait for framework hydration to complete (Next.js, Nuxt, etc.)\n * This prevents the framework from removing dynamically added CSS during reconciliation.\n * Uses requestIdleCallback + double requestAnimationFrame for reliable detection.\n */\n const waitForHydration = (callback: () => void): void => {\n let executed = false;\n \n const execute = () => {\n if (executed) return;\n executed = true;\n callback();\n };\n\n const afterDom = () => {\n // Strategy 1: Use requestIdleCallback if available (best for detecting idle after hydration)\n if (typeof requestIdleCallback !== 'undefined') {\n requestIdleCallback(() => {\n // Double requestAnimationFrame ensures at least one full paint cycle completed\n requestAnimationFrame(() => {\n requestAnimationFrame(execute);\n });\n }, { timeout: 3000 }); // Max wait 3 seconds, then proceed anyway\n } else {\n // Strategy 2: Fallback for Safari (no requestIdleCallback)\n // 300ms is typically enough for hydration on most pages\n setTimeout(execute, 300);\n }\n };\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', afterDom);\n } else {\n // DOM already ready, but still wait for potential hydration\n afterDom();\n }\n };\n\n // Load CSS\n const loadCSS = (): Promise<void> => {\n return new Promise((resolve, reject) => {\n if (isCssLoaded()) {\n resolve();\n return;\n }\n\n const link = document.createElement(\"link\");\n link.rel = \"stylesheet\";\n link.href = cssUrl;\n link.setAttribute(\"data-persona\", \"true\");\n \n link.onload = () => resolve();\n link.onerror = () => reject(new Error(`Failed to load CSS from ${cssUrl}`));\n document.head.appendChild(link);\n });\n };\n\n // Load JS\n const loadJS = (): Promise<void> => {\n return new Promise((resolve, reject) => {\n if (isJsLoaded()) {\n resolve();\n return;\n }\n\n const script = document.createElement(\"script\");\n script.src = jsUrl;\n script.async = true;\n script.onload = () => resolve();\n script.onerror = () => reject(new Error(`Failed to load JS from ${jsUrl}`));\n document.head.appendChild(script);\n });\n };\n\n // Initialize widget\n const initWidget = () => {\n if (!window.AgentWidget || !window.AgentWidget.initAgentWidget) {\n console.warn(\"AgentWidget not available. Make sure the script loaded successfully.\");\n return;\n }\n\n const target = config.target || \"body\";\n // Merge top-level config options into widget config\n const widgetConfig = { ...config.config };\n \n // Merge apiUrl from top-level config into widget config if present\n if (config.apiUrl && !widgetConfig.apiUrl) {\n widgetConfig.apiUrl = config.apiUrl;\n }\n \n // Merge clientToken from top-level config into widget config if present\n if (config.clientToken && !widgetConfig.clientToken) {\n widgetConfig.clientToken = config.clientToken;\n }\n \n // Merge flowId from top-level config into widget config if present\n if (config.flowId && !widgetConfig.flowId) {\n widgetConfig.flowId = config.flowId;\n }\n\n // Only initialize if we have either apiUrl OR clientToken (or other config)\n const hasApiConfig = widgetConfig.apiUrl || widgetConfig.clientToken;\n if (!hasApiConfig && Object.keys(widgetConfig).length === 0) {\n return;\n }\n\n // Auto-apply markdown postprocessor if not explicitly set and available\n if (!widgetConfig.postprocessMessage && window.AgentWidget.markdownPostprocessor) {\n widgetConfig.postprocessMessage = ({ text }: { text: string }) => \n window.AgentWidget.markdownPostprocessor(text);\n }\n\n try {\n window.AgentWidget.initAgentWidget({\n target,\n config: widgetConfig,\n // Explicitly disable shadow DOM for better CSS compatibility with host page\n useShadowDom: config.useShadowDom ?? false\n });\n } catch (error) {\n console.error(\"Failed to initialize AgentWidget:\", error);\n }\n };\n\n // Main installation flow (called after hydration completes)\n const install = async () => {\n try {\n await loadCSS();\n await loadJS();\n \n // Auto-init if we have config OR apiUrl OR clientToken\n const shouldAutoInit = autoInit && (\n config.config || \n config.apiUrl || \n config.clientToken\n );\n \n if (shouldAutoInit) {\n // Wait a tick to ensure AgentWidget is fully initialized\n setTimeout(initWidget, 0);\n }\n } catch (error) {\n console.error(\"Failed to install AgentWidget:\", error);\n }\n };\n\n // Start installation after hydration completes\n // This prevents Next.js/Nuxt/etc. from removing dynamically added CSS\n waitForHydration(install);\n})();\n\n"],"mappings":"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","names":["install_exports","scriptConfig","script","configJson","normalizedJson","parsedConfig","_a","_b","_c","safeConfig","e","token","flowId","apiUrl","previewQueryParam","config","value","version","cdn","autoInit","getCdnBase","basePath","cssUrl","jsUrl","isCssLoaded","isJsLoaded","waitForHydration","callback","executed","execute","afterDom","loadCSS","resolve","reject","link","loadJS","initWidget","target","widgetConfig","text","error"]}
|
|
1
|
+
{"version":3,"sources":["../src/install.ts"],"sourcesContent":["/**\n * Standalone installer script for easy script tag installation\n * This script automatically loads CSS and JS, then initializes the widget\n * if configuration is provided via window.siteAgentConfig\n */\n\nexport {};\n\ninterface SiteAgentInstallConfig {\n version?: string;\n cdn?: \"unpkg\" | \"jsdelivr\";\n cssUrl?: string;\n jsUrl?: string;\n target?: string | HTMLElement;\n config?: any;\n autoInit?: boolean;\n // Client token mode options (can also be set via data attributes)\n clientToken?: string;\n flowId?: string;\n apiUrl?: string;\n // Optional query param key that gates widget installation in preview mode\n previewQueryParam?: string;\n // Shadow DOM option (defaults to false for better CSS compatibility)\n useShadowDom?: boolean;\n // Expose the widget handle on window[windowKey] for programmatic access\n windowKey?: string;\n // Called when the widget is initialized and ready for interaction\n onReady?: (handle: any) => void;\n}\n\ndeclare global {\n interface Window {\n siteAgentConfig?: SiteAgentInstallConfig;\n AgentWidget?: any;\n }\n}\n\n(function() {\n \"use strict\";\n\n // Prevent double installation\n if ((window as any).__siteAgentInstallerLoaded) {\n return;\n }\n (window as any).__siteAgentInstallerLoaded = true;\n\n /**\n * Read configuration from data attributes on the current script tag.\n * Supports: data-config (JSON), data-runtype-token, data-flow-id, data-api-url\n */\n const getConfigFromScript = (): Partial<SiteAgentInstallConfig> => {\n // Try to get the current script element\n const script = document.currentScript as HTMLScriptElement | null;\n if (!script) return {};\n\n const scriptConfig: Partial<SiteAgentInstallConfig> = {};\n\n // Full config from data-config attribute (JSON string)\n const configJson = script.getAttribute('data-config');\n if (configJson) {\n try {\n // HTML attributes preserve literal newlines/tabs which are invalid\n // control characters inside JSON string literals — strip them.\n const normalizedJson = configJson.replace(/[\\r\\n]+\\s*/g, '');\n const parsedConfig = JSON.parse(normalizedJson);\n // If it has nested 'config' property, use it; otherwise treat as widget config\n if (parsedConfig.config) {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { __proto__: _a, constructor: _b, prototype: _c, ...safeConfig } = parsedConfig;\n Object.assign(scriptConfig, safeConfig);\n } else {\n // Treat the entire object as widget config\n scriptConfig.config = parsedConfig;\n }\n } catch (e) {\n console.error(\"Failed to parse data-config JSON:\", e);\n }\n }\n\n // Client token from data attribute (primary method for client token mode)\n const token = script.getAttribute('data-runtype-token');\n if (token) {\n scriptConfig.clientToken = token;\n }\n\n // Optional flow ID\n const flowId = script.getAttribute('data-flow-id');\n if (flowId) {\n scriptConfig.flowId = flowId;\n }\n\n // Optional API URL override\n const apiUrl = script.getAttribute('data-api-url');\n if (apiUrl) {\n scriptConfig.apiUrl = apiUrl;\n }\n\n // Optional preview query param gate\n const previewQueryParam = script.getAttribute('data-preview-param');\n if (previewQueryParam) {\n scriptConfig.previewQueryParam = previewQueryParam;\n }\n\n return scriptConfig;\n };\n\n // Get config from script attributes (must be called synchronously during script execution)\n const scriptConfig = getConfigFromScript();\n\n // Merge script attributes with window config (script attributes take precedence)\n const windowConfig: SiteAgentInstallConfig = window.siteAgentConfig || {};\n const config: SiteAgentInstallConfig = { ...windowConfig, ...scriptConfig };\n\n const isPreviewModeEnabled = (): boolean => {\n if (!config.previewQueryParam) {\n return true;\n }\n\n const params = new URLSearchParams(window.location.search);\n const value = params.get(config.previewQueryParam);\n return value !== null && value !== \"\" && value.toLowerCase() !== \"false\" && value !== \"0\";\n };\n\n if (!isPreviewModeEnabled()) {\n return;\n }\n \n const version = config.version || \"latest\";\n const cdn = config.cdn || \"jsdelivr\";\n const autoInit = config.autoInit !== false; // Default to true\n\n // Determine CDN base URL\n const getCdnBase = () => {\n if (config.cssUrl && config.jsUrl) {\n return { cssUrl: config.cssUrl, jsUrl: config.jsUrl };\n }\n \n const packageName = \"@runtypelabs/persona\";\n const basePath = `/npm/${packageName}@${version}/dist`;\n \n if (cdn === \"unpkg\") {\n return {\n cssUrl: `https://unpkg.com${basePath}/widget.css`,\n jsUrl: `https://unpkg.com${basePath}/index.global.js`\n };\n } else {\n return {\n cssUrl: `https://cdn.jsdelivr.net${basePath}/widget.css`,\n jsUrl: `https://cdn.jsdelivr.net${basePath}/index.global.js`\n };\n }\n };\n\n const { cssUrl, jsUrl } = getCdnBase();\n\n // Check if CSS is already loaded\n const isCssLoaded = () => {\n return !!document.head.querySelector('link[data-persona]') ||\n !!document.head.querySelector(`link[href*=\"widget.css\"]`);\n };\n\n // Check if JS is already loaded\n const isJsLoaded = () => {\n return !!(window as any).AgentWidget;\n };\n\n /**\n * Wait for framework hydration to complete (Next.js, Nuxt, etc.)\n * This prevents the framework from removing dynamically added CSS during reconciliation.\n * Uses requestIdleCallback + double requestAnimationFrame for reliable detection.\n */\n const waitForHydration = (callback: () => void): void => {\n let executed = false;\n \n const execute = () => {\n if (executed) return;\n executed = true;\n callback();\n };\n\n const afterDom = () => {\n // Strategy 1: Use requestIdleCallback if available (best for detecting idle after hydration)\n if (typeof requestIdleCallback !== 'undefined') {\n requestIdleCallback(() => {\n // Double requestAnimationFrame ensures at least one full paint cycle completed\n requestAnimationFrame(() => {\n requestAnimationFrame(execute);\n });\n }, { timeout: 3000 }); // Max wait 3 seconds, then proceed anyway\n } else {\n // Strategy 2: Fallback for Safari (no requestIdleCallback)\n // 300ms is typically enough for hydration on most pages\n setTimeout(execute, 300);\n }\n };\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', afterDom);\n } else {\n // DOM already ready, but still wait for potential hydration\n afterDom();\n }\n };\n\n // Load CSS\n const loadCSS = (): Promise<void> => {\n return new Promise((resolve, reject) => {\n if (isCssLoaded()) {\n resolve();\n return;\n }\n\n const link = document.createElement(\"link\");\n link.rel = \"stylesheet\";\n link.href = cssUrl;\n link.setAttribute(\"data-persona\", \"true\");\n \n link.onload = () => resolve();\n link.onerror = () => reject(new Error(`Failed to load CSS from ${cssUrl}`));\n document.head.appendChild(link);\n });\n };\n\n // Load JS\n const loadJS = (): Promise<void> => {\n return new Promise((resolve, reject) => {\n if (isJsLoaded()) {\n resolve();\n return;\n }\n\n const script = document.createElement(\"script\");\n script.src = jsUrl;\n script.async = true;\n script.onload = () => resolve();\n script.onerror = () => reject(new Error(`Failed to load JS from ${jsUrl}`));\n document.head.appendChild(script);\n });\n };\n\n // Initialize widget\n const initWidget = () => {\n if (!window.AgentWidget || !window.AgentWidget.initAgentWidget) {\n console.warn(\"AgentWidget not available. Make sure the script loaded successfully.\");\n return;\n }\n\n const target = config.target || \"body\";\n // Merge top-level config options into widget config\n const widgetConfig = { ...config.config };\n \n // Merge apiUrl from top-level config into widget config if present\n if (config.apiUrl && !widgetConfig.apiUrl) {\n widgetConfig.apiUrl = config.apiUrl;\n }\n \n // Merge clientToken from top-level config into widget config if present\n if (config.clientToken && !widgetConfig.clientToken) {\n widgetConfig.clientToken = config.clientToken;\n }\n \n // Merge flowId from top-level config into widget config if present\n if (config.flowId && !widgetConfig.flowId) {\n widgetConfig.flowId = config.flowId;\n }\n\n // Only initialize if we have either apiUrl OR clientToken (or other config)\n const hasApiConfig = widgetConfig.apiUrl || widgetConfig.clientToken;\n if (!hasApiConfig && Object.keys(widgetConfig).length === 0) {\n return;\n }\n\n // Auto-apply markdown postprocessor if not explicitly set and available\n if (!widgetConfig.postprocessMessage && window.AgentWidget.markdownPostprocessor) {\n widgetConfig.postprocessMessage = ({ text }: { text: string }) => \n window.AgentWidget.markdownPostprocessor(text);\n }\n\n try {\n const handle = window.AgentWidget.initAgentWidget({\n target,\n config: widgetConfig,\n // Explicitly disable shadow DOM for better CSS compatibility with host page\n useShadowDom: config.useShadowDom ?? false,\n windowKey: config.windowKey\n });\n\n config.onReady?.(handle);\n window.dispatchEvent(new CustomEvent(\"persona:ready\", { detail: handle }));\n } catch (error) {\n console.error(\"Failed to initialize AgentWidget:\", error);\n }\n };\n\n // Main installation flow (called after hydration completes)\n const install = async () => {\n try {\n await loadCSS();\n await loadJS();\n \n // Auto-init if we have config OR apiUrl OR clientToken\n const shouldAutoInit = autoInit && (\n config.config || \n config.apiUrl || \n config.clientToken\n );\n \n if (shouldAutoInit) {\n // Wait a tick to ensure AgentWidget is fully initialized\n setTimeout(initWidget, 0);\n }\n } catch (error) {\n console.error(\"Failed to install AgentWidget:\", error);\n }\n };\n\n // Start installation after hydration completes\n // This prevents Next.js/Nuxt/etc. from removing dynamically added CSS\n waitForHydration(install);\n})();\n\n"],"mappings":"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","names":["install_exports","scriptConfig","script","configJson","normalizedJson","parsedConfig","_a","_b","_c","safeConfig","e","token","flowId","apiUrl","previewQueryParam","config","value","version","cdn","autoInit","getCdnBase","basePath","cssUrl","jsUrl","isCssLoaded","isJsLoaded","waitForHydration","callback","executed","execute","afterDom","loadCSS","resolve","reject","link","loadJS","initWidget","target","widgetConfig","text","handle","error"]}
|
package/dist/theme-editor.cjs
CHANGED
|
@@ -4329,7 +4329,7 @@ var AgentWidgetClient = class {
|
|
|
4329
4329
|
}
|
|
4330
4330
|
}
|
|
4331
4331
|
async streamResponse(body, onEvent, assistantMessageId) {
|
|
4332
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z, __, _$, _aa, _ba, _ca, _da, _ea, _fa, _ga, _ha, _ia, _ja, _ka, _la, _ma, _na, _oa, _pa, _qa, _ra, _sa, _ta, _ua, _va, _wa, _xa, _ya, _za, _Aa, _Ba, _Ca, _Da, _Ea, _Fa, _Ga, _Ha, _Ia, _Ja, _Ka, _La, _Ma, _Na, _Oa, _Pa, _Qa, _Ra, _Sa, _Ta, _Ua, _Va, _Wa, _Xa, _Ya, _Za, __a, _$a, _ab, _bb, _cb, _db, _eb, _fb, _gb, _hb, _ib, _jb, _kb, _lb;
|
|
4332
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z, __, _$, _aa, _ba, _ca, _da, _ea, _fa, _ga, _ha, _ia, _ja, _ka, _la, _ma, _na, _oa, _pa, _qa, _ra, _sa, _ta, _ua, _va, _wa, _xa, _ya, _za, _Aa, _Ba, _Ca, _Da, _Ea, _Fa, _Ga, _Ha, _Ia, _Ja, _Ka, _La, _Ma, _Na, _Oa, _Pa, _Qa, _Ra, _Sa, _Ta, _Ua, _Va, _Wa, _Xa, _Ya, _Za, __a, _$a, _ab, _bb, _cb, _db, _eb, _fb, _gb, _hb, _ib, _jb, _kb, _lb, _mb, _nb;
|
|
4333
4333
|
const reader = body.getReader();
|
|
4334
4334
|
const decoder = new TextDecoder();
|
|
4335
4335
|
let buffer = "";
|
|
@@ -5620,11 +5620,34 @@ var AgentWidgetClient = class {
|
|
|
5620
5620
|
streamParsers.delete(id);
|
|
5621
5621
|
rawContentBuffers.delete(id);
|
|
5622
5622
|
seqChunkBuffers.delete(id);
|
|
5623
|
-
} else if (payloadType === "error"
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
})
|
|
5623
|
+
} else if (payloadType === "error" || payloadType === "step_error" || payloadType === "dispatch_error" || payloadType === "flow_error") {
|
|
5624
|
+
let resolvedError = null;
|
|
5625
|
+
if (payload.error instanceof Error) {
|
|
5626
|
+
resolvedError = payload.error;
|
|
5627
|
+
} else if (payloadType === "dispatch_error") {
|
|
5628
|
+
const msg = (_mb = payload.message) != null ? _mb : payload.error;
|
|
5629
|
+
if (msg != null && msg !== "") {
|
|
5630
|
+
resolvedError = new Error(String(msg));
|
|
5631
|
+
}
|
|
5632
|
+
} else if (payloadType === "step_error" || payloadType === "flow_error") {
|
|
5633
|
+
const e = payload.error;
|
|
5634
|
+
if (typeof e === "string" && e !== "") {
|
|
5635
|
+
resolvedError = new Error(e);
|
|
5636
|
+
} else if (e != null && typeof e === "object" && "message" in e) {
|
|
5637
|
+
resolvedError = new Error(String((_nb = e.message) != null ? _nb : e));
|
|
5638
|
+
}
|
|
5639
|
+
} else if (payloadType === "error" && payload.error != null && payload.error !== "") {
|
|
5640
|
+
resolvedError = new Error(String(payload.error));
|
|
5641
|
+
}
|
|
5642
|
+
if (resolvedError) {
|
|
5643
|
+
onEvent({ type: "error", error: resolvedError });
|
|
5644
|
+
const finalMsg = assistantMessage;
|
|
5645
|
+
if (finalMsg && finalMsg.streaming) {
|
|
5646
|
+
finalMsg.streaming = false;
|
|
5647
|
+
emitMessage(finalMsg);
|
|
5648
|
+
}
|
|
5649
|
+
onEvent({ type: "status", status: "idle" });
|
|
5650
|
+
}
|
|
5628
5651
|
}
|
|
5629
5652
|
}
|
|
5630
5653
|
}
|
package/dist/theme-editor.js
CHANGED
|
@@ -4220,7 +4220,7 @@ var AgentWidgetClient = class {
|
|
|
4220
4220
|
}
|
|
4221
4221
|
}
|
|
4222
4222
|
async streamResponse(body, onEvent, assistantMessageId) {
|
|
4223
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z, __, _$, _aa, _ba, _ca, _da, _ea, _fa, _ga, _ha, _ia, _ja, _ka, _la, _ma, _na, _oa, _pa, _qa, _ra, _sa, _ta, _ua, _va, _wa, _xa, _ya, _za, _Aa, _Ba, _Ca, _Da, _Ea, _Fa, _Ga, _Ha, _Ia, _Ja, _Ka, _La, _Ma, _Na, _Oa, _Pa, _Qa, _Ra, _Sa, _Ta, _Ua, _Va, _Wa, _Xa, _Ya, _Za, __a, _$a, _ab, _bb, _cb, _db, _eb, _fb, _gb, _hb, _ib, _jb, _kb, _lb;
|
|
4223
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z, __, _$, _aa, _ba, _ca, _da, _ea, _fa, _ga, _ha, _ia, _ja, _ka, _la, _ma, _na, _oa, _pa, _qa, _ra, _sa, _ta, _ua, _va, _wa, _xa, _ya, _za, _Aa, _Ba, _Ca, _Da, _Ea, _Fa, _Ga, _Ha, _Ia, _Ja, _Ka, _La, _Ma, _Na, _Oa, _Pa, _Qa, _Ra, _Sa, _Ta, _Ua, _Va, _Wa, _Xa, _Ya, _Za, __a, _$a, _ab, _bb, _cb, _db, _eb, _fb, _gb, _hb, _ib, _jb, _kb, _lb, _mb, _nb;
|
|
4224
4224
|
const reader = body.getReader();
|
|
4225
4225
|
const decoder = new TextDecoder();
|
|
4226
4226
|
let buffer = "";
|
|
@@ -5511,11 +5511,34 @@ var AgentWidgetClient = class {
|
|
|
5511
5511
|
streamParsers.delete(id);
|
|
5512
5512
|
rawContentBuffers.delete(id);
|
|
5513
5513
|
seqChunkBuffers.delete(id);
|
|
5514
|
-
} else if (payloadType === "error"
|
|
5515
|
-
|
|
5516
|
-
|
|
5517
|
-
|
|
5518
|
-
})
|
|
5514
|
+
} else if (payloadType === "error" || payloadType === "step_error" || payloadType === "dispatch_error" || payloadType === "flow_error") {
|
|
5515
|
+
let resolvedError = null;
|
|
5516
|
+
if (payload.error instanceof Error) {
|
|
5517
|
+
resolvedError = payload.error;
|
|
5518
|
+
} else if (payloadType === "dispatch_error") {
|
|
5519
|
+
const msg = (_mb = payload.message) != null ? _mb : payload.error;
|
|
5520
|
+
if (msg != null && msg !== "") {
|
|
5521
|
+
resolvedError = new Error(String(msg));
|
|
5522
|
+
}
|
|
5523
|
+
} else if (payloadType === "step_error" || payloadType === "flow_error") {
|
|
5524
|
+
const e = payload.error;
|
|
5525
|
+
if (typeof e === "string" && e !== "") {
|
|
5526
|
+
resolvedError = new Error(e);
|
|
5527
|
+
} else if (e != null && typeof e === "object" && "message" in e) {
|
|
5528
|
+
resolvedError = new Error(String((_nb = e.message) != null ? _nb : e));
|
|
5529
|
+
}
|
|
5530
|
+
} else if (payloadType === "error" && payload.error != null && payload.error !== "") {
|
|
5531
|
+
resolvedError = new Error(String(payload.error));
|
|
5532
|
+
}
|
|
5533
|
+
if (resolvedError) {
|
|
5534
|
+
onEvent({ type: "error", error: resolvedError });
|
|
5535
|
+
const finalMsg = assistantMessage;
|
|
5536
|
+
if (finalMsg && finalMsg.streaming) {
|
|
5537
|
+
finalMsg.streaming = false;
|
|
5538
|
+
emitMessage(finalMsg);
|
|
5539
|
+
}
|
|
5540
|
+
onEvent({ type: "status", status: "idle" });
|
|
5541
|
+
}
|
|
5519
5542
|
}
|
|
5520
5543
|
}
|
|
5521
5544
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@runtypelabs/persona",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.14.0",
|
|
4
4
|
"description": "Themeable, pluggable streaming agent widget for websites, in plain JS with support for voice input and reasoning / tool output.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
package/src/client.test.ts
CHANGED
|
@@ -1121,6 +1121,70 @@ describe('AgentWidgetClient - Agent Event Streaming', () => {
|
|
|
1121
1121
|
}
|
|
1122
1122
|
});
|
|
1123
1123
|
|
|
1124
|
+
it('should emit error and finalize streaming on step_error', async () => {
|
|
1125
|
+
const events: AgentWidgetEvent[] = [];
|
|
1126
|
+
|
|
1127
|
+
global.fetch = createAgentStreamFetch([
|
|
1128
|
+
'data: {"type":"flow_start","flowId":"f1","flowName":"Test","totalSteps":1}\n\n',
|
|
1129
|
+
'data: {"type":"step_delta","id":"s1","name":"Prompt","executionType":"prompt","text":"partial"}\n\n',
|
|
1130
|
+
sseEvent('step_error', { error: 'step blew up', seq: 3 }),
|
|
1131
|
+
]);
|
|
1132
|
+
|
|
1133
|
+
const client = new AgentWidgetClient({
|
|
1134
|
+
apiUrl: 'http://localhost:8000',
|
|
1135
|
+
agent: { name: 'Test', model: 'openai:gpt-4o-mini', systemPrompt: 'test' },
|
|
1136
|
+
});
|
|
1137
|
+
|
|
1138
|
+
await client.dispatch(
|
|
1139
|
+
{ messages: [{ id: 'usr_1', role: 'user', content: 'Hi', createdAt: new Date().toISOString() }] },
|
|
1140
|
+
(event) => events.push(event)
|
|
1141
|
+
);
|
|
1142
|
+
|
|
1143
|
+
const errorEvents = events.filter(e => e.type === 'error');
|
|
1144
|
+
expect(errorEvents.length).toBe(1);
|
|
1145
|
+
if (errorEvents[0].type === 'error') {
|
|
1146
|
+
expect(errorEvents[0].error.message).toBe('step blew up');
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
const statusIdle = events.filter(e => e.type === 'status' && e.status === 'idle');
|
|
1150
|
+
expect(statusIdle.length).toBeGreaterThanOrEqual(1);
|
|
1151
|
+
|
|
1152
|
+
const messageEvents = events.filter(e => e.type === 'message');
|
|
1153
|
+
const lastAssistant = [...messageEvents]
|
|
1154
|
+
.reverse()
|
|
1155
|
+
.find(e => e.type === 'message' && e.message.role === 'assistant' && !e.message.variant);
|
|
1156
|
+
expect(lastAssistant?.type === 'message' && lastAssistant.message.streaming).toBe(false);
|
|
1157
|
+
});
|
|
1158
|
+
|
|
1159
|
+
it('should emit error and finalize streaming on dispatch_error (message only)', async () => {
|
|
1160
|
+
const events: AgentWidgetEvent[] = [];
|
|
1161
|
+
|
|
1162
|
+
global.fetch = createAgentStreamFetch([
|
|
1163
|
+
'data: {"type":"flow_start","flowId":"f1","flowName":"Test","totalSteps":1}\n\n',
|
|
1164
|
+
'data: {"type":"step_delta","id":"s1","name":"Prompt","executionType":"prompt","text":"x"}\n\n',
|
|
1165
|
+
sseEvent('dispatch_error', { message: 'bad config', seq: 2 }),
|
|
1166
|
+
]);
|
|
1167
|
+
|
|
1168
|
+
const client = new AgentWidgetClient({
|
|
1169
|
+
apiUrl: 'http://localhost:8000',
|
|
1170
|
+
agent: { name: 'Test', model: 'openai:gpt-4o-mini', systemPrompt: 'test' },
|
|
1171
|
+
});
|
|
1172
|
+
|
|
1173
|
+
await client.dispatch(
|
|
1174
|
+
{ messages: [{ id: 'usr_1', role: 'user', content: 'Hi', createdAt: new Date().toISOString() }] },
|
|
1175
|
+
(event) => events.push(event)
|
|
1176
|
+
);
|
|
1177
|
+
|
|
1178
|
+
const errorEvents = events.filter(e => e.type === 'error');
|
|
1179
|
+
expect(errorEvents.length).toBe(1);
|
|
1180
|
+
if (errorEvents[0].type === 'error') {
|
|
1181
|
+
expect(errorEvents[0].error.message).toBe('bad config');
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
const statusIdle = events.filter(e => e.type === 'status' && e.status === 'idle');
|
|
1185
|
+
expect(statusIdle.length).toBeGreaterThanOrEqual(1);
|
|
1186
|
+
});
|
|
1187
|
+
|
|
1124
1188
|
it('should handle agent reflection events', async () => {
|
|
1125
1189
|
const events: AgentWidgetEvent[] = [];
|
|
1126
1190
|
const execId = 'exec_test_7';
|
package/src/client.ts
CHANGED
|
@@ -2592,14 +2592,43 @@ export class AgentWidgetClient {
|
|
|
2592
2592
|
streamParsers.delete(id);
|
|
2593
2593
|
rawContentBuffers.delete(id);
|
|
2594
2594
|
seqChunkBuffers.delete(id);
|
|
2595
|
-
} else if (
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2595
|
+
} else if (
|
|
2596
|
+
payloadType === "error" ||
|
|
2597
|
+
payloadType === "step_error" ||
|
|
2598
|
+
payloadType === "dispatch_error" ||
|
|
2599
|
+
payloadType === "flow_error"
|
|
2600
|
+
) {
|
|
2601
|
+
let resolvedError: Error | null = null;
|
|
2602
|
+
if (payload.error instanceof Error) {
|
|
2603
|
+
resolvedError = payload.error;
|
|
2604
|
+
} else if (payloadType === "dispatch_error") {
|
|
2605
|
+
const msg = payload.message ?? payload.error;
|
|
2606
|
+
if (msg != null && msg !== "") {
|
|
2607
|
+
resolvedError = new Error(String(msg));
|
|
2608
|
+
}
|
|
2609
|
+
} else if (
|
|
2610
|
+
payloadType === "step_error" ||
|
|
2611
|
+
payloadType === "flow_error"
|
|
2612
|
+
) {
|
|
2613
|
+
const e = payload.error;
|
|
2614
|
+
if (typeof e === "string" && e !== "") {
|
|
2615
|
+
resolvedError = new Error(e);
|
|
2616
|
+
} else if (e != null && typeof e === "object" && "message" in e) {
|
|
2617
|
+
resolvedError = new Error(String((e as { message?: unknown }).message ?? e));
|
|
2618
|
+
}
|
|
2619
|
+
} else if (payloadType === "error" && payload.error != null && payload.error !== "") {
|
|
2620
|
+
resolvedError = new Error(String(payload.error));
|
|
2621
|
+
}
|
|
2622
|
+
|
|
2623
|
+
if (resolvedError) {
|
|
2624
|
+
onEvent({ type: "error", error: resolvedError });
|
|
2625
|
+
const finalMsg = assistantMessage as AgentWidgetMessage | null;
|
|
2626
|
+
if (finalMsg && finalMsg.streaming) {
|
|
2627
|
+
finalMsg.streaming = false;
|
|
2628
|
+
emitMessage(finalMsg);
|
|
2629
|
+
}
|
|
2630
|
+
onEvent({ type: "status", status: "idle" });
|
|
2631
|
+
}
|
|
2603
2632
|
}
|
|
2604
2633
|
}
|
|
2605
2634
|
}
|
package/src/install.ts
CHANGED
|
@@ -22,6 +22,10 @@ interface SiteAgentInstallConfig {
|
|
|
22
22
|
previewQueryParam?: string;
|
|
23
23
|
// Shadow DOM option (defaults to false for better CSS compatibility)
|
|
24
24
|
useShadowDom?: boolean;
|
|
25
|
+
// Expose the widget handle on window[windowKey] for programmatic access
|
|
26
|
+
windowKey?: string;
|
|
27
|
+
// Called when the widget is initialized and ready for interaction
|
|
28
|
+
onReady?: (handle: any) => void;
|
|
25
29
|
}
|
|
26
30
|
|
|
27
31
|
declare global {
|
|
@@ -273,12 +277,16 @@ declare global {
|
|
|
273
277
|
}
|
|
274
278
|
|
|
275
279
|
try {
|
|
276
|
-
window.AgentWidget.initAgentWidget({
|
|
280
|
+
const handle = window.AgentWidget.initAgentWidget({
|
|
277
281
|
target,
|
|
278
282
|
config: widgetConfig,
|
|
279
283
|
// Explicitly disable shadow DOM for better CSS compatibility with host page
|
|
280
|
-
useShadowDom: config.useShadowDom ?? false
|
|
284
|
+
useShadowDom: config.useShadowDom ?? false,
|
|
285
|
+
windowKey: config.windowKey
|
|
281
286
|
});
|
|
287
|
+
|
|
288
|
+
config.onReady?.(handle);
|
|
289
|
+
window.dispatchEvent(new CustomEvent("persona:ready", { detail: handle }));
|
|
282
290
|
} catch (error) {
|
|
283
291
|
console.error("Failed to initialize AgentWidget:", error);
|
|
284
292
|
}
|
package/src/runtime/init.test.ts
CHANGED
|
@@ -85,6 +85,138 @@ function createMockController(config?: { launcher?: { enabled?: boolean; autoExp
|
|
|
85
85
|
};
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
describe("initAgentWidget windowKey and ready notifications", () => {
|
|
89
|
+
beforeEach(() => {
|
|
90
|
+
document.body.innerHTML = "";
|
|
91
|
+
createAgentExperienceMock.mockReset();
|
|
92
|
+
createAgentExperienceMock.mockImplementation((_mount, config) => createMockController(config));
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("assigns the handle to window[windowKey] when windowKey is provided", async () => {
|
|
96
|
+
const { initAgentWidget } = await import("./init");
|
|
97
|
+
document.body.innerHTML = `<div id="target"></div>`;
|
|
98
|
+
|
|
99
|
+
const handle = initAgentWidget({
|
|
100
|
+
target: "#target",
|
|
101
|
+
windowKey: "testWidget",
|
|
102
|
+
config: { launcher: { enabled: false } },
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
expect((window as any).testWidget).toBe(handle);
|
|
106
|
+
|
|
107
|
+
handle.destroy();
|
|
108
|
+
expect((window as any).testWidget).toBeUndefined();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("does not set a window key when windowKey is omitted", async () => {
|
|
112
|
+
const { initAgentWidget } = await import("./init");
|
|
113
|
+
document.body.innerHTML = `<div id="target"></div>`;
|
|
114
|
+
|
|
115
|
+
const handle = initAgentWidget({
|
|
116
|
+
target: "#target",
|
|
117
|
+
config: { launcher: { enabled: false } },
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// No arbitrary key should have been set
|
|
121
|
+
expect((window as any).testWidget2).toBeUndefined();
|
|
122
|
+
handle.destroy();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("calls onReady after initialization", async () => {
|
|
126
|
+
const { initAgentWidget } = await import("./init");
|
|
127
|
+
document.body.innerHTML = `<div id="target"></div>`;
|
|
128
|
+
|
|
129
|
+
const onReady = vi.fn();
|
|
130
|
+
const handle = initAgentWidget({
|
|
131
|
+
target: "#target",
|
|
132
|
+
onReady,
|
|
133
|
+
config: { launcher: { enabled: false } },
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
expect(onReady).toHaveBeenCalledOnce();
|
|
137
|
+
handle.destroy();
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("the window key handle proxies controller methods", async () => {
|
|
141
|
+
const { initAgentWidget } = await import("./init");
|
|
142
|
+
document.body.innerHTML = `<div id="target"></div>`;
|
|
143
|
+
|
|
144
|
+
initAgentWidget({
|
|
145
|
+
target: "#target",
|
|
146
|
+
windowKey: "proxyTest",
|
|
147
|
+
config: { launcher: { enabled: false } },
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const proxy = (window as any).proxyTest;
|
|
151
|
+
expect(proxy).toBeDefined();
|
|
152
|
+
expect(typeof proxy.open).toBe("function");
|
|
153
|
+
expect(typeof proxy.close).toBe("function");
|
|
154
|
+
expect(typeof proxy.on).toBe("function");
|
|
155
|
+
expect(typeof proxy.destroy).toBe("function");
|
|
156
|
+
expect(typeof proxy.getState).toBe("function");
|
|
157
|
+
|
|
158
|
+
proxy.destroy();
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
describe("install script onReady and persona:ready event", () => {
|
|
163
|
+
beforeEach(() => {
|
|
164
|
+
document.body.innerHTML = "";
|
|
165
|
+
createAgentExperienceMock.mockReset();
|
|
166
|
+
createAgentExperienceMock.mockImplementation((_mount, config) => createMockController(config));
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it("persona:ready event fires with the handle as detail", async () => {
|
|
170
|
+
const { initAgentWidget } = await import("./init");
|
|
171
|
+
document.body.innerHTML = `<div id="target"></div>`;
|
|
172
|
+
|
|
173
|
+
const eventPromise = new Promise<any>((resolve) => {
|
|
174
|
+
window.addEventListener("persona:ready", (e) => {
|
|
175
|
+
resolve((e as CustomEvent).detail);
|
|
176
|
+
}, { once: true });
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
const handle = initAgentWidget({
|
|
180
|
+
target: "#target",
|
|
181
|
+
windowKey: "eventTest",
|
|
182
|
+
config: { launcher: { enabled: false } },
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Simulate what install.ts does after initAgentWidget returns
|
|
186
|
+
window.dispatchEvent(new CustomEvent("persona:ready", { detail: handle }));
|
|
187
|
+
|
|
188
|
+
const detail = await eventPromise;
|
|
189
|
+
expect(detail).toBe(handle);
|
|
190
|
+
expect(typeof detail.open).toBe("function");
|
|
191
|
+
expect(typeof detail.on).toBe("function");
|
|
192
|
+
|
|
193
|
+
handle.destroy();
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it("persona:ready event listener set up before init receives the handle", async () => {
|
|
197
|
+
const { initAgentWidget } = await import("./init");
|
|
198
|
+
document.body.innerHTML = `<div id="target"></div>`;
|
|
199
|
+
|
|
200
|
+
const received: any[] = [];
|
|
201
|
+
window.addEventListener("persona:ready", (e) => {
|
|
202
|
+
received.push((e as CustomEvent).detail);
|
|
203
|
+
}, { once: true });
|
|
204
|
+
|
|
205
|
+
const handle = initAgentWidget({
|
|
206
|
+
target: "#target",
|
|
207
|
+
config: { launcher: { enabled: false } },
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// Simulate install.ts dispatching the event
|
|
211
|
+
window.dispatchEvent(new CustomEvent("persona:ready", { detail: handle }));
|
|
212
|
+
|
|
213
|
+
expect(received).toHaveLength(1);
|
|
214
|
+
expect(received[0]).toBe(handle);
|
|
215
|
+
|
|
216
|
+
handle.destroy();
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
|
|
88
220
|
describe("initAgentWidget docked mode", () => {
|
|
89
221
|
beforeEach(() => {
|
|
90
222
|
document.body.innerHTML = "";
|
|
@@ -620,3 +620,60 @@ describe("CDN Version", () => {
|
|
|
620
620
|
expect(VERSION).toMatch(/^\d+\.\d+\.\d+/);
|
|
621
621
|
});
|
|
622
622
|
});
|
|
623
|
+
|
|
624
|
+
// =============================================================================
|
|
625
|
+
// windowKey option
|
|
626
|
+
// =============================================================================
|
|
627
|
+
|
|
628
|
+
describe("windowKey option", () => {
|
|
629
|
+
it("script-installer with windowKey nests config and includes windowKey in JSON", () => {
|
|
630
|
+
const code = generateCodeSnippet(minimalConfig, "script-installer", { windowKey: "myWidget" });
|
|
631
|
+
|
|
632
|
+
// Parse the data-config JSON from the output
|
|
633
|
+
const match = code.match(/data-config='([^']*)'/);
|
|
634
|
+
expect(match).not.toBeNull();
|
|
635
|
+
const parsed = JSON.parse(match![1]);
|
|
636
|
+
|
|
637
|
+
expect(parsed.windowKey).toBe("myWidget");
|
|
638
|
+
expect(parsed.config).toBeDefined();
|
|
639
|
+
expect(parsed.config.apiUrl).toBe(minimalConfig.apiUrl);
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
it("script-installer without windowKey uses flat config (no nesting)", () => {
|
|
643
|
+
const code = generateCodeSnippet(minimalConfig, "script-installer");
|
|
644
|
+
|
|
645
|
+
const match = code.match(/data-config='([^']*)'/);
|
|
646
|
+
expect(match).not.toBeNull();
|
|
647
|
+
const parsed = JSON.parse(match![1]);
|
|
648
|
+
|
|
649
|
+
expect(parsed.windowKey).toBeUndefined();
|
|
650
|
+
expect(parsed.config).toBeUndefined();
|
|
651
|
+
expect(parsed.apiUrl).toBe(minimalConfig.apiUrl);
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
it("script-manual with windowKey includes windowKey and captures handle", () => {
|
|
655
|
+
const code = generateCodeSnippet(minimalConfig, "script-manual", { windowKey: "myWidget" });
|
|
656
|
+
|
|
657
|
+
expect(code).toContain("var handle = window.AgentWidget.initAgentWidget(");
|
|
658
|
+
expect(code).toContain("windowKey: 'myWidget'");
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
it("script-manual without windowKey still captures handle but omits windowKey", () => {
|
|
662
|
+
const code = generateCodeSnippet(minimalConfig, "script-manual");
|
|
663
|
+
|
|
664
|
+
expect(code).toContain("var handle = window.AgentWidget.initAgentWidget(");
|
|
665
|
+
expect(code).not.toContain("windowKey");
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
it("script-advanced with windowKey includes windowKey in initAgentWidget call", () => {
|
|
669
|
+
const code = generateCodeSnippet(minimalConfig, "script-advanced", { windowKey: "myWidget" });
|
|
670
|
+
|
|
671
|
+
expect(code).toContain("windowKey: 'myWidget'");
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
it("script-advanced without windowKey omits windowKey", () => {
|
|
675
|
+
const code = generateCodeSnippet(minimalConfig, "script-advanced");
|
|
676
|
+
|
|
677
|
+
expect(code).not.toContain("windowKey");
|
|
678
|
+
});
|
|
679
|
+
});
|
|
@@ -134,6 +134,13 @@ export type CodeGeneratorOptions = {
|
|
|
134
134
|
* @default true
|
|
135
135
|
*/
|
|
136
136
|
includeHookComments?: boolean;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* If provided, emits `windowKey` in the generated `initAgentWidget()` call
|
|
140
|
+
* so the widget handle is stored on `window[windowKey]`.
|
|
141
|
+
* Only affects script formats (script-installer, script-manual, script-advanced).
|
|
142
|
+
*/
|
|
143
|
+
windowKey?: string;
|
|
137
144
|
};
|
|
138
145
|
|
|
139
146
|
// Internal type for normalized hooks (always strings)
|
|
@@ -551,7 +558,7 @@ export function generateCodeSnippet(
|
|
|
551
558
|
if (format === "esm") {
|
|
552
559
|
return generateESMCode(cleanConfig, normalizedOptions);
|
|
553
560
|
} else if (format === "script-installer") {
|
|
554
|
-
return generateScriptInstallerCode(cleanConfig);
|
|
561
|
+
return generateScriptInstallerCode(cleanConfig, normalizedOptions);
|
|
555
562
|
} else if (format === "script-advanced") {
|
|
556
563
|
return generateScriptAdvancedCode(cleanConfig, normalizedOptions);
|
|
557
564
|
} else if (format === "react-component") {
|
|
@@ -1346,12 +1353,18 @@ function buildSerializableConfig(config: any): Record<string, any> {
|
|
|
1346
1353
|
return serializableConfig;
|
|
1347
1354
|
}
|
|
1348
1355
|
|
|
1349
|
-
function generateScriptInstallerCode(config: any): string {
|
|
1356
|
+
function generateScriptInstallerCode(config: any, options?: CodeGeneratorOptions): string {
|
|
1350
1357
|
const serializableConfig = buildSerializableConfig(config);
|
|
1351
|
-
|
|
1358
|
+
|
|
1359
|
+
// When windowKey is provided, nest the widget config under `config` so the
|
|
1360
|
+
// install script's parsedConfig.config detection picks it up alongside windowKey.
|
|
1361
|
+
const payload = options?.windowKey
|
|
1362
|
+
? { config: serializableConfig, windowKey: options.windowKey }
|
|
1363
|
+
: serializableConfig;
|
|
1364
|
+
|
|
1352
1365
|
// Escape single quotes in JSON for HTML attribute
|
|
1353
|
-
const configJson = JSON.stringify(
|
|
1354
|
-
|
|
1366
|
+
const configJson = JSON.stringify(payload, null, 0).replace(/'/g, "'");
|
|
1367
|
+
|
|
1355
1368
|
return `<script src="https://cdn.jsdelivr.net/npm/@runtypelabs/persona@${VERSION}/dist/install.global.js" data-config='${configJson}'></script>`;
|
|
1356
1369
|
}
|
|
1357
1370
|
|
|
@@ -1369,8 +1382,9 @@ function generateScriptManualCode(config: any, options?: CodeGeneratorOptions):
|
|
|
1369
1382
|
"",
|
|
1370
1383
|
"<!-- Initialize widget -->",
|
|
1371
1384
|
"<script>",
|
|
1372
|
-
" window.AgentWidget.initAgentWidget({",
|
|
1385
|
+
" var handle = window.AgentWidget.initAgentWidget({",
|
|
1373
1386
|
" target: 'body',",
|
|
1387
|
+
...(options?.windowKey ? [` windowKey: '${options.windowKey}',`] : []),
|
|
1374
1388
|
" config: {"
|
|
1375
1389
|
];
|
|
1376
1390
|
|
|
@@ -1723,6 +1737,7 @@ function generateScriptAdvancedCode(config: any, options?: CodeGeneratorOptions)
|
|
|
1723
1737
|
" var handle = agentWidget.initAgentWidget({",
|
|
1724
1738
|
" target: 'body',",
|
|
1725
1739
|
" useShadowDom: false,",
|
|
1740
|
+
...(options?.windowKey ? [` windowKey: '${options.windowKey}',`] : []),
|
|
1726
1741
|
" config: widgetConfig",
|
|
1727
1742
|
" });",
|
|
1728
1743
|
"",
|