@mcp-b/char 0.1.3 → 0.1.4
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 +11 -13
- package/dist/custom-elements.json +491 -141
- package/dist/display-mode-policy.d.ts.map +1 -1
- package/dist/index.d.ts +89 -114
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +699 -281
- package/dist/index.js.map +1 -1
- package/dist/shell-component.d.ts +79 -86
- package/dist/shell-component.d.ts.map +1 -1
- package/dist/shell-component.js +689 -281
- package/dist/shell-component.js.map +1 -1
- package/dist/shell-standalone.iife.js +689 -281
- package/dist/shell-standalone.iife.js.map +1 -1
- package/dist/utils.d.ts +0 -13
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +1 -2
- package/dist/utils.js.map +1 -1
- package/dist/web-component-standalone.iife.js +25 -89
- package/dist/web-component-standalone.iife.js.map +1 -1
- package/dist/web-component.d.ts +20 -83
- package/dist/web-component.d.ts.map +1 -1
- package/dist/web-component.js +25 -89
- package/dist/web-component.js.map +1 -1
- package/package.json +3 -3
package/dist/utils.d.ts
CHANGED
|
@@ -121,17 +121,6 @@ interface CharSafeAreaInsets {
|
|
|
121
121
|
bottom?: number;
|
|
122
122
|
left?: number;
|
|
123
123
|
}
|
|
124
|
-
/**
|
|
125
|
-
* Host shell capability flags advertised to the iframe runtime.
|
|
126
|
-
*
|
|
127
|
-
* @public
|
|
128
|
-
*/
|
|
129
|
-
interface CharHostCapabilities {
|
|
130
|
-
supportedDisplayModes?: CharDisplayMode[];
|
|
131
|
-
supportsOpenLink?: boolean;
|
|
132
|
-
supportsTeardown?: boolean;
|
|
133
|
-
maxContainerWidth?: number;
|
|
134
|
-
}
|
|
135
124
|
/**
|
|
136
125
|
* Host context payload sent from the embedding page to the iframe runtime.
|
|
137
126
|
*
|
|
@@ -148,8 +137,6 @@ interface CharHostContext {
|
|
|
148
137
|
platform?: CharPlatform;
|
|
149
138
|
deviceCapabilities?: CharDeviceCapabilities;
|
|
150
139
|
safeAreaInsets?: CharSafeAreaInsets;
|
|
151
|
-
userAgent?: string;
|
|
152
|
-
hostCapabilities?: CharHostCapabilities;
|
|
153
140
|
}
|
|
154
141
|
//#endregion
|
|
155
142
|
//#region src/utils/host-context-merge.d.ts
|
package/dist/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","names":[],"sources":["../src/utils/css-resolver.ts","../src/utils/css-sanitizer.ts","../src/utils/css-variables.ts","../src/types.ts","../src/utils/host-context-merge.ts"],"mappings":";;;AAUA;;;;;AAEA;;;cAFa,yBAAA;AAAA,iBAEG,iBAAA,CAAkB,KAAA,UAAe,cAAA;AAAA,iBAWjC,kBAAA,CAAmB,KAAA;AAAA,iBAWnB,eAAA,CACf,QAAA,sBACA,QAAA,EAAU,mBAAA,EACV,IAAA,EAAM,GAAA,UACN,KAAA;AAAA,iBAMe,kBAAA,CACf,YAAA,UACA,QAAA,sBACA,QAAA,EAAU,mBAAA,EACV,IAAA,EAAM,GAAA,UACN,KAAA;AAAA,iBAuBe,eAAA,CACf,KAAA,UACA,QAAA,EAAU,mBAAA,EACV,IAAA,EAAM,GAAA,UACN,KAAA;;;;AAlED;;;;;AAEA;;;;;AAWA;;;cCTa,6BAAA;AAAA,cACA,wBAAA;ADmBb;;;;;;;AAAA,iBCegB,wBAAA,CAAyB,KAAA,UAAe,YAAA;;;;;iBAuBxC,mBAAA,CAAoB,OAAA;;;;AD9DpC;;;;;AAEA;;;;;cE6Ma,uBAAA;;;;
|
|
1
|
+
{"version":3,"file":"utils.d.ts","names":[],"sources":["../src/utils/css-resolver.ts","../src/utils/css-sanitizer.ts","../src/utils/css-variables.ts","../src/types.ts","../src/utils/host-context-merge.ts"],"mappings":";;;AAUA;;;;;AAEA;;;cAFa,yBAAA;AAAA,iBAEG,iBAAA,CAAkB,KAAA,UAAe,cAAA;AAAA,iBAWjC,kBAAA,CAAmB,KAAA;AAAA,iBAWnB,eAAA,CACf,QAAA,sBACA,QAAA,EAAU,mBAAA,EACV,IAAA,EAAM,GAAA,UACN,KAAA;AAAA,iBAMe,kBAAA,CACf,YAAA,UACA,QAAA,sBACA,QAAA,EAAU,mBAAA,EACV,IAAA,EAAM,GAAA,UACN,KAAA;AAAA,iBAuBe,eAAA,CACf,KAAA,UACA,QAAA,EAAU,mBAAA,EACV,IAAA,EAAM,GAAA,UACN,KAAA;;;;AAlED;;;;;AAEA;;;;;AAWA;;;cCTa,6BAAA;AAAA,cACA,wBAAA;ADmBb;;;;;;;AAAA,iBCegB,wBAAA,CAAyB,KAAA,UAAe,YAAA;;;;;iBAuBxC,mBAAA,CAAoB,OAAA;;;;AD9DpC;;;;;AAEA;;;;;cE6Ma,uBAAA;;;;AFvLb;;;;KGbY,SAAA;;;;;;KAOA,eAAA;;AHgBZ;;;;KGTY,YAAA;;;;;;UAOK,cAAA;EAChB,SAAA,GAAY,MAAA;EACZ,KAAA;AAAA;;;;;;UAQgB,uBAAA;EAChB,KAAA;EACA,MAAA;EACA,QAAA;EACA,SAAA;AAAA;;;;AF1CD;;UEkDiB,sBAAA;EAChB,KAAA;EACA,KAAA;EACA,WAAA;AAAA;;;;AFlBD;;UE0BiB,kBAAA;EAChB,GAAA;EACA,KAAA;EACA,MAAA;EACA,IAAA;AAAA;;;;;;UAQgB,eAAA;EAChB,KAAA,GAAQ,SAAA;EACR,MAAA,GAAS,cAAA;EACT,WAAA,GAAc,eAAA;EACd,qBAAA,GAAwB,eAAA;EACxB,mBAAA,GAAsB,uBAAA;EACtB,MAAA;EACA,QAAA;EACA,QAAA,GAAW,YAAA;EACX,kBAAA,GAAqB,sBAAA;EACrB,cAAA,GAAiB,kBAAA;AAAA;;;iBCvFF,WAAA,CACf,OAAA,EAAS,eAAA,wBACT,KAAA,EAAO,eAAA,yBACL,eAAA;AAAA,iBAUa,YAAA,kBAAA,CACf,OAAA,EAAS,CAAA,cACT,KAAA,EAAO,CAAA,eACL,CAAA;AAAA,iBAKa,gBAAA,CACf,OAAA,EAAS,eAAA,SACT,KAAA,EAAO,eAAA,GACL,eAAA"}
|
package/dist/utils.js
CHANGED
|
@@ -383,8 +383,7 @@ function mergeHostContext(current, patch) {
|
|
|
383
383
|
styles: mergeStyles(previous.styles, patch.styles),
|
|
384
384
|
containerDimensions: shallowMerge(previous.containerDimensions, patch.containerDimensions),
|
|
385
385
|
deviceCapabilities: shallowMerge(previous.deviceCapabilities, patch.deviceCapabilities),
|
|
386
|
-
safeAreaInsets: shallowMerge(previous.safeAreaInsets, patch.safeAreaInsets)
|
|
387
|
-
hostCapabilities: shallowMerge(previous.hostCapabilities, patch.hostCapabilities)
|
|
386
|
+
safeAreaInsets: shallowMerge(previous.safeAreaInsets, patch.safeAreaInsets)
|
|
388
387
|
};
|
|
389
388
|
}
|
|
390
389
|
|
package/dist/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","names":[],"sources":["../src/utils/css-resolver.ts","../src/utils/css-sanitizer.ts","../src/utils/css-variables.ts","../src/utils/host-context-merge.ts"],"sourcesContent":["/**\n * CSS var() Resolver\n *\n * Recursively resolves CSS custom property references (`var(--x, fallback)`)\n * to concrete values using getComputedStyle(). Necessary because `var()`\n * references are not valid across iframe boundaries.\n *\n * Handles nested var() references, fallbacks, and cycle detection.\n */\n\nexport const MAX_CSS_VAR_RESOLVE_DEPTH = 12\n\nexport function findMatchingParen(value: string, openParenIndex: number): number {\n\tlet depth = 1\n\tfor (let i = openParenIndex + 1; i < value.length; i++) {\n\t\tconst char = value[i]\n\t\tif (char === '(') depth += 1\n\t\telse if (char === ')') depth -= 1\n\t\tif (depth === 0) return i\n\t}\n\treturn -1\n}\n\nexport function splitTopLevelComma(value: string): [string, string | undefined] {\n\tlet depth = 0\n\tfor (let i = 0; i < value.length; i++) {\n\t\tconst char = value[i]\n\t\tif (char === '(') depth += 1\n\t\telse if (char === ')') depth = Math.max(0, depth - 1)\n\t\telse if (char === ',' && depth === 0) return [value.slice(0, i), value.slice(i + 1)]\n\t}\n\treturn [value, undefined]\n}\n\nexport function resolveFallback(\n\tfallback: string | undefined,\n\tcomputed: CSSStyleDeclaration,\n\tseen: Set<string>,\n\tdepth: number,\n): string | undefined {\n\tif (!fallback) return undefined\n\treturn resolveCssValue(fallback, computed, seen, depth + 1) || undefined\n}\n\nexport function resolveCssVariable(\n\tvariableName: string,\n\tfallback: string | undefined,\n\tcomputed: CSSStyleDeclaration,\n\tseen: Set<string>,\n\tdepth: number,\n): string | undefined {\n\tif (depth > MAX_CSS_VAR_RESOLVE_DEPTH) {\n\t\tconsole.warn(`[Char] CSS variable resolution depth exceeded for \"${variableName}\". Possible circular reference.`)\n\t\treturn fallback?.trim()\n\t}\n\n\tif (!variableName.startsWith('--') || seen.has(variableName)) {\n\t\treturn resolveFallback(fallback, computed, seen, depth)\n\t}\n\n\tconst nextSeen = new Set(seen)\n\tnextSeen.add(variableName)\n\n\tconst rawValue = computed.getPropertyValue(variableName).trim()\n\tif (rawValue) {\n\t\tconst resolvedRaw = resolveCssValue(rawValue, computed, nextSeen, depth + 1)\n\t\tif (resolvedRaw) return resolvedRaw\n\t}\n\n\treturn resolveFallback(fallback, computed, seen, depth)\n}\n\nexport function resolveCssValue(\n\tvalue: string,\n\tcomputed: CSSStyleDeclaration,\n\tseen: Set<string>,\n\tdepth = 0,\n): string {\n\tconst trimmed = value.trim()\n\tif (!trimmed || !trimmed.includes('var(') || depth > MAX_CSS_VAR_RESOLVE_DEPTH) {\n\t\treturn trimmed\n\t}\n\n\tlet cursor = 0\n\tlet resolved = ''\n\tlet replacedAny = false\n\n\twhile (cursor < trimmed.length) {\n\t\tconst varStart = trimmed.indexOf('var(', cursor)\n\t\tif (varStart === -1) {\n\t\t\tresolved += trimmed.slice(cursor)\n\t\t\tbreak\n\t\t}\n\n\t\tresolved += trimmed.slice(cursor, varStart)\n\n\t\tconst openParenIndex = varStart + 3\n\t\tconst closeParenIndex = findMatchingParen(trimmed, openParenIndex)\n\t\tif (closeParenIndex === -1) {\n\t\t\tresolved += trimmed.slice(varStart)\n\t\t\tbreak\n\t\t}\n\n\t\tconst rawArgs = trimmed.slice(openParenIndex + 1, closeParenIndex)\n\t\tconst [rawVariableName, rawFallback] = splitTopLevelComma(rawArgs)\n\t\tconst variableName = rawVariableName.trim()\n\t\tconst fallback = rawFallback?.trim()\n\n\t\tconst replacement = variableName\n\t\t\t? resolveCssVariable(variableName, fallback, computed, seen, depth + 1)\n\t\t\t: undefined\n\n\t\tif (replacement !== undefined) {\n\t\t\tresolved += replacement\n\t\t\treplacedAny = true\n\t\t} else {\n\t\t\tresolved += trimmed.slice(varStart, closeParenIndex + 1)\n\t\t}\n\n\t\tcursor = closeParenIndex + 1\n\t}\n\n\tconst normalized = resolved.trim()\n\tif (!replacedAny || !normalized.includes('var(')) {\n\t\treturn normalized\n\t}\n\n\treturn resolveCssValue(normalized, computed, seen, depth + 1)\n}\n","/**\n * Maximum allowed length for CSS custom property values transported to iframe.\n *\n * NOTE: This value and the blocked-token policy intentionally mirror\n * `packages/shared-types/src/schemas/embed-bridge.ts` so validation happens\n * both before transport (host) and at contract parse-time (iframe).\n *\n * CONTROL_CHAR_PATTERN here is intentionally more permissive than\n * embed-bridge.ts: it allows tabs/newlines/CR/FF because\n * normalizeCssFormattingWhitespace converts them to spaces before the\n * control-char check. The iframe-side schema (embed-bridge.ts) uses\n * the stricter pattern as defense-in-depth for values that bypass\n * host-side sanitization.\n */\nexport const MAX_CSS_VARIABLE_VALUE_LENGTH = 1024\nexport const MAX_HOST_FONT_CSS_LENGTH = 16_384\n\nconst CONTROL_CHAR_PATTERN = /[\\u0000-\\u0008\\u000b\\u000e-\\u001f\\u007f]/u\nconst FONT_RULES_PATTERN = /^(?:\\s*@font-face\\s*\\{[^{}]*\\}\\s*)+$/iu\nconst CSS_COMMENT_PATTERN = /\\/\\*[\\s\\S]*?\\*\\//g\nconst CSS_FORMATTING_WHITESPACE_PATTERN = /[\\t\\n\\r\\f]+/gu\n\n/**\n * Normalizes CSS values for dangerous-token scanning.\n * Lowercases and removes CSS comments/whitespace/control chars to catch obfuscation.\n */\nfunction normalizeForSecurityScan(value: string): string {\n\tconst withoutComments = value.replace(CSS_COMMENT_PATTERN, '')\n\treturn withoutComments.toLowerCase().replace(/[\\s\\u0000-\\u001f\\u007f]+/gu, '')\n}\n\n/**\n * Collapses CSS formatting whitespace (tabs, newlines, carriage returns, form feeds)\n * into single spaces. Applied to the output value before trimming, so multiline\n * host-provided values (e.g., font stacks) become single-line without losing\n * meaningful token boundaries. This also ensures split-token obfuscation\n * (e.g., \"java\\nscript:\") is collapsed before the security scan.\n */\nfunction normalizeCssFormattingWhitespace(value: string): string {\n\treturn value.replace(CSS_FORMATTING_WHITESPACE_PATTERN, ' ')\n}\n\n/**\n * Sanitizes host-provided CSS variable values before iframe transport.\n * Returns `undefined` for unsafe or invalid values.\n *\n * @param value - The raw CSS property value.\n * @param variableName - Optional variable name for diagnostic logging.\n */\nexport function sanitizeCssVariableValue(value: string, variableName?: string): string | undefined {\n\tconst trimmed = normalizeCssFormattingWhitespace(value).trim()\n\tif (!trimmed) return undefined\n\tif (trimmed.length > MAX_CSS_VARIABLE_VALUE_LENGTH) {\n\t\tconsole.warn(`[Char] CSS variable ${variableName ?? '(unknown)'} rejected: value exceeds max length (${trimmed.length} > ${MAX_CSS_VARIABLE_VALUE_LENGTH})`)\n\t\treturn undefined\n\t}\n\tif (CONTROL_CHAR_PATTERN.test(trimmed)) {\n\t\tconsole.warn(`[Char] CSS variable ${variableName ?? '(unknown)'} rejected: contains control characters`)\n\t\treturn undefined\n\t}\n\n\tconst normalized = normalizeForSecurityScan(trimmed)\n\tconst blockedToken = getBlockedCssVariableToken(normalized)\n\tif (blockedToken) return warnAndReject(variableName, blockedToken)\n\n\treturn trimmed\n}\n\n/**\n * Sanitizes host-provided font CSS before iframe injection.\n * Only `@font-face` rules are allowed.\n */\nexport function sanitizeHostFontCss(fontCss: string): string | undefined {\n\tconst trimmed = fontCss.trim()\n\tif (!trimmed) return undefined\n\tif (trimmed.length > MAX_HOST_FONT_CSS_LENGTH) {\n\t\tconsole.warn(`[Char] Host font CSS rejected: value exceeds max length (${trimmed.length} > ${MAX_HOST_FONT_CSS_LENGTH})`)\n\t\treturn undefined\n\t}\n\tif (CONTROL_CHAR_PATTERN.test(trimmed)) {\n\t\tconsole.warn('[Char] Host font CSS rejected: contains control characters')\n\t\treturn undefined\n\t}\n\n\tconst normalized = normalizeForSecurityScan(trimmed)\n\tconst blockedToken = getBlockedFontToken(normalized)\n\tif (blockedToken) {\n\t\tconsole.warn(`[Char] Host font CSS rejected: contains blocked token \"${blockedToken}\"`)\n\t\treturn undefined\n\t}\n\n\tconst withoutComments = trimmed.replace(/\\/\\*[\\s\\S]*?\\*\\//g, '').trim()\n\tif (!FONT_RULES_PATTERN.test(withoutComments)) {\n\t\tconsole.warn('[Char] Host font CSS rejected: only @font-face rules are allowed')\n\t\treturn undefined\n\t}\n\n\treturn trimmed\n}\n\nfunction getBlockedCssVariableToken(normalizedValue: string): string | undefined {\n\tif (normalizedValue.includes('javascript:')) return 'javascript:'\n\tif (normalizedValue.includes('vbscript:')) return 'vbscript:'\n\tif (normalizedValue.includes('expression(')) return 'expression('\n\tif (normalizedValue.includes('@import')) return '@import'\n\tif (normalizedValue.includes('-moz-binding')) return '-moz-binding'\n\tif (normalizedValue.includes('url(')) return 'url('\n\treturn undefined\n}\n\nfunction getBlockedFontToken(normalizedValue: string): string | undefined {\n\tif (normalizedValue.includes('javascript:')) return 'javascript:'\n\tif (normalizedValue.includes('vbscript:')) return 'vbscript:'\n\tif (normalizedValue.includes('expression(')) return 'expression('\n\tif (normalizedValue.includes('-moz-binding')) return '-moz-binding'\n\treturn undefined\n}\n\nfunction warnAndReject(variableName: string | undefined, blockedToken: string): undefined {\n\tconsole.warn(`[Char] CSS variable ${variableName ?? '(unknown)'} rejected: contains blocked token \"${blockedToken}\"`)\n\treturn undefined\n}\n","/**\n * Public CSS variable names for the Char widget.\n *\n * These are the external API — host pages set these to customise the widget.\n * We maintain static lists because `getComputedStyle()` cannot enumerate\n * custom properties.\n *\n * Source of truth:\n * - `src/styles/globals.css` (`--char-*`)\n * - WebMCP ext-apps UI spec (`--color-*`, `--font-*`, etc.)\n */\n\nconst CHAR_PUBLIC_VARIABLE_NAMES = [\n\t// Colors - Core\n\t'--char-color-background',\n\t'--char-color-foreground',\n\t'--char-color-card',\n\t'--char-color-card-foreground',\n\t'--char-color-popover',\n\t'--char-color-popover-foreground',\n\t'--char-color-primary',\n\t'--char-color-primary-foreground',\n\t'--char-color-secondary',\n\t'--char-color-secondary-foreground',\n\t'--char-color-muted',\n\t'--char-color-muted-foreground',\n\t'--char-color-accent',\n\t'--char-color-accent-foreground',\n\t'--char-color-destructive',\n\t'--char-color-destructive-foreground',\n\t'--char-color-border',\n\n\t// Colors - Semantic\n\t'--char-color-success',\n\t'--char-color-warning',\n\t'--char-color-error',\n\n\t// Colors - Derived\n\t'--char-color-input',\n\t'--char-color-ring',\n\n\t// Message Bubbles\n\t'--char-user-bubble-bg',\n\t'--char-user-bubble-text',\n\t'--char-assistant-bubble-bg',\n\t'--char-assistant-bubble-text',\n\n\t// Composer\n\t'--char-composer-bg',\n\t'--char-composer-border',\n\t'--char-composer-text',\n\t'--char-composer-placeholder',\n\t'--char-composer-button-bg',\n\t'--char-composer-button-text',\n\n\t// Tool Cards\n\t'--char-tool-bg',\n\t'--char-tool-border',\n\t'--char-tool-text',\n\t'--char-tool-header-bg',\n\t'--char-tool-approve-bg',\n\t'--char-tool-approve-text',\n\t'--char-tool-deny-bg',\n\t'--char-tool-deny-text',\n\n\t// Code Blocks\n\t'--char-code-bg',\n\t'--char-code-text',\n\t'--char-code-header-bg',\n\n\t// Sizing\n\t'--char-radius',\n\t'--char-radius-sm',\n\t'--char-radius-md',\n\t'--char-radius-lg',\n\t'--char-radius-xl',\n\t'--char-radius-2xl',\n\t'--char-radius-3xl',\n\t'--char-radius-full',\n\t'--char-spacing-unit',\n\n\t// Typography\n\t'--char-font-sans',\n\t'--char-font-mono',\n\t'--char-font-size-xs',\n\t'--char-font-size-sm',\n\t'--char-font-size-base',\n\t'--char-font-size-lg',\n\n\t// Motion\n\t'--char-duration-fast',\n\t'--char-duration-normal',\n\t'--char-duration-slow',\n\t'--char-easing-default',\n\t'--char-easing-spring',\n\n\t// Z-index\n\t'--char-z-base',\n\t'--char-z-content',\n\t'--char-z-overlay',\n\t'--char-z-max',\n\n\t// Shadows\n\t'--char-shadow-sm',\n\t'--char-shadow-md',\n\t'--char-shadow-lg',\n\n\t// Blur\n\t'--char-blur-sm',\n\t'--char-blur-md',\n\t'--char-blur-lg',\n] as const\n\nconst MCP_UI_STYLE_VARIABLE_NAMES = [\n\t// Background colors\n\t'--color-background-primary',\n\t'--color-background-secondary',\n\t'--color-background-tertiary',\n\t'--color-background-inverse',\n\t'--color-background-ghost',\n\t'--color-background-info',\n\t'--color-background-danger',\n\t'--color-background-success',\n\t'--color-background-warning',\n\t'--color-background-disabled',\n\n\t// Text colors\n\t'--color-text-primary',\n\t'--color-text-secondary',\n\t'--color-text-tertiary',\n\t'--color-text-inverse',\n\t'--color-text-ghost',\n\t'--color-text-info',\n\t'--color-text-danger',\n\t'--color-text-success',\n\t'--color-text-warning',\n\t'--color-text-disabled',\n\n\t// Border colors\n\t'--color-border-primary',\n\t'--color-border-secondary',\n\t'--color-border-tertiary',\n\t'--color-border-inverse',\n\t'--color-border-ghost',\n\t'--color-border-info',\n\t'--color-border-danger',\n\t'--color-border-success',\n\t'--color-border-warning',\n\t'--color-border-disabled',\n\n\t// Ring colors\n\t'--color-ring-primary',\n\t'--color-ring-secondary',\n\t'--color-ring-inverse',\n\t'--color-ring-info',\n\t'--color-ring-danger',\n\t'--color-ring-success',\n\t'--color-ring-warning',\n\n\t// Typography - Family\n\t'--font-sans',\n\t'--font-mono',\n\n\t// Typography - Weight\n\t'--font-weight-normal',\n\t'--font-weight-medium',\n\t'--font-weight-semibold',\n\t'--font-weight-bold',\n\n\t// Typography - Text Size\n\t'--font-text-xs-size',\n\t'--font-text-sm-size',\n\t'--font-text-md-size',\n\t'--font-text-lg-size',\n\n\t// Typography - Heading Size\n\t'--font-heading-xs-size',\n\t'--font-heading-sm-size',\n\t'--font-heading-md-size',\n\t'--font-heading-lg-size',\n\t'--font-heading-xl-size',\n\t'--font-heading-2xl-size',\n\t'--font-heading-3xl-size',\n\n\t// Typography - Text Line Height\n\t'--font-text-xs-line-height',\n\t'--font-text-sm-line-height',\n\t'--font-text-md-line-height',\n\t'--font-text-lg-line-height',\n\n\t// Typography - Heading Line Height\n\t'--font-heading-xs-line-height',\n\t'--font-heading-sm-line-height',\n\t'--font-heading-md-line-height',\n\t'--font-heading-lg-line-height',\n\t'--font-heading-xl-line-height',\n\t'--font-heading-2xl-line-height',\n\t'--font-heading-3xl-line-height',\n\n\t// Border radius\n\t'--border-radius-xs',\n\t'--border-radius-sm',\n\t'--border-radius-md',\n\t'--border-radius-lg',\n\t'--border-radius-xl',\n\t'--border-radius-full',\n\n\t// Border width\n\t'--border-width-regular',\n\n\t// Shadows\n\t'--shadow-hairline',\n\t'--shadow-sm',\n\t'--shadow-md',\n\t'--shadow-lg',\n] as const\n\nexport const CHAR_CSS_VARIABLE_NAMES = [\n\t...CHAR_PUBLIC_VARIABLE_NAMES,\n\t...MCP_UI_STYLE_VARIABLE_NAMES,\n] as const\n","/**\n * Host Context Merge Utilities\n *\n * Canonical merge logic for CharHostContext deltas. Used by both the host-side\n * web component and the iframe-side useHostContext hook to ensure identical\n * merge behavior on both sides of the postMessage bridge.\n */\n\nimport type { CharHostContext } from '../types'\n\nexport function mergeStyles(\n\tcurrent: CharHostContext['styles'] | undefined,\n\tpatch: CharHostContext['styles'] | undefined,\n): CharHostContext['styles'] | undefined {\n\tif (!patch) return current\n\treturn {\n\t\t...current,\n\t\t...patch,\n\t\t// variables are sent as a snapshot so removals propagate\n\t\tvariables: patch.variables ?? current?.variables,\n\t\tfonts: patch.fonts ?? current?.fonts,\n\t}\n}\n\nexport function shallowMerge<T extends object>(\n\tcurrent: T | undefined,\n\tpatch: T | undefined,\n): T | undefined {\n\tif (!patch) return current\n\treturn { ...current, ...patch }\n}\n\nexport function mergeHostContext(\n\tcurrent: CharHostContext | null,\n\tpatch: CharHostContext,\n): CharHostContext {\n\tconst previous = current ?? {}\n\treturn {\n\t\t...previous,\n\t\t...patch,\n\t\tstyles: mergeStyles(previous.styles, patch.styles),\n\t\tcontainerDimensions: shallowMerge(previous.containerDimensions, patch.containerDimensions),\n\t\tdeviceCapabilities: shallowMerge(previous.deviceCapabilities, patch.deviceCapabilities),\n\t\tsafeAreaInsets: shallowMerge(previous.safeAreaInsets, patch.safeAreaInsets),\n\t\thostCapabilities: shallowMerge(previous.hostCapabilities, patch.hostCapabilities),\n\t}\n}\n"],"mappings":"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A;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CAGA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CACA;CAGA;CACA;CACA;CACA;AAED,MAAM,8BAA8B;CAEnC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CAGA;CAGA;CACA;CACA;CACA;CACA;AAED,MAAa,0BAA0B,CACtC,GAAG,4BACH,GAAG,4BACH;;;;AClND,SAAgB,YACf,SACA,OACwC;AACxC,KAAI,CAAC,MAAO,QAAO;AACnB,QAAO;EACN,GAAG;EACH,GAAG;EAEH,WAAW,MAAM,aAAa,SAAS;EACvC,OAAO,MAAM,SAAS,SAAS;EAC/B;;AAGF,SAAgB,aACf,SACA,OACgB;AAChB,KAAI,CAAC,MAAO,QAAO;AACnB,QAAO;EAAE,GAAG;EAAS,GAAG;EAAO;;AAGhC,SAAgB,iBACf,SACA,OACkB;CAClB,MAAM,WAAW,WAAW,EAAE;AAC9B,QAAO;EACN,GAAG;EACH,GAAG;EACH,QAAQ,YAAY,SAAS,QAAQ,MAAM,OAAO;EAClD,qBAAqB,aAAa,SAAS,qBAAqB,MAAM,oBAAoB;EAC1F,oBAAoB,aAAa,SAAS,oBAAoB,MAAM,mBAAmB;EACvF,gBAAgB,aAAa,SAAS,gBAAgB,MAAM,eAAe;EAC3E,kBAAkB,aAAa,SAAS,kBAAkB,MAAM,iBAAiB;EACjF"}
|
|
1
|
+
{"version":3,"file":"utils.js","names":[],"sources":["../src/utils/css-resolver.ts","../src/utils/css-sanitizer.ts","../src/utils/css-variables.ts","../src/utils/host-context-merge.ts"],"sourcesContent":["/**\n * CSS var() Resolver\n *\n * Recursively resolves CSS custom property references (`var(--x, fallback)`)\n * to concrete values using getComputedStyle(). Necessary because `var()`\n * references are not valid across iframe boundaries.\n *\n * Handles nested var() references, fallbacks, and cycle detection.\n */\n\nexport const MAX_CSS_VAR_RESOLVE_DEPTH = 12\n\nexport function findMatchingParen(value: string, openParenIndex: number): number {\n\tlet depth = 1\n\tfor (let i = openParenIndex + 1; i < value.length; i++) {\n\t\tconst char = value[i]\n\t\tif (char === '(') depth += 1\n\t\telse if (char === ')') depth -= 1\n\t\tif (depth === 0) return i\n\t}\n\treturn -1\n}\n\nexport function splitTopLevelComma(value: string): [string, string | undefined] {\n\tlet depth = 0\n\tfor (let i = 0; i < value.length; i++) {\n\t\tconst char = value[i]\n\t\tif (char === '(') depth += 1\n\t\telse if (char === ')') depth = Math.max(0, depth - 1)\n\t\telse if (char === ',' && depth === 0) return [value.slice(0, i), value.slice(i + 1)]\n\t}\n\treturn [value, undefined]\n}\n\nexport function resolveFallback(\n\tfallback: string | undefined,\n\tcomputed: CSSStyleDeclaration,\n\tseen: Set<string>,\n\tdepth: number,\n): string | undefined {\n\tif (!fallback) return undefined\n\treturn resolveCssValue(fallback, computed, seen, depth + 1) || undefined\n}\n\nexport function resolveCssVariable(\n\tvariableName: string,\n\tfallback: string | undefined,\n\tcomputed: CSSStyleDeclaration,\n\tseen: Set<string>,\n\tdepth: number,\n): string | undefined {\n\tif (depth > MAX_CSS_VAR_RESOLVE_DEPTH) {\n\t\tconsole.warn(`[Char] CSS variable resolution depth exceeded for \"${variableName}\". Possible circular reference.`)\n\t\treturn fallback?.trim()\n\t}\n\n\tif (!variableName.startsWith('--') || seen.has(variableName)) {\n\t\treturn resolveFallback(fallback, computed, seen, depth)\n\t}\n\n\tconst nextSeen = new Set(seen)\n\tnextSeen.add(variableName)\n\n\tconst rawValue = computed.getPropertyValue(variableName).trim()\n\tif (rawValue) {\n\t\tconst resolvedRaw = resolveCssValue(rawValue, computed, nextSeen, depth + 1)\n\t\tif (resolvedRaw) return resolvedRaw\n\t}\n\n\treturn resolveFallback(fallback, computed, seen, depth)\n}\n\nexport function resolveCssValue(\n\tvalue: string,\n\tcomputed: CSSStyleDeclaration,\n\tseen: Set<string>,\n\tdepth = 0,\n): string {\n\tconst trimmed = value.trim()\n\tif (!trimmed || !trimmed.includes('var(') || depth > MAX_CSS_VAR_RESOLVE_DEPTH) {\n\t\treturn trimmed\n\t}\n\n\tlet cursor = 0\n\tlet resolved = ''\n\tlet replacedAny = false\n\n\twhile (cursor < trimmed.length) {\n\t\tconst varStart = trimmed.indexOf('var(', cursor)\n\t\tif (varStart === -1) {\n\t\t\tresolved += trimmed.slice(cursor)\n\t\t\tbreak\n\t\t}\n\n\t\tresolved += trimmed.slice(cursor, varStart)\n\n\t\tconst openParenIndex = varStart + 3\n\t\tconst closeParenIndex = findMatchingParen(trimmed, openParenIndex)\n\t\tif (closeParenIndex === -1) {\n\t\t\tresolved += trimmed.slice(varStart)\n\t\t\tbreak\n\t\t}\n\n\t\tconst rawArgs = trimmed.slice(openParenIndex + 1, closeParenIndex)\n\t\tconst [rawVariableName, rawFallback] = splitTopLevelComma(rawArgs)\n\t\tconst variableName = rawVariableName.trim()\n\t\tconst fallback = rawFallback?.trim()\n\n\t\tconst replacement = variableName\n\t\t\t? resolveCssVariable(variableName, fallback, computed, seen, depth + 1)\n\t\t\t: undefined\n\n\t\tif (replacement !== undefined) {\n\t\t\tresolved += replacement\n\t\t\treplacedAny = true\n\t\t} else {\n\t\t\tresolved += trimmed.slice(varStart, closeParenIndex + 1)\n\t\t}\n\n\t\tcursor = closeParenIndex + 1\n\t}\n\n\tconst normalized = resolved.trim()\n\tif (!replacedAny || !normalized.includes('var(')) {\n\t\treturn normalized\n\t}\n\n\treturn resolveCssValue(normalized, computed, seen, depth + 1)\n}\n","/**\n * Maximum allowed length for CSS custom property values transported to iframe.\n *\n * NOTE: This value and the blocked-token policy intentionally mirror\n * `packages/shared-types/src/schemas/embed-bridge.ts` so validation happens\n * both before transport (host) and at contract parse-time (iframe).\n *\n * CONTROL_CHAR_PATTERN here is intentionally more permissive than\n * embed-bridge.ts: it allows tabs/newlines/CR/FF because\n * normalizeCssFormattingWhitespace converts them to spaces before the\n * control-char check. The iframe-side schema (embed-bridge.ts) uses\n * the stricter pattern as defense-in-depth for values that bypass\n * host-side sanitization.\n */\nexport const MAX_CSS_VARIABLE_VALUE_LENGTH = 1024\nexport const MAX_HOST_FONT_CSS_LENGTH = 16_384\n\nconst CONTROL_CHAR_PATTERN = /[\\u0000-\\u0008\\u000b\\u000e-\\u001f\\u007f]/u\nconst FONT_RULES_PATTERN = /^(?:\\s*@font-face\\s*\\{[^{}]*\\}\\s*)+$/iu\nconst CSS_COMMENT_PATTERN = /\\/\\*[\\s\\S]*?\\*\\//g\nconst CSS_FORMATTING_WHITESPACE_PATTERN = /[\\t\\n\\r\\f]+/gu\n\n/**\n * Normalizes CSS values for dangerous-token scanning.\n * Lowercases and removes CSS comments/whitespace/control chars to catch obfuscation.\n */\nfunction normalizeForSecurityScan(value: string): string {\n\tconst withoutComments = value.replace(CSS_COMMENT_PATTERN, '')\n\treturn withoutComments.toLowerCase().replace(/[\\s\\u0000-\\u001f\\u007f]+/gu, '')\n}\n\n/**\n * Collapses CSS formatting whitespace (tabs, newlines, carriage returns, form feeds)\n * into single spaces. Applied to the output value before trimming, so multiline\n * host-provided values (e.g., font stacks) become single-line without losing\n * meaningful token boundaries. This also ensures split-token obfuscation\n * (e.g., \"java\\nscript:\") is collapsed before the security scan.\n */\nfunction normalizeCssFormattingWhitespace(value: string): string {\n\treturn value.replace(CSS_FORMATTING_WHITESPACE_PATTERN, ' ')\n}\n\n/**\n * Sanitizes host-provided CSS variable values before iframe transport.\n * Returns `undefined` for unsafe or invalid values.\n *\n * @param value - The raw CSS property value.\n * @param variableName - Optional variable name for diagnostic logging.\n */\nexport function sanitizeCssVariableValue(value: string, variableName?: string): string | undefined {\n\tconst trimmed = normalizeCssFormattingWhitespace(value).trim()\n\tif (!trimmed) return undefined\n\tif (trimmed.length > MAX_CSS_VARIABLE_VALUE_LENGTH) {\n\t\tconsole.warn(`[Char] CSS variable ${variableName ?? '(unknown)'} rejected: value exceeds max length (${trimmed.length} > ${MAX_CSS_VARIABLE_VALUE_LENGTH})`)\n\t\treturn undefined\n\t}\n\tif (CONTROL_CHAR_PATTERN.test(trimmed)) {\n\t\tconsole.warn(`[Char] CSS variable ${variableName ?? '(unknown)'} rejected: contains control characters`)\n\t\treturn undefined\n\t}\n\n\tconst normalized = normalizeForSecurityScan(trimmed)\n\tconst blockedToken = getBlockedCssVariableToken(normalized)\n\tif (blockedToken) return warnAndReject(variableName, blockedToken)\n\n\treturn trimmed\n}\n\n/**\n * Sanitizes host-provided font CSS before iframe injection.\n * Only `@font-face` rules are allowed.\n */\nexport function sanitizeHostFontCss(fontCss: string): string | undefined {\n\tconst trimmed = fontCss.trim()\n\tif (!trimmed) return undefined\n\tif (trimmed.length > MAX_HOST_FONT_CSS_LENGTH) {\n\t\tconsole.warn(`[Char] Host font CSS rejected: value exceeds max length (${trimmed.length} > ${MAX_HOST_FONT_CSS_LENGTH})`)\n\t\treturn undefined\n\t}\n\tif (CONTROL_CHAR_PATTERN.test(trimmed)) {\n\t\tconsole.warn('[Char] Host font CSS rejected: contains control characters')\n\t\treturn undefined\n\t}\n\n\tconst normalized = normalizeForSecurityScan(trimmed)\n\tconst blockedToken = getBlockedFontToken(normalized)\n\tif (blockedToken) {\n\t\tconsole.warn(`[Char] Host font CSS rejected: contains blocked token \"${blockedToken}\"`)\n\t\treturn undefined\n\t}\n\n\tconst withoutComments = trimmed.replace(/\\/\\*[\\s\\S]*?\\*\\//g, '').trim()\n\tif (!FONT_RULES_PATTERN.test(withoutComments)) {\n\t\tconsole.warn('[Char] Host font CSS rejected: only @font-face rules are allowed')\n\t\treturn undefined\n\t}\n\n\treturn trimmed\n}\n\nfunction getBlockedCssVariableToken(normalizedValue: string): string | undefined {\n\tif (normalizedValue.includes('javascript:')) return 'javascript:'\n\tif (normalizedValue.includes('vbscript:')) return 'vbscript:'\n\tif (normalizedValue.includes('expression(')) return 'expression('\n\tif (normalizedValue.includes('@import')) return '@import'\n\tif (normalizedValue.includes('-moz-binding')) return '-moz-binding'\n\tif (normalizedValue.includes('url(')) return 'url('\n\treturn undefined\n}\n\nfunction getBlockedFontToken(normalizedValue: string): string | undefined {\n\tif (normalizedValue.includes('javascript:')) return 'javascript:'\n\tif (normalizedValue.includes('vbscript:')) return 'vbscript:'\n\tif (normalizedValue.includes('expression(')) return 'expression('\n\tif (normalizedValue.includes('-moz-binding')) return '-moz-binding'\n\treturn undefined\n}\n\nfunction warnAndReject(variableName: string | undefined, blockedToken: string): undefined {\n\tconsole.warn(`[Char] CSS variable ${variableName ?? '(unknown)'} rejected: contains blocked token \"${blockedToken}\"`)\n\treturn undefined\n}\n","/**\n * Public CSS variable names for the Char widget.\n *\n * These are the external API — host pages set these to customise the widget.\n * We maintain static lists because `getComputedStyle()` cannot enumerate\n * custom properties.\n *\n * Source of truth:\n * - `src/styles/globals.css` (`--char-*`)\n * - WebMCP ext-apps UI spec (`--color-*`, `--font-*`, etc.)\n */\n\nconst CHAR_PUBLIC_VARIABLE_NAMES = [\n\t// Colors - Core\n\t'--char-color-background',\n\t'--char-color-foreground',\n\t'--char-color-card',\n\t'--char-color-card-foreground',\n\t'--char-color-popover',\n\t'--char-color-popover-foreground',\n\t'--char-color-primary',\n\t'--char-color-primary-foreground',\n\t'--char-color-secondary',\n\t'--char-color-secondary-foreground',\n\t'--char-color-muted',\n\t'--char-color-muted-foreground',\n\t'--char-color-accent',\n\t'--char-color-accent-foreground',\n\t'--char-color-destructive',\n\t'--char-color-destructive-foreground',\n\t'--char-color-border',\n\n\t// Colors - Semantic\n\t'--char-color-success',\n\t'--char-color-warning',\n\t'--char-color-error',\n\n\t// Colors - Derived\n\t'--char-color-input',\n\t'--char-color-ring',\n\n\t// Message Bubbles\n\t'--char-user-bubble-bg',\n\t'--char-user-bubble-text',\n\t'--char-assistant-bubble-bg',\n\t'--char-assistant-bubble-text',\n\n\t// Composer\n\t'--char-composer-bg',\n\t'--char-composer-border',\n\t'--char-composer-text',\n\t'--char-composer-placeholder',\n\t'--char-composer-button-bg',\n\t'--char-composer-button-text',\n\n\t// Tool Cards\n\t'--char-tool-bg',\n\t'--char-tool-border',\n\t'--char-tool-text',\n\t'--char-tool-header-bg',\n\t'--char-tool-approve-bg',\n\t'--char-tool-approve-text',\n\t'--char-tool-deny-bg',\n\t'--char-tool-deny-text',\n\n\t// Code Blocks\n\t'--char-code-bg',\n\t'--char-code-text',\n\t'--char-code-header-bg',\n\n\t// Sizing\n\t'--char-radius',\n\t'--char-radius-sm',\n\t'--char-radius-md',\n\t'--char-radius-lg',\n\t'--char-radius-xl',\n\t'--char-radius-2xl',\n\t'--char-radius-3xl',\n\t'--char-radius-full',\n\t'--char-spacing-unit',\n\n\t// Typography\n\t'--char-font-sans',\n\t'--char-font-mono',\n\t'--char-font-size-xs',\n\t'--char-font-size-sm',\n\t'--char-font-size-base',\n\t'--char-font-size-lg',\n\n\t// Motion\n\t'--char-duration-fast',\n\t'--char-duration-normal',\n\t'--char-duration-slow',\n\t'--char-easing-default',\n\t'--char-easing-spring',\n\n\t// Z-index\n\t'--char-z-base',\n\t'--char-z-content',\n\t'--char-z-overlay',\n\t'--char-z-max',\n\n\t// Shadows\n\t'--char-shadow-sm',\n\t'--char-shadow-md',\n\t'--char-shadow-lg',\n\n\t// Blur\n\t'--char-blur-sm',\n\t'--char-blur-md',\n\t'--char-blur-lg',\n] as const\n\nconst MCP_UI_STYLE_VARIABLE_NAMES = [\n\t// Background colors\n\t'--color-background-primary',\n\t'--color-background-secondary',\n\t'--color-background-tertiary',\n\t'--color-background-inverse',\n\t'--color-background-ghost',\n\t'--color-background-info',\n\t'--color-background-danger',\n\t'--color-background-success',\n\t'--color-background-warning',\n\t'--color-background-disabled',\n\n\t// Text colors\n\t'--color-text-primary',\n\t'--color-text-secondary',\n\t'--color-text-tertiary',\n\t'--color-text-inverse',\n\t'--color-text-ghost',\n\t'--color-text-info',\n\t'--color-text-danger',\n\t'--color-text-success',\n\t'--color-text-warning',\n\t'--color-text-disabled',\n\n\t// Border colors\n\t'--color-border-primary',\n\t'--color-border-secondary',\n\t'--color-border-tertiary',\n\t'--color-border-inverse',\n\t'--color-border-ghost',\n\t'--color-border-info',\n\t'--color-border-danger',\n\t'--color-border-success',\n\t'--color-border-warning',\n\t'--color-border-disabled',\n\n\t// Ring colors\n\t'--color-ring-primary',\n\t'--color-ring-secondary',\n\t'--color-ring-inverse',\n\t'--color-ring-info',\n\t'--color-ring-danger',\n\t'--color-ring-success',\n\t'--color-ring-warning',\n\n\t// Typography - Family\n\t'--font-sans',\n\t'--font-mono',\n\n\t// Typography - Weight\n\t'--font-weight-normal',\n\t'--font-weight-medium',\n\t'--font-weight-semibold',\n\t'--font-weight-bold',\n\n\t// Typography - Text Size\n\t'--font-text-xs-size',\n\t'--font-text-sm-size',\n\t'--font-text-md-size',\n\t'--font-text-lg-size',\n\n\t// Typography - Heading Size\n\t'--font-heading-xs-size',\n\t'--font-heading-sm-size',\n\t'--font-heading-md-size',\n\t'--font-heading-lg-size',\n\t'--font-heading-xl-size',\n\t'--font-heading-2xl-size',\n\t'--font-heading-3xl-size',\n\n\t// Typography - Text Line Height\n\t'--font-text-xs-line-height',\n\t'--font-text-sm-line-height',\n\t'--font-text-md-line-height',\n\t'--font-text-lg-line-height',\n\n\t// Typography - Heading Line Height\n\t'--font-heading-xs-line-height',\n\t'--font-heading-sm-line-height',\n\t'--font-heading-md-line-height',\n\t'--font-heading-lg-line-height',\n\t'--font-heading-xl-line-height',\n\t'--font-heading-2xl-line-height',\n\t'--font-heading-3xl-line-height',\n\n\t// Border radius\n\t'--border-radius-xs',\n\t'--border-radius-sm',\n\t'--border-radius-md',\n\t'--border-radius-lg',\n\t'--border-radius-xl',\n\t'--border-radius-full',\n\n\t// Border width\n\t'--border-width-regular',\n\n\t// Shadows\n\t'--shadow-hairline',\n\t'--shadow-sm',\n\t'--shadow-md',\n\t'--shadow-lg',\n] as const\n\nexport const CHAR_CSS_VARIABLE_NAMES = [\n\t...CHAR_PUBLIC_VARIABLE_NAMES,\n\t...MCP_UI_STYLE_VARIABLE_NAMES,\n] as const\n","/**\n * Host Context Merge Utilities\n *\n * Canonical merge logic for CharHostContext deltas. Used by both the host-side\n * web component and the iframe-side useHostContext hook to ensure identical\n * merge behavior on both sides of the postMessage bridge.\n */\n\nimport type { CharHostContext } from '../types'\n\nexport function mergeStyles(\n\tcurrent: CharHostContext['styles'] | undefined,\n\tpatch: CharHostContext['styles'] | undefined,\n): CharHostContext['styles'] | undefined {\n\tif (!patch) return current\n\treturn {\n\t\t...current,\n\t\t...patch,\n\t\tvariables: patch.variables ?? current?.variables,\n\t\tfonts: patch.fonts ?? current?.fonts,\n\t}\n}\n\nexport function shallowMerge<T extends object>(\n\tcurrent: T | undefined,\n\tpatch: T | undefined,\n): T | undefined {\n\tif (!patch) return current\n\treturn { ...current, ...patch }\n}\n\nexport function mergeHostContext(\n\tcurrent: CharHostContext | null,\n\tpatch: CharHostContext,\n): CharHostContext {\n\tconst previous = current ?? {}\n\treturn {\n\t\t...previous,\n\t\t...patch,\n\t\tstyles: mergeStyles(previous.styles, patch.styles),\n\t\tcontainerDimensions: shallowMerge(previous.containerDimensions, patch.containerDimensions),\n\t\tdeviceCapabilities: shallowMerge(previous.deviceCapabilities, patch.deviceCapabilities),\n\t\tsafeAreaInsets: shallowMerge(previous.safeAreaInsets, patch.safeAreaInsets),\n\t}\n}\n"],"mappings":";;;;;;;;;;;AAUA,MAAa,4BAA4B;AAEzC,SAAgB,kBAAkB,OAAe,gBAAgC;CAChF,IAAI,QAAQ;AACZ,MAAK,IAAI,IAAI,iBAAiB,GAAG,IAAI,MAAM,QAAQ,KAAK;EACvD,MAAM,OAAO,MAAM;AACnB,MAAI,SAAS,IAAK,UAAS;WAClB,SAAS,IAAK,UAAS;AAChC,MAAI,UAAU,EAAG,QAAO;;AAEzB,QAAO;;AAGR,SAAgB,mBAAmB,OAA6C;CAC/E,IAAI,QAAQ;AACZ,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACtC,MAAM,OAAO,MAAM;AACnB,MAAI,SAAS,IAAK,UAAS;WAClB,SAAS,IAAK,SAAQ,KAAK,IAAI,GAAG,QAAQ,EAAE;WAC5C,SAAS,OAAO,UAAU,EAAG,QAAO,CAAC,MAAM,MAAM,GAAG,EAAE,EAAE,MAAM,MAAM,IAAI,EAAE,CAAC;;AAErF,QAAO,CAAC,OAAO,OAAU;;AAG1B,SAAgB,gBACf,UACA,UACA,MACA,OACqB;AACrB,KAAI,CAAC,SAAU,QAAO;AACtB,QAAO,gBAAgB,UAAU,UAAU,MAAM,QAAQ,EAAE,IAAI;;AAGhE,SAAgB,mBACf,cACA,UACA,UACA,MACA,OACqB;AACrB,KAAI,QAAQ,2BAA2B;AACtC,UAAQ,KAAK,sDAAsD,aAAa,iCAAiC;AACjH,SAAO,UAAU,MAAM;;AAGxB,KAAI,CAAC,aAAa,WAAW,KAAK,IAAI,KAAK,IAAI,aAAa,CAC3D,QAAO,gBAAgB,UAAU,UAAU,MAAM,MAAM;CAGxD,MAAM,WAAW,IAAI,IAAI,KAAK;AAC9B,UAAS,IAAI,aAAa;CAE1B,MAAM,WAAW,SAAS,iBAAiB,aAAa,CAAC,MAAM;AAC/D,KAAI,UAAU;EACb,MAAM,cAAc,gBAAgB,UAAU,UAAU,UAAU,QAAQ,EAAE;AAC5E,MAAI,YAAa,QAAO;;AAGzB,QAAO,gBAAgB,UAAU,UAAU,MAAM,MAAM;;AAGxD,SAAgB,gBACf,OACA,UACA,MACA,QAAQ,GACC;CACT,MAAM,UAAU,MAAM,MAAM;AAC5B,KAAI,CAAC,WAAW,CAAC,QAAQ,SAAS,OAAO,IAAI,QAAQ,0BACpD,QAAO;CAGR,IAAI,SAAS;CACb,IAAI,WAAW;CACf,IAAI,cAAc;AAElB,QAAO,SAAS,QAAQ,QAAQ;EAC/B,MAAM,WAAW,QAAQ,QAAQ,QAAQ,OAAO;AAChD,MAAI,aAAa,IAAI;AACpB,eAAY,QAAQ,MAAM,OAAO;AACjC;;AAGD,cAAY,QAAQ,MAAM,QAAQ,SAAS;EAE3C,MAAM,iBAAiB,WAAW;EAClC,MAAM,kBAAkB,kBAAkB,SAAS,eAAe;AAClE,MAAI,oBAAoB,IAAI;AAC3B,eAAY,QAAQ,MAAM,SAAS;AACnC;;EAID,MAAM,CAAC,iBAAiB,eAAe,mBADvB,QAAQ,MAAM,iBAAiB,GAAG,gBAAgB,CACA;EAClE,MAAM,eAAe,gBAAgB,MAAM;EAC3C,MAAM,WAAW,aAAa,MAAM;EAEpC,MAAM,cAAc,eACjB,mBAAmB,cAAc,UAAU,UAAU,MAAM,QAAQ,EAAE,GACrE;AAEH,MAAI,gBAAgB,QAAW;AAC9B,eAAY;AACZ,iBAAc;QAEd,aAAY,QAAQ,MAAM,UAAU,kBAAkB,EAAE;AAGzD,WAAS,kBAAkB;;CAG5B,MAAM,aAAa,SAAS,MAAM;AAClC,KAAI,CAAC,eAAe,CAAC,WAAW,SAAS,OAAO,CAC/C,QAAO;AAGR,QAAO,gBAAgB,YAAY,UAAU,MAAM,QAAQ,EAAE;;;;;;;;;;;;;;;;;;;ACjH9D,MAAa,gCAAgC;AAC7C,MAAa,2BAA2B;AAExC,MAAM,uBAAuB;AAC7B,MAAM,qBAAqB;AAC3B,MAAM,sBAAsB;AAC5B,MAAM,oCAAoC;;;;;AAM1C,SAAS,yBAAyB,OAAuB;AAExD,QADwB,MAAM,QAAQ,qBAAqB,GAAG,CACvC,aAAa,CAAC,QAAQ,8BAA8B,GAAG;;;;;;;;;AAU/E,SAAS,iCAAiC,OAAuB;AAChE,QAAO,MAAM,QAAQ,mCAAmC,IAAI;;;;;;;;;AAU7D,SAAgB,yBAAyB,OAAe,cAA2C;CAClG,MAAM,UAAU,iCAAiC,MAAM,CAAC,MAAM;AAC9D,KAAI,CAAC,QAAS,QAAO;AACrB,KAAI,QAAQ,SAAS,+BAA+B;AACnD,UAAQ,KAAK,uBAAuB,gBAAgB,YAAY,uCAAuC,QAAQ,OAAO,KAAK,8BAA8B,GAAG;AAC5J;;AAED,KAAI,qBAAqB,KAAK,QAAQ,EAAE;AACvC,UAAQ,KAAK,uBAAuB,gBAAgB,YAAY,wCAAwC;AACxG;;CAID,MAAM,eAAe,2BADF,yBAAyB,QAAQ,CACO;AAC3D,KAAI,aAAc,QAAO,cAAc,cAAc,aAAa;AAElE,QAAO;;;;;;AAOR,SAAgB,oBAAoB,SAAqC;CACxE,MAAM,UAAU,QAAQ,MAAM;AAC9B,KAAI,CAAC,QAAS,QAAO;AACrB,KAAI,QAAQ,SAAS,0BAA0B;AAC9C,UAAQ,KAAK,4DAA4D,QAAQ,OAAO,KAAK,yBAAyB,GAAG;AACzH;;AAED,KAAI,qBAAqB,KAAK,QAAQ,EAAE;AACvC,UAAQ,KAAK,6DAA6D;AAC1E;;CAID,MAAM,eAAe,oBADF,yBAAyB,QAAQ,CACA;AACpD,KAAI,cAAc;AACjB,UAAQ,KAAK,0DAA0D,aAAa,GAAG;AACvF;;CAGD,MAAM,kBAAkB,QAAQ,QAAQ,qBAAqB,GAAG,CAAC,MAAM;AACvE,KAAI,CAAC,mBAAmB,KAAK,gBAAgB,EAAE;AAC9C,UAAQ,KAAK,mEAAmE;AAChF;;AAGD,QAAO;;AAGR,SAAS,2BAA2B,iBAA6C;AAChF,KAAI,gBAAgB,SAAS,cAAc,CAAE,QAAO;AACpD,KAAI,gBAAgB,SAAS,YAAY,CAAE,QAAO;AAClD,KAAI,gBAAgB,SAAS,cAAc,CAAE,QAAO;AACpD,KAAI,gBAAgB,SAAS,UAAU,CAAE,QAAO;AAChD,KAAI,gBAAgB,SAAS,eAAe,CAAE,QAAO;AACrD,KAAI,gBAAgB,SAAS,OAAO,CAAE,QAAO;;AAI9C,SAAS,oBAAoB,iBAA6C;AACzE,KAAI,gBAAgB,SAAS,cAAc,CAAE,QAAO;AACpD,KAAI,gBAAgB,SAAS,YAAY,CAAE,QAAO;AAClD,KAAI,gBAAgB,SAAS,cAAc,CAAE,QAAO;AACpD,KAAI,gBAAgB,SAAS,eAAe,CAAE,QAAO;;AAItD,SAAS,cAAc,cAAkC,cAAiC;AACzF,SAAQ,KAAK,uBAAuB,gBAAgB,YAAY,qCAAqC,aAAa,GAAG;;;;;;;;;;;;;;;;AC3GtH,MAAM,6BAA6B;CAElC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CAGA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CACA;CAGA;CACA;CACA;CACA;AAED,MAAM,8BAA8B;CAEnC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CAGA;CAGA;CACA;CACA;CACA;CACA;AAED,MAAa,0BAA0B,CACtC,GAAG,4BACH,GAAG,4BACH;;;;AClND,SAAgB,YACf,SACA,OACwC;AACxC,KAAI,CAAC,MAAO,QAAO;AACnB,QAAO;EACN,GAAG;EACH,GAAG;EACH,WAAW,MAAM,aAAa,SAAS;EACvC,OAAO,MAAM,SAAS,SAAS;EAC/B;;AAGF,SAAgB,aACf,SACA,OACgB;AAChB,KAAI,CAAC,MAAO,QAAO;AACnB,QAAO;EAAE,GAAG;EAAS,GAAG;EAAO;;AAGhC,SAAgB,iBACf,SACA,OACkB;CAClB,MAAM,WAAW,WAAW,EAAE;AAC9B,QAAO;EACN,GAAG;EACH,GAAG;EACH,QAAQ,YAAY,SAAS,QAAQ,MAAM,OAAO;EAClD,qBAAqB,aAAa,SAAS,qBAAqB,MAAM,oBAAoB;EAC1F,oBAAoB,aAAa,SAAS,oBAAoB,MAAM,mBAAmB;EACvF,gBAAgB,aAAa,SAAS,gBAAgB,MAAM,eAAe;EAC3E"}
|
|
@@ -419,8 +419,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
419
419
|
styles: mergeStyles(previous.styles, patch.styles),
|
|
420
420
|
containerDimensions: shallowMerge(previous.containerDimensions, patch.containerDimensions),
|
|
421
421
|
deviceCapabilities: shallowMerge(previous.deviceCapabilities, patch.deviceCapabilities),
|
|
422
|
-
safeAreaInsets: shallowMerge(previous.safeAreaInsets, patch.safeAreaInsets)
|
|
423
|
-
hostCapabilities: shallowMerge(previous.hostCapabilities, patch.hostCapabilities)
|
|
422
|
+
safeAreaInsets: shallowMerge(previous.safeAreaInsets, patch.safeAreaInsets)
|
|
424
423
|
};
|
|
425
424
|
}
|
|
426
425
|
|
|
@@ -455,25 +454,6 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
455
454
|
"pip"
|
|
456
455
|
];
|
|
457
456
|
/**
|
|
458
|
-
* Normalizes an API key by trimming whitespace and collapsing empty strings.
|
|
459
|
-
*/
|
|
460
|
-
function normalizeApiKey(value) {
|
|
461
|
-
const trimmed = value?.trim();
|
|
462
|
-
return trimmed ? trimmed : void 0;
|
|
463
|
-
}
|
|
464
|
-
/**
|
|
465
|
-
* Produces a sanitized dev-mode configuration and drops empty payloads.
|
|
466
|
-
*/
|
|
467
|
-
function resolveDevMode(devMode) {
|
|
468
|
-
if (!devMode) return void 0;
|
|
469
|
-
const normalized = {
|
|
470
|
-
anthropicApiKey: normalizeApiKey(devMode.anthropicApiKey),
|
|
471
|
-
openaiApiKey: normalizeApiKey(devMode.openaiApiKey),
|
|
472
|
-
useLocalApi: devMode.useLocalApi === true ? true : void 0
|
|
473
|
-
};
|
|
474
|
-
return !!normalized.anthropicApiKey || !!normalized.openaiApiKey || !!normalized.useLocalApi ? normalized : void 0;
|
|
475
|
-
}
|
|
476
|
-
/**
|
|
477
457
|
* Runtime guard for the message envelope emitted by the iframe.
|
|
478
458
|
*/
|
|
479
459
|
function isCharIframeMessageData(value) {
|
|
@@ -612,7 +592,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
612
592
|
*/
|
|
613
593
|
var CharAgentElement = class extends HTMLElement {
|
|
614
594
|
static observedAttributes = [
|
|
615
|
-
"
|
|
595
|
+
"publishable-key",
|
|
616
596
|
"enable-debug-tools",
|
|
617
597
|
"display-mode",
|
|
618
598
|
"api-base"
|
|
@@ -662,12 +642,12 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
662
642
|
return;
|
|
663
643
|
}
|
|
664
644
|
if (!this.style.display) this.style.display = "block";
|
|
665
|
-
const
|
|
666
|
-
const apiBase = this._resolveApiBaseOverride() ?? (resolvedDevMode?.useLocalApi ? window.location.origin : WEBMCP_PRODUCTION_API_BASE);
|
|
645
|
+
const apiBase = this._resolveApiBaseOverride() ?? WEBMCP_PRODUCTION_API_BASE;
|
|
667
646
|
const iframeOrigin = this._resolveIframeOrigin(apiBase);
|
|
668
647
|
const iframe = this._createIframe(apiBase);
|
|
669
648
|
this._iframe = iframe;
|
|
670
|
-
|
|
649
|
+
const publishableKey = this.publishableKey ?? this.getAttribute("publishable-key") ?? void 0;
|
|
650
|
+
if (publishableKey) this._pendingAuth = { publishableKey };
|
|
671
651
|
this._proxy = new CharIframeProxy(iframe, iframeOrigin);
|
|
672
652
|
this._proxy.start();
|
|
673
653
|
this._messageListener = this._createMessageListener(iframe, iframeOrigin);
|
|
@@ -876,15 +856,21 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
876
856
|
/**
|
|
877
857
|
* Reacts to observed attribute updates after iframe readiness.
|
|
878
858
|
* Attributes that affect iframe boot configuration are intentionally ignored
|
|
879
|
-
* after mount (`
|
|
859
|
+
* after mount (`enable-debug-tools`, `api-base`).
|
|
880
860
|
*/
|
|
881
861
|
attributeChangedCallback(name, _oldValue, newValue) {
|
|
882
862
|
if (!this._iframe || !this._iframeReady) return;
|
|
883
863
|
switch (name) {
|
|
864
|
+
case "publishable-key":
|
|
865
|
+
if (newValue) {
|
|
866
|
+
this._pendingAuth = { publishableKey: newValue };
|
|
867
|
+
const iframeOrigin = this._getIframeOrigin();
|
|
868
|
+
if (iframeOrigin) this._postAuth(iframeOrigin);
|
|
869
|
+
}
|
|
870
|
+
break;
|
|
884
871
|
case "display-mode":
|
|
885
872
|
this.setHostContext({ displayMode: resolveDisplayModeFromAttribute(newValue) });
|
|
886
873
|
break;
|
|
887
|
-
case "dev-mode":
|
|
888
874
|
case "enable-debug-tools":
|
|
889
875
|
case "api-base": break;
|
|
890
876
|
}
|
|
@@ -895,23 +881,17 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
895
881
|
* The token is stored as a JavaScript property (not as a DOM attribute),
|
|
896
882
|
* preventing exposure to DOM inspection and session replay tools.
|
|
897
883
|
*
|
|
898
|
-
* @param options - Authentication payload
|
|
884
|
+
* @param options - Authentication payload.
|
|
899
885
|
* @returns `true` when the payload is accepted; `false` when validation fails.
|
|
900
886
|
*/
|
|
901
887
|
connect(options) {
|
|
902
|
-
if (!options?.
|
|
903
|
-
this._emitCharError("
|
|
904
|
-
return false;
|
|
905
|
-
}
|
|
906
|
-
if (options?.idToken && !options?.ticketAuth && !options?.clientId) {
|
|
907
|
-
this._emitCharError("MISSING_CLIENT_ID", "connect() requires clientId when using idToken authentication");
|
|
888
|
+
if (!options?.publishableKey) {
|
|
889
|
+
this._emitCharError("MISSING_PUBLISHABLE_KEY", "connect() requires publishableKey");
|
|
908
890
|
return false;
|
|
909
891
|
}
|
|
910
892
|
this._pendingAuth = {
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
organizationId: options.ticketAuth ? void 0 : options.organizationId,
|
|
914
|
-
ticketAuth: options.ticketAuth
|
|
893
|
+
publishableKey: options.publishableKey,
|
|
894
|
+
idToken: options.idToken
|
|
915
895
|
};
|
|
916
896
|
const iframeOrigin = this._getIframeOrigin();
|
|
917
897
|
if (this._iframeReady && iframeOrigin) this._postAuth(iframeOrigin);
|
|
@@ -930,7 +910,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
930
910
|
}
|
|
931
911
|
/**
|
|
932
912
|
* Disconnect from the Char agent.
|
|
933
|
-
* Clears the
|
|
913
|
+
* Clears pending auth state and posts a disconnect message to the iframe.
|
|
934
914
|
*
|
|
935
915
|
* @returns `true` when the disconnect message was sent; `false` when the iframe was not ready.
|
|
936
916
|
*/
|
|
@@ -994,11 +974,6 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
994
974
|
};
|
|
995
975
|
const safeAreaInsets = getSafeAreaInsets();
|
|
996
976
|
const displayMode = resolveDisplayModeFromAttribute(displayModeAttr) ?? "inline";
|
|
997
|
-
const hostCapabilities = {
|
|
998
|
-
supportedDisplayModes: [...DISPLAY_MODES],
|
|
999
|
-
supportsOpenLink: true,
|
|
1000
|
-
supportsTeardown: true
|
|
1001
|
-
};
|
|
1002
977
|
const context = {
|
|
1003
978
|
theme,
|
|
1004
979
|
styles: { variables: vars },
|
|
@@ -1009,9 +984,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
1009
984
|
timeZone,
|
|
1010
985
|
platform: detectPlatform(),
|
|
1011
986
|
deviceCapabilities: getDeviceCapabilities(),
|
|
1012
|
-
safeAreaInsets
|
|
1013
|
-
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : void 0,
|
|
1014
|
-
hostCapabilities
|
|
987
|
+
safeAreaInsets
|
|
1015
988
|
};
|
|
1016
989
|
this._hostContext = context;
|
|
1017
990
|
if (!this._iframe?.contentWindow) {
|
|
@@ -1054,22 +1027,15 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
1054
1027
|
*/
|
|
1055
1028
|
_postAuth(iframeOrigin) {
|
|
1056
1029
|
if (!this._pendingAuth || !this._iframe?.contentWindow) return;
|
|
1057
|
-
if (!this._pendingAuth.
|
|
1058
|
-
console.error("[Char] _postAuth called but _pendingAuth has no
|
|
1030
|
+
if (!this._pendingAuth.publishableKey) {
|
|
1031
|
+
console.error("[Char] _postAuth called but _pendingAuth has no publishableKey");
|
|
1059
1032
|
this._pendingAuth = null;
|
|
1060
1033
|
return;
|
|
1061
1034
|
}
|
|
1062
|
-
const message =
|
|
1035
|
+
const message = {
|
|
1063
1036
|
type: "char-auth",
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
type: "char-auth",
|
|
1067
|
-
anthropicApiKey: this._pendingAuth.anthropicApiKey
|
|
1068
|
-
} : {
|
|
1069
|
-
type: "char-auth",
|
|
1070
|
-
idToken: this._pendingAuth.idToken,
|
|
1071
|
-
clientId: this._pendingAuth.clientId,
|
|
1072
|
-
organizationId: this._pendingAuth.organizationId
|
|
1037
|
+
publishableKey: this._pendingAuth.publishableKey,
|
|
1038
|
+
idToken: this._pendingAuth.idToken
|
|
1073
1039
|
};
|
|
1074
1040
|
this._iframe.contentWindow.postMessage(message, iframeOrigin);
|
|
1075
1041
|
}
|
|
@@ -1216,36 +1182,6 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
1216
1182
|
this._containerResizeObserver.observe(this);
|
|
1217
1183
|
}
|
|
1218
1184
|
/**
|
|
1219
|
-
* Resolve devMode from property (React 19) or attribute.
|
|
1220
|
-
*
|
|
1221
|
-
* @returns Parsed dev-mode configuration when valid.
|
|
1222
|
-
*/
|
|
1223
|
-
_resolveDevMode() {
|
|
1224
|
-
if (this.devMode && typeof this.devMode === "object") return this.devMode;
|
|
1225
|
-
const devModeAttr = this.getAttribute("dev-mode");
|
|
1226
|
-
if (!devModeAttr) return void 0;
|
|
1227
|
-
try {
|
|
1228
|
-
const parsed = JSON.parse(devModeAttr);
|
|
1229
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1230
|
-
const msg = `dev-mode attribute must be a JSON object, got: ${Array.isArray(parsed) ? "array" : typeof parsed}`;
|
|
1231
|
-
console.warn(`[Char] ${msg}`);
|
|
1232
|
-
this._emitCharError("INVALID_DEV_MODE", msg);
|
|
1233
|
-
return;
|
|
1234
|
-
}
|
|
1235
|
-
const obj = parsed;
|
|
1236
|
-
return {
|
|
1237
|
-
anthropicApiKey: typeof obj.anthropicApiKey === "string" ? obj.anthropicApiKey : void 0,
|
|
1238
|
-
openaiApiKey: typeof obj.openaiApiKey === "string" ? obj.openaiApiKey : void 0,
|
|
1239
|
-
useLocalApi: typeof obj.useLocalApi === "boolean" ? obj.useLocalApi : void 0
|
|
1240
|
-
};
|
|
1241
|
-
} catch (e) {
|
|
1242
|
-
const msg = `Failed to parse dev-mode attribute as JSON: ${e instanceof Error ? e.message : String(e)}`;
|
|
1243
|
-
console.warn(`[Char] ${msg}`);
|
|
1244
|
-
this._emitCharError("INVALID_DEV_MODE", msg);
|
|
1245
|
-
return;
|
|
1246
|
-
}
|
|
1247
|
-
}
|
|
1248
|
-
/**
|
|
1249
1185
|
* Resolve apiBase override from property (React 19) or attribute.
|
|
1250
1186
|
*
|
|
1251
1187
|
* @returns Sanitized API base URL without trailing slash.
|