@certrev/cert-block 0.3.0 → 0.5.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 (129) hide show
  1. package/README.md +48 -1
  2. package/dist/builder/cert-chrome-data.d.ts +129 -0
  3. package/dist/builder/cert-chrome-data.d.ts.map +1 -0
  4. package/dist/builder/cert-chrome-data.js +59 -0
  5. package/dist/builder/cert-chrome-data.js.map +1 -0
  6. package/dist/builder/cert-review-card.d.ts +68 -0
  7. package/dist/builder/cert-review-card.d.ts.map +1 -0
  8. package/dist/builder/cert-review-card.js +129 -0
  9. package/dist/builder/cert-review-card.js.map +1 -0
  10. package/dist/builder/index.d.ts +2 -0
  11. package/dist/builder/index.d.ts.map +1 -1
  12. package/dist/builder/index.js +3 -0
  13. package/dist/builder/index.js.map +1 -1
  14. package/dist/components/preview-block.d.ts +43 -0
  15. package/dist/components/preview-block.d.ts.map +1 -0
  16. package/dist/components/preview-block.js +53 -0
  17. package/dist/components/preview-block.js.map +1 -0
  18. package/dist/components/render-cert-block.d.ts +50 -7
  19. package/dist/components/render-cert-block.d.ts.map +1 -1
  20. package/dist/components/render-cert-block.js +304 -80
  21. package/dist/components/render-cert-block.js.map +1 -1
  22. package/dist/components/render-def.d.ts +27 -0
  23. package/dist/components/render-def.d.ts.map +1 -1
  24. package/dist/components/render-def.js +38 -0
  25. package/dist/components/render-def.js.map +1 -1
  26. package/dist/index.d.ts +3 -2
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +3 -1
  29. package/dist/index.js.map +1 -1
  30. package/dist/modal/avatar-resize.d.ts +60 -0
  31. package/dist/modal/avatar-resize.d.ts.map +1 -0
  32. package/dist/modal/avatar-resize.js +91 -0
  33. package/dist/modal/avatar-resize.js.map +1 -0
  34. package/dist/modal/cert-modal-view.d.ts +74 -0
  35. package/dist/modal/cert-modal-view.d.ts.map +1 -0
  36. package/dist/modal/cert-modal-view.js +160 -0
  37. package/dist/modal/cert-modal-view.js.map +1 -0
  38. package/dist/modal/certrev-cert-modal.d.ts +72 -0
  39. package/dist/modal/certrev-cert-modal.d.ts.map +1 -0
  40. package/dist/modal/certrev-cert-modal.js +270 -0
  41. package/dist/modal/certrev-cert-modal.js.map +1 -0
  42. package/dist/modal/certrev-cert-modal.styles.d.ts +35 -0
  43. package/dist/modal/certrev-cert-modal.styles.d.ts.map +1 -0
  44. package/dist/modal/certrev-cert-modal.styles.js +488 -0
  45. package/dist/modal/certrev-cert-modal.styles.js.map +1 -0
  46. package/dist/modal/certrev-cert.entry.d.ts +19 -0
  47. package/dist/modal/certrev-cert.entry.d.ts.map +1 -0
  48. package/dist/modal/certrev-cert.entry.js +39 -0
  49. package/dist/modal/certrev-cert.entry.js.map +1 -0
  50. package/dist/modal/certrev-cert.js +557 -0
  51. package/dist/modal/display-strings.d.ts +22 -0
  52. package/dist/modal/display-strings.d.ts.map +1 -0
  53. package/dist/modal/display-strings.js +35 -0
  54. package/dist/modal/display-strings.js.map +1 -0
  55. package/dist/modal/escape.d.ts +13 -0
  56. package/dist/modal/escape.d.ts.map +1 -0
  57. package/dist/modal/escape.js +20 -0
  58. package/dist/modal/escape.js.map +1 -0
  59. package/dist/modal/font-face.d.ts +4 -0
  60. package/dist/modal/font-face.d.ts.map +1 -0
  61. package/dist/modal/font-face.js +137 -0
  62. package/dist/modal/font-face.js.map +1 -0
  63. package/dist/modal/ftc-disclosure.d.ts +12 -0
  64. package/dist/modal/ftc-disclosure.d.ts.map +1 -0
  65. package/dist/modal/ftc-disclosure.js +12 -0
  66. package/dist/modal/ftc-disclosure.js.map +1 -0
  67. package/dist/modal/ftc-guard.d.ts +50 -0
  68. package/dist/modal/ftc-guard.d.ts.map +1 -0
  69. package/dist/modal/ftc-guard.js +117 -0
  70. package/dist/modal/ftc-guard.js.map +1 -0
  71. package/dist/modal/index.d.ts +20 -0
  72. package/dist/modal/index.d.ts.map +1 -0
  73. package/dist/modal/index.js +25 -0
  74. package/dist/modal/index.js.map +1 -0
  75. package/dist/modal/interactions.d.ts +53 -0
  76. package/dist/modal/interactions.d.ts.map +1 -0
  77. package/dist/modal/interactions.js +147 -0
  78. package/dist/modal/interactions.js.map +1 -0
  79. package/dist/modal/logo-paths.d.ts +15 -0
  80. package/dist/modal/logo-paths.d.ts.map +1 -0
  81. package/dist/modal/logo-paths.js +24 -0
  82. package/dist/modal/logo-paths.js.map +1 -0
  83. package/dist/modal/placement.d.ts +95 -0
  84. package/dist/modal/placement.d.ts.map +1 -0
  85. package/dist/modal/placement.js +302 -0
  86. package/dist/modal/placement.js.map +1 -0
  87. package/dist/modal/revalidate.d.ts +48 -0
  88. package/dist/modal/revalidate.d.ts.map +1 -0
  89. package/dist/modal/revalidate.js +65 -0
  90. package/dist/modal/revalidate.js.map +1 -0
  91. package/dist/modal/verify-client.d.ts +24 -0
  92. package/dist/modal/verify-client.d.ts.map +1 -0
  93. package/dist/modal/verify-client.js +38 -0
  94. package/dist/modal/verify-client.js.map +1 -0
  95. package/package.json +10 -4
  96. package/src/__tests__/__snapshots__/render-cert-block.test.ts.snap +10 -6
  97. package/src/__tests__/preview-block.test.ts +84 -0
  98. package/src/__tests__/render-cert-block.test.ts +380 -5
  99. package/src/builder/__tests__/builder.test.tsx +71 -0
  100. package/src/builder/cert-chrome-data.ts +159 -0
  101. package/src/builder/cert-review-card.tsx +177 -0
  102. package/src/builder/index.tsx +18 -0
  103. package/src/components/preview-block.ts +74 -0
  104. package/src/components/render-cert-block.ts +387 -102
  105. package/src/components/render-def.ts +55 -0
  106. package/src/index.ts +9 -0
  107. package/src/modal/avatar-resize.ts +99 -0
  108. package/src/modal/cert-modal-view.test.ts +175 -0
  109. package/src/modal/cert-modal-view.ts +210 -0
  110. package/src/modal/certrev-cert-modal.styles.ts +494 -0
  111. package/src/modal/certrev-cert-modal.test.ts +125 -0
  112. package/src/modal/certrev-cert-modal.ts +273 -0
  113. package/src/modal/certrev-cert.entry.ts +44 -0
  114. package/src/modal/display-strings.ts +38 -0
  115. package/src/modal/escape.ts +20 -0
  116. package/src/modal/font-face.ts +138 -0
  117. package/src/modal/ftc-disclosure.ts +13 -0
  118. package/src/modal/ftc-guard.test.ts +153 -0
  119. package/src/modal/ftc-guard.ts +119 -0
  120. package/src/modal/index.ts +34 -0
  121. package/src/modal/interactions.test.ts +250 -0
  122. package/src/modal/interactions.ts +166 -0
  123. package/src/modal/logo-paths.ts +23 -0
  124. package/src/modal/placement.test.ts +398 -0
  125. package/src/modal/placement.ts +331 -0
  126. package/src/modal/revalidate.test.ts +111 -0
  127. package/src/modal/revalidate.ts +81 -0
  128. package/src/modal/verify-client.test.ts +164 -0
  129. package/src/modal/verify-client.ts +42 -0
package/README.md CHANGED
@@ -112,6 +112,53 @@ Preferred mode is **SSR + hydrate** (crawlable): a server-side include emits the
112
112
  </script>
113
113
  ```
114
114
 
115
+ ## Usage — Builder.io (the cert component)
116
+
117
+ Two ways to place the visible cert on a Builder.io page. **PUSH is the primary story** for
118
+ exporter-driven brands (the cert block arrives with the CertREV-drafted entry); the PULL anchor
119
+ is the editor-placed alternative (W4, POR-10721).
120
+
121
+ ### PUSH — the registered cert block (recommended)
122
+
123
+ CertREV's exporter emits a cert block whose options are the `BuilderCertChromeData` projection.
124
+ Register the ready component once, from the `./builder` subpath — no hand-mapped options, no
125
+ hand-maintained inputs array:
126
+
127
+ ```ts
128
+ import { Builder } from '@builder.io/react'
129
+ import { BUILDER_REGISTRATION } from '@certrev/cert-block/builder'
130
+
131
+ Builder.registerComponent(BUILDER_REGISTRATION.component, BUILDER_REGISTRATION)
132
+ ```
133
+
134
+ `CERT_COMPONENT_NAME` is the canonical block name the portal's `certComponent` connection field
135
+ defaults to; `BUILDER_REGISTRATION.inputs` are single-sourced from the `BuilderCertChromeData`
136
+ wire type (drift-locked — the exporter option keys, the registered inputs, and the type can
137
+ never diverge silently). The component enforces the fused-credential rule by construction (a
138
+ credential is unrenderable without its dated verification) and themes to the brand's render-def
139
+ (accent / surface / corners / font + v2 bar-ink / body-ink).
140
+
141
+ ### The certificate modal + CSP
142
+
143
+ The "View certificate" affordance opens an envelope-verified modal. Add the modal element +
144
+ its browser bundle. *(Modal-in-package is POR-10721 W2 — until it lands, vendor the IIFE bundle
145
+ from the Shopify theme extension's `certrev-cert.js`, exactly as the canary demo does.)*
146
+
147
+ Two CSP allowances for the headless host:
148
+ - `connect-src` → the CertREV Delivery origin (the modal fetches the signed envelope).
149
+ - `img-src` → `cdn.builder.io` + the CertREV asset origin (headshots).
150
+
151
+ `data-delivery-api` is the **origin only** — the element appends
152
+ `/api/cert/v1/delivery/{platform}/{externalId}`. Preview drafts with `includeUnpublished` on the
153
+ public key.
154
+
155
+ ### PULL — the editor-placed anchor (alternative)
156
+
157
+ `CertRevProvider` + the zero-input `CertRevAnchor` (`@certrev/cert-block/builder`) let an editor
158
+ drop the cert wherever they want it (panel-validated). Use this when placement is editorial
159
+ rather than exporter-driven. *(The anchor is preset-default only today — render-def theming on
160
+ the anchor path is a follow-up.)*
161
+
115
162
  ## Fail-closed, everywhere
116
163
 
117
164
  The package never renders an unverified credential. Every error path — bad signature, unknown kid, wrong post, revoked, expired, content drift, network failure, malformed JSON, a throwing resolver — collapses to a `suppress` verdict (or a `null` render), never an exception that a caller could swallow into a render. `<CertReview>` and `<CertRevBacklink>` also fail closed at the component boundary (suppress / unsafe URL → render nothing). URLs pass through `safeHttpUrl` (drops `javascript:`/`data:`); accent colors through `safeCssColor`; the JSON-LD body neutralizes `<`/`>` so a hostile memo can't break out of the `<script>` tag.
@@ -151,4 +198,4 @@ Facts are mocked via `makeMockPayload` / `makeSignedEnvelope` (`src/contract/fix
151
198
 
152
199
  ## Version
153
200
 
154
- `0.1.0` — initial scaffold against the settled contract shape, bound to the local contract stub. Publishes **publicly** to npm as `@certrev/cert-block` (`publishConfig.access: public`).
201
+ `0.4.0` (this branch, `feat/cert-block-v2-render`) — the locked cert-render faces (`renderCertBlock`, 3 modes + custom), v2 brand-ink theming (`barInk`/`inkColor`), and the Builder.io PUSH kit (`CertReviewCard` + `BUILDER_REGISTRATION` + the single-sourced `BuilderCertChromeData` wire type, POR-10721 W1/W3). npm is at `0.3.0`; `main` trails on `0.1.3` — the version lineage + branch strategy are tracked in the repo `COORDINATION.md`. Publishes **publicly** to npm as `@certrev/cert-block` (`publishConfig.access: public`).
@@ -0,0 +1,129 @@
1
+ /**
2
+ * ─────────────────────────────────────────────────────────────────────────────
3
+ * `BuilderCertChromeData` — the CANONICAL headless cert-chrome wire type
4
+ * ─────────────────────────────────────────────────────────────────────────────
5
+ *
6
+ * The brand-render projection of the cert envelope (portal POR-10091) — the OPTIONS
7
+ * of a brand's native cert component (e.g. e.l.f.'s registered `CertReviewCard`) on
8
+ * a headless blocks-mode entry (Builder.io). CertREV serves the data + the ready-made
9
+ * strings; the brand renders it within the design-guide ladder (enforced live by the
10
+ * crawl monitor + the MSA anti-stripping teeth).
11
+ *
12
+ * SINGLE SOURCE (POR-10721 W1): this type is HOMED HERE so the portal exporter
13
+ * (`src/lib/builder/cert-chrome.ts`) and the kit component (`CertReviewCard`) +
14
+ * `BUILDER_REGISTRATION.inputs` all reference ONE definition — killing the drift the
15
+ * hand-rolled Hydrogen-replica component suffered (it carried a stale separate
16
+ * `credential`/`credentialVerifiedAt` shape from before the fused-pair projection).
17
+ * Keep byte-for-byte in sync with the portal interface until the portal migrates to
18
+ * import THIS (that migration is gated on publishing the package that carries it).
19
+ *
20
+ * PURITY: types + a pure key tuple only — no runtime deps, tree-shakeable.
21
+ */
22
+ /**
23
+ * The baked render-def projection (the Shopify `render_def` metafield shape) the
24
+ * exporter delivers so a brand's Builder face themes byte-identically to its Shopify
25
+ * face. The component maps `{ tokens, version }` onto `renderCertBlock`'s theme +
26
+ * `data-certrev-def-version` stamp. Structurally matches the portal's
27
+ * `ShopifyRenderDefMetafield` (single-sourcing that metafield type too is a follow-up).
28
+ */
29
+ export interface CertChromeRenderDef {
30
+ /** Monotonic per-brand def version → the `data-certrev-def-version` stamp. */
31
+ readonly v: number;
32
+ readonly preset: string;
33
+ readonly rung: string;
34
+ /**
35
+ * The resolved themeable tokens (the baked metafield's `tokens`) — includes the v2
36
+ * `barInk`/`inkColor`, so this is a superset of the input `CertThemeTokens` (which
37
+ * predates brand-ink theming). Inlined structurally to match what the exporter bakes.
38
+ */
39
+ readonly tokens: {
40
+ readonly accentColor?: string;
41
+ readonly surface?: string;
42
+ readonly cornerRadius?: string;
43
+ readonly fontSlot?: string;
44
+ readonly barInk?: string;
45
+ readonly inkColor?: string;
46
+ };
47
+ /** The auto-contrast bar foreground — present only on a barInk-themed face (v2). */
48
+ readonly barInkFg?: '#141414' | '#ffffff';
49
+ /** The subtractive field-visibility hide-set. */
50
+ readonly hidden: readonly string[];
51
+ /** v2 layout axis. Omitted ⇒ 'banner'. */
52
+ readonly layout?: string;
53
+ /** Custom-face composition — present only when layout === 'custom'. */
54
+ readonly customFace?: {
55
+ readonly placedFields: readonly string[];
56
+ };
57
+ }
58
+ /**
59
+ * The OPTIONS a brand's native cert component receives. Delivered per certified
60
+ * article; until the brand registers the component the block renders nothing.
61
+ */
62
+ export interface BuilderCertChromeData {
63
+ /**
64
+ * The credentialed byline — credential + dated CertREV verification as ONE
65
+ * inseparable string. Null when the R1 gate fails (a displayed credential is
66
+ * unverified / missing `verified_at`); the brand then has only `bylinePlain`.
67
+ */
68
+ bylineCredentialed: string | null;
69
+ /**
70
+ * The RAW verified credential welded to its dated verification, delivered as ONE
71
+ * INSEPARABLE PAIR (present together) or `null` (omitted together). The WHOLE
72
+ * point: the type hands the brand `credential` and `verifiedAt` only as the two
73
+ * keys of one object, so a bare credential without its dated verification is
74
+ * UNCONSTRUCTIBLE — the fused-verification invariant enforced by construction, not
75
+ * a hopeful side rule. Non-null iff `bylineCredentialed` is. The sidebar/banner
76
+ * faces map this to their `credential` + `credentialVerifiedAt` inputs.
77
+ */
78
+ credentialVerification: {
79
+ credential: string;
80
+ verifiedAt: string;
81
+ } | null;
82
+ /** The name-only byline (`Reviewed [date] by [Name].`). Always present. */
83
+ bylinePlain: string;
84
+ /** The reviewer's resolved professional name (QA-safe), raw, for a name-only layout. */
85
+ reviewerName: string;
86
+ /** Reviewer's short verified bio, or null (verified-only). */
87
+ bio: string | null;
88
+ /** Reviewer's longer verified background, or null (verified-only). */
89
+ background: string | null;
90
+ /** The QA-SCREENED `verifiedExpertMemo` (never the raw in-flight `expertMemo`), or null. */
91
+ memo: string | null;
92
+ /** The compensation cue (`Compensated expert`). Null for a pro-bono reviewer. */
93
+ compensationCue: string | null;
94
+ /** The verbatim FTC scope disclaimer. Always present. */
95
+ scopeLine: string;
96
+ /** Reviewer photo URL (cert-frozen override ?? live), or null. */
97
+ reviewerPhoto: string | null;
98
+ /** The CertREV expert-profile URL (`certrev.com/expert/{id}`) — never LinkedIn. */
99
+ reviewerProfileUrl: string | null;
100
+ /** The public certificate-verification URL (`/verify/{id}`). */
101
+ verifyUrl: string;
102
+ /** When the article was certified (ISO 8601) — the same date baked into the bylines. */
103
+ certifiedAt: string;
104
+ /** The last real content change after certification (ISO 8601), or null. */
105
+ contentModifiedAt: string | null;
106
+ /** The article's AUTHOR (a different person from the reviewer; carries no verification claim). */
107
+ authorName: string | null;
108
+ authorTitle: string | null;
109
+ /** Brand display prefs the rendering component honors. */
110
+ display: {
111
+ showExpertPhoto: boolean;
112
+ /** When false the component renders `bylinePlain` even if the credentialed form was delivered. */
113
+ showCredentials: boolean;
114
+ };
115
+ /** The brand's ACTIVE resolved render-def projection, or null (⇒ preset default). */
116
+ renderDef: CertChromeRenderDef | null;
117
+ /** Version of the delivered string set (`data-certrev-strings-version` for the crawl monitor). */
118
+ stringsVersion: string;
119
+ }
120
+ /**
121
+ * The ordered keys of `BuilderCertChromeData` — the SINGLE SOURCE the Builder
122
+ * `inputs` array and the W3 exporter⇄inputs⇄wire-type parity lock derive from, so
123
+ * the three can never silently drift again (POR-10721 W3). Adding a field to the
124
+ * interface without adding it here (or vice-versa) fails the `keyof` exhaustiveness
125
+ * check in `assertChromeKeysExhaustive` below.
126
+ */
127
+ export declare const BUILDER_CERT_CHROME_KEYS: readonly ["bylineCredentialed", "credentialVerification", "bylinePlain", "reviewerName", "bio", "background", "memo", "compensationCue", "scopeLine", "reviewerPhoto", "reviewerProfileUrl", "verifyUrl", "certifiedAt", "contentModifiedAt", "authorName", "authorTitle", "display", "renderDef", "stringsVersion"];
128
+ export type BuilderCertChromeKey = (typeof BUILDER_CERT_CHROME_KEYS)[number];
129
+ //# sourceMappingURL=cert-chrome-data.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cert-chrome-data.d.ts","sourceRoot":"","sources":["../../src/builder/cert-chrome-data.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IACnC,8EAA8E;IAC9E,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE;QAChB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;QAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;QACzB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;QAC9B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;QAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;QACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAC1B,CAAA;IACD,oFAAoF;IACpF,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,GAAG,SAAS,CAAA;IACzC,iDAAiD;IACjD,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;IAClC,0CAA0C;IAC1C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,uEAAuE;IACvE,QAAQ,CAAC,UAAU,CAAC,EAAE;QAAE,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,CAAA;CAClE;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACrC;;;;OAIG;IACH,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC;;;;;;;;OAQG;IACH,sBAAsB,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IACzE,2EAA2E;IAC3E,WAAW,EAAE,MAAM,CAAA;IACnB,wFAAwF;IACxF,YAAY,EAAE,MAAM,CAAA;IACpB,8DAA8D;IAC9D,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB,sEAAsE;IACtE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,4FAA4F;IAC5F,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,iFAAiF;IACjF,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAA;IACjB,kEAAkE;IAClE,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,mFAAmF;IACnF,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAA;IACjB,wFAAwF;IACxF,WAAW,EAAE,MAAM,CAAA;IACnB,4EAA4E;IAC5E,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,kGAAkG;IAClG,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,0DAA0D;IAC1D,OAAO,EAAE;QACR,eAAe,EAAE,OAAO,CAAA;QACxB,kGAAkG;QAClG,eAAe,EAAE,OAAO,CAAA;KACxB,CAAA;IACD,qFAAqF;IACrF,SAAS,EAAE,mBAAmB,GAAG,IAAI,CAAA;IACrC,kGAAkG;IAClG,cAAc,EAAE,MAAM,CAAA;CACtB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,sTAoB3B,CAAA;AAEV,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,wBAAwB,CAAC,CAAC,MAAM,CAAC,CAAA"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * ─────────────────────────────────────────────────────────────────────────────
3
+ * `BuilderCertChromeData` — the CANONICAL headless cert-chrome wire type
4
+ * ─────────────────────────────────────────────────────────────────────────────
5
+ *
6
+ * The brand-render projection of the cert envelope (portal POR-10091) — the OPTIONS
7
+ * of a brand's native cert component (e.g. e.l.f.'s registered `CertReviewCard`) on
8
+ * a headless blocks-mode entry (Builder.io). CertREV serves the data + the ready-made
9
+ * strings; the brand renders it within the design-guide ladder (enforced live by the
10
+ * crawl monitor + the MSA anti-stripping teeth).
11
+ *
12
+ * SINGLE SOURCE (POR-10721 W1): this type is HOMED HERE so the portal exporter
13
+ * (`src/lib/builder/cert-chrome.ts`) and the kit component (`CertReviewCard`) +
14
+ * `BUILDER_REGISTRATION.inputs` all reference ONE definition — killing the drift the
15
+ * hand-rolled Hydrogen-replica component suffered (it carried a stale separate
16
+ * `credential`/`credentialVerifiedAt` shape from before the fused-pair projection).
17
+ * Keep byte-for-byte in sync with the portal interface until the portal migrates to
18
+ * import THIS (that migration is gated on publishing the package that carries it).
19
+ *
20
+ * PURITY: types + a pure key tuple only — no runtime deps, tree-shakeable.
21
+ */
22
+ /**
23
+ * The ordered keys of `BuilderCertChromeData` — the SINGLE SOURCE the Builder
24
+ * `inputs` array and the W3 exporter⇄inputs⇄wire-type parity lock derive from, so
25
+ * the three can never silently drift again (POR-10721 W3). Adding a field to the
26
+ * interface without adding it here (or vice-versa) fails the `keyof` exhaustiveness
27
+ * check in `assertChromeKeysExhaustive` below.
28
+ */
29
+ export const BUILDER_CERT_CHROME_KEYS = [
30
+ 'bylineCredentialed',
31
+ 'credentialVerification',
32
+ 'bylinePlain',
33
+ 'reviewerName',
34
+ 'bio',
35
+ 'background',
36
+ 'memo',
37
+ 'compensationCue',
38
+ 'scopeLine',
39
+ 'reviewerPhoto',
40
+ 'reviewerProfileUrl',
41
+ 'verifyUrl',
42
+ 'certifiedAt',
43
+ 'contentModifiedAt',
44
+ 'authorName',
45
+ 'authorTitle',
46
+ 'display',
47
+ 'renderDef',
48
+ 'stringsVersion',
49
+ ];
50
+ /**
51
+ * Compile-time proof that `BUILDER_CERT_CHROME_KEYS` === `keyof BuilderCertChromeData`
52
+ * exactly (both directions). If a field is added/removed on the interface without the
53
+ * matching key edit, ONE of these two assignments stops type-checking — so the tuple
54
+ * can never drift from the type silently.
55
+ */
56
+ const _keysCoverType = Object.fromEntries(BUILDER_CERT_CHROME_KEYS.map((k) => [k, true]));
57
+ const _typeCoversKeys = _keysCoverType;
58
+ void _typeCoversKeys;
59
+ //# sourceMappingURL=cert-chrome-data.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cert-chrome-data.js","sourceRoot":"","sources":["../../src/builder/cert-chrome-data.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAiGH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACvC,oBAAoB;IACpB,wBAAwB;IACxB,aAAa;IACb,cAAc;IACd,KAAK;IACL,YAAY;IACZ,MAAM;IACN,iBAAiB;IACjB,WAAW;IACX,eAAe;IACf,oBAAoB;IACpB,WAAW;IACX,aAAa;IACb,mBAAmB;IACnB,YAAY;IACZ,aAAa;IACb,SAAS;IACT,WAAW;IACX,gBAAgB;CACP,CAAA;AAIV;;;;;GAKG;AACH,MAAM,cAAc,GAA8C,MAAM,CAAC,WAAW,CACnF,wBAAwB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CACR,CAAA;AACvC,MAAM,eAAe,GAAuC,cAAc,CAAA;AAC1E,KAAK,eAAe,CAAA"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * ─────────────────────────────────────────────────────────────────────────────
3
+ * `CertReviewCard` — the ready-to-register Builder.io PUSH cert component (W1)
4
+ * ─────────────────────────────────────────────────────────────────────────────
5
+ *
6
+ * A headless brand (e.l.f.) registers `BUILDER_REGISTRATION` in its Builder
7
+ * `customComponents` and CertREV's exporter emits a block whose options are the
8
+ * `BuilderCertChromeData` projection — so the visible cert chrome renders itself,
9
+ * design-identical to the Shopify Liquid + WordPress PHP faces (same `renderCertBlock`
10
+ * string renderer). This REPLACES the ~200-line hand-rolled component every headless
11
+ * brand copied from the demo (POR-10721 W1).
12
+ *
13
+ * Improvements over the canary demo's hand-rolled version:
14
+ * - consumes the CANONICAL `BuilderCertChromeData` (the fused `credentialVerification`
15
+ * pair — a credential is UNCONSTRUCTIBLE without its dated verification), not the
16
+ * demo's stale separate `credential`/`credentialVerifiedAt` fields;
17
+ * - maps v2 `barInk`/`inkColor` into the theme (the demo predated brand-ink theming);
18
+ * - honors `display.showCredentials`.
19
+ */
20
+ import { type CertBlockMode } from '../components/render-cert-block.js';
21
+ import type { BuilderCertChromeData } from './cert-chrome-data.js';
22
+ /**
23
+ * The canonical Builder block name. A brand's registration + the portal
24
+ * `certComponent` connection field default to THIS; config exists only for
25
+ * deviation (portal `client-for-brand.ts` gains the default in a follow-up).
26
+ */
27
+ export declare const CERT_COMPONENT_NAME = "CertREV Cert";
28
+ export interface CertReviewCardProps extends Partial<BuilderCertChromeData> {
29
+ /** Placement face — the ENTRY/editor chooses this, NOT the exporter (not a wire-type field). */
30
+ mode?: CertBlockMode | null;
31
+ /**
32
+ * Banner memo placement (not a wire-type field — the ENTRY/editor chooses it). Passed
33
+ * straight through to `renderCertBlock`'s first-class `part` (POR-10741): `'full'`
34
+ * (default) renders header + memo in one block; `'header'`/`'memo'` render only that
35
+ * card so the memo can be placed as its own block under the article body. The old
36
+ * string-splitting shim is gone — `renderCertBlock` composes each part directly.
37
+ */
38
+ part?: 'full' | 'header' | 'memo' | null;
39
+ }
40
+ export declare function CertReviewCard(props: CertReviewCardProps): import("react").JSX.Element | null;
41
+ /** The subset of the Builder.io input schema this kit emits. */
42
+ export interface BuilderInput {
43
+ readonly name: string;
44
+ readonly type: 'string' | 'longText' | 'object' | 'boolean' | 'number';
45
+ readonly friendlyName?: string;
46
+ readonly helperText?: string;
47
+ }
48
+ /**
49
+ * The wire-type inputs — DERIVED from `BUILDER_CERT_CHROME_KEYS`, so the 19-field
50
+ * option set can never drift from the type (the W3 lock asserts name-parity). This
51
+ * kills the demo's hand-maintained 17-entry array.
52
+ */
53
+ export declare const WIRE_INPUTS: readonly BuilderInput[];
54
+ /** Placement inputs — chosen by the entry/editor, NOT emitted by the exporter (not wire-type keys). */
55
+ export declare const PLACEMENT_INPUTS: readonly BuilderInput[];
56
+ /** A Builder.io custom-component registration (the general shape; the anchor uses the same). */
57
+ export interface CertBlockBuilderRegistration {
58
+ readonly name: string;
59
+ readonly component: typeof CertReviewCard;
60
+ readonly inputs: readonly BuilderInput[];
61
+ }
62
+ /**
63
+ * Ready to spread into a brand's Builder `customComponents`:
64
+ * `Builder.registerComponent(BUILDER_REGISTRATION.component, BUILDER_REGISTRATION)`.
65
+ * The exporter-emitted option keys === these `inputs` === the wire type — locked by W3.
66
+ */
67
+ export declare const BUILDER_REGISTRATION: CertBlockBuilderRegistration;
68
+ //# sourceMappingURL=cert-review-card.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cert-review-card.d.ts","sourceRoot":"","sources":["../../src/builder/cert-review-card.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,KAAK,aAAa,EAAmB,MAAM,oCAAoC,CAAA;AAExF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAGlE;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,iBAAiB,CAAA;AAEjD,MAAM,WAAW,mBAAoB,SAAQ,OAAO,CAAC,qBAAqB,CAAC;IAC1E,gGAAgG;IAChG,IAAI,CAAC,EAAE,aAAa,GAAG,IAAI,CAAA;IAC3B;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,IAAI,CAAA;CACxC;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,sCAyDxD;AAMD,gEAAgE;AAChE,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAA;IACtE,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAC5B;AAyBD;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,SAAS,YAAY,EAG5C,CAAA;AAEH,uGAAuG;AACvG,eAAO,MAAM,gBAAgB,EAAE,SAAS,YAAY,EAQnD,CAAA;AAED,gGAAgG;AAChG,MAAM,WAAW,4BAA4B;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,SAAS,EAAE,OAAO,cAAc,CAAA;IACzC,QAAQ,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,CAAA;CACxC;AAED;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,EAAE,4BAIlC,CAAA"}
@@ -0,0 +1,129 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * ─────────────────────────────────────────────────────────────────────────────
4
+ * `CertReviewCard` — the ready-to-register Builder.io PUSH cert component (W1)
5
+ * ─────────────────────────────────────────────────────────────────────────────
6
+ *
7
+ * A headless brand (e.l.f.) registers `BUILDER_REGISTRATION` in its Builder
8
+ * `customComponents` and CertREV's exporter emits a block whose options are the
9
+ * `BuilderCertChromeData` projection — so the visible cert chrome renders itself,
10
+ * design-identical to the Shopify Liquid + WordPress PHP faces (same `renderCertBlock`
11
+ * string renderer). This REPLACES the ~200-line hand-rolled component every headless
12
+ * brand copied from the demo (POR-10721 W1).
13
+ *
14
+ * Improvements over the canary demo's hand-rolled version:
15
+ * - consumes the CANONICAL `BuilderCertChromeData` (the fused `credentialVerification`
16
+ * pair — a credential is UNCONSTRUCTIBLE without its dated verification), not the
17
+ * demo's stale separate `credential`/`credentialVerifiedAt` fields;
18
+ * - maps v2 `barInk`/`inkColor` into the theme (the demo predated brand-ink theming);
19
+ * - honors `display.showCredentials`.
20
+ */
21
+ import { renderCertBlock } from '../components/render-cert-block.js';
22
+ import { BUILDER_CERT_CHROME_KEYS } from './cert-chrome-data.js';
23
+ /**
24
+ * The canonical Builder block name. A brand's registration + the portal
25
+ * `certComponent` connection field default to THIS; config exists only for
26
+ * deviation (portal `client-for-brand.ts` gains the default in a follow-up).
27
+ */
28
+ export const CERT_COMPONENT_NAME = 'CertREV Cert';
29
+ export function CertReviewCard(props) {
30
+ const { reviewerName, verifyUrl } = props;
31
+ if (!reviewerName || !verifyUrl)
32
+ return null; // no chrome pre-cert
33
+ // The credential + its verification date arrive ONLY as the fused pair, so we can
34
+ // never obtain one without the other (name-only face when the pair is null, or when
35
+ // the brand set showCredentials:false). This IS the R1 gate, enforced by the type.
36
+ const cv = props.credentialVerification ?? null;
37
+ const showCred = props.display?.showCredentials !== false;
38
+ const withCred = cv && showCred ? cv : null;
39
+ const tokens = props.renderDef?.tokens;
40
+ const theme = tokens
41
+ ? {
42
+ accentColor: tokens.accentColor,
43
+ surface: tokens.surface,
44
+ cornerRadius: tokens.cornerRadius,
45
+ fontSlot: tokens.fontSlot,
46
+ // v2 brand-ink (post-demo): the bar + free-content ink follow the def.
47
+ barInk: tokens.barInk,
48
+ inkColor: tokens.inkColor,
49
+ }
50
+ : undefined;
51
+ const html = renderCertBlock({
52
+ mode: props.mode ?? 'banner',
53
+ theme,
54
+ // First-class banner memo split (POR-10741) — renderCertBlock composes the requested
55
+ // part directly; no string-splitting. Ignored outside banner mode.
56
+ ...(props.part ? { part: props.part } : {}),
57
+ facts: {
58
+ authorName: props.authorName ?? 'Editorial',
59
+ authorTitle: props.authorTitle ?? undefined,
60
+ reviewerName,
61
+ credential: withCred ? withCred.credential : '',
62
+ credentialVerifiedAt: withCred ? withCred.verifiedAt : '',
63
+ certifiedAt: props.certifiedAt ?? '',
64
+ memo: props.memo ?? '',
65
+ bio: props.bio ?? undefined,
66
+ profileUrl: props.reviewerProfileUrl ?? '',
67
+ certificateUrl: verifyUrl,
68
+ // Locked strings default byte-verbatim inside renderCertBlock; pass the
69
+ // delivered cue/scope through so a pro-bono (null-cue) delivery is honored.
70
+ ...(props.compensationCue ? { compensationCue: props.compensationCue } : {}),
71
+ ...(props.scopeLine ? { scopeLine: props.scopeLine } : {}),
72
+ },
73
+ });
74
+ return (_jsx("div", { "data-certrev-cert-chrome": "", "data-certrev-strings-version": props.stringsVersion ?? undefined, "data-certrev-def-version": props.renderDef?.v ?? undefined,
75
+ // biome-ignore lint/security/noDangerouslySetInnerHtml: renderCertBlock is the escaping-safe SSR string renderer
76
+ dangerouslySetInnerHTML: { __html: html } }));
77
+ }
78
+ /** Builder input TYPE per wire-type key — the exporter serializes these option values. */
79
+ const WIRE_INPUT_TYPE = {
80
+ bylineCredentialed: 'string',
81
+ credentialVerification: 'object',
82
+ bylinePlain: 'string',
83
+ reviewerName: 'string',
84
+ bio: 'longText',
85
+ background: 'longText',
86
+ memo: 'longText',
87
+ compensationCue: 'string',
88
+ scopeLine: 'string',
89
+ reviewerPhoto: 'string',
90
+ reviewerProfileUrl: 'string',
91
+ verifyUrl: 'string',
92
+ certifiedAt: 'string',
93
+ contentModifiedAt: 'string',
94
+ authorName: 'string',
95
+ authorTitle: 'string',
96
+ display: 'object',
97
+ renderDef: 'object',
98
+ stringsVersion: 'string',
99
+ };
100
+ /**
101
+ * The wire-type inputs — DERIVED from `BUILDER_CERT_CHROME_KEYS`, so the 19-field
102
+ * option set can never drift from the type (the W3 lock asserts name-parity). This
103
+ * kills the demo's hand-maintained 17-entry array.
104
+ */
105
+ export const WIRE_INPUTS = BUILDER_CERT_CHROME_KEYS.map((name) => ({
106
+ name,
107
+ type: WIRE_INPUT_TYPE[name],
108
+ }));
109
+ /** Placement inputs — chosen by the entry/editor, NOT emitted by the exporter (not wire-type keys). */
110
+ export const PLACEMENT_INPUTS = [
111
+ {
112
+ name: 'mode',
113
+ type: 'string',
114
+ friendlyName: 'Placement',
115
+ helperText: 'banner (default) · sidebar · floating',
116
+ },
117
+ { name: 'part', type: 'string', friendlyName: 'Banner part', helperText: 'full (default) · header · memo' },
118
+ ];
119
+ /**
120
+ * Ready to spread into a brand's Builder `customComponents`:
121
+ * `Builder.registerComponent(BUILDER_REGISTRATION.component, BUILDER_REGISTRATION)`.
122
+ * The exporter-emitted option keys === these `inputs` === the wire type — locked by W3.
123
+ */
124
+ export const BUILDER_REGISTRATION = {
125
+ name: CERT_COMPONENT_NAME,
126
+ component: CertReviewCard,
127
+ inputs: [...PLACEMENT_INPUTS, ...WIRE_INPUTS],
128
+ };
129
+ //# sourceMappingURL=cert-review-card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cert-review-card.js","sourceRoot":"","sources":["../../src/builder/cert-review-card.tsx"],"names":[],"mappings":"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"}
@@ -72,4 +72,6 @@ export interface CertRevBuilderRegistration {
72
72
  * (truth). Matched by NAME at render, so Write-API content works without visual-editor setup.
73
73
  */
74
74
  export declare const certRevAnchorComponent: CertRevBuilderRegistration;
75
+ export { BUILDER_CERT_CHROME_KEYS, type BuilderCertChromeData, type BuilderCertChromeKey, type CertChromeRenderDef, } from './cert-chrome-data.js';
76
+ export { BUILDER_REGISTRATION, type BuilderInput, CERT_COMPONENT_NAME, type CertBlockBuilderRegistration, CertReviewCard, type CertReviewCardProps, PLACEMENT_INPUTS, WIRE_INPUTS, } from './cert-review-card.js';
75
77
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/builder/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAiB,KAAK,SAAS,EAAc,MAAM,OAAO,CAAA;AAEjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAExD,2FAA2F;AAC3F,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,sGAAsG;IACtG,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CACzB;AASD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,EAC/B,OAAO,EACP,SAAiB,EACjB,QAAQ,GACR,EAAE;IACF,QAAQ,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAAA;IAC1C,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;IAC5B,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAA;CAC5B,+BAEA;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,gCAiBvC;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,uCAO5B;AAED;;;;GAIG;AACH,MAAM,WAAW,0BAA0B;IAC1C,SAAS,EAAE,OAAO,aAAa,CAAA;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,KAAK,EAAE,CAAA;CACf;AAED;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,0BAIpC,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/builder/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAiB,KAAK,SAAS,EAAc,MAAM,OAAO,CAAA;AAEjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAExD,2FAA2F;AAC3F,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,sGAAsG;IACtG,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CACzB;AASD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,EAC/B,OAAO,EACP,SAAiB,EACjB,QAAQ,GACR,EAAE;IACF,QAAQ,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAAA;IAC1C,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;IAC5B,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAA;CAC5B,+BAEA;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,gCAiBvC;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,uCAO5B;AAED;;;;GAIG;AACH,MAAM,WAAW,0BAA0B;IAC1C,SAAS,EAAE,OAAO,aAAa,CAAA;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,KAAK,EAAE,CAAA;CACf;AAED;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,0BAIpC,CAAA;AAGD,OAAO,EACN,wBAAwB,EACxB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,GACxB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACN,oBAAoB,EACpB,KAAK,YAAY,EACjB,mBAAmB,EACnB,KAAK,4BAA4B,EACjC,cAAc,EACd,KAAK,mBAAmB,EACxB,gBAAgB,EAChB,WAAW,GACX,MAAM,uBAAuB,CAAA"}
@@ -77,4 +77,7 @@ export const certRevAnchorComponent = {
77
77
  name: 'CertREV Review',
78
78
  inputs: [],
79
79
  };
80
+ // ── POR-10721 W1/W3: the Builder.io PUSH kit (CertReviewCard) + single-sourced wire type ──
81
+ export { BUILDER_CERT_CHROME_KEYS, } from './cert-chrome-data.js';
82
+ export { BUILDER_REGISTRATION, CERT_COMPONENT_NAME, CertReviewCard, PLACEMENT_INPUTS, WIRE_INPUTS, } from './cert-review-card.js';
80
83
  //# sourceMappingURL=index.js.map
@@ -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"}
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,cAAc,EAEd,gBAAgB,EAChB,WAAW,GACX,MAAM,uBAAuB,CAAA"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * `renderPreviewBlock` — a STATIC, NON-DELIVERY preview of the cert block for pre-sales
3
+ * look-demos (POR-10743, from the e.l.f. Builder demo).
4
+ *
5
+ * The visual is BYTE-FOR-BYTE `renderCertBlock` (pixel-parity — "see it on your own site
6
+ * today"), wrapped in a `[data-certrev-preview]` container carrying a REQUIRED expiry.
7
+ *
8
+ * Why it is safe by construction — and can NEVER be delivery:
9
+ * - A preview has NO signed envelope and NO modal. CertREV's verify / badge-revalidation /
10
+ * crawl-audit all key off a signed `CertDeliveryEnvelope`; an unsigned block is SUPPRESSED
11
+ * on any live verify, never rendered as a certification. So a preview literally cannot pass
12
+ * as a delivered cert — the "never delivery" property is structural, not a policy.
13
+ * - The container marker (`data-certrev-preview="1"`) + the default-on visible label make it
14
+ * unmistakable to humans AND tooling; the required `data-certrev-preview-expires` bounds its
15
+ * life so a stale snapshot can be refused downstream.
16
+ * - It is NOT revocable — the kill-switch cannot reach a static HTML snapshot — so it must stay
17
+ * demo / draft-only. That is the CALLER's duty; this renderer bakes the markers that let the
18
+ * surrounding systems enforce it.
19
+ */
20
+ import { type RenderCertBlockInput } from './render-cert-block.js';
21
+ export interface PreviewBlockOptions {
22
+ /**
23
+ * REQUIRED — when this preview expires (ISO 8601). A preview MUST be time-bounded so a stale
24
+ * snapshot can be refused downstream; there is deliberately NO default (an unbounded preview
25
+ * is the drift-into-shadow-delivery risk this option exists to prevent).
26
+ */
27
+ readonly expiresAt: string;
28
+ /**
29
+ * Show the visible caption. Default `true` — the SAFE default. Opt out (`false`) ONLY for a
30
+ * true pixel-parity look-demo; the machine marker + expiry + non-delivery guarantee remain.
31
+ */
32
+ readonly showLabel?: boolean;
33
+ /** Override the caption text (default below). */
34
+ readonly labelText?: string;
35
+ }
36
+ /** Extract a preview block's baked expiry (ISO), or null if the HTML isn't a preview block. */
37
+ export declare function previewExpiry(html: string): string | null;
38
+ /**
39
+ * Render a static, clearly-labeled, time-bounded preview of the cert block. See the module doc
40
+ * for the safety model. `expiresAt` is required; throws without it.
41
+ */
42
+ export declare function renderPreviewBlock(input: RenderCertBlockInput, options: PreviewBlockOptions): string;
43
+ //# sourceMappingURL=preview-block.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-block.d.ts","sourceRoot":"","sources":["../../src/components/preview-block.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,EAAE,KAAK,oBAAoB,EAAmB,MAAM,wBAAwB,CAAA;AAEnF,MAAM,WAAW,mBAAmB;IACnC;;;;OAIG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;IAC5B,iDAAiD;IACjD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAC3B;AAID,+FAA+F;AAC/F,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGzD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,oBAAoB,EAAE,OAAO,EAAE,mBAAmB,GAAG,MAAM,CAsBpG"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * `renderPreviewBlock` — a STATIC, NON-DELIVERY preview of the cert block for pre-sales
3
+ * look-demos (POR-10743, from the e.l.f. Builder demo).
4
+ *
5
+ * The visual is BYTE-FOR-BYTE `renderCertBlock` (pixel-parity — "see it on your own site
6
+ * today"), wrapped in a `[data-certrev-preview]` container carrying a REQUIRED expiry.
7
+ *
8
+ * Why it is safe by construction — and can NEVER be delivery:
9
+ * - A preview has NO signed envelope and NO modal. CertREV's verify / badge-revalidation /
10
+ * crawl-audit all key off a signed `CertDeliveryEnvelope`; an unsigned block is SUPPRESSED
11
+ * on any live verify, never rendered as a certification. So a preview literally cannot pass
12
+ * as a delivered cert — the "never delivery" property is structural, not a policy.
13
+ * - The container marker (`data-certrev-preview="1"`) + the default-on visible label make it
14
+ * unmistakable to humans AND tooling; the required `data-certrev-preview-expires` bounds its
15
+ * life so a stale snapshot can be refused downstream.
16
+ * - It is NOT revocable — the kill-switch cannot reach a static HTML snapshot — so it must stay
17
+ * demo / draft-only. That is the CALLER's duty; this renderer bakes the markers that let the
18
+ * surrounding systems enforce it.
19
+ */
20
+ import { escapeAttribute, escapeHtml } from './escape.js';
21
+ import { renderCertBlock } from './render-cert-block.js';
22
+ const DEFAULT_LABEL = 'Preview — not a verified certification';
23
+ /** Extract a preview block's baked expiry (ISO), or null if the HTML isn't a preview block. */
24
+ export function previewExpiry(html) {
25
+ const m = html.match(/data-certrev-preview-expires="([^"]*)"/);
26
+ return m ? m[1] : null;
27
+ }
28
+ /**
29
+ * Render a static, clearly-labeled, time-bounded preview of the cert block. See the module doc
30
+ * for the safety model. `expiresAt` is required; throws without it.
31
+ */
32
+ export function renderPreviewBlock(input, options) {
33
+ const expires = options.expiresAt.trim();
34
+ if (!expires) {
35
+ throw new Error('renderPreviewBlock: `expiresAt` is required — a preview must be time-bounded.');
36
+ }
37
+ // The bound must be REAL — an unparseable expiry is a toothless bound (downstream can't refuse a
38
+ // stale preview it can't date). Reject anything Date can't parse, so `previewExpiry()` consumers
39
+ // always get a usable timestamp.
40
+ if (Number.isNaN(new Date(expires).getTime())) {
41
+ throw new Error(`renderPreviewBlock: \`expiresAt\` must be a parseable date (got "${expires}").`);
42
+ }
43
+ const showLabel = options.showLabel !== false;
44
+ const label = showLabel
45
+ ? `<div data-certrev-preview-label style="font:500 11px/1.4 ui-monospace,SFMono-Regular,Menlo,monospace;letter-spacing:.08em;text-transform:uppercase;color:#9a9a9a;margin-bottom:8px;">${escapeHtml(options.labelText ?? DEFAULT_LABEL)}</div>`
46
+ : '';
47
+ return (`<!-- CertREV PREVIEW — static, non-revocable, NOT a verified certification. Expires ${escapeHtml(expires)}. Never use as delivery. -->` +
48
+ `<div data-certrev-preview="1" data-certrev-preview-expires="${escapeAttribute(expires)}">` +
49
+ label +
50
+ renderCertBlock(input) +
51
+ `</div>`);
52
+ }
53
+ //# sourceMappingURL=preview-block.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-block.js","sourceRoot":"","sources":["../../src/components/preview-block.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACzD,OAAO,EAA6B,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAkBnF,MAAM,aAAa,GAAG,wCAAwC,CAAA;AAE9D,+FAA+F;AAC/F,MAAM,UAAU,aAAa,CAAC,IAAY;IACzC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAA;IAC9D,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;AACvB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAA2B,EAAE,OAA4B;IAC3F,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,CAAA;IACxC,IAAI,CAAC,OAAO,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,+EAA+E,CAAC,CAAA;IACjG,CAAC;IACD,iGAAiG;IACjG,iGAAiG;IACjG,iCAAiC;IACjC,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,oEAAoE,OAAO,KAAK,CAAC,CAAA;IAClG,CAAC;IACD,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,KAAK,KAAK,CAAA;IAC7C,MAAM,KAAK,GAAG,SAAS;QACtB,CAAC,CAAC,wLAAwL,UAAU,CAAC,OAAO,CAAC,SAAS,IAAI,aAAa,CAAC,QAAQ;QAChP,CAAC,CAAC,EAAE,CAAA;IACL,OAAO,CACN,uFAAuF,UAAU,CAAC,OAAO,CAAC,8BAA8B;QACxI,+DAA+D,eAAe,CAAC,OAAO,CAAC,IAAI;QAC3F,KAAK;QACL,eAAe,CAAC,KAAK,CAAC;QACtB,QAAQ,CACR,CAAA;AACF,CAAC"}