@certrev/cert-block 0.5.4 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/CHANGELOG.md +195 -12
  2. package/LICENSE +21 -0
  3. package/README.md +422 -50
  4. package/dist/builder/cert-chrome-data.d.ts +26 -8
  5. package/dist/builder/cert-chrome-data.d.ts.map +1 -1
  6. package/dist/builder/cert-chrome-data.js +5 -5
  7. package/dist/builder/cert-chrome-data.js.map +1 -1
  8. package/dist/builder/cert-review-card.d.ts +35 -5
  9. package/dist/builder/cert-review-card.d.ts.map +1 -1
  10. package/dist/builder/cert-review-card.js +115 -34
  11. package/dist/builder/cert-review-card.js.map +1 -1
  12. package/dist/builder/index.d.ts +33 -16
  13. package/dist/builder/index.d.ts.map +1 -1
  14. package/dist/builder/index.js +33 -16
  15. package/dist/builder/index.js.map +1 -1
  16. package/dist/components/CertBadge.d.ts +2 -2
  17. package/dist/components/CertBadge.d.ts.map +1 -1
  18. package/dist/components/CertBadge.js +11 -5
  19. package/dist/components/CertBadge.js.map +1 -1
  20. package/dist/components/CertJsonLd.d.ts.map +1 -1
  21. package/dist/components/CertJsonLd.js.map +1 -1
  22. package/dist/components/CertRevBacklink.d.ts.map +1 -1
  23. package/dist/components/CertRevBacklink.js +3 -2
  24. package/dist/components/CertRevBacklink.js.map +1 -1
  25. package/dist/components/CertReview.d.ts +1 -1
  26. package/dist/components/ExpertBio.d.ts.map +1 -1
  27. package/dist/components/ExpertBio.js +12 -4
  28. package/dist/components/ExpertBio.js.map +1 -1
  29. package/dist/components/escape.d.ts +28 -1
  30. package/dist/components/escape.d.ts.map +1 -1
  31. package/dist/components/escape.js +37 -8
  32. package/dist/components/escape.js.map +1 -1
  33. package/dist/components/format.d.ts +51 -2
  34. package/dist/components/format.d.ts.map +1 -1
  35. package/dist/components/format.js +103 -6
  36. package/dist/components/format.js.map +1 -1
  37. package/dist/components/preview-block.d.ts +1 -1
  38. package/dist/components/preview-block.js +3 -3
  39. package/dist/components/preview-block.js.map +1 -1
  40. package/dist/components/rel.d.ts +24 -0
  41. package/dist/components/rel.d.ts.map +1 -0
  42. package/dist/components/rel.js +24 -0
  43. package/dist/components/rel.js.map +1 -0
  44. package/dist/components/render-cert-block.d.ts +50 -22
  45. package/dist/components/render-cert-block.d.ts.map +1 -1
  46. package/dist/components/render-cert-block.js +146 -80
  47. package/dist/components/render-cert-block.js.map +1 -1
  48. package/dist/components/render-def.d.ts +13 -14
  49. package/dist/components/render-def.d.ts.map +1 -1
  50. package/dist/components/render-def.js +14 -15
  51. package/dist/components/render-def.js.map +1 -1
  52. package/dist/index.d.ts +1 -0
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +4 -2
  55. package/dist/index.js.map +1 -1
  56. package/dist/jsonld/project.d.ts +46 -13
  57. package/dist/jsonld/project.d.ts.map +1 -1
  58. package/dist/jsonld/project.js +139 -56
  59. package/dist/jsonld/project.js.map +1 -1
  60. package/dist/modal/avatar-resize.d.ts +10 -10
  61. package/dist/modal/avatar-resize.js +14 -14
  62. package/dist/modal/avatar-resize.js.map +1 -1
  63. package/dist/modal/cert-modal-view.d.ts +27 -7
  64. package/dist/modal/cert-modal-view.d.ts.map +1 -1
  65. package/dist/modal/cert-modal-view.js +49 -25
  66. package/dist/modal/cert-modal-view.js.map +1 -1
  67. package/dist/modal/certrev-cert-modal.d.ts +5 -5
  68. package/dist/modal/certrev-cert-modal.js +9 -9
  69. package/dist/modal/certrev-cert-modal.js.map +1 -1
  70. package/dist/modal/certrev-cert-modal.styles.d.ts +6 -6
  71. package/dist/modal/certrev-cert-modal.styles.d.ts.map +1 -1
  72. package/dist/modal/certrev-cert-modal.styles.js +8 -8
  73. package/dist/modal/certrev-cert-modal.styles.js.map +1 -1
  74. package/dist/modal/certrev-cert.entry.d.ts +8 -6
  75. package/dist/modal/certrev-cert.entry.d.ts.map +1 -1
  76. package/dist/modal/certrev-cert.entry.js +9 -7
  77. package/dist/modal/certrev-cert.entry.js.map +1 -1
  78. package/dist/modal/certrev-cert.js +6 -6
  79. package/dist/modal/display-strings.d.ts +25 -5
  80. package/dist/modal/display-strings.d.ts.map +1 -1
  81. package/dist/modal/display-strings.js +27 -5
  82. package/dist/modal/display-strings.js.map +1 -1
  83. package/dist/modal/escape.d.ts +26 -3
  84. package/dist/modal/escape.d.ts.map +1 -1
  85. package/dist/modal/escape.js +32 -3
  86. package/dist/modal/escape.js.map +1 -1
  87. package/dist/modal/font-face.d.ts.map +1 -1
  88. package/dist/modal/font-face.js +8 -11
  89. package/dist/modal/font-face.js.map +1 -1
  90. package/dist/modal/ftc-disclosure.d.ts +2 -2
  91. package/dist/modal/ftc-disclosure.js +2 -2
  92. package/dist/modal/ftc-guard.d.ts +44 -9
  93. package/dist/modal/ftc-guard.d.ts.map +1 -1
  94. package/dist/modal/ftc-guard.js +50 -17
  95. package/dist/modal/ftc-guard.js.map +1 -1
  96. package/dist/modal/index.d.ts +1 -1
  97. package/dist/modal/index.js +1 -1
  98. package/dist/modal/interactions.d.ts +2 -2
  99. package/dist/modal/interactions.js +2 -2
  100. package/dist/modal/logo-paths.js +2 -2
  101. package/dist/modal/logo-paths.js.map +1 -1
  102. package/dist/modal/placement.d.ts +4 -3
  103. package/dist/modal/placement.d.ts.map +1 -1
  104. package/dist/modal/placement.js +7 -6
  105. package/dist/modal/placement.js.map +1 -1
  106. package/dist/modal/revalidate.d.ts +2 -3
  107. package/dist/modal/revalidate.d.ts.map +1 -1
  108. package/dist/modal/revalidate.js +2 -3
  109. package/dist/modal/revalidate.js.map +1 -1
  110. package/dist/modal/verify-client.d.ts +2 -2
  111. package/dist/modal/verify-client.js +2 -2
  112. package/dist/verify/cache.d.ts +21 -6
  113. package/dist/verify/cache.d.ts.map +1 -1
  114. package/dist/verify/cache.js +29 -8
  115. package/dist/verify/cache.js.map +1 -1
  116. package/dist/verify/fetch-with-deadline.d.ts +35 -0
  117. package/dist/verify/fetch-with-deadline.d.ts.map +1 -0
  118. package/dist/verify/fetch-with-deadline.js +55 -0
  119. package/dist/verify/fetch-with-deadline.js.map +1 -0
  120. package/dist/verify/get-verified-envelope.d.ts +23 -9
  121. package/dist/verify/get-verified-envelope.d.ts.map +1 -1
  122. package/dist/verify/get-verified-envelope.js +89 -40
  123. package/dist/verify/get-verified-envelope.js.map +1 -1
  124. package/dist/verify/resolve-kid.d.ts +3 -0
  125. package/dist/verify/resolve-kid.d.ts.map +1 -1
  126. package/dist/verify/resolve-kid.js +17 -8
  127. package/dist/verify/resolve-kid.js.map +1 -1
  128. package/dist/webcomponent/certrev-badge.d.ts.map +1 -1
  129. package/dist/webcomponent/certrev-badge.js +24 -2
  130. package/dist/webcomponent/certrev-badge.js.map +1 -1
  131. package/dist/webcomponent/render-badge-html.d.ts +1 -1
  132. package/dist/webcomponent/render-badge-html.d.ts.map +1 -1
  133. package/dist/webcomponent/render-badge-html.js +36 -17
  134. package/dist/webcomponent/render-badge-html.js.map +1 -1
  135. package/package.json +14 -2
  136. package/src/builder/cert-chrome-data.ts +26 -8
  137. package/src/builder/cert-review-card.tsx +147 -38
  138. package/src/builder/index.tsx +34 -17
  139. package/src/components/CertBadge.tsx +14 -9
  140. package/src/components/CertJsonLd.tsx +1 -1
  141. package/src/components/CertRevBacklink.tsx +10 -3
  142. package/src/components/CertReview.tsx +1 -1
  143. package/src/components/ExpertBio.tsx +12 -7
  144. package/src/components/escape.ts +37 -7
  145. package/src/components/format.ts +102 -6
  146. package/src/components/preview-block.ts +3 -3
  147. package/src/components/rel.ts +23 -0
  148. package/src/components/render-cert-block.ts +192 -109
  149. package/src/components/render-def.ts +14 -15
  150. package/src/index.ts +4 -2
  151. package/src/jsonld/project.ts +155 -59
  152. package/src/modal/avatar-resize.ts +14 -14
  153. package/src/modal/cert-modal-view.ts +52 -27
  154. package/src/modal/certrev-cert-modal.styles.ts +8 -8
  155. package/src/modal/certrev-cert-modal.ts +9 -9
  156. package/src/modal/certrev-cert.entry.ts +9 -7
  157. package/src/modal/display-strings.ts +26 -5
  158. package/src/modal/escape.ts +32 -4
  159. package/src/modal/font-face.ts +8 -11
  160. package/src/modal/ftc-disclosure.ts +2 -2
  161. package/src/modal/ftc-guard.ts +73 -18
  162. package/src/modal/index.ts +1 -1
  163. package/src/modal/interactions.ts +2 -2
  164. package/src/modal/logo-paths.ts +2 -2
  165. package/src/modal/placement.ts +7 -6
  166. package/src/modal/revalidate.ts +2 -3
  167. package/src/modal/verify-client.ts +2 -2
  168. package/src/verify/cache.ts +34 -8
  169. package/src/verify/fetch-with-deadline.ts +60 -0
  170. package/src/verify/get-verified-envelope.ts +131 -48
  171. package/src/verify/resolve-kid.ts +22 -6
  172. package/src/webcomponent/certrev-badge.ts +24 -2
  173. package/src/webcomponent/render-badge-html.ts +66 -19
  174. package/src/__tests__/__snapshots__/render-cert-block.test.ts.snap +0 -17
  175. package/src/__tests__/components.test.tsx +0 -206
  176. package/src/__tests__/preview-block.test.ts +0 -84
  177. package/src/__tests__/project.test.ts +0 -128
  178. package/src/__tests__/render-cert-block.test.ts +0 -657
  179. package/src/__tests__/render-def.test.tsx +0 -295
  180. package/src/__tests__/verify.test.ts +0 -239
  181. package/src/__tests__/webcomponent.test.tsx +0 -116
  182. package/src/builder/__tests__/builder.test.tsx +0 -158
  183. package/src/modal/cert-modal-view.test.ts +0 -175
  184. package/src/modal/certrev-cert-modal.test.ts +0 -125
  185. package/src/modal/ftc-guard.test.ts +0 -153
  186. package/src/modal/interactions.test.ts +0 -250
  187. package/src/modal/placement.test.ts +0 -398
  188. package/src/modal/revalidate.test.ts +0 -111
  189. package/src/modal/verify-client.test.ts +0 -164
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/builder/index.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAE,aAAa,EAAkB,UAAU,EAAE,MAAM,OAAO,CAAA;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAA;AAexD,MAAM,cAAc,GAAG,aAAa,CAAsB,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAA;AAE9F;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,EAC/B,OAAO,EACP,SAAS,GAAG,KAAK,EACjB,QAAQ,GAKR;IACA,OAAO,KAAC,cAAc,CAAC,QAAQ,IAAC,KAAK,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,YAAG,QAAQ,GAA2B,CAAA;AACpG,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB;IACvC,OAAO,CACN,kDACiC,EAAE,EAClC,KAAK,EAAE;YACN,MAAM,EAAE,oBAAoB;YAC5B,YAAY,EAAE,CAAC;YACf,OAAO,EAAE,WAAW;YACpB,IAAI,EAAE,gBAAgB;YACtB,KAAK,EAAE,SAAS;YAChB,UAAU,EAAE,SAAS;SACrB,uCAEgB,8CAA0B,oJAEtC,CACN,CAAA;AACF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa;IAC5B,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,UAAU,CAAC,cAAc,CAAC,CAAA;IACzD,IAAI,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACtD,OAAO,KAAC,UAAU,IAAC,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,UAAU,SAAG,CAAA;IACrF,CAAC;IACD,IAAI,SAAS;QAAE,OAAO,KAAC,wBAAwB,KAAG,CAAA;IAClD,OAAO,IAAI,CAAA;AACZ,CAAC;AAaD;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAA+B;IACjE,SAAS,EAAE,aAAa;IACxB,IAAI,EAAE,gBAAgB;IACtB,MAAM,EAAE,EAAE;CACV,CAAA;AAED,6FAA6F;AAC7F,OAAO,EACN,wBAAwB,GAIxB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACN,oBAAoB,EAEpB,mBAAmB,EAEnB,oBAAoB,EACpB,cAAc,EAGd,gBAAgB,EAChB,WAAW,GACX,MAAM,uBAAuB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/builder/index.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,OAAO,EAAE,aAAa,EAAkB,UAAU,EAAE,MAAM,OAAO,CAAA;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAA;AAexD,MAAM,cAAc,GAAG,aAAa,CAAsB,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAA;AAE9F;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,EAC/B,OAAO,EACP,SAAS,GAAG,KAAK,EACjB,QAAQ,GAKR;IACA,OAAO,KAAC,cAAc,CAAC,QAAQ,IAAC,KAAK,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,YAAG,QAAQ,GAA2B,CAAA;AACpG,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB;IACvC,OAAO,CACN,kDACiC,EAAE,EAClC,KAAK,EAAE;YACN,MAAM,EAAE,oBAAoB;YAC5B,YAAY,EAAE,CAAC;YACf,OAAO,EAAE,WAAW;YACpB,IAAI,EAAE,gBAAgB;YACtB,KAAK,EAAE,SAAS;YAChB,UAAU,EAAE,SAAS;SACrB,uCAEgB,8CAA0B,oJAEtC,CACN,CAAA;AACF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa;IAC5B,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,UAAU,CAAC,cAAc,CAAC,CAAA;IACzD,IAAI,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACtD,OAAO,KAAC,UAAU,IAAC,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,UAAU,SAAG,CAAA;IACrF,CAAC;IACD,IAAI,SAAS;QAAE,OAAO,KAAC,wBAAwB,KAAG,CAAA;IAClD,OAAO,IAAI,CAAA;AACZ,CAAC;AAaD;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAA+B;IACjE,SAAS,EAAE,aAAa;IACxB,IAAI,EAAE,gBAAgB;IACtB,MAAM,EAAE,EAAE;CACV,CAAA;AAED,gFAAgF;AAChF,OAAO,EACN,wBAAwB,GAIxB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACN,oBAAoB,EAEpB,mBAAmB,EAEnB,cAAc,EAGd,oBAAoB,EACpB,gBAAgB,EAChB,WAAW,GACX,MAAM,uBAAuB,CAAA"}
@@ -15,7 +15,7 @@
15
15
  * `'full'` shows the photo, certified/updated dates, and the memo (per display flags). BOTH
16
16
  * styles carry the compensation cue + FTC scope line (the always-on compliance disclosures).
17
17
  *
18
- * THEMING (WS6): presentation defaults come from `payload.content.display`; an optional
18
+ * THEMING: presentation defaults come from `payload.content.display`; an optional
19
19
  * `renderDef` prop layers a brand's validated themeable tokens (emitted as `--certrev-*`
20
20
  * CSS custom properties) and its subtractive field-visibility on top. No `renderDef` → the
21
21
  * CertREV preset default (theme output byte-identical to un-themed).
@@ -32,7 +32,7 @@ export interface CertBadgeProps {
32
32
  /** Override the badge style independent of the signed display config. */
33
33
  readonly badgeStyle?: 'full' | 'compact';
34
34
  /**
35
- * The brand render-def (WS6). ABSENT → the badge renders the CertREV preset
35
+ * The brand render-def. ABSENT → the badge renders the CertREV preset
36
36
  * default (fail-safe; theme output byte-identical to un-themed). PRESENT → its
37
37
  * validated themeable tokens are emitted as `--certrev-*` CSS custom properties
38
38
  * and its subtractive hide-set removes brand-toggleable fields. Invalid tokens
@@ -1 +1 @@
1
- {"version":3,"file":"CertBadge.d.ts","sourceRoot":"","sources":["../../src/components/CertBadge.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAe,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAGrE,OAAO,EAGN,KAAK,aAAa,EAGlB,MAAM,iBAAiB,CAAA;AAExB,MAAM,WAAW,cAAc;IAC9B,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,oGAAoG;IACpG,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,mEAAmE;IACnE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,yEAAyE;IACzE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACxC;;;;;;OAMG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAA;CAClC;AAuCD,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,+BA6H9C"}
1
+ {"version":3,"file":"CertBadge.d.ts","sourceRoot":"","sources":["../../src/components/CertBadge.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAe,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAIrE,OAAO,EAGN,KAAK,aAAa,EAGlB,MAAM,iBAAiB,CAAA;AAExB,MAAM,WAAW,cAAc;IAC9B,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,oGAAoG;IACpG,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,mEAAmE;IACnE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,yEAAyE;IACzE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACxC;;;;;;OAMG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAA;CAClC;AAwCD,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,+BAgI9C"}
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { safeHttpUrl } from './escape.js';
3
- import { credentialSuffix, expertNameWithCredentials, formatDate, resolveDisplay } from './format.js';
3
+ import { credentialSuffix, dedupeCredential, expertNameWithCredentials, formatDate, resolveDisplay } from './format.js';
4
+ import { CERTREV_LINK_REL } from './rel.js';
4
5
  import { CERT_SCOPE_LINE, CERT_STRINGS_VERSION, resolveRenderTheme, themeCssVarDeclarations, } from './render-def.js';
5
6
  const ROOT_CLASS = 'certrev-badge';
6
7
  /** Inline-SVG verified checkmark — no external asset, accent-tinted, decorative. */
@@ -11,7 +12,9 @@ function ExpertPhoto({ content, show }) {
11
12
  const src = show ? safeHttpUrl(content.expert.photoUrl) : null;
12
13
  if (!src)
13
14
  return null;
14
- return (_jsx("img", { className: `${ROOT_CLASS}__photo`, src: src, alt: `Photo of ${content.expert.displayName}`, width: 40, height: 40, loading: "lazy", decoding: "async" }));
15
+ return (_jsx("img", { className: `${ROOT_CLASS}__photo`, src: src,
16
+ // Decorative: the byline renders the same name as text right beside it.
17
+ alt: "", width: 40, height: 40, loading: "lazy", decoding: "async" }));
15
18
  }
16
19
  export function CertBadge(props) {
17
20
  const { payload } = props;
@@ -23,7 +26,10 @@ export function CertBadge(props) {
23
26
  const profileUrl = safeHttpUrl(content.expert.profileUrl);
24
27
  const certifiedLabel = formatDate(content.certifiedAt);
25
28
  const updatedLabel = formatDate(content.contentModifiedAt);
26
- const suffix = credentialSuffix(content);
29
+ // Composed name + suffix, so the suffix drops a credential the stored display name already
30
+ // carries ("Dr. Erik Schraga, MD" + "MD, EM" → one "MD"). `credentialSuffix` stays whole for
31
+ // callers that render the list away from the name.
32
+ const suffix = dedupeCredential(content.expert.displayName, credentialSuffix(content));
27
33
  const accent = display.accentColor;
28
34
  const rootClass = `${ROOT_CLASS} ${ROOT_CLASS}--${style}${props.className ? ` ${props.className}` : ''}`;
29
35
  // Accent is exposed as a custom property so brand themes can read it, and applied
@@ -31,12 +37,12 @@ export function CertBadge(props) {
31
37
  // A THEMED brand additionally emits the surface/radius/font vars; with no render-def
32
38
  // `themeCssVarDeclarations` is empty, so the un-themed style is byte-identical.
33
39
  const rootStyle = {
34
- ['--certrev-accent']: accent,
40
+ '--certrev-accent': accent,
35
41
  borderInlineStartColor: accent,
36
42
  };
37
43
  for (const [name, value] of themeCssVarDeclarations(theme))
38
44
  rootStyle[name] = value;
39
45
  const expertName = (_jsxs("span", { className: `${ROOT_CLASS}__expert-name`, children: [content.expert.displayName, suffix ? _jsxs("span", { className: `${ROOT_CLASS}__credentials`, children: [", ", suffix] }) : null] }));
40
- return (_jsxs("section", { className: rootClass, style: rootStyle, "data-certrev-cert-id": payload.certId, "data-certrev-strings-version": CERT_STRINGS_VERSION, "data-certrev-def-version": theme.version, "aria-label": `Content reviewed by ${expertNameWithCredentials(content)}`, children: [_jsxs("div", { className: `${ROOT_CLASS}__header`, children: [_jsx(VerifiedMark, { accent: accent }), style === 'full' ? _jsx(ExpertPhoto, { content: content, show: display.showExpertPhoto }) : null, _jsxs("div", { className: `${ROOT_CLASS}__heading`, children: [_jsx("span", { className: `${ROOT_CLASS}__eyebrow`, children: "Expert reviewed" }), _jsxs("span", { className: `${ROOT_CLASS}__byline`, children: ["Reviewed by", ' ', profileUrl ? (_jsx("a", { className: `${ROOT_CLASS}__expert-link`, href: profileUrl, rel: "noopener", children: expertName })) : (expertName)] })] })] }), style === 'full' ? (_jsxs(_Fragment, { children: [display.showMemo && content.memo ? _jsx("p", { className: `${ROOT_CLASS}__memo`, children: content.memo }) : null, display.showAuthor && content.author.name && content.author.name !== content.expert.displayName ? (_jsxs("p", { className: `${ROOT_CLASS}__author`, children: ["Written by ", content.author.name, content.author.title ? `, ${content.author.title}` : ''] })) : null, _jsxs("dl", { className: `${ROOT_CLASS}__dates`, children: [certifiedLabel ? (_jsxs("div", { className: `${ROOT_CLASS}__date`, children: [_jsx("dt", { children: "Certified" }), _jsx("dd", { children: _jsx("time", { dateTime: content.certifiedAt, children: certifiedLabel }) })] })) : null, updatedLabel ? (_jsxs("div", { className: `${ROOT_CLASS}__date`, children: [_jsx("dt", { children: "Last updated" }), _jsx("dd", { children: _jsx("time", { dateTime: content.contentModifiedAt ?? undefined, children: updatedLabel }) })] })) : null] })] })) : null, _jsxs("div", { className: `${ROOT_CLASS}__disclosure`, children: [_jsxs("span", { className: `${ROOT_CLASS}__compensated-cue`, children: ["Compensated", ' ', profileUrl ? (_jsx("a", { className: `${ROOT_CLASS}__cue-link`, href: profileUrl, rel: "noopener", children: "expert" })) : ('expert')] }), _jsx("p", { className: `${ROOT_CLASS}__ftc`, "data-ftc-disclosure": true, children: _jsx("span", { "data-ftc-line": true, children: CERT_SCOPE_LINE }) })] }), verifyUrl ? (_jsx("a", { className: `${ROOT_CLASS}__verify`, href: verifyUrl, rel: "noopener", "aria-label": "Verify this certification on CertREV", children: "Verify on CertREV" })) : null] }));
46
+ return (_jsxs("section", { className: rootClass, style: rootStyle, "data-certrev-cert-id": payload.certId, "data-certrev-strings-version": CERT_STRINGS_VERSION, "data-certrev-def-version": theme.version, "aria-label": `Content reviewed by ${expertNameWithCredentials(content)}`, children: [_jsxs("div", { className: `${ROOT_CLASS}__header`, children: [_jsx(VerifiedMark, { accent: accent }), style === 'full' ? _jsx(ExpertPhoto, { content: content, show: display.showExpertPhoto }) : null, _jsxs("div", { className: `${ROOT_CLASS}__heading`, children: [_jsx("span", { className: `${ROOT_CLASS}__eyebrow`, children: "Expert reviewed" }), _jsxs("span", { className: `${ROOT_CLASS}__byline`, children: ["Reviewed by", ' ', profileUrl ? (_jsx("a", { className: `${ROOT_CLASS}__expert-link`, href: profileUrl, rel: CERTREV_LINK_REL, children: expertName })) : (expertName)] })] })] }), style === 'full' ? (_jsxs(_Fragment, { children: [display.showMemo && content.memo ? _jsx("p", { className: `${ROOT_CLASS}__memo`, children: content.memo }) : null, display.showAuthor && content.author.name && content.author.name !== content.expert.displayName ? (_jsxs("p", { className: `${ROOT_CLASS}__author`, children: ["Written by ", content.author.name, content.author.title ? `, ${content.author.title}` : ''] })) : null, _jsxs("dl", { className: `${ROOT_CLASS}__dates`, children: [certifiedLabel ? (_jsxs("div", { className: `${ROOT_CLASS}__date`, children: [_jsx("dt", { children: "Certified" }), _jsx("dd", { children: _jsx("time", { dateTime: content.certifiedAt, children: certifiedLabel }) })] })) : null, updatedLabel ? (_jsxs("div", { className: `${ROOT_CLASS}__date`, children: [_jsx("dt", { children: "Last updated" }), _jsx("dd", { children: _jsx("time", { dateTime: content.contentModifiedAt ?? undefined, children: updatedLabel }) })] })) : null] })] })) : null, _jsxs("div", { className: `${ROOT_CLASS}__disclosure`, children: [_jsxs("span", { className: `${ROOT_CLASS}__compensated-cue`, children: ["Compensated", ' ', profileUrl ? (_jsx("a", { className: `${ROOT_CLASS}__cue-link`, href: profileUrl, rel: CERTREV_LINK_REL, children: "expert" })) : ('expert')] }), _jsx("p", { className: `${ROOT_CLASS}__ftc`, "data-ftc-disclosure": true, children: _jsx("span", { "data-ftc-line": true, children: CERT_SCOPE_LINE }) })] }), verifyUrl ? (_jsx("a", { className: `${ROOT_CLASS}__verify`, href: verifyUrl, rel: CERTREV_LINK_REL, "aria-label": "Verify this certification on CertREV", children: "Verify on CertREV" })) : null] }));
41
47
  }
42
48
  //# sourceMappingURL=CertBadge.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CertBadge.js","sourceRoot":"","sources":["../../src/components/CertBadge.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"CertBadge.js","sourceRoot":"","sources":["../../src/components/CertBadge.tsx"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CertJsonLd.d.ts","sourceRoot":"","sources":["../../src/components/CertJsonLd.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,EAAqB,KAAK,oBAAoB,EAA4B,MAAM,sBAAsB,CAAA;AAE7G,MAAM,WAAW,eAAgB,SAAQ,oBAAoB;IAC5D,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,mFAAmF;IACnF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,+BAWhD"}
1
+ {"version":3,"file":"CertJsonLd.d.ts","sourceRoot":"","sources":["../../src/components/CertJsonLd.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,EAAE,KAAK,oBAAoB,EAA+C,MAAM,sBAAsB,CAAA;AAE7G,MAAM,WAAW,eAAgB,SAAQ,oBAAoB;IAC5D,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,mFAAmF;IACnF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,+BAWhD"}
@@ -1 +1 @@
1
- {"version":3,"file":"CertJsonLd.js","sourceRoot":"","sources":["../../src/components/CertJsonLd.tsx"],"names":[],"mappings":";AAgBA,OAAO,EAAE,iBAAiB,EAA6B,wBAAwB,EAAE,MAAM,sBAAsB,CAAA;AAQ7G,MAAM,UAAU,UAAU,CAAC,KAAsB;IAChD,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,CAAA;IAC5C,MAAM,IAAI,GAAG,wBAAwB,CAAC,iBAAiB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAA;IACvE,OAAO,CACN,iBACC,IAAI,EAAC,qBAAqB,EAC1B,EAAE,EAAE,QAAQ;QACZ,8MAA8M;QAC9M,uBAAuB,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,GACxC,CACF,CAAA;AACF,CAAC"}
1
+ {"version":3,"file":"CertJsonLd.js","sourceRoot":"","sources":["../../src/components/CertJsonLd.tsx"],"names":[],"mappings":";AAgBA,OAAO,EAA6B,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAA;AAQ7G,MAAM,UAAU,UAAU,CAAC,KAAsB;IAChD,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,CAAA;IAC5C,MAAM,IAAI,GAAG,wBAAwB,CAAC,iBAAiB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAA;IACvE,OAAO,CACN,iBACC,IAAI,EAAC,qBAAqB,EAC1B,EAAE,EAAE,QAAQ;QACZ,8MAA8M;QAC9M,uBAAuB,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,GACxC,CACF,CAAA;AACF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"CertRevBacklink.d.ts","sourceRoot":"","sources":["../../src/components/CertRevBacklink.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAIxD,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,+CAA+C;IAC/C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CACvB;AAID,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,sCAuC1D"}
1
+ {"version":3,"file":"CertRevBacklink.d.ts","sourceRoot":"","sources":["../../src/components/CertRevBacklink.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAKxD,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,+CAA+C;IAC/C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CACvB;AAID,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,sCA6C1D"}
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { safeHttpUrl } from './escape.js';
3
3
  import { resolveDisplay } from './format.js';
4
+ import { CERTREV_LINK_REL } from './rel.js';
4
5
  const ROOT_CLASS = 'certrev-backlink';
5
6
  export function CertRevBacklink(props) {
6
7
  const verifyUrl = safeHttpUrl(props.payload.content.verifyUrl);
@@ -10,7 +11,7 @@ export function CertRevBacklink(props) {
10
11
  const rootClass = `${ROOT_CLASS}${props.className ? ` ${props.className}` : ''}`;
11
12
  // Typed as a string record (not inline) so the `--certrev-accent` CSS custom property
12
13
  // is accepted — React's CSSProperties rejects arbitrary custom props on an inline literal.
13
- const rootStyle = { ['--certrev-accent']: display.accentColor, color: display.accentColor };
14
- return (_jsxs("a", { className: rootClass, style: rootStyle, href: verifyUrl, rel: "noopener", "aria-label": "Verify this certification on CertREV", children: [_jsxs("svg", { className: `${ROOT_CLASS}__icon`, width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", focusable: "false", children: [_jsx("title", { children: "Verified" }), _jsx("path", { d: "M12 2l7 3v6c0 4.4-3 8.3-7 9-4-0.7-7-4.6-7-9V5l7-3z", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinejoin: "round" }), _jsx("path", { d: "M8.5 12l2.3 2.3L15.5 9", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" })] }), _jsx("span", { className: `${ROOT_CLASS}__label`, children: props.label ?? 'Verify on CertREV' })] }));
14
+ const rootStyle = { '--certrev-accent': display.accentColor, color: display.accentColor };
15
+ return (_jsxs("a", { className: rootClass, style: rootStyle, href: verifyUrl, rel: CERTREV_LINK_REL, "aria-label": "Verify this certification on CertREV", children: [_jsxs("svg", { className: `${ROOT_CLASS}__icon`, width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", focusable: "false", children: [_jsx("title", { children: "Verified" }), _jsx("path", { d: "M12 2l7 3v6c0 4.4-3 8.3-7 9-4-0.7-7-4.6-7-9V5l7-3z", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinejoin: "round" }), _jsx("path", { d: "M8.5 12l2.3 2.3L15.5 9", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" })] }), _jsx("span", { className: `${ROOT_CLASS}__label`, children: props.label ?? 'Verify on CertREV' })] }));
15
16
  }
16
17
  //# sourceMappingURL=CertRevBacklink.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CertRevBacklink.js","sourceRoot":"","sources":["../../src/components/CertRevBacklink.tsx"],"names":[],"mappings":";AAUA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAU5C,MAAM,UAAU,GAAG,kBAAkB,CAAA;AAErC,MAAM,UAAU,eAAe,CAAC,KAA2B;IAC1D,MAAM,SAAS,GAAG,WAAW,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;IAC9D,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAA;IAC3B,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,WAAW,CAAC,CAAA;IAChF,MAAM,SAAS,GAAG,GAAG,UAAU,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAA;IAChF,sFAAsF;IACtF,2FAA2F;IAC3F,MAAM,SAAS,GAA2B,EAAE,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC,WAAW,EAAE,KAAK,EAAE,OAAO,CAAC,WAAW,EAAE,CAAA;IAEnH,OAAO,CACN,aACC,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,SAAS,EAChB,IAAI,EAAE,SAAS,EACf,GAAG,EAAC,UAAU,gBACH,sCAAsC,aAEjD,eACC,SAAS,EAAE,GAAG,UAAU,QAAQ,EAChC,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,EACX,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,MAAM,iBACC,MAAM,EAClB,SAAS,EAAC,OAAO,aAEjB,uCAAuB,EACvB,eACC,CAAC,EAAC,oDAAoD,EACtD,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,KAAK,EACjB,cAAc,EAAC,OAAO,GACrB,EACF,eAAM,CAAC,EAAC,wBAAwB,EAAC,MAAM,EAAC,cAAc,EAAC,WAAW,EAAC,KAAK,EAAC,aAAa,EAAC,OAAO,EAAC,cAAc,EAAC,OAAO,GAAG,IACnH,EACN,eAAM,SAAS,EAAE,GAAG,UAAU,SAAS,YAAG,KAAK,CAAC,KAAK,IAAI,mBAAmB,GAAQ,IACjF,CACJ,CAAA;AACF,CAAC"}
1
+ {"version":3,"file":"CertRevBacklink.js","sourceRoot":"","sources":["../../src/components/CertRevBacklink.tsx"],"names":[],"mappings":";AAUA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAU3C,MAAM,UAAU,GAAG,kBAAkB,CAAA;AAErC,MAAM,UAAU,eAAe,CAAC,KAA2B;IAC1D,MAAM,SAAS,GAAG,WAAW,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;IAC9D,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAA;IAC3B,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,WAAW,CAAC,CAAA;IAChF,MAAM,SAAS,GAAG,GAAG,UAAU,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAA;IAChF,sFAAsF;IACtF,2FAA2F;IAC3F,MAAM,SAAS,GAA2B,EAAE,kBAAkB,EAAE,OAAO,CAAC,WAAW,EAAE,KAAK,EAAE,OAAO,CAAC,WAAW,EAAE,CAAA;IAEjH,OAAO,CACN,aACC,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,SAAS,EAChB,IAAI,EAAE,SAAS,EACf,GAAG,EAAE,gBAAgB,gBACV,sCAAsC,aAEjD,eACC,SAAS,EAAE,GAAG,UAAU,QAAQ,EAChC,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,EACX,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,MAAM,iBACC,MAAM,EAClB,SAAS,EAAC,OAAO,aAEjB,uCAAuB,EACvB,eACC,CAAC,EAAC,oDAAoD,EACtD,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,KAAK,EACjB,cAAc,EAAC,OAAO,GACrB,EACF,eACC,CAAC,EAAC,wBAAwB,EAC1B,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,KAAK,EACjB,aAAa,EAAC,OAAO,EACrB,cAAc,EAAC,OAAO,GACrB,IACG,EACN,eAAM,SAAS,EAAE,GAAG,UAAU,SAAS,YAAG,KAAK,CAAC,KAAK,IAAI,mBAAmB,GAAQ,IACjF,CACJ,CAAA;AACF,CAAC"}
@@ -18,7 +18,7 @@ export interface CertReviewProps {
18
18
  readonly className?: string;
19
19
  readonly badgeStyle?: 'full' | 'compact';
20
20
  /**
21
- * The brand render-def (WS6). Forwarded to `<CertBadge>` for themeable-token
21
+ * The brand render-def. Forwarded to `<CertBadge>` for themeable-token
22
22
  * emission + subtractive visibility; absent → the CertREV preset default. The
23
23
  * JSON-LD is presentation-blind, so the def never touches the structured data.
24
24
  */
@@ -1 +1 @@
1
- {"version":3,"file":"ExpertBio.d.ts","sourceRoot":"","sources":["../../src/components/ExpertBio.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAIxD,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,0FAA0F;IAC1F,QAAQ,CAAC,YAAY,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;CAC1C;AAID,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,+BAsD9C"}
1
+ {"version":3,"file":"ExpertBio.d.ts","sourceRoot":"","sources":["../../src/components/ExpertBio.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAKxD,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,0FAA0F;IAC1F,QAAQ,CAAC,YAAY,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;CAC1C;AAID,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,+BA0D9C"}
@@ -1,6 +1,7 @@
1
1
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
2
  import { safeHttpUrl } from './escape.js';
3
- import { resolveDisplay } from './format.js';
3
+ import { credentialSuffix, dedupeCredential, resolveDisplay } from './format.js';
4
+ import { CERTREV_LINK_REL } from './rel.js';
4
5
  const ROOT_CLASS = 'certrev-expert-bio';
5
6
  export function ExpertBio(props) {
6
7
  const { payload } = props;
@@ -10,8 +11,15 @@ export function ExpertBio(props) {
10
11
  const photoUrl = display.showExpertPhoto ? safeHttpUrl(expert.photoUrl) : null;
11
12
  const Heading = props.headingLevel ?? 'h3';
12
13
  const rootClass = `${ROOT_CLASS}${props.className ? ` ${props.className}` : ''}`;
13
- const rootStyle = { ['--certrev-accent']: display.accentColor };
14
- const nameNode = (_jsxs(Heading, { className: `${ROOT_CLASS}__name`, children: [expert.displayName, expert.credentials.length > 0 ? (_jsxs("span", { className: `${ROOT_CLASS}__credentials`, children: [", ", expert.credentials.map((c) => c.abbreviation).join(', ')] })) : null] }));
15
- return (_jsxs("aside", { className: rootClass, style: rootStyle, "aria-label": `About the reviewing expert, ${expert.displayName}`, children: [photoUrl ? (_jsx("img", { className: `${ROOT_CLASS}__photo`, src: photoUrl, alt: `Photo of ${expert.displayName}`, width: 64, height: 64, loading: "lazy", decoding: "async" })) : null, _jsxs("div", { className: `${ROOT_CLASS}__body`, children: [profileUrl ? (_jsx("a", { className: `${ROOT_CLASS}__name-link`, href: profileUrl, rel: "noopener", children: nameNode })) : (nameNode), expert.credentials.length > 0 ? (_jsx("ul", { className: `${ROOT_CLASS}__credential-list`, children: expert.credentials.map((c) => (_jsxs("li", { className: `${ROOT_CLASS}__credential`, children: [_jsx("abbr", { title: c.fullName, children: c.abbreviation }), _jsxs("span", { className: `${ROOT_CLASS}__credential-full`, children: [" \u2014 ", c.fullName] })] }, `${c.abbreviation}:${c.fullName}`))) })) : null, _jsx("p", { className: `${ROOT_CLASS}__role`, children: "Verified expert reviewer" })] })] }));
14
+ const rootStyle = { '--certrev-accent': display.accentColor };
15
+ // Same composition rule as <CertBadge> / renderBadgeHtml: drop a credential the stored
16
+ // display name already carries, so one payload never reads "Dr. Erik Schraga, MD, MD, EM".
17
+ // The full list still renders below, away from the name, where every credential belongs.
18
+ const nameSuffix = dedupeCredential(expert.displayName, credentialSuffix(payload.content));
19
+ const nameNode = (_jsxs(Heading, { className: `${ROOT_CLASS}__name`, children: [expert.displayName, nameSuffix ? _jsxs("span", { className: `${ROOT_CLASS}__credentials`, children: [", ", nameSuffix] }) : null] }));
20
+ return (_jsxs("aside", { className: rootClass, style: rootStyle, "aria-label": `About the reviewing expert, ${expert.displayName}`, children: [photoUrl ? (_jsx("img", { className: `${ROOT_CLASS}__photo`, src: photoUrl,
21
+ // Decorative: the name is announced by this aside's aria-label AND rendered as text
22
+ // immediately below, so alt text repeats it a third time. Matches the modal's avatar.
23
+ alt: "", width: 64, height: 64, loading: "lazy", decoding: "async" })) : null, _jsxs("div", { className: `${ROOT_CLASS}__body`, children: [profileUrl ? (_jsx("a", { className: `${ROOT_CLASS}__name-link`, href: profileUrl, rel: CERTREV_LINK_REL, children: nameNode })) : (nameNode), expert.credentials.length > 0 ? (_jsx("ul", { className: `${ROOT_CLASS}__credential-list`, children: expert.credentials.map((c) => (_jsxs("li", { className: `${ROOT_CLASS}__credential`, children: [_jsx("abbr", { title: c.fullName, children: c.abbreviation }), _jsxs("span", { className: `${ROOT_CLASS}__credential-full`, children: [" \u2014 ", c.fullName] })] }, `${c.abbreviation}:${c.fullName}`))) })) : null, _jsx("p", { className: `${ROOT_CLASS}__role`, children: "Verified expert reviewer" })] })] }));
16
24
  }
17
25
  //# sourceMappingURL=ExpertBio.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ExpertBio.js","sourceRoot":"","sources":["../../src/components/ExpertBio.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ExpertBio.js","sourceRoot":"","sources":["../../src/components/ExpertBio.tsx"],"names":[],"mappings":"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"}
@@ -12,9 +12,36 @@
12
12
  *
13
13
  * Everything here is pure and runtime-agnostic (no DOM, no Node APIs) so it runs in a
14
14
  * server component, an edge runtime, and the Web Component identically.
15
+ *
16
+ * Every helper here is TYPE-HOSTILE-SAFE, and that is load-bearing rather than defensive
17
+ * habit: these run downstream of `get-verified-envelope.ts`'s bare
18
+ * `JSON.parse(...) as CertDeliveryEnvelope` cast, and `verifyEnvelope` checks the SIGNATURE,
19
+ * never the SHAPE — so a number / object / array arrives where the contract promised a
20
+ * string. A `TypeError` here is not a missing badge: under `renderToReadableStream` it aborts
21
+ * the whole article route with zero HTML, and the Builder SDK ships no error boundary on its
22
+ * node/edge builds. So the family's contract is NEVER THROW — see `asText`.
23
+ */
24
+ /**
25
+ * Coerce an untyped value to renderable text. The rule is deliberately narrow:
26
+ * • a string passes through untouched — every helper below preserves its exact string
27
+ * semantics, because `escapeHtml` is the ONLY XSS boundary four hand-built-HTML
28
+ * renderers have and any change to its string output is a security regression;
29
+ * • a finite number renders as its digits (a stray count is at least legible copy);
30
+ * • EVERYTHING ELSE — null, undefined, booleans, objects, arrays, functions, symbols,
31
+ * NaN/Infinity — renders as empty.
32
+ *
33
+ * The last clause is the important one, and it is why this is not `String(input)`. Blind
34
+ * coercion would splice `[object Object]` (or a hostile `toString`'s payload) into the
35
+ * article body, and on a null-prototype object it throws the very TypeError this exists to
36
+ * prevent. A value that is not text is not copy; the correct render of it is nothing.
37
+ *
38
+ * Exported for `format.ts` only — so a helper there can't drift back to a bare dereference —
39
+ * and deliberately NOT re-exported from the package entry: an internal invariant, not a
40
+ * contract surface consumers should build on.
15
41
  */
42
+ export declare function asText(input: unknown): string;
16
43
  /** Escape text for safe inclusion in HTML element content or double-quoted attributes. */
17
- export declare function escapeHtml(input: string): string;
44
+ export declare function escapeHtml(input: string | null | undefined): string;
18
45
  /** Alias kept explicit at call sites where the value lands in an attribute. */
19
46
  export declare const escapeAttribute: typeof escapeHtml;
20
47
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"escape.d.ts","sourceRoot":"","sources":["../../src/components/escape.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAUH,0FAA0F;AAC1F,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,+EAA+E;AAC/E,eAAO,MAAM,eAAe,mBAAa,CAAA;AAEzC;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAY3E;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAQ5E;AAED,2EAA2E;AAC3E,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAEjE"}
1
+ {"version":3,"file":"escape.d.ts","sourceRoot":"","sources":["../../src/components/escape.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAI7C;AAUD,0FAA0F;AAC1F,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAEnE;AAED,+EAA+E;AAC/E,eAAO,MAAM,eAAe,mBAAa,CAAA;AAEzC;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAW3E;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAO5E;AAED,2EAA2E;AAC3E,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAEjE"}
@@ -12,7 +12,40 @@
12
12
  *
13
13
  * Everything here is pure and runtime-agnostic (no DOM, no Node APIs) so it runs in a
14
14
  * server component, an edge runtime, and the Web Component identically.
15
+ *
16
+ * Every helper here is TYPE-HOSTILE-SAFE, and that is load-bearing rather than defensive
17
+ * habit: these run downstream of `get-verified-envelope.ts`'s bare
18
+ * `JSON.parse(...) as CertDeliveryEnvelope` cast, and `verifyEnvelope` checks the SIGNATURE,
19
+ * never the SHAPE — so a number / object / array arrives where the contract promised a
20
+ * string. A `TypeError` here is not a missing badge: under `renderToReadableStream` it aborts
21
+ * the whole article route with zero HTML, and the Builder SDK ships no error boundary on its
22
+ * node/edge builds. So the family's contract is NEVER THROW — see `asText`.
23
+ */
24
+ /**
25
+ * Coerce an untyped value to renderable text. The rule is deliberately narrow:
26
+ * • a string passes through untouched — every helper below preserves its exact string
27
+ * semantics, because `escapeHtml` is the ONLY XSS boundary four hand-built-HTML
28
+ * renderers have and any change to its string output is a security regression;
29
+ * • a finite number renders as its digits (a stray count is at least legible copy);
30
+ * • EVERYTHING ELSE — null, undefined, booleans, objects, arrays, functions, symbols,
31
+ * NaN/Infinity — renders as empty.
32
+ *
33
+ * The last clause is the important one, and it is why this is not `String(input)`. Blind
34
+ * coercion would splice `[object Object]` (or a hostile `toString`'s payload) into the
35
+ * article body, and on a null-prototype object it throws the very TypeError this exists to
36
+ * prevent. A value that is not text is not copy; the correct render of it is nothing.
37
+ *
38
+ * Exported for `format.ts` only — so a helper there can't drift back to a bare dereference —
39
+ * and deliberately NOT re-exported from the package entry: an internal invariant, not a
40
+ * contract surface consumers should build on.
15
41
  */
42
+ export function asText(input) {
43
+ if (typeof input === 'string')
44
+ return input;
45
+ if (typeof input === 'number')
46
+ return Number.isFinite(input) ? String(input) : '';
47
+ return '';
48
+ }
16
49
  const HTML_ESCAPES = {
17
50
  '&': '&amp;',
18
51
  '<': '&lt;',
@@ -22,7 +55,7 @@ const HTML_ESCAPES = {
22
55
  };
23
56
  /** Escape text for safe inclusion in HTML element content or double-quoted attributes. */
24
57
  export function escapeHtml(input) {
25
- return input.replace(/[&<>"']/g, (c) => HTML_ESCAPES[c] ?? c);
58
+ return asText(input).replace(/[&<>"']/g, (c) => HTML_ESCAPES[c] ?? c);
26
59
  }
27
60
  /** Alias kept explicit at call sites where the value lands in an attribute. */
28
61
  export const escapeAttribute = escapeHtml;
@@ -33,9 +66,7 @@ export const escapeAttribute = escapeHtml;
33
66
  * Callers treat null as "omit the link".
34
67
  */
35
68
  export function safeHttpUrl(input) {
36
- if (!input)
37
- return null;
38
- const trimmed = input.trim();
69
+ const trimmed = asText(input).trim();
39
70
  if (trimmed === '')
40
71
  return null;
41
72
  // Site-relative or protocol-relative URLs are safe (no scheme to abuse).
@@ -56,9 +87,7 @@ export function safeHttpUrl(input) {
56
87
  * `url(...)`, or break out of the style attribute. Returns null on anything suspicious.
57
88
  */
58
89
  export function safeCssColor(input) {
59
- if (!input)
60
- return null;
61
- const v = input.trim();
90
+ const v = asText(input).trim();
62
91
  if (v === '')
63
92
  return null;
64
93
  if (/^#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(v))
@@ -71,6 +100,6 @@ export function safeCssColor(input) {
71
100
  }
72
101
  /** Collapse interior whitespace + trim. Used to normalize display text. */
73
102
  export function tidyText(input) {
74
- return (input ?? '').replace(/\s+/g, ' ').trim();
103
+ return asText(input).replace(/\s+/g, ' ').trim();
75
104
  }
76
105
  //# sourceMappingURL=escape.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"escape.js","sourceRoot":"","sources":["../../src/components/escape.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,YAAY,GAA2B;IAC5C,GAAG,EAAE,OAAO;IACZ,GAAG,EAAE,MAAM;IACX,GAAG,EAAE,MAAM;IACX,GAAG,EAAE,QAAQ;IACb,GAAG,EAAE,OAAO;CACZ,CAAA;AAED,0FAA0F;AAC1F,MAAM,UAAU,UAAU,CAAC,KAAa;IACvC,OAAO,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA;AAC9D,CAAC;AAED,+EAA+E;AAC/E,MAAM,CAAC,MAAM,eAAe,GAAG,UAAU,CAAA;AAEzC;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,KAAgC;IAC3D,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAA;IACvB,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;IAC5B,IAAI,OAAO,KAAK,EAAE;QAAE,OAAO,IAAI,CAAA;IAC/B,yEAAyE;IACzE,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,OAAO,CAAA;IACjG,IAAI,CAAC;QACJ,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAA;QAC1B,OAAO,CAAC,CAAC,QAAQ,KAAK,OAAO,IAAI,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;IAC/E,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAA;IACZ,CAAC;AACF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,KAAgC;IAC5D,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAA;IACvB,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;IACtB,IAAI,CAAC,KAAK,EAAE;QAAE,OAAO,IAAI,CAAA;IACzB,IAAI,uDAAuD,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAA;IAC7E,IAAI,4CAA4C,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAA;IAClE,IAAI,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAA;IACxC,OAAO,IAAI,CAAA;AACZ,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,QAAQ,CAAC,KAAgC;IACxD,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAA;AACjD,CAAC"}
1
+ {"version":3,"file":"escape.js","sourceRoot":"","sources":["../../src/components/escape.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,MAAM,CAAC,KAAc;IACpC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAA;IAC3C,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IACjF,OAAO,EAAE,CAAA;AACV,CAAC;AAED,MAAM,YAAY,GAA2B;IAC5C,GAAG,EAAE,OAAO;IACZ,GAAG,EAAE,MAAM;IACX,GAAG,EAAE,MAAM;IACX,GAAG,EAAE,QAAQ;IACb,GAAG,EAAE,OAAO;CACZ,CAAA;AAED,0FAA0F;AAC1F,MAAM,UAAU,UAAU,CAAC,KAAgC;IAC1D,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA;AACtE,CAAC;AAED,+EAA+E;AAC/E,MAAM,CAAC,MAAM,eAAe,GAAG,UAAU,CAAA;AAEzC;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,KAAgC;IAC3D,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAA;IACpC,IAAI,OAAO,KAAK,EAAE;QAAE,OAAO,IAAI,CAAA;IAC/B,yEAAyE;IACzE,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,OAAO,CAAA;IACjG,IAAI,CAAC;QACJ,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAA;QAC1B,OAAO,CAAC,CAAC,QAAQ,KAAK,OAAO,IAAI,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;IAC/E,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAA;IACZ,CAAC;AACF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,KAAgC;IAC5D,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAA;IAC9B,IAAI,CAAC,KAAK,EAAE;QAAE,OAAO,IAAI,CAAA;IACzB,IAAI,uDAAuD,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAA;IAC7E,IAAI,4CAA4C,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAA;IAClE,IAAI,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAA;IACxC,OAAO,IAAI,CAAA;AACZ,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,QAAQ,CAAC,KAAgC;IACxD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAA;AACjD,CAAC"}
@@ -27,8 +27,57 @@ export interface ResolvedDisplay {
27
27
  */
28
28
  export declare function resolveDisplay(display: CertDisplayConfig | undefined, accentOverride?: string, renderDef?: CertRenderDef): ResolvedDisplay;
29
29
  export declare function formatDate(iso: string | null | undefined): string | null;
30
- /** "PhD, RD" — the expert's credential abbreviations, comma-joined. */
30
+ /**
31
+ * "PhD, RD" — the expert's credential abbreviations, comma-joined.
32
+ *
33
+ * Shape-guarded because the envelope reaching here was only SIGNATURE-checked: a signed
34
+ * payload whose `credentials` is absent or is not an array used to throw
35
+ * `TypeError: ...map is not a function` and take the host route down with it. An entry with
36
+ * no usable abbreviation is DROPPED rather than joined as empty — the old code rendered
37
+ * "Dr. Jane Doe, , RD" off one missing field. A well-formed envelope is byte-identical.
38
+ */
31
39
  export declare function credentialSuffix(content: CertContent): string;
32
- /** "Dr. Jane Doe, PhD, RD" — display name with credential suffix appended. */
40
+ /**
41
+ * Drop from `credential` any token the display NAME already prints, so a name that arrives
42
+ * carrying its own post-nominal doesn't get it appended a second time — the defect that put
43
+ * "Dr. Erik Schraga, MD, MD, EM" on a live customer page (P2-6).
44
+ *
45
+ * THE ROOT CAUSE IS UPSTREAM DATA, NOT THIS FUNCTION. Portal cleans an expert display name
46
+ * with `cleanExpertDisplayName` and has DB CHECK constraints for exactly this shape
47
+ * (migration 20260506151204), but `resolveStampReviewerName` skips the cleaner and
48
+ * `package_drafts.expert_display_name` carries no constraint — so the duplicate is STORED,
49
+ * and a consumer reading the Delivery API directly still receives it. This is the render
50
+ * boundary refusing to print the same claim twice, nothing more.
51
+ *
52
+ * THE RULE, and why it is drawn this narrowly — a false positive here would delete a REAL
53
+ * credential from a compliance-facing card, which is worse than the duplicate it fixes:
54
+ * • a credential token is dropped only when it equals a WHOLE comma-separated segment of
55
+ * the name. Never a substring: "MD" must survive against "Dr. MDonald", "Jane Amdahl"
56
+ * and "Md. Rahman", all of which a naive `name.includes(credential)` would gut;
57
+ * • comparison is `credentialKey`'d, so "M.D." in the name cancels "MD" in the credential;
58
+ * • the credential may be a LIST ("MD, FAAD"): each token is judged on its own, and the
59
+ * survivors keep their original spelling and order. A name carrying only "MD" therefore
60
+ * still renders ", FAAD" — no verified credential is ever lost to a partial overlap;
61
+ * • nothing duplicated ⇒ the credential string is returned VERBATIM (not re-joined), so
62
+ * the overwhelmingly common case is byte-identical to the pre-guard render.
63
+ *
64
+ * What it deliberately does NOT do: it never edits the NAME. "Dr. Erik Schraga, MD" keeps its
65
+ * inline "MD" — un-embedding a post-nominal is `cleanExpertDisplayName`'s job on the write
66
+ * path, and guessing at it here would be the same false-positive class in the other direction.
67
+ * It also cannot catch a duplicate spelled differently in the two fields ("Doctor of Medicine"
68
+ * in the name against "MD" in the credential) or one embedded mid-name rather than as its own
69
+ * segment ("Erik Schraga MD" with no comma) — both stay for the upstream fix.
70
+ */
71
+ export declare function dedupeCredential(name: unknown, credential: unknown): string;
72
+ /**
73
+ * "Dr. Jane Doe, PhD, RD" — display name with credential suffix appended.
74
+ *
75
+ * With no name there is nobody to attribute the review to, so the whole label is empty:
76
+ * credentials alone would read as "Content reviewed by PhD, RD" in `CertBadge`'s aria-label.
77
+ *
78
+ * The suffix passes `dedupeCredential` because this composes name AND credential into one
79
+ * string; `credentialSuffix` above stays the full verified list, so a caller rendering the
80
+ * credentials AWAY from the name (a chip row, a standalone line) still gets every one of them.
81
+ */
33
82
  export declare function expertNameWithCredentials(content: CertContent): string;
34
83
  //# sourceMappingURL=format.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/components/format.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAC3E,OAAO,EAAE,KAAK,aAAa,EAAqC,MAAM,iBAAiB,CAAA;AAEvF,iEAAiE;AACjE,eAAO,MAAM,cAAc,YAAY,CAAA;AAEvC,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAA;IACjC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAA;IAC5B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;CACvC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAC7B,OAAO,EAAE,iBAAiB,GAAG,SAAS,EACtC,cAAc,CAAC,EAAE,MAAM,EACvB,SAAS,CAAC,EAAE,aAAa,GACvB,eAAe,CASjB;AAUD,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAMxE;AAED,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAE7D;AAED,8EAA8E;AAC9E,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAGtE"}
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/components/format.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAE3E,OAAO,EAAE,KAAK,aAAa,EAAqC,MAAM,iBAAiB,CAAA;AAEvF,iEAAiE;AACjE,eAAO,MAAM,cAAc,YAAY,CAAA;AAEvC,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAA;IACjC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAA;IAC5B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;CACvC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAC7B,OAAO,EAAE,iBAAiB,GAAG,SAAS,EACtC,cAAc,CAAC,EAAE,MAAM,EACvB,SAAS,CAAC,EAAE,aAAa,GACvB,eAAe,CASjB;AAiBD,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAMxE;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAO7D;AAqBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,GAAG,MAAM,CAY3E;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAKtE"}
@@ -2,6 +2,7 @@
2
2
  * Presentation helpers shared by the React components + Web Component. Pure, no DOM,
3
3
  * no React — so the same formatting drives JSX and the hand-built Web Component string.
4
4
  */
5
+ import { asText } from './escape.js';
5
6
  import { isFieldHidden, resolveRenderTheme } from './render-def.js';
6
7
  /** Default accent if the brand didn't pin one (CertREV teal). */
7
8
  export const DEFAULT_ACCENT = '#0f766e';
@@ -32,10 +33,17 @@ export function resolveDisplay(display, accentOverride, renderDef) {
32
33
  * We deliberately DON'T use `toLocaleDateString` — its output varies by runtime ICU data,
33
34
  * which would make SSR output non-deterministic + cause hydration mismatches. Fixed
34
35
  * English month abbreviations keep server + client byte-identical.
36
+ *
37
+ * ISO-8601 STRINGS ONLY — a non-string yields null. `Date.parse` coerces its argument, so an
38
+ * unguarded number sailed straight through as a YEAR: `formatDate(42)` returned "Jan 1, 2042"
39
+ * off a malformed envelope, while the number a caller would actually MEAN (epoch ms,
40
+ * 1750000000000) returned null. A date helper that invents a confident wrong certification
41
+ * date is worse than one that renders nothing, and the wire contract (`certifiedAt`,
42
+ * `contentModifiedAt`) is ISO-8601 strings — there is no second input format to honour.
35
43
  */
36
44
  const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
37
45
  export function formatDate(iso) {
38
- if (!iso)
46
+ if (typeof iso !== 'string' || iso === '')
39
47
  return null;
40
48
  const ms = Date.parse(iso);
41
49
  if (Number.isNaN(ms))
@@ -43,13 +51,102 @@ export function formatDate(iso) {
43
51
  const d = new Date(ms);
44
52
  return `${MONTHS[d.getUTCMonth()]} ${d.getUTCDate()}, ${d.getUTCFullYear()}`;
45
53
  }
46
- /** "PhD, RD" — the expert's credential abbreviations, comma-joined. */
54
+ /**
55
+ * "PhD, RD" — the expert's credential abbreviations, comma-joined.
56
+ *
57
+ * Shape-guarded because the envelope reaching here was only SIGNATURE-checked: a signed
58
+ * payload whose `credentials` is absent or is not an array used to throw
59
+ * `TypeError: ...map is not a function` and take the host route down with it. An entry with
60
+ * no usable abbreviation is DROPPED rather than joined as empty — the old code rendered
61
+ * "Dr. Jane Doe, , RD" off one missing field. A well-formed envelope is byte-identical.
62
+ */
47
63
  export function credentialSuffix(content) {
48
- return content.expert.credentials.map((c) => c.abbreviation).join(', ');
64
+ const credentials = content?.expert?.credentials;
65
+ if (!Array.isArray(credentials))
66
+ return '';
67
+ return credentials
68
+ .map((c) => asText(c?.abbreviation))
69
+ .filter((abbreviation) => abbreviation !== '')
70
+ .join(', ');
71
+ }
72
+ /**
73
+ * Split a comma-separated label into its trimmed, non-empty parts.
74
+ * "Dr. Erik Schraga, MD" → `['Dr. Erik Schraga', 'MD']`.
75
+ */
76
+ function commaTokens(value) {
77
+ return value
78
+ .split(',')
79
+ .map((token) => token.trim())
80
+ .filter((token) => token !== '');
81
+ }
82
+ /**
83
+ * Fold one token to the form two spellings of the SAME credential share: case-insensitive,
84
+ * periods dropped, interior whitespace collapsed. "M.D." / "MD." / "md" → `md`.
85
+ */
86
+ function credentialKey(token) {
87
+ return token.toLowerCase().replace(/\./g, '').replace(/\s+/g, ' ').trim();
49
88
  }
50
- /** "Dr. Jane Doe, PhD, RD" — display name with credential suffix appended. */
89
+ /**
90
+ * Drop from `credential` any token the display NAME already prints, so a name that arrives
91
+ * carrying its own post-nominal doesn't get it appended a second time — the defect that put
92
+ * "Dr. Erik Schraga, MD, MD, EM" on a live customer page (P2-6).
93
+ *
94
+ * THE ROOT CAUSE IS UPSTREAM DATA, NOT THIS FUNCTION. Portal cleans an expert display name
95
+ * with `cleanExpertDisplayName` and has DB CHECK constraints for exactly this shape
96
+ * (migration 20260506151204), but `resolveStampReviewerName` skips the cleaner and
97
+ * `package_drafts.expert_display_name` carries no constraint — so the duplicate is STORED,
98
+ * and a consumer reading the Delivery API directly still receives it. This is the render
99
+ * boundary refusing to print the same claim twice, nothing more.
100
+ *
101
+ * THE RULE, and why it is drawn this narrowly — a false positive here would delete a REAL
102
+ * credential from a compliance-facing card, which is worse than the duplicate it fixes:
103
+ * • a credential token is dropped only when it equals a WHOLE comma-separated segment of
104
+ * the name. Never a substring: "MD" must survive against "Dr. MDonald", "Jane Amdahl"
105
+ * and "Md. Rahman", all of which a naive `name.includes(credential)` would gut;
106
+ * • comparison is `credentialKey`'d, so "M.D." in the name cancels "MD" in the credential;
107
+ * • the credential may be a LIST ("MD, FAAD"): each token is judged on its own, and the
108
+ * survivors keep their original spelling and order. A name carrying only "MD" therefore
109
+ * still renders ", FAAD" — no verified credential is ever lost to a partial overlap;
110
+ * • nothing duplicated ⇒ the credential string is returned VERBATIM (not re-joined), so
111
+ * the overwhelmingly common case is byte-identical to the pre-guard render.
112
+ *
113
+ * What it deliberately does NOT do: it never edits the NAME. "Dr. Erik Schraga, MD" keeps its
114
+ * inline "MD" — un-embedding a post-nominal is `cleanExpertDisplayName`'s job on the write
115
+ * path, and guessing at it here would be the same false-positive class in the other direction.
116
+ * It also cannot catch a duplicate spelled differently in the two fields ("Doctor of Medicine"
117
+ * in the name against "MD" in the credential) or one embedded mid-name rather than as its own
118
+ * segment ("Erik Schraga MD" with no comma) — both stay for the upstream fix.
119
+ */
120
+ export function dedupeCredential(name, credential) {
121
+ const suffix = asText(credential);
122
+ const tokens = commaTokens(suffix);
123
+ if (tokens.length === 0)
124
+ return suffix;
125
+ // Every segment of the name, not just the trailing ones: for a well-formed name the first
126
+ // segment is a human name and can never equal a credential token, and the one case where it
127
+ // does — a stored name that is NOTHING but a credential ("MD") — is exactly the case where
128
+ // dropping the suffix is right, since the token still renders once as the name.
129
+ const carried = new Set(commaTokens(asText(name)).map(credentialKey));
130
+ const kept = tokens.filter((token) => !carried.has(credentialKey(token)));
131
+ if (kept.length === tokens.length)
132
+ return suffix;
133
+ return kept.join(', ');
134
+ }
135
+ /**
136
+ * "Dr. Jane Doe, PhD, RD" — display name with credential suffix appended.
137
+ *
138
+ * With no name there is nobody to attribute the review to, so the whole label is empty:
139
+ * credentials alone would read as "Content reviewed by PhD, RD" in `CertBadge`'s aria-label.
140
+ *
141
+ * The suffix passes `dedupeCredential` because this composes name AND credential into one
142
+ * string; `credentialSuffix` above stays the full verified list, so a caller rendering the
143
+ * credentials AWAY from the name (a chip row, a standalone line) still gets every one of them.
144
+ */
51
145
  export function expertNameWithCredentials(content) {
52
- const suffix = credentialSuffix(content);
53
- return suffix ? `${content.expert.displayName}, ${suffix}` : content.expert.displayName;
146
+ const name = asText(content?.expert?.displayName);
147
+ if (name === '')
148
+ return '';
149
+ const suffix = dedupeCredential(name, credentialSuffix(content));
150
+ return suffix ? `${name}, ${suffix}` : name;
54
151
  }
55
152
  //# sourceMappingURL=format.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/components/format.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAsB,aAAa,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAEvF,iEAAiE;AACjE,MAAM,CAAC,MAAM,cAAc,GAAG,SAAS,CAAA;AAUvC;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,cAAc,CAC7B,OAAsC,EACtC,cAAuB,EACvB,SAAyB;IAEzB,MAAM,KAAK,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAA;IAC3C,OAAO;QACN,WAAW,EAAE,cAAc,IAAI,KAAK,CAAC,MAAM,IAAI,OAAO,EAAE,WAAW,IAAI,cAAc;QACrF,eAAe,EAAE,CAAC,OAAO,EAAE,eAAe,IAAI,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,SAAS,EAAE,eAAe,CAAC;QACjG,UAAU,EAAE,OAAO,EAAE,UAAU,IAAI,IAAI;QACvC,QAAQ,EAAE,CAAC,OAAO,EAAE,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,SAAS,EAAE,MAAM,CAAC;QAC1E,UAAU,EAAE,OAAO,EAAE,UAAU,IAAI,MAAM;KACzC,CAAA;AACF,CAAC;AAED;;;;;GAKG;AACH,MAAM,MAAM,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAA;AAEnG,MAAM,UAAU,UAAU,CAAC,GAA8B;IACxD,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAA;IACrB,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC1B,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QAAE,OAAO,IAAI,CAAA;IACjC,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,EAAE,CAAC,CAAA;IACtB,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,cAAc,EAAE,EAAE,CAAA;AAC7E,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,gBAAgB,CAAC,OAAoB;IACpD,OAAO,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACxE,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,yBAAyB,CAAC,OAAoB;IAC7D,MAAM,MAAM,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAA;IACxC,OAAO,MAAM,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,CAAA;AACxF,CAAC"}
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/components/format.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * `renderPreviewBlock` — a STATIC, NON-DELIVERY preview of the cert block for pre-sales
3
- * look-demos (POR-10743, from the Builder demo).
3
+ * look-demos (lifted from the Builder demo).
4
4
  *
5
5
  * The visual is BYTE-FOR-BYTE `renderCertBlock` (pixel-parity — "see it on your own site
6
6
  * today"), wrapped in a `[data-certrev-preview]` container carrying a REQUIRED expiry.