@wix/web5-core 1.63.17 → 1.63.19
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/dist/cjs/components/ui/SearchSection.css +0 -36
- package/dist/cjs/components/ui/SearchSection.js +18 -36
- package/dist/cjs/components/ui/SearchSection.js.map +1 -1
- package/dist/cjs/components/ui/UserQuery.css +106 -0
- package/dist/cjs/components/ui/UserQuery.js +64 -19
- package/dist/cjs/components/ui/UserQuery.js.map +1 -1
- package/dist/cjs/privacy/consentGate.js +1 -1
- package/dist/cjs/privacy/consentGate.js.map +1 -1
- package/dist/cjs/privacy/detectProvider.js +1 -1
- package/dist/cjs/privacy/detectProvider.js.map +1 -1
- package/dist/cjs/privacy/providers/hostSuppliedProvider.js +6 -2
- package/dist/cjs/privacy/providers/hostSuppliedProvider.js.map +1 -1
- package/dist/cjs/privacy/providers/oneTrustProvider.js +1 -1
- package/dist/cjs/privacy/providers/oneTrustProvider.js.map +1 -1
- package/dist/cjs/privacy/providers/shopifyProvider.js +7 -3
- package/dist/cjs/privacy/providers/shopifyProvider.js.map +1 -1
- package/dist/esm/components/ui/SearchSection.css +0 -36
- package/dist/esm/components/ui/SearchSection.js +1 -7
- package/dist/esm/components/ui/SearchSection.js.map +1 -1
- package/dist/esm/components/ui/UserQuery.css +106 -0
- package/dist/esm/components/ui/UserQuery.js +19 -4
- package/dist/esm/components/ui/UserQuery.js.map +1 -1
- package/dist/esm/privacy/consentGate.js +1 -1
- package/dist/esm/privacy/consentGate.js.map +1 -1
- package/dist/esm/privacy/detectProvider.js +1 -1
- package/dist/esm/privacy/detectProvider.js.map +1 -1
- package/dist/esm/privacy/providers/hostSuppliedProvider.js +6 -2
- package/dist/esm/privacy/providers/hostSuppliedProvider.js.map +1 -1
- package/dist/esm/privacy/providers/oneTrustProvider.js +1 -1
- package/dist/esm/privacy/providers/oneTrustProvider.js.map +1 -1
- package/dist/esm/privacy/providers/shopifyProvider.js +7 -3
- package/dist/esm/privacy/providers/shopifyProvider.js.map +1 -1
- package/dist/types/components/ui/SearchSection.d.ts.map +1 -1
- package/dist/types/components/ui/UserQuery.d.ts.map +1 -1
- package/dist/types/privacy/consentGate.d.ts.map +1 -1
- package/dist/types/privacy/providers/hostSuppliedProvider.d.ts.map +1 -1
- package/dist/types/privacy/providers/oneTrustProvider.d.ts.map +1 -1
- package/dist/types/privacy/providers/shopifyProvider.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/components/ui/SearchSection.css +0 -36
- package/src/components/ui/UserQuery.css +106 -0
|
@@ -326,16 +326,34 @@ const UserQuery = ({
|
|
|
326
326
|
lineNumber: 294,
|
|
327
327
|
columnNumber: 7
|
|
328
328
|
}
|
|
329
|
-
}, /*#__PURE__*/_react.default.createElement("
|
|
330
|
-
className: "
|
|
329
|
+
}, /*#__PURE__*/_react.default.createElement("span", {
|
|
330
|
+
className: "web5-prompt-hint",
|
|
331
|
+
"aria-hidden": "true",
|
|
331
332
|
__self: void 0,
|
|
332
333
|
__source: {
|
|
333
334
|
fileName: _jsxFileName,
|
|
334
|
-
lineNumber:
|
|
335
|
+
lineNumber: 307,
|
|
336
|
+
columnNumber: 9
|
|
337
|
+
}
|
|
338
|
+
}, "Press ", /*#__PURE__*/_react.default.createElement("kbd", {
|
|
339
|
+
className: "web5-prompt-hint__key",
|
|
340
|
+
__self: void 0,
|
|
341
|
+
__source: {
|
|
342
|
+
fileName: _jsxFileName,
|
|
343
|
+
lineNumber: 308,
|
|
344
|
+
columnNumber: 17
|
|
345
|
+
}
|
|
346
|
+
}, "/"), " to search"), /*#__PURE__*/_react.default.createElement("div", {
|
|
347
|
+
className: "web5-user-query__action group relative",
|
|
348
|
+
__self: void 0,
|
|
349
|
+
__source: {
|
|
350
|
+
fileName: _jsxFileName,
|
|
351
|
+
lineNumber: 311,
|
|
335
352
|
columnNumber: 9
|
|
336
353
|
}
|
|
337
354
|
}, /*#__PURE__*/_react.default.createElement("button", {
|
|
338
355
|
type: "button",
|
|
356
|
+
"aria-label": "Share this",
|
|
339
357
|
onClick: handleShare,
|
|
340
358
|
disabled: isLoading || isCopied,
|
|
341
359
|
className: `web5-user-query__share flex items-center gap-2 p-[6px] sm:px-[13px] sm:py-[5px] rounded-[30px] text-[13px] leading-5 border-0 cursor-pointer transition-colors disabled:cursor-default
|
|
@@ -343,21 +361,21 @@ const UserQuery = ({
|
|
|
343
361
|
__self: void 0,
|
|
344
362
|
__source: {
|
|
345
363
|
fileName: _jsxFileName,
|
|
346
|
-
lineNumber:
|
|
364
|
+
lineNumber: 312,
|
|
347
365
|
columnNumber: 11
|
|
348
366
|
}
|
|
349
367
|
}, isCopied ? /*#__PURE__*/_react.default.createElement(_CheckIcon.CheckIcon, {
|
|
350
368
|
__self: void 0,
|
|
351
369
|
__source: {
|
|
352
370
|
fileName: _jsxFileName,
|
|
353
|
-
lineNumber:
|
|
371
|
+
lineNumber: 324,
|
|
354
372
|
columnNumber: 25
|
|
355
373
|
}
|
|
356
374
|
}) : /*#__PURE__*/_react.default.createElement(_ShareIcon.ShareIcon, {
|
|
357
375
|
__self: void 0,
|
|
358
376
|
__source: {
|
|
359
377
|
fileName: _jsxFileName,
|
|
360
|
-
lineNumber:
|
|
378
|
+
lineNumber: 324,
|
|
361
379
|
columnNumber: 41
|
|
362
380
|
}
|
|
363
381
|
}), /*#__PURE__*/_react.default.createElement("span", {
|
|
@@ -365,47 +383,74 @@ const UserQuery = ({
|
|
|
365
383
|
__self: void 0,
|
|
366
384
|
__source: {
|
|
367
385
|
fileName: _jsxFileName,
|
|
368
|
-
lineNumber:
|
|
386
|
+
lineNumber: 325,
|
|
369
387
|
columnNumber: 13
|
|
370
388
|
}
|
|
371
|
-
}, isLoading ? 'Sharing…' : isCopied ? copiedLabel : 'Share')), isCopied && /*#__PURE__*/_react.default.createElement("
|
|
389
|
+
}, isLoading ? 'Sharing…' : isCopied ? copiedLabel : 'Share')), !isCopied && /*#__PURE__*/_react.default.createElement("span", {
|
|
390
|
+
className: "web5-user-query__tooltip",
|
|
391
|
+
role: "tooltip",
|
|
392
|
+
__self: void 0,
|
|
393
|
+
__source: {
|
|
394
|
+
fileName: _jsxFileName,
|
|
395
|
+
lineNumber: 335,
|
|
396
|
+
columnNumber: 13
|
|
397
|
+
}
|
|
398
|
+
}, "Share this"), isCopied && /*#__PURE__*/_react.default.createElement("div", {
|
|
372
399
|
className: "web5-user-query__copied-toast sm:hidden absolute top-full right-0 mt-1 flex items-center gap-2 bg-[#2b2b2b] text-white text-[12px] leading-none px-3 py-2 rounded-[8px] whitespace-nowrap",
|
|
373
400
|
__self: void 0,
|
|
374
401
|
__source: {
|
|
375
402
|
fileName: _jsxFileName,
|
|
376
|
-
lineNumber:
|
|
403
|
+
lineNumber: 341,
|
|
377
404
|
columnNumber: 13
|
|
378
405
|
}
|
|
379
406
|
}, /*#__PURE__*/_react.default.createElement(_CheckIcon.CheckIcon, {
|
|
380
407
|
__self: void 0,
|
|
381
408
|
__source: {
|
|
382
409
|
fileName: _jsxFileName,
|
|
383
|
-
lineNumber:
|
|
410
|
+
lineNumber: 342,
|
|
384
411
|
columnNumber: 15
|
|
385
412
|
}
|
|
386
|
-
}), copiedLabel)), onNewConversation && /*#__PURE__*/_react.default.createElement("
|
|
413
|
+
}), copiedLabel)), onNewConversation && /*#__PURE__*/_react.default.createElement("div", {
|
|
414
|
+
className: "web5-user-query__action group relative",
|
|
415
|
+
__self: void 0,
|
|
416
|
+
__source: {
|
|
417
|
+
fileName: _jsxFileName,
|
|
418
|
+
lineNumber: 348,
|
|
419
|
+
columnNumber: 11
|
|
420
|
+
}
|
|
421
|
+
}, /*#__PURE__*/_react.default.createElement("button", {
|
|
387
422
|
type: "button",
|
|
423
|
+
"aria-label": newConversationLabel,
|
|
388
424
|
onClick: handleNewConversation,
|
|
389
425
|
className: "web5-user-query__new-conversation flex items-center gap-2 p-[6px] sm:px-[13px] sm:py-[5px] rounded-[30px] text-[13px] leading-5 text-text-body bg-white border-0 cursor-pointer hover:bg-gray-50 transition-colors",
|
|
390
426
|
__self: void 0,
|
|
391
427
|
__source: {
|
|
392
428
|
fileName: _jsxFileName,
|
|
393
|
-
lineNumber:
|
|
394
|
-
columnNumber:
|
|
429
|
+
lineNumber: 349,
|
|
430
|
+
columnNumber: 13
|
|
395
431
|
}
|
|
396
432
|
}, /*#__PURE__*/_react.default.createElement(_PlusIcon.PlusIcon, {
|
|
397
433
|
__self: void 0,
|
|
398
434
|
__source: {
|
|
399
435
|
fileName: _jsxFileName,
|
|
400
|
-
lineNumber:
|
|
401
|
-
columnNumber:
|
|
436
|
+
lineNumber: 355,
|
|
437
|
+
columnNumber: 15
|
|
402
438
|
}
|
|
403
439
|
}), /*#__PURE__*/_react.default.createElement("span", {
|
|
404
440
|
className: "hidden sm:inline",
|
|
405
441
|
__self: void 0,
|
|
406
442
|
__source: {
|
|
407
443
|
fileName: _jsxFileName,
|
|
408
|
-
lineNumber:
|
|
444
|
+
lineNumber: 356,
|
|
445
|
+
columnNumber: 15
|
|
446
|
+
}
|
|
447
|
+
}, newConversationLabel)), /*#__PURE__*/_react.default.createElement("span", {
|
|
448
|
+
className: "web5-user-query__tooltip",
|
|
449
|
+
role: "tooltip",
|
|
450
|
+
__self: void 0,
|
|
451
|
+
__source: {
|
|
452
|
+
fileName: _jsxFileName,
|
|
453
|
+
lineNumber: 358,
|
|
409
454
|
columnNumber: 13
|
|
410
455
|
}
|
|
411
456
|
}, newConversationLabel)), onToggleCommentMode && /*#__PURE__*/_react.default.createElement("button", {
|
|
@@ -418,14 +463,14 @@ const UserQuery = ({
|
|
|
418
463
|
__self: void 0,
|
|
419
464
|
__source: {
|
|
420
465
|
fileName: _jsxFileName,
|
|
421
|
-
lineNumber:
|
|
466
|
+
lineNumber: 364,
|
|
422
467
|
columnNumber: 11
|
|
423
468
|
}
|
|
424
469
|
}, /*#__PURE__*/_react.default.createElement(_CommentIcon.CommentIcon, {
|
|
425
470
|
__self: void 0,
|
|
426
471
|
__source: {
|
|
427
472
|
fileName: _jsxFileName,
|
|
428
|
-
lineNumber:
|
|
473
|
+
lineNumber: 376,
|
|
429
474
|
columnNumber: 13
|
|
430
475
|
}
|
|
431
476
|
}), commentCount > 0 && /*#__PURE__*/_react.default.createElement("span", {
|
|
@@ -433,7 +478,7 @@ const UserQuery = ({
|
|
|
433
478
|
__self: void 0,
|
|
434
479
|
__source: {
|
|
435
480
|
fileName: _jsxFileName,
|
|
436
|
-
lineNumber:
|
|
481
|
+
lineNumber: 378,
|
|
437
482
|
columnNumber: 15
|
|
438
483
|
}
|
|
439
484
|
}, commentCount))));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_react","_interopRequireWildcard","require","_ShareIcon","_CheckIcon","_PlusIcon","_UndoIcon","_RedoIcon","_CommentIcon","_navigationStack","_jsxFileName","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","DEFAULT_LOADING_MESSAGES","headerClassName","UserQuery","query","onNewConversation","onNavigateToTriggerId","predecessorTriggerId","currentTriggerId","onShare","onToggleCommentMode","commentMode","commentCount","loading","newConversationLabel","copiedLabel","copiedDurationMs","productBackContext","onBackToProduct","forwardStack","useMemo","getForwardStack","canGoForward","length","canGoBack","hasNavigation","handleBack","useCallback","pushToForwardStack","handleForward","nextTid","popFromForwardStack","handleNewConversation","clearForwardStack","shareState","setShareState","useState","productImageError","setProductImageError","useEffect","imageUrl","handleShare","urlToCopy","window","location","href","err","console","warn","navigator","clipboard","writeText","catch","setTimeout","cyclingMessages","messages","cycledMessage","setCycledMessage","progressMessage","index","interval","setInterval","clearInterval","isCopied","isLoading","productBadge","title","backUrl","label","canNavigateBack","content","createElement","Fragment","UndoIcon","className","__self","__source","fileName","lineNumber","columnNumber","src","alt","onError","type","onClick","progressText","role","pendingQuery","disabled","RedoIcon","CheckIcon","ShareIcon","PlusIcon","CommentIcon","exports"],"sources":["../../../../src/components/ui/UserQuery.tsx"],"sourcesContent":["import React, { FC, useCallback, useEffect, useMemo, useState } from 'react';\nimport { ShareIcon } from './icons/ShareIcon';\nimport { CheckIcon } from './icons/CheckIcon';\nimport { PlusIcon } from './icons/PlusIcon';\nimport { UndoIcon } from './icons/UndoIcon';\nimport { RedoIcon } from './icons/RedoIcon';\nimport { CommentIcon } from './icons/CommentIcon';\nimport {\n getForwardStack,\n pushToForwardStack,\n popFromForwardStack,\n clearForwardStack,\n} from '../../utils/navigationStack';\n\ntype ShareState = 'idle' | 'loading' | 'copied';\n\nconst DEFAULT_LOADING_MESSAGES = [\n 'Thinking…',\n 'Recognizing intent…',\n 'Connecting the dots…',\n 'Synthesizing insights…',\n 'Formulating a response…',\n];\n\nconst headerClassName =\n 'web5-user-query sticky top-0 z-10 flex items-center justify-between bg-white/60 backdrop-blur-sm pl-5 pr-[10px] py-[7px] sm:px-[30px] sm:py-[17px]';\n\nexport interface UserQueryLoadingState {\n pendingQuery: string;\n /** Static progress message. If omitted, falls back to cycling through `messages`. */\n progressMessage?: string;\n /** Messages cycled while loading when `progressMessage` is not set. */\n messages?: string[];\n}\n\n/**\n * Product the visitor was redirected from (DL #114). Rendered as a back badge\n * above the query: back arrow + thumbnail + product name. All fields are\n * resolved and validated app-side (allowed-domain `backUrl`, `https:`\n * `imageUrl`) before reaching this presentational component.\n */\nexport interface ProductBackContext {\n /** Product display name. Required — without it the badge does not render. */\n title: string;\n /** Validated product thumbnail URL. Omitted/failed to load → arrow + title only. */\n imageUrl?: string;\n /** Validated product-page URL to return to. Omitted → badge is non-clickable. */\n backUrl?: string;\n /** Accessible label override. Defaults to `Back to ${title}`. */\n label?: string;\n}\n\nexport interface UserQueryProps {\n query: string;\n onNewConversation?: () => void;\n onNavigateToTriggerId?: (triggerId: string) => void;\n predecessorTriggerId?: string;\n currentTriggerId?: string;\n onShare?: (triggerId: string) => Promise<string>;\n /** Optional comment-mode toggle. When provided, render the chat-bubble\n * toggle button next to Share. The button reflects `commentMode` via\n * `is-on` styling and `aria-pressed`. */\n onToggleCommentMode?: () => void;\n commentMode?: boolean;\n /** Total number of comments on this response — shown as a badge next to the\n * toggle when > 0. */\n commentCount?: number;\n loading?: UserQueryLoadingState;\n /** Label for the \"new conversation\" action. Defaults to \"New conversation\". */\n newConversationLabel?: string;\n /** Label shown after the share link is copied. Defaults to \"Link Copied\". */\n copiedLabel?: string;\n /** How long the copied state stays visible, in ms. Defaults to 2000. */\n copiedDurationMs?: number;\n /** When set (visitor arrived from a product page), render a back-to-product\n * badge above the query (DL #114). */\n productBackContext?: ProductBackContext;\n /** Invoked with the validated `backUrl` when the badge is activated. */\n onBackToProduct?: (backUrl: string) => void;\n}\n\nexport const UserQuery: FC<UserQueryProps> = ({\n query,\n onNewConversation,\n onNavigateToTriggerId,\n predecessorTriggerId,\n currentTriggerId,\n onShare,\n onToggleCommentMode,\n commentMode = false,\n commentCount = 0,\n loading,\n newConversationLabel = 'New conversation',\n copiedLabel = 'Link Copied',\n copiedDurationMs = 2000,\n productBackContext,\n onBackToProduct,\n}) => {\n const forwardStack = useMemo(() => getForwardStack(), [currentTriggerId]);\n const canGoForward = forwardStack.length > 0;\n const canGoBack = !!predecessorTriggerId;\n const hasNavigation = canGoBack || canGoForward;\n\n const handleBack = useCallback(() => {\n if (!predecessorTriggerId || !currentTriggerId || !onNavigateToTriggerId) {\n return;\n }\n pushToForwardStack(currentTriggerId);\n onNavigateToTriggerId(predecessorTriggerId);\n }, [predecessorTriggerId, currentTriggerId, onNavigateToTriggerId]);\n\n const handleForward = useCallback(() => {\n if (!onNavigateToTriggerId) {\n return;\n }\n const nextTid = popFromForwardStack();\n if (!nextTid) {\n return;\n }\n onNavigateToTriggerId(nextTid);\n }, [onNavigateToTriggerId]);\n\n const handleNewConversation = useCallback(() => {\n clearForwardStack();\n onNewConversation?.();\n }, [onNewConversation]);\n\n const [shareState, setShareState] = useState<ShareState>('idle');\n\n const [productImageError, setProductImageError] = useState(false);\n useEffect(() => {\n setProductImageError(false);\n }, [productBackContext?.imageUrl]);\n\n const handleShare = useCallback(async () => {\n if (shareState !== 'idle') {\n return;\n }\n setShareState('loading');\n let urlToCopy = window.location.href;\n try {\n if (onShare && currentTriggerId) {\n urlToCopy = await onShare(currentTriggerId);\n }\n } catch (err) {\n console.warn('[share] failed, falling back to current URL:', err);\n }\n navigator.clipboard\n .writeText(urlToCopy)\n .catch((err) => console.warn('[share] clipboard write failed:', err));\n setShareState('copied');\n setTimeout(() => setShareState('idle'), copiedDurationMs);\n }, [shareState, onShare, currentTriggerId, copiedDurationMs]);\n\n const cyclingMessages = useMemo(\n () =>\n loading?.messages && loading.messages.length > 0\n ? loading.messages\n : DEFAULT_LOADING_MESSAGES,\n [loading?.messages],\n );\n const [cycledMessage, setCycledMessage] = useState(cyclingMessages[0]);\n\n useEffect(() => {\n if (!loading || loading.progressMessage) {\n return;\n }\n setCycledMessage(cyclingMessages[0]);\n if (cyclingMessages.length <= 1) {\n return;\n }\n let index = 0;\n const interval = setInterval(() => {\n index = (index + 1) % cyclingMessages.length;\n setCycledMessage(cyclingMessages[index]);\n }, 4000);\n return () => clearInterval(interval);\n }, [loading, cyclingMessages]);\n\n if (!query && !loading) {\n return null;\n }\n\n const isCopied = shareState === 'copied';\n const isLoading = shareState === 'loading';\n\n const productBadge = productBackContext\n ? (() => {\n const { title, imageUrl, backUrl, label } = productBackContext;\n const canNavigateBack = !!backUrl && !!onBackToProduct;\n const content = (\n <>\n <UndoIcon className=\"web5-user-query__product-back-icon\" />\n {imageUrl && !productImageError && (\n <span className=\"web5-user-query__product-back-thumb\">\n <img\n src={imageUrl}\n alt=\"\"\n onError={() => setProductImageError(true)}\n />\n </span>\n )}\n <span className=\"web5-user-query__product-back-title\">{title}</span>\n </>\n );\n return canNavigateBack ? (\n <button\n type=\"button\"\n onClick={() => onBackToProduct?.(backUrl!)}\n aria-label={label ?? `Back to ${title}`}\n className=\"web5-user-query__product-back\"\n >\n {content}\n </button>\n ) : (\n <div className=\"web5-user-query__product-back web5-user-query__product-back--static\">\n {content}\n </div>\n );\n })()\n : null;\n\n if (loading) {\n const progressText = loading.progressMessage ?? cycledMessage;\n return (\n <div\n className={headerClassName}\n role=\"status\"\n aria-busy=\"true\"\n aria-live=\"polite\"\n >\n <div className=\"web5-user-query__left flex-1\">\n {productBadge}\n <p\n className=\"text-[14px] leading-5 text-text-body truncate min-w-0 max-w-[700px]\"\n title={`Asking: ${loading.pendingQuery}`}\n >\n <span className=\"text-text-body/70\">Asking: </span>\n {loading.pendingQuery}\n </p>\n </div>\n {progressText && (\n <div className=\"hidden sm:flex items-center gap-2 ml-[10px] shrink-0 min-w-0\">\n <div\n className=\"w-4 h-4 shrink-0 animate-spin inline-block border-[2px] border-current border-t-transparent text-blue-600 dark:text-blue-500 rounded-full\"\n aria-hidden=\"true\"\n />\n <p className=\"text-[13px] leading-5 text-text-body/70 truncate max-w-[240px]\">\n {progressText}\n </p>\n </div>\n )}\n </div>\n );\n }\n\n return (\n <div className={headerClassName}>\n <div className=\"flex items-center gap-[30px] flex-1 min-w-0\">\n <div className=\"web5-user-query__left\">\n {productBadge}\n <p\n className=\"web5-user-query__title text-[14px] leading-5 text-text-body truncate min-w-0 max-w-[700px]\"\n title={query}\n >\n {query}\n </p>\n </div>\n {hasNavigation && (\n <div className=\"flex items-center gap-[9px]\">\n <button\n type=\"button\"\n onClick={handleBack}\n disabled={!canGoBack}\n title=\"Go to previous conversation\"\n aria-label=\"Go to previous conversation\"\n className=\"w-6 h-6 flex items-center justify-center text-text-body hover:text-black transition-colors bg-transparent border-0 cursor-pointer disabled:opacity-30 disabled:cursor-default\"\n >\n <UndoIcon />\n </button>\n <button\n type=\"button\"\n onClick={handleForward}\n disabled={!canGoForward}\n title=\"Go to next conversation\"\n aria-label=\"Go to next conversation\"\n className=\"w-6 h-6 flex items-center justify-center text-text-body hover:text-black transition-colors bg-transparent border-0 cursor-pointer disabled:opacity-30 disabled:cursor-default\"\n >\n <RedoIcon />\n </button>\n </div>\n )}\n </div>\n <div className=\"flex items-center gap-[10px] ml-[10px] sm:ml-0 shrink-0\">\n {/* Share button — wraps button + mobile tooltip in a relative container */}\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={handleShare}\n disabled={isLoading || isCopied}\n className={`web5-user-query__share flex items-center gap-2 p-[6px] sm:px-[13px] sm:py-[5px] rounded-[30px] text-[13px] leading-5 border-0 cursor-pointer transition-colors disabled:cursor-default\n ${\n isCopied\n ? 'is-copied bg-[#2b2b2b] text-white'\n : 'text-text-body bg-white hover:bg-gray-50'\n }`}\n >\n {isCopied ? <CheckIcon /> : <ShareIcon />}\n <span className=\"hidden sm:inline\">\n {isLoading ? 'Sharing…' : isCopied ? copiedLabel : 'Share'}\n </span>\n </button>\n {/* Mobile-only tooltip: drops below the button when link is copied */}\n {isCopied && (\n <div className=\"web5-user-query__copied-toast sm:hidden absolute top-full right-0 mt-1 flex items-center gap-2 bg-[#2b2b2b] text-white text-[12px] leading-none px-3 py-2 rounded-[8px] whitespace-nowrap\">\n <CheckIcon />\n {copiedLabel}\n </div>\n )}\n </div>\n {onNewConversation && (\n <button\n type=\"button\"\n onClick={handleNewConversation}\n className=\"web5-user-query__new-conversation flex items-center gap-2 p-[6px] sm:px-[13px] sm:py-[5px] rounded-[30px] text-[13px] leading-5 text-text-body bg-white border-0 cursor-pointer hover:bg-gray-50 transition-colors\"\n >\n <PlusIcon />\n <span className=\"hidden sm:inline\">{newConversationLabel}</span>\n </button>\n )}\n {onToggleCommentMode && (\n <button\n type=\"button\"\n onClick={onToggleCommentMode}\n aria-pressed={commentMode}\n title=\"Add a comment\"\n aria-label=\"Add a comment\"\n className={`relative flex items-center gap-2 p-[6px] sm:px-[13px] sm:py-[5px] rounded-[30px] text-[13px] leading-5 border-0 cursor-pointer transition-colors ${\n commentMode\n ? 'bg-[#2b2b2b] text-white'\n : 'text-text-body bg-white hover:bg-gray-50'\n }`}\n >\n <CommentIcon />\n {commentCount > 0 && (\n <span\n className={`absolute -top-1 -right-1 min-w-[18px] h-[18px] px-1 rounded-full text-[10px] leading-[18px] text-center font-semibold ${\n commentMode ? 'bg-white text-[#2b2b2b]' : 'bg-[#2b2b2b] text-white'\n }`}\n >\n {commentCount}\n </span>\n )}\n </button>\n )}\n </div>\n </div>\n );\n};\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["_react","_interopRequireWildcard","require","_ShareIcon","_CheckIcon","_PlusIcon","_UndoIcon","_RedoIcon","_CommentIcon","_navigationStack","_jsxFileName","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","DEFAULT_LOADING_MESSAGES","headerClassName","UserQuery","query","onNewConversation","onNavigateToTriggerId","predecessorTriggerId","currentTriggerId","onShare","onToggleCommentMode","commentMode","commentCount","loading","newConversationLabel","copiedLabel","copiedDurationMs","productBackContext","onBackToProduct","forwardStack","useMemo","getForwardStack","canGoForward","length","canGoBack","hasNavigation","handleBack","useCallback","pushToForwardStack","handleForward","nextTid","popFromForwardStack","handleNewConversation","clearForwardStack","shareState","setShareState","useState","productImageError","setProductImageError","useEffect","imageUrl","handleShare","urlToCopy","window","location","href","err","console","warn","navigator","clipboard","writeText","catch","setTimeout","cyclingMessages","messages","cycledMessage","setCycledMessage","progressMessage","index","interval","setInterval","clearInterval","isCopied","isLoading","productBadge","title","backUrl","label","canNavigateBack","content","createElement","Fragment","UndoIcon","className","__self","__source","fileName","lineNumber","columnNumber","src","alt","onError","type","onClick","progressText","role","pendingQuery","disabled","RedoIcon","CheckIcon","ShareIcon","PlusIcon","CommentIcon","exports"],"sources":["../../../../src/components/ui/UserQuery.tsx"],"sourcesContent":["import React, { FC, useCallback, useEffect, useMemo, useState } from 'react';\nimport { ShareIcon } from './icons/ShareIcon';\nimport { CheckIcon } from './icons/CheckIcon';\nimport { PlusIcon } from './icons/PlusIcon';\nimport { UndoIcon } from './icons/UndoIcon';\nimport { RedoIcon } from './icons/RedoIcon';\nimport { CommentIcon } from './icons/CommentIcon';\nimport {\n getForwardStack,\n pushToForwardStack,\n popFromForwardStack,\n clearForwardStack,\n} from '../../utils/navigationStack';\n\ntype ShareState = 'idle' | 'loading' | 'copied';\n\nconst DEFAULT_LOADING_MESSAGES = [\n 'Thinking…',\n 'Recognizing intent…',\n 'Connecting the dots…',\n 'Synthesizing insights…',\n 'Formulating a response…',\n];\n\nconst headerClassName =\n 'web5-user-query sticky top-0 z-10 flex items-center justify-between bg-white/60 backdrop-blur-sm pl-5 pr-[10px] py-[7px] sm:px-[30px] sm:py-[17px]';\n\nexport interface UserQueryLoadingState {\n pendingQuery: string;\n /** Static progress message. If omitted, falls back to cycling through `messages`. */\n progressMessage?: string;\n /** Messages cycled while loading when `progressMessage` is not set. */\n messages?: string[];\n}\n\n/**\n * Product the visitor was redirected from (DL #114). Rendered as a back badge\n * above the query: back arrow + thumbnail + product name. All fields are\n * resolved and validated app-side (allowed-domain `backUrl`, `https:`\n * `imageUrl`) before reaching this presentational component.\n */\nexport interface ProductBackContext {\n /** Product display name. Required — without it the badge does not render. */\n title: string;\n /** Validated product thumbnail URL. Omitted/failed to load → arrow + title only. */\n imageUrl?: string;\n /** Validated product-page URL to return to. Omitted → badge is non-clickable. */\n backUrl?: string;\n /** Accessible label override. Defaults to `Back to ${title}`. */\n label?: string;\n}\n\nexport interface UserQueryProps {\n query: string;\n onNewConversation?: () => void;\n onNavigateToTriggerId?: (triggerId: string) => void;\n predecessorTriggerId?: string;\n currentTriggerId?: string;\n onShare?: (triggerId: string) => Promise<string>;\n /** Optional comment-mode toggle. When provided, render the chat-bubble\n * toggle button next to Share. The button reflects `commentMode` via\n * `is-on` styling and `aria-pressed`. */\n onToggleCommentMode?: () => void;\n commentMode?: boolean;\n /** Total number of comments on this response — shown as a badge next to the\n * toggle when > 0. */\n commentCount?: number;\n loading?: UserQueryLoadingState;\n /** Label for the \"new conversation\" action. Defaults to \"New conversation\". */\n newConversationLabel?: string;\n /** Label shown after the share link is copied. Defaults to \"Link Copied\". */\n copiedLabel?: string;\n /** How long the copied state stays visible, in ms. Defaults to 2000. */\n copiedDurationMs?: number;\n /** When set (visitor arrived from a product page), render a back-to-product\n * badge above the query (DL #114). */\n productBackContext?: ProductBackContext;\n /** Invoked with the validated `backUrl` when the badge is activated. */\n onBackToProduct?: (backUrl: string) => void;\n}\n\nexport const UserQuery: FC<UserQueryProps> = ({\n query,\n onNewConversation,\n onNavigateToTriggerId,\n predecessorTriggerId,\n currentTriggerId,\n onShare,\n onToggleCommentMode,\n commentMode = false,\n commentCount = 0,\n loading,\n newConversationLabel = 'New conversation',\n copiedLabel = 'Link Copied',\n copiedDurationMs = 2000,\n productBackContext,\n onBackToProduct,\n}) => {\n const forwardStack = useMemo(() => getForwardStack(), [currentTriggerId]);\n const canGoForward = forwardStack.length > 0;\n const canGoBack = !!predecessorTriggerId;\n const hasNavigation = canGoBack || canGoForward;\n\n const handleBack = useCallback(() => {\n if (!predecessorTriggerId || !currentTriggerId || !onNavigateToTriggerId) {\n return;\n }\n pushToForwardStack(currentTriggerId);\n onNavigateToTriggerId(predecessorTriggerId);\n }, [predecessorTriggerId, currentTriggerId, onNavigateToTriggerId]);\n\n const handleForward = useCallback(() => {\n if (!onNavigateToTriggerId) {\n return;\n }\n const nextTid = popFromForwardStack();\n if (!nextTid) {\n return;\n }\n onNavigateToTriggerId(nextTid);\n }, [onNavigateToTriggerId]);\n\n const handleNewConversation = useCallback(() => {\n clearForwardStack();\n onNewConversation?.();\n }, [onNewConversation]);\n\n const [shareState, setShareState] = useState<ShareState>('idle');\n\n const [productImageError, setProductImageError] = useState(false);\n useEffect(() => {\n setProductImageError(false);\n }, [productBackContext?.imageUrl]);\n\n const handleShare = useCallback(async () => {\n if (shareState !== 'idle') {\n return;\n }\n setShareState('loading');\n let urlToCopy = window.location.href;\n try {\n if (onShare && currentTriggerId) {\n urlToCopy = await onShare(currentTriggerId);\n }\n } catch (err) {\n console.warn('[share] failed, falling back to current URL:', err);\n }\n navigator.clipboard\n .writeText(urlToCopy)\n .catch((err) => console.warn('[share] clipboard write failed:', err));\n setShareState('copied');\n setTimeout(() => setShareState('idle'), copiedDurationMs);\n }, [shareState, onShare, currentTriggerId, copiedDurationMs]);\n\n const cyclingMessages = useMemo(\n () =>\n loading?.messages && loading.messages.length > 0\n ? loading.messages\n : DEFAULT_LOADING_MESSAGES,\n [loading?.messages],\n );\n const [cycledMessage, setCycledMessage] = useState(cyclingMessages[0]);\n\n useEffect(() => {\n if (!loading || loading.progressMessage) {\n return;\n }\n setCycledMessage(cyclingMessages[0]);\n if (cyclingMessages.length <= 1) {\n return;\n }\n let index = 0;\n const interval = setInterval(() => {\n index = (index + 1) % cyclingMessages.length;\n setCycledMessage(cyclingMessages[index]);\n }, 4000);\n return () => clearInterval(interval);\n }, [loading, cyclingMessages]);\n\n if (!query && !loading) {\n return null;\n }\n\n const isCopied = shareState === 'copied';\n const isLoading = shareState === 'loading';\n\n const productBadge = productBackContext\n ? (() => {\n const { title, imageUrl, backUrl, label } = productBackContext;\n const canNavigateBack = !!backUrl && !!onBackToProduct;\n const content = (\n <>\n <UndoIcon className=\"web5-user-query__product-back-icon\" />\n {imageUrl && !productImageError && (\n <span className=\"web5-user-query__product-back-thumb\">\n <img\n src={imageUrl}\n alt=\"\"\n onError={() => setProductImageError(true)}\n />\n </span>\n )}\n <span className=\"web5-user-query__product-back-title\">{title}</span>\n </>\n );\n return canNavigateBack ? (\n <button\n type=\"button\"\n onClick={() => onBackToProduct?.(backUrl!)}\n aria-label={label ?? `Back to ${title}`}\n className=\"web5-user-query__product-back\"\n >\n {content}\n </button>\n ) : (\n <div className=\"web5-user-query__product-back web5-user-query__product-back--static\">\n {content}\n </div>\n );\n })()\n : null;\n\n if (loading) {\n const progressText = loading.progressMessage ?? cycledMessage;\n return (\n <div\n className={headerClassName}\n role=\"status\"\n aria-busy=\"true\"\n aria-live=\"polite\"\n >\n <div className=\"web5-user-query__left flex-1\">\n {productBadge}\n <p\n className=\"text-[14px] leading-5 text-text-body truncate min-w-0 max-w-[700px]\"\n title={`Asking: ${loading.pendingQuery}`}\n >\n <span className=\"text-text-body/70\">Asking: </span>\n {loading.pendingQuery}\n </p>\n </div>\n {progressText && (\n <div className=\"hidden sm:flex items-center gap-2 ml-[10px] shrink-0 min-w-0\">\n <div\n className=\"w-4 h-4 shrink-0 animate-spin inline-block border-[2px] border-current border-t-transparent text-blue-600 dark:text-blue-500 rounded-full\"\n aria-hidden=\"true\"\n />\n <p className=\"text-[13px] leading-5 text-text-body/70 truncate max-w-[240px]\">\n {progressText}\n </p>\n </div>\n )}\n </div>\n );\n }\n\n return (\n <div className={headerClassName}>\n <div className=\"flex items-center gap-[30px] flex-1 min-w-0\">\n <div className=\"web5-user-query__left\">\n {productBadge}\n <p\n className=\"web5-user-query__title text-[14px] leading-5 text-text-body truncate min-w-0 max-w-[700px]\"\n title={query}\n >\n {query}\n </p>\n </div>\n {hasNavigation && (\n <div className=\"flex items-center gap-[9px]\">\n <button\n type=\"button\"\n onClick={handleBack}\n disabled={!canGoBack}\n title=\"Go to previous conversation\"\n aria-label=\"Go to previous conversation\"\n className=\"w-6 h-6 flex items-center justify-center text-text-body hover:text-black transition-colors bg-transparent border-0 cursor-pointer disabled:opacity-30 disabled:cursor-default\"\n >\n <UndoIcon />\n </button>\n <button\n type=\"button\"\n onClick={handleForward}\n disabled={!canGoForward}\n title=\"Go to next conversation\"\n aria-label=\"Go to next conversation\"\n className=\"w-6 h-6 flex items-center justify-center text-text-body hover:text-black transition-colors bg-transparent border-0 cursor-pointer disabled:opacity-30 disabled:cursor-default\"\n >\n <RedoIcon />\n </button>\n </div>\n )}\n </div>\n <div className=\"flex items-center gap-[10px] ml-[10px] sm:ml-0 shrink-0\">\n {/*\n Keyboard hint. It used to sit inside the prompt field, which was the\n wrong home for it twice over: it competed with the placeholder for the\n same space, and it disappeared exactly when the reader engaged with\n the input — so the shortcut was only ever advertised to someone who\n was not using it. Here it sits with the other page-level affordances\n and stays legible while the reader types.\n\n `aria-hidden` because the shortcut is already announced on the input\n itself via `aria-keyshortcuts`; this is the visual reminder, and\n repeating it to assistive tech would just be noise.\n */}\n <span className=\"web5-prompt-hint\" aria-hidden=\"true\">\n Press <kbd className=\"web5-prompt-hint__key\">/</kbd> to search\n </span>\n {/* Share button — wraps button + tooltips in a relative container */}\n <div className=\"web5-user-query__action group relative\">\n <button\n type=\"button\"\n aria-label=\"Share this\"\n onClick={handleShare}\n disabled={isLoading || isCopied}\n className={`web5-user-query__share flex items-center gap-2 p-[6px] sm:px-[13px] sm:py-[5px] rounded-[30px] text-[13px] leading-5 border-0 cursor-pointer transition-colors disabled:cursor-default\n ${\n isCopied\n ? 'is-copied bg-[#2b2b2b] text-white'\n : 'text-text-body bg-white hover:bg-gray-50'\n }`}\n >\n {isCopied ? <CheckIcon /> : <ShareIcon />}\n <span className=\"hidden sm:inline\">\n {isLoading ? 'Sharing…' : isCopied ? copiedLabel : 'Share'}\n </span>\n </button>\n {/*\n Hover tooltip. Suppressed once the link is copied, because the\n copied state is its own message and two overlapping bubbles saying\n different things is worse than none.\n */}\n {!isCopied && (\n <span className=\"web5-user-query__tooltip\" role=\"tooltip\">\n Share this\n </span>\n )}\n {/* Mobile-only tooltip: drops below the button when link is copied */}\n {isCopied && (\n <div className=\"web5-user-query__copied-toast sm:hidden absolute top-full right-0 mt-1 flex items-center gap-2 bg-[#2b2b2b] text-white text-[12px] leading-none px-3 py-2 rounded-[8px] whitespace-nowrap\">\n <CheckIcon />\n {copiedLabel}\n </div>\n )}\n </div>\n {onNewConversation && (\n <div className=\"web5-user-query__action group relative\">\n <button\n type=\"button\"\n aria-label={newConversationLabel}\n onClick={handleNewConversation}\n className=\"web5-user-query__new-conversation flex items-center gap-2 p-[6px] sm:px-[13px] sm:py-[5px] rounded-[30px] text-[13px] leading-5 text-text-body bg-white border-0 cursor-pointer hover:bg-gray-50 transition-colors\"\n >\n <PlusIcon />\n <span className=\"hidden sm:inline\">{newConversationLabel}</span>\n </button>\n <span className=\"web5-user-query__tooltip\" role=\"tooltip\">\n {newConversationLabel}\n </span>\n </div>\n )}\n {onToggleCommentMode && (\n <button\n type=\"button\"\n onClick={onToggleCommentMode}\n aria-pressed={commentMode}\n title=\"Add a comment\"\n aria-label=\"Add a comment\"\n className={`relative flex items-center gap-2 p-[6px] sm:px-[13px] sm:py-[5px] rounded-[30px] text-[13px] leading-5 border-0 cursor-pointer transition-colors ${\n commentMode\n ? 'bg-[#2b2b2b] text-white'\n : 'text-text-body bg-white hover:bg-gray-50'\n }`}\n >\n <CommentIcon />\n {commentCount > 0 && (\n <span\n className={`absolute -top-1 -right-1 min-w-[18px] h-[18px] px-1 rounded-full text-[10px] leading-[18px] text-center font-semibold ${\n commentMode ? 'bg-white text-[#2b2b2b]' : 'bg-[#2b2b2b] text-white'\n }`}\n >\n {commentCount}\n </span>\n )}\n </button>\n )}\n </div>\n </div>\n );\n};\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_types","require","DEFAULT_BUFFER_LIMIT","LOG_PREFIX","log","args","console","describe","s","analytics","performance","marketing","snapshot","UNKNOWN_CONSENT","engaged","provider","unsubscribeProvider","buffer","bufferLimit","droppedFromOverflow","listeners","Set","view","Object","freeze","consent","rebuildView","notify","listener","mayTransmit","purpose","state","exports","settleBuffer","length","stillHeld","releasing","held","push","dropped","label","send","transmit","unlockOnUserAction","isUserEngaged","mayPersistIdentity","getConsentSnapshot","getGateView","subscribeToConsent","add","delete","applySnapshot","next","installConsentProvider","initial","read","name","subscribe","getInstalledProviderName","_provider","setConsentBufferLimit","limit","Math","max","getConsentGateStats","resetConsentGateForTests","clear"],"sources":["../../../src/privacy/consentGate.ts"],"sourcesContent":["/**\n * The consent gate — DL #218.\n *\n * Every BI, telemetry and analytics emitter in web5 goes through `transmit()`\n * instead of calling its transport directly. The gate exists because the\n * consent signal resolves *after* the events that need it: Shopify's consent\n * API is not on the page by default and has to be requested, and the fedops\n * app-load pair fires inside that window. A boolean checked at each call site\n * can only ever drop an event that is already in flight; the gate can hold it.\n *\n * Two independent unlocks, and they do not compose as a plain \"either/or\":\n *\n * | consent | engaged | result |\n * |-----------|---------|---------------------------------|\n * | granted | either | transmit |\n * | unknown | no | hold in the buffer |\n * | unknown | yes | transmit, and flush what's held |\n * | denied | yes | still nothing |\n *\n * Denied outranks engaged. A visitor who refused analytics in the banner does\n * not re-enable tracking by typing a question.\n */\n\nimport {\n UNKNOWN_CONSENT,\n type ConsentProvider,\n type ConsentPurpose,\n type ConsentSnapshot,\n type GatedPurpose,\n} from './types';\n\n/** Held events are dropped past this many — a runaway emitter must not become a leak. */\nconst DEFAULT_BUFFER_LIMIT = 50;\n\n/**\n * One prefix for the whole consent trail, so `[w5-consent]` in the console\n * filter shows the full story: which CMP was found, what it answered, and for\n * every event whether it was sent, held, dropped or replayed.\n */\nconst LOG_PREFIX = '[w5-consent]';\n\nconst log = (...args: unknown[]): void => {\n try {\n console.log(LOG_PREFIX, ...args);\n } catch {\n // A console that throws must never break a send.\n }\n};\n\nconst describe = (s: ConsentSnapshot): string =>\n `analytics=${s.analytics} performance=${s.performance} marketing=${s.marketing}`;\n\ninterface HeldEvent {\n purpose: GatedPurpose;\n send: () => void;\n label: string;\n}\n\nexport interface GateView {\n consent: ConsentSnapshot;\n engaged: boolean;\n}\n\ntype Listener = () => void;\n\nlet snapshot: ConsentSnapshot = UNKNOWN_CONSENT;\nlet engaged = false;\nlet provider: ConsentProvider | null = null;\nlet unsubscribeProvider: (() => void) | null = null;\nlet buffer: HeldEvent[] = [];\nlet bufferLimit = DEFAULT_BUFFER_LIMIT;\nlet droppedFromOverflow = 0;\n\nconst listeners = new Set<Listener>();\n\n/**\n * Cached so `getGateView` is referentially stable between changes —\n * `useSyncExternalStore` re-renders forever if the snapshot is a fresh object\n * on every read.\n */\nlet view: GateView = Object.freeze({ consent: UNKNOWN_CONSENT, engaged: false });\n\nconst rebuildView = (): void => {\n view = Object.freeze({ consent: snapshot, engaged });\n};\n\nconst notify = (): void => {\n for (const listener of listeners) {\n try {\n listener();\n } catch {\n // A subscriber that throws must not stop the others, or stop a flush.\n }\n }\n};\n\n/**\n * Whether an event for `purpose` may go on the wire right now.\n *\n * `necessary` is always allowed. For everything else: explicit consent wins in\n * both directions, and engagement resolves only the `unknown` case.\n */\nexport const mayTransmit = (purpose: ConsentPurpose): boolean => {\n if (purpose === 'necessary') {\n return true;\n }\n const state = snapshot[purpose];\n if (state === 'denied') {\n return false;\n }\n if (state === 'granted') {\n return true;\n }\n return engaged;\n};\n\n/**\n * Release everything the gate is holding that is now allowed, and drop\n * everything that is now refused. Anything still `unknown` stays held.\n *\n * Order is preserved among released events, but a flush necessarily emits them\n * later than they were raised — a held app-load event reaches the wire after\n * the prompt that unlocked it. Anything reading these as a timeline has to\n * know that.\n */\nconst settleBuffer = (): void => {\n if (buffer.length === 0) {\n return;\n }\n const stillHeld: HeldEvent[] = [];\n const releasing: HeldEvent[] = [];\n\n for (const held of buffer) {\n if (mayTransmit(held.purpose)) {\n releasing.push(held);\n } else if (snapshot[held.purpose] === 'denied') {\n // dropped on the floor, deliberately and permanently\n } else {\n stillHeld.push(held);\n }\n }\n\n const dropped = buffer.length - stillHeld.length - releasing.length;\n buffer = stillHeld;\n\n if (releasing.length || dropped) {\n log(\n `settle — releasing ${releasing.length}` +\n (dropped ? `, dropping ${dropped} (refused)` : '') +\n (stillHeld.length ? `, still holding ${stillHeld.length}` : ''),\n );\n }\n\n for (const held of releasing) {\n try {\n log(` ↳ REPLAY ${held.label} (${held.purpose})`);\n held.send();\n } catch {\n // A failed send must not strand the rest of the flush.\n }\n }\n};\n\n/**\n * The single entry point for anything that would put a request on the wire.\n *\n * Allowed → sent now. Refused → dropped. Not yet known → held until an unlock\n * settles it, or discarded with the page.\n */\nexport const transmit = (\n purpose: ConsentPurpose,\n send: () => void,\n label = 'event',\n): void => {\n if (mayTransmit(purpose)) {\n log(`SEND ${label} (${purpose}) — allowed`);\n send();\n return;\n }\n if (purpose !== 'necessary' && snapshot[purpose] === 'denied') {\n log(`DROP ${label} (${purpose}) — visitor refused`);\n return;\n }\n if (buffer.length >= bufferLimit) {\n droppedFromOverflow += 1;\n log(\n `DROP ${label} (${purpose}) — buffer full at ${bufferLimit}, ${droppedFromOverflow} lost`,\n );\n return;\n }\n buffer.push({ purpose: purpose as GatedPurpose, send, label });\n log(`HOLD ${label} (${purpose}) — no answer yet, ${buffer.length} held`);\n};\n\n/**\n * The visitor did something deliberate — submitted a prompt — so the session\n * may be measured even though no CMP has answered.\n *\n * This is the unlock that keeps web5 measurable at all: on a Shopify store\n * with no privacy configuration the consent API never loads, consent stays\n * `unknown` forever, and without this every store would report nothing.\n *\n * It unlocks **transmission only**. Persistent identity stays gated on real\n * consent (see `mayPersistIdentity`) — typing a question is a reason to\n * measure the session, not a reason to persist an identifier across visits.\n * It also cannot override a `denied`.\n */\nexport const unlockOnUserAction = (): void => {\n if (engaged) {\n return;\n }\n engaged = true;\n log(\n `UNLOCK — visitor submitted a prompt; ${buffer.length} held event(s) to settle`,\n );\n rebuildView();\n settleBuffer();\n notify();\n};\n\n/** Whether the visitor has taken the deliberate action that unlocks the session. */\nexport const isUserEngaged = (): boolean => engaged;\n\n/**\n * Persistent, cross-visit storage of a visitor identifier requires real\n * consent — engagement is deliberately not enough.\n */\nexport const mayPersistIdentity = (): boolean => snapshot.analytics === 'granted';\n\nexport const getConsentSnapshot = (): ConsentSnapshot => snapshot;\n\n/** Referentially stable between changes, for `useSyncExternalStore`. */\nexport const getGateView = (): GateView => view;\n\nexport const subscribeToConsent = (listener: Listener): (() => void) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n};\n\nconst applySnapshot = (next: ConsentSnapshot): void => {\n if (\n next.analytics === snapshot.analytics &&\n next.marketing === snapshot.marketing &&\n next.performance === snapshot.performance\n ) {\n return;\n }\n log(`ANSWER — ${describe(snapshot)} → ${describe(next)}`);\n snapshot = Object.freeze({ ...next });\n rebuildView();\n settleBuffer();\n notify();\n};\n\n/**\n * Install the host's provider. Reads its current state immediately, *then*\n * subscribes — DL #217: a listener registered into an already-rendered page is\n * bound to an event that may have fired long before the bundle executed, so\n * subscribing alone would leave the gate permanently at `unknown`.\n */\nexport const installConsentProvider = (next: ConsentProvider): void => {\n unsubscribeProvider?.();\n provider = next;\n const initial = next.read();\n log(`provider installed: ${next.name} — reads ${describe(initial)}`);\n applySnapshot(initial);\n unsubscribeProvider = next.subscribe(applySnapshot);\n};\n\nexport const getInstalledProviderName = (): string | null => provider?.name ?? null;\n\nexport const setConsentBufferLimit = (limit: number): void => {\n bufferLimit = Math.max(0, limit);\n};\n\n/** Diagnostics only — how much the gate is holding, and what it had to drop. */\nexport const getConsentGateStats = (): {\n held: number;\n droppedFromOverflow: number;\n} => ({ held: buffer.length, droppedFromOverflow });\n\nexport const resetConsentGateForTests = (): void => {\n unsubscribeProvider?.();\n unsubscribeProvider = null;\n provider = null;\n snapshot = UNKNOWN_CONSENT;\n engaged = false;\n buffer = [];\n bufferLimit = DEFAULT_BUFFER_LIMIT;\n droppedFromOverflow = 0;\n listeners.clear();\n rebuildView();\n};\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["_types","require","DEFAULT_BUFFER_LIMIT","LOG_PREFIX","log","args","console","debug","describe","s","analytics","performance","marketing","snapshot","UNKNOWN_CONSENT","engaged","provider","unsubscribeProvider","buffer","bufferLimit","droppedFromOverflow","listeners","Set","view","Object","freeze","consent","rebuildView","notify","listener","mayTransmit","purpose","state","exports","settleBuffer","length","stillHeld","releasing","held","push","dropped","label","send","transmit","unlockOnUserAction","isUserEngaged","mayPersistIdentity","getConsentSnapshot","getGateView","subscribeToConsent","add","delete","applySnapshot","next","installConsentProvider","initial","read","name","subscribe","getInstalledProviderName","_provider","setConsentBufferLimit","limit","Math","max","getConsentGateStats","resetConsentGateForTests","clear"],"sources":["../../../src/privacy/consentGate.ts"],"sourcesContent":["/**\n * The consent gate — DL #218.\n *\n * Every BI, telemetry and analytics emitter in web5 goes through `transmit()`\n * instead of calling its transport directly. The gate exists because the\n * consent signal resolves *after* the events that need it: Shopify's consent\n * API is not on the page by default and has to be requested, and the fedops\n * app-load pair fires inside that window. A boolean checked at each call site\n * can only ever drop an event that is already in flight; the gate can hold it.\n *\n * Two independent unlocks, and they do not compose as a plain \"either/or\":\n *\n * | consent | engaged | result |\n * |-----------|---------|---------------------------------|\n * | granted | either | transmit |\n * | unknown | no | hold in the buffer |\n * | unknown | yes | transmit, and flush what's held |\n * | denied | yes | still nothing |\n *\n * Denied outranks engaged. A visitor who refused analytics in the banner does\n * not re-enable tracking by typing a question.\n */\n\nimport {\n UNKNOWN_CONSENT,\n type ConsentProvider,\n type ConsentPurpose,\n type ConsentSnapshot,\n type GatedPurpose,\n} from './types';\n\n/** Held events are dropped past this many — a runaway emitter must not become a leak. */\nconst DEFAULT_BUFFER_LIMIT = 50;\n\n/**\n * One prefix for the whole consent trail, so `[w5-consent]` in the console\n * filter shows the full story: which CMP was found, what it answered, and for\n * every event whether it was sent, held, dropped or replayed.\n */\nconst LOG_PREFIX = '[w5-consent]';\n\nconst log = (...args: unknown[]): void => {\n try {\n console.debug(LOG_PREFIX, ...args);\n } catch {\n // A console that throws must never break a send.\n }\n};\n\nconst describe = (s: ConsentSnapshot): string =>\n `analytics=${s.analytics} performance=${s.performance} marketing=${s.marketing}`;\n\ninterface HeldEvent {\n purpose: GatedPurpose;\n send: () => void;\n label: string;\n}\n\nexport interface GateView {\n consent: ConsentSnapshot;\n engaged: boolean;\n}\n\ntype Listener = () => void;\n\nlet snapshot: ConsentSnapshot = UNKNOWN_CONSENT;\nlet engaged = false;\nlet provider: ConsentProvider | null = null;\nlet unsubscribeProvider: (() => void) | null = null;\nlet buffer: HeldEvent[] = [];\nlet bufferLimit = DEFAULT_BUFFER_LIMIT;\nlet droppedFromOverflow = 0;\n\nconst listeners = new Set<Listener>();\n\n/**\n * Cached so `getGateView` is referentially stable between changes —\n * `useSyncExternalStore` re-renders forever if the snapshot is a fresh object\n * on every read.\n */\nlet view: GateView = Object.freeze({\n consent: UNKNOWN_CONSENT,\n engaged: false,\n});\n\nconst rebuildView = (): void => {\n view = Object.freeze({ consent: snapshot, engaged });\n};\n\nconst notify = (): void => {\n for (const listener of listeners) {\n try {\n listener();\n } catch {\n // A subscriber that throws must not stop the others, or stop a flush.\n }\n }\n};\n\n/**\n * Whether an event for `purpose` may go on the wire right now.\n *\n * `necessary` is always allowed. For everything else: explicit consent wins in\n * both directions, and engagement resolves only the `unknown` case.\n */\nexport const mayTransmit = (purpose: ConsentPurpose): boolean => {\n if (purpose === 'necessary') {\n return true;\n }\n const state = snapshot[purpose];\n if (state === 'denied') {\n return false;\n }\n if (state === 'granted') {\n return true;\n }\n return engaged;\n};\n\n/**\n * Release everything the gate is holding that is now allowed, and drop\n * everything that is now refused. Anything still `unknown` stays held.\n *\n * Order is preserved among released events, but a flush necessarily emits them\n * later than they were raised — a held app-load event reaches the wire after\n * the prompt that unlocked it. Anything reading these as a timeline has to\n * know that.\n */\nconst settleBuffer = (): void => {\n if (buffer.length === 0) {\n return;\n }\n const stillHeld: HeldEvent[] = [];\n const releasing: HeldEvent[] = [];\n\n for (const held of buffer) {\n if (mayTransmit(held.purpose)) {\n releasing.push(held);\n } else if (snapshot[held.purpose] === 'denied') {\n // dropped on the floor, deliberately and permanently\n } else {\n stillHeld.push(held);\n }\n }\n\n const dropped = buffer.length - stillHeld.length - releasing.length;\n buffer = stillHeld;\n\n if (releasing.length || dropped) {\n log(\n `settle — releasing ${releasing.length}` +\n (dropped ? `, dropping ${dropped} (refused)` : '') +\n (stillHeld.length ? `, still holding ${stillHeld.length}` : ''),\n );\n }\n\n for (const held of releasing) {\n try {\n log(` ↳ REPLAY ${held.label} (${held.purpose})`);\n held.send();\n } catch {\n // A failed send must not strand the rest of the flush.\n }\n }\n};\n\n/**\n * The single entry point for anything that would put a request on the wire.\n *\n * Allowed → sent now. Refused → dropped. Not yet known → held until an unlock\n * settles it, or discarded with the page.\n */\nexport const transmit = (\n purpose: ConsentPurpose,\n send: () => void,\n label = 'event',\n): void => {\n if (mayTransmit(purpose)) {\n log(`SEND ${label} (${purpose}) — allowed`);\n send();\n return;\n }\n if (purpose !== 'necessary' && snapshot[purpose] === 'denied') {\n log(`DROP ${label} (${purpose}) — visitor refused`);\n return;\n }\n if (buffer.length >= bufferLimit) {\n droppedFromOverflow += 1;\n log(\n `DROP ${label} (${purpose}) — buffer full at ${bufferLimit}, ${droppedFromOverflow} lost`,\n );\n return;\n }\n buffer.push({ purpose: purpose as GatedPurpose, send, label });\n log(`HOLD ${label} (${purpose}) — no answer yet, ${buffer.length} held`);\n};\n\n/**\n * The visitor did something deliberate — submitted a prompt — so the session\n * may be measured even though no CMP has answered.\n *\n * This is the unlock that keeps web5 measurable at all: on a Shopify store\n * with no privacy configuration the consent API never loads, consent stays\n * `unknown` forever, and without this every store would report nothing.\n *\n * It unlocks **transmission only**. Persistent identity stays gated on real\n * consent (see `mayPersistIdentity`) — typing a question is a reason to\n * measure the session, not a reason to persist an identifier across visits.\n * It also cannot override a `denied`.\n */\nexport const unlockOnUserAction = (): void => {\n if (engaged) {\n return;\n }\n engaged = true;\n log(\n `UNLOCK — visitor submitted a prompt; ${buffer.length} held event(s) to settle`,\n );\n rebuildView();\n settleBuffer();\n notify();\n};\n\n/** Whether the visitor has taken the deliberate action that unlocks the session. */\nexport const isUserEngaged = (): boolean => engaged;\n\n/**\n * Persistent, cross-visit storage of a visitor identifier requires real\n * consent — engagement is deliberately not enough.\n */\nexport const mayPersistIdentity = (): boolean =>\n snapshot.analytics === 'granted';\n\nexport const getConsentSnapshot = (): ConsentSnapshot => snapshot;\n\n/** Referentially stable between changes, for `useSyncExternalStore`. */\nexport const getGateView = (): GateView => view;\n\nexport const subscribeToConsent = (listener: Listener): (() => void) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n};\n\nconst applySnapshot = (next: ConsentSnapshot): void => {\n if (\n next.analytics === snapshot.analytics &&\n next.marketing === snapshot.marketing &&\n next.performance === snapshot.performance\n ) {\n return;\n }\n log(`ANSWER — ${describe(snapshot)} → ${describe(next)}`);\n snapshot = Object.freeze({ ...next });\n rebuildView();\n settleBuffer();\n notify();\n};\n\n/**\n * Install the host's provider. Reads its current state immediately, *then*\n * subscribes — DL #217: a listener registered into an already-rendered page is\n * bound to an event that may have fired long before the bundle executed, so\n * subscribing alone would leave the gate permanently at `unknown`.\n */\nexport const installConsentProvider = (next: ConsentProvider): void => {\n unsubscribeProvider?.();\n provider = next;\n const initial = next.read();\n log(`provider installed: ${next.name} — reads ${describe(initial)}`);\n applySnapshot(initial);\n unsubscribeProvider = next.subscribe(applySnapshot);\n};\n\nexport const getInstalledProviderName = (): string | null =>\n provider?.name ?? null;\n\nexport const setConsentBufferLimit = (limit: number): void => {\n bufferLimit = Math.max(0, limit);\n};\n\n/** Diagnostics only — how much the gate is holding, and what it had to drop. */\nexport const getConsentGateStats = (): {\n held: number;\n droppedFromOverflow: number;\n} => ({ held: buffer.length, droppedFromOverflow });\n\nexport const resetConsentGateForTests = (): void => {\n unsubscribeProvider?.();\n unsubscribeProvider = null;\n provider = null;\n snapshot = UNKNOWN_CONSENT;\n engaged = false;\n buffer = [];\n bufferLimit = DEFAULT_BUFFER_LIMIT;\n droppedFromOverflow = 0;\n listeners.clear();\n rebuildView();\n};\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -60,7 +60,7 @@ const initConsentGate = () => {
|
|
|
60
60
|
(0, _consentGate.installConsentProvider)(provider);
|
|
61
61
|
} else {
|
|
62
62
|
try {
|
|
63
|
-
console.
|
|
63
|
+
console.debug('[w5-consent] no CMP detected (no host-supplied consent, no Shopify customerPrivacy, no OneTrust) — everything held until the visitor submits a prompt');
|
|
64
64
|
} catch {
|
|
65
65
|
/* never break on a console */
|
|
66
66
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_consentGate","require","_consentOverride","_hostSuppliedProvider","_shopifyProvider","_oneTrustProvider","detectConsentProvider","forced","getConsentOverride","console","warn","name","read","overrideSnapshot","subscribe","hasHostSuppliedConsent","createHostSuppliedConsentProvider","isShopifyHost","createShopifyConsentProvider","isOneTrustHost","createOneTrustConsentProvider","exports","initConsentGate","provider","installConsentProvider","
|
|
1
|
+
{"version":3,"names":["_consentGate","require","_consentOverride","_hostSuppliedProvider","_shopifyProvider","_oneTrustProvider","detectConsentProvider","forced","getConsentOverride","console","warn","name","read","overrideSnapshot","subscribe","hasHostSuppliedConsent","createHostSuppliedConsentProvider","isShopifyHost","createShopifyConsentProvider","isOneTrustHost","createOneTrustConsentProvider","exports","initConsentGate","provider","installConsentProvider","debug"],"sources":["../../../src/privacy/detectProvider.ts"],"sourcesContent":["/**\n * Provider selection — DL #218.\n *\n * Detected at boot, not configured per client, because a merchant can install\n * a CMP long after we deploy and the bundle is not the source of truth for a\n * store's behaviour (DL #214).\n *\n * Order is host-supplied → Shopify → OneTrust → none. A host that publishes an\n * answer outranks anything we could sniff, because it knows things we cannot:\n * a server-side consent record, a CMP behind its own abstraction, or a legal\n * position we have no business guessing at.\n *\n * When nothing is detected the gate keeps its default — everything `unknown`,\n * held until the visitor acts. That is the case on every Shopify store with no\n * privacy configuration, which today is most of them.\n */\n\nimport { installConsentProvider } from './consentGate';\nimport { getConsentOverride, overrideSnapshot } from './consentOverride';\nimport type { ConsentProvider } from './types';\nimport {\n createHostSuppliedConsentProvider,\n hasHostSuppliedConsent,\n} from './providers/hostSuppliedProvider';\nimport {\n createShopifyConsentProvider,\n isShopifyHost,\n} from './providers/shopifyProvider';\nimport {\n createOneTrustConsentProvider,\n isOneTrustHost,\n} from './providers/oneTrustProvider';\n\nexport const detectConsentProvider = (): ConsentProvider | null => {\n // Debug override outranks every real signal, so the states a region or an\n // unconfigured store makes unreachable can still be walked by hand.\n const forced = getConsentOverride();\n if (forced) {\n console.warn(\n `[w5-consent] OVERRIDE ACTIVE — consent forced to \"${forced}\". This is a debug switch, not a real answer.`,\n );\n return {\n name: `override:${forced}`,\n read: () => overrideSnapshot(forced),\n subscribe: () => () => {},\n };\n }\n if (hasHostSuppliedConsent()) {\n return createHostSuppliedConsentProvider();\n }\n if (isShopifyHost()) {\n return createShopifyConsentProvider();\n }\n if (isOneTrustHost()) {\n return createOneTrustConsentProvider();\n }\n return null;\n};\n\n/**\n * Install the detected provider, if any. Call once at boot, before the first\n * emitter runs — anything raised earlier is held rather than lost, but the\n * sooner this runs the less the buffer has to carry.\n */\nexport const initConsentGate = (): ConsentProvider | null => {\n const provider = detectConsentProvider();\n if (provider) {\n installConsentProvider(provider);\n } else {\n try {\n console.debug(\n '[w5-consent] no CMP detected (no host-supplied consent, no Shopify customerPrivacy, no OneTrust) — everything held until the visitor submits a prompt',\n );\n } catch {\n /* never break on a console */\n }\n }\n return provider;\n};\n"],"mappings":";;;;AAiBA,IAAAA,YAAA,GAAAC,OAAA;AACA,IAAAC,gBAAA,GAAAD,OAAA;AAEA,IAAAE,qBAAA,GAAAF,OAAA;AAIA,IAAAG,gBAAA,GAAAH,OAAA;AAIA,IAAAI,iBAAA,GAAAJ,OAAA;AA5BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAkBO,MAAMK,qBAAqB,GAAGA,CAAA,KAA8B;EACjE;EACA;EACA,MAAMC,MAAM,GAAG,IAAAC,mCAAkB,EAAC,CAAC;EACnC,IAAID,MAAM,EAAE;IACVE,OAAO,CAACC,IAAI,CACV,qDAAqDH,MAAM,+CAC7D,CAAC;IACD,OAAO;MACLI,IAAI,EAAE,YAAYJ,MAAM,EAAE;MAC1BK,IAAI,EAAEA,CAAA,KAAM,IAAAC,iCAAgB,EAACN,MAAM,CAAC;MACpCO,SAAS,EAAEA,CAAA,KAAM,MAAM,CAAC;IAC1B,CAAC;EACH;EACA,IAAI,IAAAC,4CAAsB,EAAC,CAAC,EAAE;IAC5B,OAAO,IAAAC,uDAAiC,EAAC,CAAC;EAC5C;EACA,IAAI,IAAAC,8BAAa,EAAC,CAAC,EAAE;IACnB,OAAO,IAAAC,6CAA4B,EAAC,CAAC;EACvC;EACA,IAAI,IAAAC,gCAAc,EAAC,CAAC,EAAE;IACpB,OAAO,IAAAC,+CAA6B,EAAC,CAAC;EACxC;EACA,OAAO,IAAI;AACb,CAAC;;AAED;AACA;AACA;AACA;AACA;AAJAC,OAAA,CAAAf,qBAAA,GAAAA,qBAAA;AAKO,MAAMgB,eAAe,GAAGA,CAAA,KAA8B;EAC3D,MAAMC,QAAQ,GAAGjB,qBAAqB,CAAC,CAAC;EACxC,IAAIiB,QAAQ,EAAE;IACZ,IAAAC,mCAAsB,EAACD,QAAQ,CAAC;EAClC,CAAC,MAAM;IACL,IAAI;MACFd,OAAO,CAACgB,KAAK,CACX,uJACF,CAAC;IACH,CAAC,CAAC,MAAM;MACN;IAAA;EAEJ;EACA,OAAOF,QAAQ;AACjB,CAAC;AAACF,OAAA,CAAAC,eAAA,GAAAA,eAAA","ignoreList":[]}
|
|
@@ -21,8 +21,12 @@ const HOST_CONSENT_GLOBAL = exports.HOST_CONSENT_GLOBAL = '__web5_consent__';
|
|
|
21
21
|
/** What a host may publish. Anything missing stays `unknown`. */
|
|
22
22
|
|
|
23
23
|
const coerce = value => {
|
|
24
|
-
if (value === true)
|
|
25
|
-
|
|
24
|
+
if (value === true) {
|
|
25
|
+
return 'granted';
|
|
26
|
+
}
|
|
27
|
+
if (value === false) {
|
|
28
|
+
return 'denied';
|
|
29
|
+
}
|
|
26
30
|
if (value === 'granted' || value === 'denied' || value === 'unknown') {
|
|
27
31
|
return value;
|
|
28
32
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_types","require","HOST_CONSENT_GLOBAL","exports","coerce","value","normalize","input","UNKNOWN_CONSENT","analytics","marketing","performance","undefined","readGlobal","window","raw","hasHostSuppliedConsent","subscribers","Set","published","publishHostConsent","subscriber","createHostSuppliedConsentProvider","name","read","subscribe","onChange","add","delete","__resetHostConsentForTests","clear"],"sources":["../../../../src/privacy/providers/hostSuppliedProvider.ts"],"sourcesContent":["/**\n * Host-supplied provider — DL #218.\n *\n * The escape hatch for every CMP we cannot detect: a host page that already\n * knows its visitor's answer publishes it, and the gate believes it. This is\n * how Circana, feature.com and any merchant on Cookiebot/Osano/Klaviyo reach\n * the gate without web5 learning each vendor's API.\n *\n * Two ways in, because hosts differ in when they know:\n * - `window.__web5_consent__` set before the bundle loads, read at install\n * - `publishHostConsent()` called at any time afterwards\n */\n\nimport {\n UNKNOWN_CONSENT,\n type ConsentProvider,\n type ConsentSnapshot,\n type ConsentState,\n} from '../types';\n\nexport const HOST_CONSENT_GLOBAL = '__web5_consent__';\n\n/** What a host may publish. Anything missing stays `unknown`. */\nexport interface HostConsentInput {\n analytics?: ConsentState | boolean;\n marketing?: ConsentState | boolean;\n performance?: ConsentState | boolean;\n}\n\nconst coerce = (value: ConsentState | boolean | undefined): ConsentState => {\n if (value === true) return 'granted';\n if (value === false) return 'denied';\n if (value === 'granted' || value === 'denied' || value === 'unknown') {\n return value;\n }\n return 'unknown';\n};\n\nconst normalize = (input: HostConsentInput | null | undefined): ConsentSnapshot => {\n if (!input || typeof input !== 'object') {\n return UNKNOWN_CONSENT;\n }\n const analytics = coerce(input.analytics);\n return {\n analytics,\n marketing: coerce(input.marketing),\n // A host that speaks only about analytics is taken to mean the same for\n // load telemetry, which is the same wire and the same recipient.\n performance:\n input.performance === undefined ? analytics : coerce(input.performance),\n };\n};\n\nconst readGlobal = (): ConsentSnapshot => {\n if (typeof window === 'undefined') {\n return UNKNOWN_CONSENT;\n }\n const raw = (window as unknown as Record<string, unknown>)[HOST_CONSENT_GLOBAL];\n return normalize(raw as HostConsentInput | undefined);\n};\n\nexport const hasHostSuppliedConsent = (): boolean => {\n if (typeof window === 'undefined') {\n return false;\n }\n const raw = (window as unknown as Record<string, unknown>)[HOST_CONSENT_GLOBAL];\n return !!raw && typeof raw === 'object';\n};\n\nconst subscribers = new Set<(snapshot: ConsentSnapshot) => void>();\nlet published: ConsentSnapshot | null = null;\n\n/**\n * Called by the host — directly, or by the loader when it is handed consent in\n * its boot options — whenever the visitor's answer is known or changes.\n */\nexport const publishHostConsent = (input: HostConsentInput): void => {\n published = normalize(input);\n for (const subscriber of subscribers) {\n subscriber(published);\n }\n};\n\nexport const createHostSuppliedConsentProvider = (): ConsentProvider => ({\n name: 'host-supplied',\n\n read: () => published ?? readGlobal(),\n\n subscribe(onChange) {\n subscribers.add(onChange);\n return () => {\n subscribers.delete(onChange);\n };\n },\n});\n\nexport const __resetHostConsentForTests = (): void => {\n published = null;\n subscribers.clear();\n};\n"],"mappings":";;;;AAaA,IAAAA,MAAA,GAAAC,OAAA;AAbA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AASO,MAAMC,mBAAmB,GAAAC,OAAA,CAAAD,mBAAA,GAAG,kBAAkB;;AAErD;;AAOA,MAAME,MAAM,GAAIC,KAAyC,IAAmB;EAC1E,IAAIA,KAAK,KAAK,IAAI,EAAE,OAAO,SAAS;
|
|
1
|
+
{"version":3,"names":["_types","require","HOST_CONSENT_GLOBAL","exports","coerce","value","normalize","input","UNKNOWN_CONSENT","analytics","marketing","performance","undefined","readGlobal","window","raw","hasHostSuppliedConsent","subscribers","Set","published","publishHostConsent","subscriber","createHostSuppliedConsentProvider","name","read","subscribe","onChange","add","delete","__resetHostConsentForTests","clear"],"sources":["../../../../src/privacy/providers/hostSuppliedProvider.ts"],"sourcesContent":["/**\n * Host-supplied provider — DL #218.\n *\n * The escape hatch for every CMP we cannot detect: a host page that already\n * knows its visitor's answer publishes it, and the gate believes it. This is\n * how Circana, feature.com and any merchant on Cookiebot/Osano/Klaviyo reach\n * the gate without web5 learning each vendor's API.\n *\n * Two ways in, because hosts differ in when they know:\n * - `window.__web5_consent__` set before the bundle loads, read at install\n * - `publishHostConsent()` called at any time afterwards\n */\n\nimport {\n UNKNOWN_CONSENT,\n type ConsentProvider,\n type ConsentSnapshot,\n type ConsentState,\n} from '../types';\n\nexport const HOST_CONSENT_GLOBAL = '__web5_consent__';\n\n/** What a host may publish. Anything missing stays `unknown`. */\nexport interface HostConsentInput {\n analytics?: ConsentState | boolean;\n marketing?: ConsentState | boolean;\n performance?: ConsentState | boolean;\n}\n\nconst coerce = (value: ConsentState | boolean | undefined): ConsentState => {\n if (value === true) {\n return 'granted';\n }\n if (value === false) {\n return 'denied';\n }\n if (value === 'granted' || value === 'denied' || value === 'unknown') {\n return value;\n }\n return 'unknown';\n};\n\nconst normalize = (\n input: HostConsentInput | null | undefined,\n): ConsentSnapshot => {\n if (!input || typeof input !== 'object') {\n return UNKNOWN_CONSENT;\n }\n const analytics = coerce(input.analytics);\n return {\n analytics,\n marketing: coerce(input.marketing),\n // A host that speaks only about analytics is taken to mean the same for\n // load telemetry, which is the same wire and the same recipient.\n performance:\n input.performance === undefined ? analytics : coerce(input.performance),\n };\n};\n\nconst readGlobal = (): ConsentSnapshot => {\n if (typeof window === 'undefined') {\n return UNKNOWN_CONSENT;\n }\n const raw = (window as unknown as Record<string, unknown>)[\n HOST_CONSENT_GLOBAL\n ];\n return normalize(raw as HostConsentInput | undefined);\n};\n\nexport const hasHostSuppliedConsent = (): boolean => {\n if (typeof window === 'undefined') {\n return false;\n }\n const raw = (window as unknown as Record<string, unknown>)[\n HOST_CONSENT_GLOBAL\n ];\n return !!raw && typeof raw === 'object';\n};\n\nconst subscribers = new Set<(snapshot: ConsentSnapshot) => void>();\nlet published: ConsentSnapshot | null = null;\n\n/**\n * Called by the host — directly, or by the loader when it is handed consent in\n * its boot options — whenever the visitor's answer is known or changes.\n */\nexport const publishHostConsent = (input: HostConsentInput): void => {\n published = normalize(input);\n for (const subscriber of subscribers) {\n subscriber(published);\n }\n};\n\nexport const createHostSuppliedConsentProvider = (): ConsentProvider => ({\n name: 'host-supplied',\n\n read: () => published ?? readGlobal(),\n\n subscribe(onChange) {\n subscribers.add(onChange);\n return () => {\n subscribers.delete(onChange);\n };\n },\n});\n\nexport const __resetHostConsentForTests = (): void => {\n published = null;\n subscribers.clear();\n};\n"],"mappings":";;;;AAaA,IAAAA,MAAA,GAAAC,OAAA;AAbA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AASO,MAAMC,mBAAmB,GAAAC,OAAA,CAAAD,mBAAA,GAAG,kBAAkB;;AAErD;;AAOA,MAAME,MAAM,GAAIC,KAAyC,IAAmB;EAC1E,IAAIA,KAAK,KAAK,IAAI,EAAE;IAClB,OAAO,SAAS;EAClB;EACA,IAAIA,KAAK,KAAK,KAAK,EAAE;IACnB,OAAO,QAAQ;EACjB;EACA,IAAIA,KAAK,KAAK,SAAS,IAAIA,KAAK,KAAK,QAAQ,IAAIA,KAAK,KAAK,SAAS,EAAE;IACpE,OAAOA,KAAK;EACd;EACA,OAAO,SAAS;AAClB,CAAC;AAED,MAAMC,SAAS,GACbC,KAA0C,IACtB;EACpB,IAAI,CAACA,KAAK,IAAI,OAAOA,KAAK,KAAK,QAAQ,EAAE;IACvC,OAAOC,sBAAe;EACxB;EACA,MAAMC,SAAS,GAAGL,MAAM,CAACG,KAAK,CAACE,SAAS,CAAC;EACzC,OAAO;IACLA,SAAS;IACTC,SAAS,EAAEN,MAAM,CAACG,KAAK,CAACG,SAAS,CAAC;IAClC;IACA;IACAC,WAAW,EACTJ,KAAK,CAACI,WAAW,KAAKC,SAAS,GAAGH,SAAS,GAAGL,MAAM,CAACG,KAAK,CAACI,WAAW;EAC1E,CAAC;AACH,CAAC;AAED,MAAME,UAAU,GAAGA,CAAA,KAAuB;EACxC,IAAI,OAAOC,MAAM,KAAK,WAAW,EAAE;IACjC,OAAON,sBAAe;EACxB;EACA,MAAMO,GAAG,GAAID,MAAM,CACjBZ,mBAAmB,CACpB;EACD,OAAOI,SAAS,CAACS,GAAmC,CAAC;AACvD,CAAC;AAEM,MAAMC,sBAAsB,GAAGA,CAAA,KAAe;EACnD,IAAI,OAAOF,MAAM,KAAK,WAAW,EAAE;IACjC,OAAO,KAAK;EACd;EACA,MAAMC,GAAG,GAAID,MAAM,CACjBZ,mBAAmB,CACpB;EACD,OAAO,CAAC,CAACa,GAAG,IAAI,OAAOA,GAAG,KAAK,QAAQ;AACzC,CAAC;AAACZ,OAAA,CAAAa,sBAAA,GAAAA,sBAAA;AAEF,MAAMC,WAAW,GAAG,IAAIC,GAAG,CAAsC,CAAC;AAClE,IAAIC,SAAiC,GAAG,IAAI;;AAE5C;AACA;AACA;AACA;AACO,MAAMC,kBAAkB,GAAIb,KAAuB,IAAW;EACnEY,SAAS,GAAGb,SAAS,CAACC,KAAK,CAAC;EAC5B,KAAK,MAAMc,UAAU,IAAIJ,WAAW,EAAE;IACpCI,UAAU,CAACF,SAAS,CAAC;EACvB;AACF,CAAC;AAAChB,OAAA,CAAAiB,kBAAA,GAAAA,kBAAA;AAEK,MAAME,iCAAiC,GAAGA,CAAA,MAAwB;EACvEC,IAAI,EAAE,eAAe;EAErBC,IAAI,EAAEA,CAAA,KAAML,SAAS,IAAIN,UAAU,CAAC,CAAC;EAErCY,SAASA,CAACC,QAAQ,EAAE;IAClBT,WAAW,CAACU,GAAG,CAACD,QAAQ,CAAC;IACzB,OAAO,MAAM;MACXT,WAAW,CAACW,MAAM,CAACF,QAAQ,CAAC;IAC9B,CAAC;EACH;AACF,CAAC,CAAC;AAACvB,OAAA,CAAAmB,iCAAA,GAAAA,iCAAA;AAEI,MAAMO,0BAA0B,GAAGA,CAAA,KAAY;EACpDV,SAAS,GAAG,IAAI;EAChBF,WAAW,CAACa,KAAK,CAAC,CAAC;AACrB,CAAC;AAAC3B,OAAA,CAAA0B,0BAAA,GAAAA,0BAAA","ignoreList":[]}
|
|
@@ -31,7 +31,7 @@ const readSnapshot = () => {
|
|
|
31
31
|
const groups = readGroups();
|
|
32
32
|
if (groups !== null) {
|
|
33
33
|
try {
|
|
34
|
-
console.
|
|
34
|
+
console.debug(`[w5-consent] onetrust: active groups "${groups}"`);
|
|
35
35
|
} catch {
|
|
36
36
|
/* never break on a console */
|
|
37
37
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["ANALYTICS_CATEGORY","MARKETING_CATEGORY","readGroups","window","groups","OnetrustActiveGroups","isOneTrustHost","exports","readSnapshot","console","
|
|
1
|
+
{"version":3,"names":["ANALYTICS_CATEGORY","MARKETING_CATEGORY","readGroups","window","groups","OnetrustActiveGroups","isOneTrustHost","exports","readSnapshot","console","debug","analytics","marketing","performance","includes","createOneTrustConsentProvider","name","read","subscribe","onChange","publish","addEventListener","removeEventListener"],"sources":["../../../../src/privacy/providers/oneTrustProvider.ts"],"sourcesContent":["/**\n * OneTrust provider — DL #218.\n *\n * Lifted from the consent check that used to live inside\n * `utils/analyticsEvents.ts`, with one deliberate behaviour change: that\n * function returned `true` when OneTrust was absent (\"host is responsible for\n * loading OneTrust\"), which on a Shopify storefront meant unconditional\n * default-allow. Absence is no longer this provider's problem — it is only\n * selected when OneTrust is actually on the page, and absence is handled by\n * the gate's own default.\n */\n\nimport type { ConsentProvider, ConsentSnapshot } from '../types';\n\n/** OneTrust's default taxonomy: C0002 is the performance/analytics category. */\nconst ANALYTICS_CATEGORY = 'C0002';\n/** C0004 is targeting/advertising. */\nconst MARKETING_CATEGORY = 'C0004';\n\nconst readGroups = (): string | null => {\n if (typeof window === 'undefined') {\n return null;\n }\n const groups = (window as unknown as { OnetrustActiveGroups?: unknown })\n .OnetrustActiveGroups;\n return typeof groups === 'string' ? groups : null;\n};\n\nexport const isOneTrustHost = (): boolean => readGroups() !== null;\n\nconst readSnapshot = (): ConsentSnapshot => {\n const groups = readGroups();\n if (groups !== null) {\n try {\n console.debug(`[w5-consent] onetrust: active groups \"${groups}\"`);\n } catch {\n /* never break on a console */\n }\n }\n if (groups === null) {\n return {\n analytics: 'unknown',\n marketing: 'unknown',\n performance: 'unknown',\n };\n }\n // OneTrust publishes the *active* groups, so a category that is absent from\n // a string OneTrust has written is a refusal, not silence.\n const analytics = groups.includes(ANALYTICS_CATEGORY) ? 'granted' : 'denied';\n const marketing = groups.includes(MARKETING_CATEGORY) ? 'granted' : 'denied';\n return { analytics, marketing, performance: analytics };\n};\n\nexport const createOneTrustConsentProvider = (): ConsentProvider => ({\n name: 'onetrust',\n\n read: readSnapshot,\n\n subscribe(onChange) {\n const publish = () => onChange(readSnapshot());\n if (typeof window === 'undefined') {\n return () => {};\n }\n // OneTrust fires this on every banner interaction.\n window.addEventListener('OneTrustGroupsUpdated', publish);\n return () => {\n window.removeEventListener('OneTrustGroupsUpdated', publish);\n };\n },\n});\n"],"mappings":";;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAIA;AACA,MAAMA,kBAAkB,GAAG,OAAO;AAClC;AACA,MAAMC,kBAAkB,GAAG,OAAO;AAElC,MAAMC,UAAU,GAAGA,CAAA,KAAqB;EACtC,IAAI,OAAOC,MAAM,KAAK,WAAW,EAAE;IACjC,OAAO,IAAI;EACb;EACA,MAAMC,MAAM,GAAID,MAAM,CACnBE,oBAAoB;EACvB,OAAO,OAAOD,MAAM,KAAK,QAAQ,GAAGA,MAAM,GAAG,IAAI;AACnD,CAAC;AAEM,MAAME,cAAc,GAAGA,CAAA,KAAeJ,UAAU,CAAC,CAAC,KAAK,IAAI;AAACK,OAAA,CAAAD,cAAA,GAAAA,cAAA;AAEnE,MAAME,YAAY,GAAGA,CAAA,KAAuB;EAC1C,MAAMJ,MAAM,GAAGF,UAAU,CAAC,CAAC;EAC3B,IAAIE,MAAM,KAAK,IAAI,EAAE;IACnB,IAAI;MACFK,OAAO,CAACC,KAAK,CAAC,yCAAyCN,MAAM,GAAG,CAAC;IACnE,CAAC,CAAC,MAAM;MACN;IAAA;EAEJ;EACA,IAAIA,MAAM,KAAK,IAAI,EAAE;IACnB,OAAO;MACLO,SAAS,EAAE,SAAS;MACpBC,SAAS,EAAE,SAAS;MACpBC,WAAW,EAAE;IACf,CAAC;EACH;EACA;EACA;EACA,MAAMF,SAAS,GAAGP,MAAM,CAACU,QAAQ,CAACd,kBAAkB,CAAC,GAAG,SAAS,GAAG,QAAQ;EAC5E,MAAMY,SAAS,GAAGR,MAAM,CAACU,QAAQ,CAACb,kBAAkB,CAAC,GAAG,SAAS,GAAG,QAAQ;EAC5E,OAAO;IAAEU,SAAS;IAAEC,SAAS;IAAEC,WAAW,EAAEF;EAAU,CAAC;AACzD,CAAC;AAEM,MAAMI,6BAA6B,GAAGA,CAAA,MAAwB;EACnEC,IAAI,EAAE,UAAU;EAEhBC,IAAI,EAAET,YAAY;EAElBU,SAASA,CAACC,QAAQ,EAAE;IAClB,MAAMC,OAAO,GAAGA,CAAA,KAAMD,QAAQ,CAACX,YAAY,CAAC,CAAC,CAAC;IAC9C,IAAI,OAAOL,MAAM,KAAK,WAAW,EAAE;MACjC,OAAO,MAAM,CAAC,CAAC;IACjB;IACA;IACAA,MAAM,CAACkB,gBAAgB,CAAC,uBAAuB,EAAED,OAAO,CAAC;IACzD,OAAO,MAAM;MACXjB,MAAM,CAACmB,mBAAmB,CAAC,uBAAuB,EAAEF,OAAO,CAAC;IAC9D,CAAC;EACH;AACF,CAAC,CAAC;AAACb,OAAA,CAAAQ,6BAAA,GAAAA,6BAAA","ignoreList":[]}
|
|
@@ -32,7 +32,7 @@ const CONSENT_FEATURE = {
|
|
|
32
32
|
};
|
|
33
33
|
const log = (...args) => {
|
|
34
34
|
try {
|
|
35
|
-
console.
|
|
35
|
+
console.debug('[w5-consent] shopify:', ...args);
|
|
36
36
|
} catch {
|
|
37
37
|
/* never break on a console */
|
|
38
38
|
}
|
|
@@ -46,8 +46,12 @@ const getShopify = () => {
|
|
|
46
46
|
const isShopifyHost = () => getShopify() !== null;
|
|
47
47
|
exports.isShopifyHost = isShopifyHost;
|
|
48
48
|
const toState = value => {
|
|
49
|
-
if (value === 'yes')
|
|
50
|
-
|
|
49
|
+
if (value === 'yes') {
|
|
50
|
+
return 'granted';
|
|
51
|
+
}
|
|
52
|
+
if (value === 'no') {
|
|
53
|
+
return 'denied';
|
|
54
|
+
}
|
|
51
55
|
return null;
|
|
52
56
|
};
|
|
53
57
|
const readSnapshot = () => {
|